diff options
295 files changed, 65877 insertions, 9397 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..6b89e1e6b2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*configure* +*autom4te.cache* +aclocal.m4 +Makefile.in +install-sh +missing +config.guess +bspopts.h.in +config.sub +INSTALL +depcomp +mdate-sh +texinfo.tex +config.h.in +compile diff --git a/aclocal/enable-drvmgr.m4 b/aclocal/enable-drvmgr.m4 new file mode 100644 index 0000000000..489f60e75f --- /dev/null +++ b/aclocal/enable-drvmgr.m4 @@ -0,0 +1,12 @@ +AC_DEFUN([RTEMS_ENABLE_DRVMGR], +[ +## AC_BEFORE([$0], [RTEMS_CHECK_DRVMGR_STARTUP])dnl + +AC_ARG_ENABLE(drvmgr, +[AS_HELP_STRING([--enable-drvmgr],[enable Driver Manager at Startup])], +[case "${enableval}" in + yes) RTEMS_DRVMGR_STARTUP=yes ;; + no) RTEMS_DRVMGR_STARTUP=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for enable-drvmgr option) ;; +esac],[RTEMS_DRVMGR_STARTUP=yes]) +]) @@ -18,6 +18,7 @@ top_srcdir=`dirname $0` verbose=""; quiet="false" mode="generate" +root=. usage() { @@ -89,6 +90,15 @@ case $1 in -r|--re|--rec|--reco|--recon|--reconf) mode="autoreconf"; shift;; +-s|--sp|--spa|--spar|--sparc) + root=./c/src/lib/libbsp/sparc + shift;; +--cpukit) + root=./cpukit + shift;; +--max) + maxdepth="--maxdepth 1" + shift;; -*) echo "unknown option $1" ; usage ;; *) echo "invalid parameter $1" ; @@ -98,7 +108,7 @@ done case $mode in preinstall) - confs=`find . -name Makefile.am -exec grep -l 'include .*/preinstall\.am' {} \;` + confs=`find $root -name Makefile.am -exec grep -l 'include .*/preinstall\.am' {} \;` for i in $confs; do dir=$(dirname $i); test "$quite" = "true" || echo "Generating $dir/preinstall.am" @@ -138,7 +148,7 @@ generate) ;; esac - confs=`find . \( -name 'configure.in' -o -name 'configure.ac' \) -print` + confs=`find $root \( -name 'configure.in' -o -name 'configure.ac' \) -print` for i in $confs; do dir=`dirname $i`; configure=`basename $i`; @@ -170,7 +180,7 @@ autoreconf) exit 1 fi - confs=`find . -name 'configure.ac' -print` + confs=`find . -name 'configure.ac' -print $maxdepth` for i in $confs; do dir=`dirname $i`; configure=`basename $i`; diff --git a/c/src/aclocal/enable-drvmgr.m4 b/c/src/aclocal/enable-drvmgr.m4 new file mode 100644 index 0000000000..489f60e75f --- /dev/null +++ b/c/src/aclocal/enable-drvmgr.m4 @@ -0,0 +1,12 @@ +AC_DEFUN([RTEMS_ENABLE_DRVMGR], +[ +## AC_BEFORE([$0], [RTEMS_CHECK_DRVMGR_STARTUP])dnl + +AC_ARG_ENABLE(drvmgr, +[AS_HELP_STRING([--enable-drvmgr],[enable Driver Manager at Startup])], +[case "${enableval}" in + yes) RTEMS_DRVMGR_STARTUP=yes ;; + no) RTEMS_DRVMGR_STARTUP=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for enable-drvmgr option) ;; +esac],[RTEMS_DRVMGR_STARTUP=yes]) +]) diff --git a/c/src/lib/libbsp/shared/include/irq-generic.h b/c/src/lib/libbsp/shared/include/irq-generic.h index 181bc58e2c..b69cbc0cb7 100644 --- a/c/src/lib/libbsp/shared/include/irq-generic.h +++ b/c/src/lib/libbsp/shared/include/irq-generic.h @@ -130,6 +130,7 @@ static inline rtems_vector_number bsp_interrupt_handler_index( * @{ */ +#if !defined(BSP_INTERRUPT_CUSTOM_VALID_VECTOR) /** * @brief Returns true if the interrupt vector with number @a vector is valid. */ @@ -138,6 +139,7 @@ static inline bool bsp_interrupt_is_valid_vector(rtems_vector_number vector) return (rtems_vector_number) BSP_INTERRUPT_VECTOR_MIN <= vector && vector <= (rtems_vector_number) BSP_INTERRUPT_VECTOR_MAX; } +#endif /** * @brief Default interrupt handler. diff --git a/c/src/lib/libbsp/shared/include/irq.h b/c/src/lib/libbsp/shared/include/irq.h index ae57bd1c54..19aed6fd40 100644 --- a/c/src/lib/libbsp/shared/include/irq.h +++ b/c/src/lib/libbsp/shared/include/irq.h @@ -72,6 +72,14 @@ typedef uint8_t bsp_interrupt_handler_index_type; #endif +/** + * @brief Enable custom vector checking + * + * If defined the BSP must implement the custom bsp_interrupt_is_valid_vector() + * vector validator check routine. + */ +#undef BSP_INTERRUPT_CUSTOM_VALID_VECTOR + /** @} */ #endif /* LIBBSP_SHARED_IRQ_CONFIG_H */ diff --git a/c/src/lib/libbsp/sparc/Makefile.am b/c/src/lib/libbsp/sparc/Makefile.am index 8081d61609..03360913cc 100644 --- a/c/src/lib/libbsp/sparc/Makefile.am +++ b/c/src/lib/libbsp/sparc/Makefile.am @@ -14,54 +14,168 @@ EXTRA_DIST += shared/bspstart.c EXTRA_DIST += shared/gnatcommon.c EXTRA_DIST += shared/start.S +# Interrupt +EXTRA_DIST += shared/irq/irq-shared.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 +EXTRA_DIST += shared/amba/ambapp_depth.c +EXTRA_DIST += shared/amba/ambapp_find_by_idx.c +EXTRA_DIST += shared/amba/ambapp_freq.c +EXTRA_DIST += shared/amba/ambapp_parent.c +EXTRA_DIST += shared/amba/ambapp_names.c +EXTRA_DIST += shared/amba/ambapp_old.c +EXTRA_DIST += shared/amba/ambapp_show.c + +# Clock Driver and Timer Library +EXTRA_DIST += shared/include/tlib.h +EXTRA_DIST += shared/timer/gptimer.c +EXTRA_DIST += shared/timer/tlib.c +EXTRA_DIST += shared/timer/tlib_ckinit.c # PCI bus -EXTRA_DIST += shared/include/pci.h -EXTRA_DIST += shared/pci/pcifinddevice.c +EXTRA_DIST += shared/include/grpci2.h +EXTRA_DIST += shared/pci/grpci.c +EXTRA_DIST += shared/pci/grpci2.c +EXTRA_DIST += shared/pci/pcif.c +EXTRA_DIST += shared/pci/pci_memreg_sparc_le.c +EXTRA_DIST += shared/pci/pci_memreg_sparc_be.c + +# PCI target boards +EXTRA_DIST += shared/include/gr_701.h +EXTRA_DIST += shared/include/gr_rasta_adcdac.h +EXTRA_DIST += shared/include/gr_rasta_io.h +EXTRA_DIST += shared/include/gr_rasta_tmtc.h +EXTRA_DIST += shared/include/gr_tmtc_1553.h +EXTRA_DIST += shared/pci/gr_701.c +EXTRA_DIST += shared/pci/gr_rasta_adcdac.c +EXTRA_DIST += shared/pci/gr_rasta_io.c +EXTRA_DIST += shared/pci/gr_rasta_spw_router.c +EXTRA_DIST += shared/pci/gr_rasta_tmtc.c +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_pci.c -EXTRA_DIST += shared/spw/grspw_rasta.c +EXTRA_DIST += shared/spw/grspw_router.c +EXTRA_DIST += shared/spw/rmap.c +EXTRA_DIST += shared/spw/rmap_drv_grspw.c EXTRA_DIST += shared/include/grspw.h -EXTRA_DIST += shared/include/grspw_pci.h -EXTRA_DIST += shared/include/grspw_rasta.h +EXTRA_DIST += shared/include/grspw_router.h +EXTRA_DIST += shared/include/rmap.h +EXTRA_DIST += shared/include/rmap_drv_grspw.h -# UART (APBUART) +# UART +EXTRA_DIST += shared/uart/cons.c +EXTRA_DIST += shared/uart/apbuart_cons.c +EXTRA_DIST += shared/include/cons.h EXTRA_DIST += shared/uart/apbuart.c -EXTRA_DIST += shared/uart/apbuart_pci.c -EXTRA_DIST += shared/uart/apbuart_rasta.c EXTRA_DIST += shared/include/apbuart.h -EXTRA_DIST += shared/include/apbuart_pci.h -EXTRA_DIST += shared/include/apbuart_rasta.h # CAN (OC_CAN, GRCAN) EXTRA_DIST += shared/can/occan.c -EXTRA_DIST += shared/can/occan_pci.c EXTRA_DIST += shared/can/grcan.c -EXTRA_DIST += shared/can/grcan_rasta.c EXTRA_DIST += shared/include/occan.h -EXTRA_DIST += shared/include/occan_pci.h EXTRA_DIST += shared/include/grcan.h -EXTRA_DIST += shared/include/grcan_rasta.h + +# MEM +EXTRA_DIST += shared/mem/mctrl.c +EXTRA_DIST += shared/mem/mctrl_rmap.c # MIL-STD-B1553 (Core1553BRM) EXTRA_DIST += shared/1553/b1553brm.c -EXTRA_DIST += shared/1553/b1553brm_pci.c -EXTRA_DIST += shared/1553/b1553brm_rasta.c +EXTRA_DIST += shared/1553/b1553rt.c EXTRA_DIST += shared/include/b1553brm.h -EXTRA_DIST += shared/include/b1553brm_pci.h -EXTRA_DIST += shared/include/b1553brm_rasta.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 + +# Driver Manager +EXTRA_DIST += shared/drvmgr/ambapp_bus.c +EXTRA_DIST += shared/drvmgr/ambapp_bus_grlib.c +EXTRA_DIST += shared/drvmgr/ambapp_bus_leon2.c +EXTRA_DIST += shared/drvmgr/ambapp_bus_rmap.c +EXTRA_DIST += shared/drvmgr/leon2_amba_bus.c +EXTRA_DIST += shared/drvmgr/spw_bus.c + +EXTRA_DIST += shared/include/drvmgr/ambapp_bus_grlib.h +EXTRA_DIST += shared/include/drvmgr/ambapp_bus_rmap.h +EXTRA_DIST += shared/include/drvmgr/ambapp_bus.h +EXTRA_DIST += shared/include/drvmgr/leon2_amba_bus.h +EXTRA_DIST += shared/include/drvmgr/spw_bus.h +EXTRA_DIST += shared/include/drvmgr/spw_bus_ids.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/tmtc/grtc_rmap.c +EXTRA_DIST += shared/include/grtc.h +EXTRA_DIST += shared/tmtc/grtm.c +EXTRA_DIST += shared/tmtc/grtm_rmap.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/erc32/Makefile.am b/c/src/lib/libbsp/sparc/erc32/Makefile.am index dd8bed26e0..fe36df054e 100644 --- a/c/src/lib/libbsp/sparc/erc32/Makefile.am +++ b/c/src/lib/libbsp/sparc/erc32/Makefile.am @@ -39,7 +39,8 @@ libbsp_a_SOURCES += ../../shared/bspclean.c ../../shared/bsplibc.c \ ../../sparc/shared/bsppretaskinghook.c ../../shared/bsppost.c \ ../../shared/bspstart.c ../../shared/bootcard.c \ ../../shared/sbrk.c startup/setvec.c startup/spurious.c \ - startup/erc32mec.c startup/boardinit.S startup/bspidle.c + startup/erc32mec.c startup/boardinit.S startup/bspidle.c \ + ../../sparc/shared/startup/early_malloc.c # gnatsupp libbsp_a_SOURCES += gnatsupp/gnatsupp.c ../../sparc/shared/gnatcommon.c # console diff --git a/c/src/lib/libbsp/sparc/erc32/include/bsp.h b/c/src/lib/libbsp/sparc/erc32/include/bsp.h index 8655732e05..0ec160ae52 100644 --- a/c/src/lib/libbsp/sparc/erc32/include/bsp.h +++ b/c/src/lib/libbsp/sparc/erc32/include/bsp.h @@ -71,6 +71,10 @@ extern int end; /* last address in the program */ /* functions */ +/* set_vec type */ +#define SET_VECTOR_RAW 0 /* Raw trap handler */ +#define SET_VECTOR_INT 1 /* Trap handler with _ISR_Handler interrupt handler */ + rtems_isr_entry set_vector( /* returns old vector */ rtems_isr_entry handler, /* isr routine */ rtems_vector_number vector, /* vector number */ @@ -81,6 +85,9 @@ void BSP_fatal_return( void ); void bsp_spurious_initialize( void ); +/* Allocate 8-byte aligned non-freeable pre-malloc memory */ +void *bsp_early_malloc(int size); + #ifdef __cplusplus } #endif diff --git a/c/src/lib/libbsp/sparc/erc32/startup/setvec.c b/c/src/lib/libbsp/sparc/erc32/startup/setvec.c index 3a7601d012..7e697a07cb 100644 --- a/c/src/lib/libbsp/sparc/erc32/startup/setvec.c +++ b/c/src/lib/libbsp/sparc/erc32/startup/setvec.c @@ -42,7 +42,7 @@ rtems_isr_entry set_vector( /* returns old vector */ uint32_t real_trap; uint32_t source; - if ( type ) + if ( type == SET_VECTOR_INT ) rtems_interrupt_catch( handler, vector, &previous_isr ); else _CPU_ISR_install_raw_handler( vector, handler, (void *)&previous_isr ); diff --git a/c/src/lib/libbsp/sparc/leon2/Makefile.am b/c/src/lib/libbsp/sparc/leon2/Makefile.am index c751c60854..d5e80a49bc 100644 --- a/c/src/lib/libbsp/sparc/leon2/Makefile.am +++ b/c/src/lib/libbsp/sparc/leon2/Makefile.am @@ -12,24 +12,7 @@ dist_project_lib_DATA = bsp_specs include_HEADERS = include/bsp.h include_HEADERS += include/tm27.h -include_HEADERS += include/rasta.h -include_HEADERS += include/cchip.h -include_HEADERS += ../../sparc/shared/include/ambapp.h -include_HEADERS += ../../sparc/shared/include/grspw.h -include_HEADERS += ../../sparc/shared/include/grspw_pci.h -include_HEADERS += ../../sparc/shared/include/grspw_rasta.h -include_HEADERS += ../../sparc/shared/include/occan.h -include_HEADERS += ../../sparc/shared/include/occan_pci.h -include_HEADERS += ../../sparc/shared/include/grcan.h -include_HEADERS += ../../sparc/shared/include/grcan_rasta.h -include_HEADERS += ../../sparc/shared/include/apbuart.h -include_HEADERS += ../../sparc/shared/include/apbuart_pci.h -include_HEADERS += ../../sparc/shared/include/apbuart_rasta.h -include_HEADERS += ../../sparc/shared/include/b1553brm.h -include_HEADERS += ../../sparc/shared/include/b1553brm_pci.h -include_HEADERS += ../../sparc/shared/include/b1553brm_rasta.h include_HEADERS += ../../sparc/shared/include/debug_defs.h -include_HEADERS += ../../sparc/shared/include/pci.h nodist_include_HEADERS = include/bspopts.h nodist_include_bsp_HEADERS = ../../shared/include/bootcard.h @@ -55,47 +38,167 @@ libbsp_a_SOURCES = # startup libbsp_a_SOURCES += ../../shared/bspclean.c ../../shared/bsplibc.c \ - ../../shared/bsppost.c ../../shared/bsppredriverhook.c \ + ../../shared/bsppost.c startup/bsppredriver.c \ startup/bspstart.c ../../sparc/shared/bsppretaskinghook.c \ ../../sparc/shared/bspgetworkarea.c ../../shared/bootcard.c \ - ../../shared/sbrk.c startup/setvec.c startup/spurious.c startup/bspidle.c + ../../shared/sbrk.c startup/setvec.c startup/spurious.c startup/bspidle.c \ + ../../sparc/shared/startup/early_malloc.c # gnatsupp libbsp_a_SOURCES += gnatsupp/gnatsupp.c ../../sparc/shared/gnatcommon.c # console libbsp_a_SOURCES += console/console.c console/debugputs.c # clock libbsp_a_SOURCES += clock/ckinit.c ../../../shared/clockdrv_shell.h -# AMBA PnP Scanning +# 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 \ + include/bsp/irq.h +libbsp_a_SOURCES += \ + ../../sparc/shared/irq/irq-shared.c \ + ../../shared/src/irq-generic.c \ + ../../shared/src/irq-legacy.c \ + ../../shared/src/irq-info.c \ + ../../shared/src/irq-shell.c \ + ../../shared/src/irq-server.c + +# AMBA bus +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 +libbsp_a_SOURCES += ../../sparc/shared/amba/ambapp_depth.c +libbsp_a_SOURCES += ../../sparc/shared/amba/ambapp_find_by_idx.c +libbsp_a_SOURCES += ../../sparc/shared/amba/ambapp_freq.c +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 +libbsp_a_SOURCES += ../../sparc/shared/timer/gptimer.c +libbsp_a_SOURCES += ../../sparc/shared/timer/tlib.c + # PCI -libbsp_a_SOURCES += pci/pci.c ../../sparc/shared/pci/pcifinddevice.c -# RASTA Kit -libbsp_a_SOURCES += rasta/rasta.c -# Companion Chip Kit -libbsp_a_SOURCES += cchip/cchip.c +include_HEADERS += ../../sparc/shared/include/grpci2.h +libbsp_a_SOURCES += ../../sparc/shared/pci/grpci2.c +libbsp_a_SOURCES += ../../sparc/shared/pci/grpci.c +libbsp_a_SOURCES += ../../sparc/shared/pci/pcif.c +libbsp_a_SOURCES += ../../sparc/shared/pci/pci_memreg_sparc_le.c +libbsp_a_SOURCES += ../../sparc/shared/pci/pci_memreg_sparc_be.c +libbsp_a_SOURCES += pci/at697_pci.c + +# PCI target devices +include_HEADERS += ../../sparc/shared/include/gr_701.h +include_HEADERS += ../../sparc/shared/include/gr_rasta_adcdac.h +include_HEADERS += ../../sparc/shared/include/gr_rasta_io.h +include_HEADERS += ../../sparc/shared/include/gr_rasta_tmtc.h +libbsp_a_SOURCES += ../../sparc/shared/pci/gr_701.c +libbsp_a_SOURCES += ../../sparc/shared/pci/gr_rasta_adcdac.c +libbsp_a_SOURCES += ../../sparc/shared/pci/gr_rasta_io.c +libbsp_a_SOURCES += ../../sparc/shared/pci/gr_rasta_spw_router.c +libbsp_a_SOURCES += ../../sparc/shared/pci/gr_rasta_tmtc.c + # B1553BRM -libbsp_a_SOURCES += ../../sparc/shared/1553/b1553brm.c \ - ../../sparc/shared/1553/b1553brm_pci.c \ - ../../sparc/shared/1553/b1553brm_rasta.c +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 +include_HEADERS += ../../sparc/shared/include/grcan.h libbsp_a_SOURCES += ../../sparc/shared/can/occan.c \ - ../../sparc/shared/can/occan_pci.c \ - ../../sparc/shared/can/grcan.c \ - ../../sparc/shared/can/grcan_rasta.c + ../../sparc/shared/can/grcan.c + # SpaceWire -libbsp_a_SOURCES += ../../sparc/shared/spw/grspw.c \ - ../../sparc/shared/spw/grspw_pci.c ../../sparc/shared/spw/grspw_rasta.c +include_HEADERS += ../../sparc/shared/include/grspw.h +include_HEADERS += ../../sparc/shared/include/grspw_router.h +include_HEADERS += ../../sparc/shared/include/rmap.h +include_HEADERS += ../../sparc/shared/include/rmap_drv_grspw.h +libbsp_a_SOURCES += ../../sparc/shared/spw/grspw.c +libbsp_a_SOURCES += ../../sparc/shared/spw/grspw_router.c +libbsp_a_SOURCES += ../../sparc/shared/spw/rmap.c +libbsp_a_SOURCES += ../../sparc/shared/spw/rmap_drv_grspw.c + # UART (RAW) -libbsp_a_SOURCES += ../../sparc/shared/uart/apbuart.c \ - ../../sparc/shared/uart/apbuart_pci.c \ - ../../sparc/shared/uart/apbuart_rasta.c +include_HEADERS += ../../sparc/shared/include/apbuart.h +libbsp_a_SOURCES += ../../sparc/shared/uart/apbuart.c + # I2CMST 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 +libbsp_a_SOURCES += ../../sparc/shared/mem/mctrl_rmap.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/grtc_rmap.c +libbsp_a_SOURCES += ../../sparc/shared/tmtc/grtm.c +libbsp_a_SOURCES += ../../sparc/shared/tmtc/grtm_rmap.c + +# Driver Manager +include_drvmgrdir = $(includedir)/drvmgr +include_drvmgr_HEADERS = ../../sparc/shared/include/drvmgr/ambapp_bus.h +include_drvmgr_HEADERS += ../../sparc/shared/include/drvmgr/ambapp_bus_rmap.h +include_drvmgr_HEADERS += ../../sparc/shared/include/drvmgr/leon2_amba_bus.h +include_drvmgr_HEADERS += ../../sparc/shared/include/drvmgr/spw_bus.h +include_drvmgr_HEADERS += ../../sparc/shared/include/drvmgr/spw_bus_ids.h +libbsp_a_SOURCES += ../../sparc/shared/drvmgr/ambapp_bus.c +libbsp_a_SOURCES += ../../sparc/shared/drvmgr/ambapp_bus_leon2.c +libbsp_a_SOURCES += ../../sparc/shared/drvmgr/ambapp_bus_rmap.c +libbsp_a_SOURCES += ../../sparc/shared/drvmgr/leon2_amba_bus.c +libbsp_a_SOURCES += ../../sparc/shared/drvmgr/spw_bus.c + if HAS_NETWORKING noinst_PROGRAMS += leon_smc91111.rel leon_smc91111_rel_SOURCES = leon_smc91111/leon_smc91111.c @@ -112,12 +215,27 @@ 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 +endif + libbsp_a_LIBADD = \ + ../../../libcpu/@RTEMS_CPU@/access.rel \ ../../../libcpu/@RTEMS_CPU@/cache.rel \ ../../../libcpu/@RTEMS_CPU@/reg_win.rel \ ../../../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/cchip/cchip.c b/c/src/lib/libbsp/sparc/leon2/cchip/cchip.c deleted file mode 100644 index e0c6364c66..0000000000 --- a/c/src/lib/libbsp/sparc/leon2/cchip/cchip.c +++ /dev/null @@ -1,358 +0,0 @@ -/* - * $Id$ - */ - -#include <bsp.h> -#include <rtems/bspIo.h> -#include <rtems.h> -#include <string.h> - -#include <rtems.h> -#include <leon.h> -#include <ambapp.h> -#include <pci.h> - -#include <b1553brm_pci.h> -#include <occan_pci.h> -#include <grspw_pci.h> -#include <apbuart_pci.h> - -#include <cchip.h> - -/* -#define DEBUG -#define DEBUG_IRQS -*/ -#define BOARD_INFO -/*#define PRINT_SPURIOUS*/ - -/* AT697 Register MAP */ -static LEON_Register_Map *regs = (LEON_Register_Map *)0x80000000; - -/* initializes interrupt management for companionship board */ -void cchip1_irq_init(void); - -/* register interrupt handler (called from drivers) */ -void cchip1_set_isr(void *handler, int irqno, void *arg); - -#define READ_REG(address) _READ_REG((unsigned int)address) -static __inline__ unsigned int _READ_REG(unsigned int addr) { - unsigned int tmp; - asm("lda [%1]1, %0 " - : "=r"(tmp) - : "r"(addr) - ); - return tmp; -} - -/* PCI bride reg layout on AMBA side */ -typedef struct { - unsigned int bar0; - unsigned int bar1; - unsigned int bar2; - unsigned int bar3; - unsigned int bar4;/* 0x10 */ - - unsigned int unused[4*3-1]; - - unsigned int ambabars[1]; /* 0x40 */ -} amba_bridge_regs; - -/* PCI bride reg layout on PCI side */ -typedef struct { - unsigned int bar0; - unsigned int bar1; - unsigned int bar2; - unsigned int bar3; - unsigned int bar4; /* 0x10 */ - - unsigned int ilevel; - unsigned int ipend; - unsigned int iforce; - unsigned int istatus; - unsigned int iclear; - unsigned int imask; -} pci_bridge_regs; - -typedef struct { - pci_bridge_regs *pcib; - amba_bridge_regs *ambab; - - /* AT697 PCI */ - unsigned int bars[5]; - int bus, dev, fun; - - /* AMBA bus */ - amba_confarea_type amba_bus; - struct amba_mmap amba_maps[2]; - - /* FT AHB SRAM */ - int ftsram_size; /* kb */ - unsigned int ftsram_start; - unsigned int ftsram_end; - -} cchip1; - -cchip1 cc1; - -int init_pcif(void){ - unsigned int com1; - int i,bus,dev,fun; - pci_bridge_regs *pcib; - amba_bridge_regs *ambab; - int amba_master_cnt; - amba_confarea_type *abus; - - if ( BSP_pciFindDevice(0x1AC8, 0x0701, 0, &bus, &dev, &fun) == 0 ) { - ; - }else if (BSP_pciFindDevice(0x16E3, 0x0210, 0, &bus, &dev, &fun) == 0) { - ; - } else { - /* didn't find any Companionship board on the PCI bus. */ - return -1; - } - - /* found Companionship PCI board, Set it up: */ - - pci_read_config_dword(bus, dev, fun, 0x10, &cc1.bars[0]); - pci_read_config_dword(bus, dev, fun, 0x14, &cc1.bars[1]); - pci_read_config_dword(bus, dev, fun, 0x18, &cc1.bars[2]); - pci_read_config_dword(bus, dev, fun, 0x1c, &cc1.bars[3]); - pci_read_config_dword(bus, dev, fun, 0x20, &cc1.bars[4]); - -#ifdef DEBUG - for(i=0; i<5; i++){ - printk("PCI: BAR%d: 0x%x\n\r",i,cc1.bars[i]); - } -#endif - - /* Set up PCI ==> AMBA */ - pcib = (void *)cc1.bars[0]; - pcib->bar0 = 0xfc000000; -/* pcib->bar1 = 0xff000000;*/ -#ifdef BOARD_INFO - printk("Found CCHIP1 Board at 0x%lx\n\r",(unsigned int)pcib); -#endif - - /* AMBA MAP cc1.bars[1] (in CPU) ==> 0xf0000000(remote amba address) */ - cc1.amba_maps[0].size = 0x04000000; - cc1.amba_maps[0].cpu_adr = cc1.bars[1]; - cc1.amba_maps[0].remote_amba_adr = 0xfc000000; - - /* Mark end of table */ - cc1.amba_maps[1].size=0; - cc1.amba_maps[1].cpu_adr = 0; - cc1.amba_maps[1].remote_amba_adr = 0; - - /* Enable I/O and Mem accesses */ - pci_read_config_dword(bus, dev, fun, 0x4, &com1); - com1 |= 0x3; - pci_write_config_dword(bus, dev, fun, 0x4, com1); - pci_read_config_dword(bus, dev, fun, 0x4, &com1); - - /* Set up AMBA Masters ==> PCI */ - ambab = (void *)(cc1.bars[1]+0x400); -#ifdef DEBUG - printk("AMBA: PCIBAR[%d]: 0x%x, 0x%x\n\r",0,ambab->bar0,pcib->bar0); - printk("AMBA: PCIBAR[%d]: 0x%x, 0x%x\n\r",1,ambab->bar1,pcib->bar1); - printk("AMBA: PCIBAR[%d]: 0x%x, 0x%x\n\r",2,ambab->bar2,pcib->bar2); -#endif - ambab->ambabars[0] = 0x40000000; /* 0xe0000000(AMBA) ==> 0x40000000(PCI) ==> 0x40000000(AT697 AMBA) */ - - /* Scan bus for AMBA devices */ - abus = &cc1.amba_bus; - memset(abus,0,sizeof(amba_confarea_type)); - amba_scan(abus,cc1.bars[1]+0x3f00000,&cc1.amba_maps[0]); - - /* Get number of amba masters */ - amba_master_cnt = abus->ahbmst.devnr; -#ifdef BOARD_INFO - printk("Found %d AMBA masters\n\r",amba_master_cnt); -#endif - for(i=1; i<amba_master_cnt; i++){ - ambab->ambabars[i] = 0x40000000; - } - - /* Enable PCI Master */ - pci_read_config_dword(bus, dev, fun, 0x4, &com1); - com1 |= 0x4; - pci_write_config_dword(bus, dev, fun, 0x4, com1); - pci_read_config_dword(bus, dev, fun, 0x4, &com1); - - cc1.pcib = pcib; - cc1.ambab = ambab; - cc1.bus = bus; - cc1.dev = dev; - cc1.fun = fun; - - return 0; -} - -#ifndef GAISLER_FTAHBRAM - #define GAISLER_FTAHBRAM 0x50 -#endif -int init_onboard_sram(void){ - amba_ahb_device ahb; - amba_apb_device apb; - unsigned int conf, size; - - /* Find SRAM controller - * 1. AHB slave interface - * 2. APB slave interface - */ - if ( amba_find_apbslv(&cc1.amba_bus,VENDOR_GAISLER,GAISLER_FTAHBRAM,&apb) != 1 ){ - printk("On Board FT SRAM not found (APB)\n"); - return -1; - } - - if ( amba_find_ahbslv(&cc1.amba_bus,VENDOR_GAISLER,GAISLER_FTAHBRAM,&ahb) != 1 ){ - printk("On Board FT SRAM not found (AHB)\n"); - return -1; - } - - /* We have found the controller. - * Get it going. - * - * Get size of SRAM - */ - conf = *(unsigned int *)apb.start; - size = (conf >>10) & 0x7; - - /* 2^x kb */ - cc1.ftsram_size = 1<<size; - cc1.ftsram_start = ahb.start[0]; - cc1.ftsram_end = size*1024 + cc1.ftsram_start; -#ifdef BOARD_INFO - printk("Found FT AHB SRAM %dkb at 0x%lx\n",cc1.ftsram_size,cc1.ftsram_start); -#endif - return 0; -} - -int cchip1_register(void){ - - /* Init AT697 PCI Controller */ - init_pci(); - - /* Find & init CChip board . - * Also scan AMBA Plug&Play info for us. - */ - if ( init_pcif() ){ - printk("Failed to initialize CCHIP board\n\r"); - return -1; - } - - /* Set interrupt common board stuff */ - cchip1_irq_init(); - - /* Find on board SRAM */ - if ( init_onboard_sram() ){ - printk("Failed to register On Board SRAM. It is needed by b1553BRM\n"); - return -1; - } - - /* Register interrupt install functions */ - b1553brm_pci_int_reg = cchip1_set_isr; - occan_pci_int_reg = cchip1_set_isr; - grspw_pci_int_reg = cchip1_set_isr; - apbuart_pci_int_reg = cchip1_set_isr; - - /* register the BRM PCI driver, use 16k FTSRAM... */ - if ( b1553brm_pci_register(&cc1.amba_bus,0,0,3,cc1.ftsram_start,0xffa00000) ){ - printk("Failed to register BRM PCI driver\n"); - return -1; - } - - /* register the BRM PCI driver, no DMA memory... */ - if ( occan_pci_register(&cc1.amba_bus) ){ - printk("Failed to register OC_CAN PCI driver\n"); - return -1; - } - - /* register the GRSPW PCI driver, use malloc... */ - if ( grspw_pci_register(&cc1.amba_bus,0,0xe0000000) ){ - printk("Failed to register GRSPW PCI driver\n"); - return -1; - } - - /* register the APBUART PCI driver, no DMA memory */ - if ( apbuart_pci_register(&cc1.amba_bus) ){ - printk("Failed to register APBUART PCI driver\n"); - return -1; - } - - return 0; -} - -static rtems_isr cchip1_interrupt_dispatcher(rtems_vector_number v); -static unsigned int cchip1_spurious_cnt; - -typedef struct { - unsigned int (*handler)(int irqno, void *arg); - void *arg; -} int_handler; - -static int_handler int_handlers[16]; - -void cchip1_irq_init(void){ - - /* Configure AT697 ioport bit 7 to input pci irq */ - regs->PIO_Direction &= ~(1<<7); - regs->PIO_Interrupt = 0x87; /* level sensitive */ - - /* Set up irq controller (mask all IRQs) */ - cc1.pcib->imask = 0x0000; - cc1.pcib->ipend = 0; - cc1.pcib->iclear = 0xffff; - cc1.pcib->iforce = 0; - cc1.pcib->ilevel = 0x0; - - memset(int_handlers,0,sizeof(int_handlers)); - - /* Reset spurious counter */ - cchip1_spurious_cnt = 0; - - /* Register interrupt handler */ - set_vector(cchip1_interrupt_dispatcher,LEON_TRAP_TYPE(CCHIP_IRQ),1); -} - -void cchip1_set_isr(void *handler, int irqno, void *arg){ - int_handlers[irqno].handler = handler; - int_handlers[irqno].arg = arg; -#ifdef DEBUG - printk("Registering IRQ %d to 0x%lx(%d,0x%lx)\n\r",irqno,(unsigned int)handler,irqno,(unsigned int)arg); -#endif - cc1.pcib->imask |= 1<<irqno; /* Enable the registered IRQ */ -} - -static rtems_isr cchip1_interrupt_dispatcher(rtems_vector_number v){ - unsigned int pending = READ_REG(&cc1.pcib->ipend); - unsigned int (*handler)(int irqno, void *arg); - unsigned int clr = pending; - int irq=1; - - if ( !pending ){ -#ifdef PRINT_SPURIOUS - printk("Spurious IRQ %d: %d\n",v,cchip1_spurious_cnt); -#endif - cchip1_spurious_cnt++; - return; - } -#ifdef DEBUG_IRQS - printk("CCIRQ: 0x%x\n",(unsigned int)pending); -#endif - /* IRQ 0 doesn't exist */ - irq=1; - pending = pending>>1; - - while ( pending ){ - if ( (pending & 1) && (handler=int_handlers[irq].handler) ){ - handler(irq,int_handlers[irq].arg); - } - irq++; - pending = pending>>1; - } - - cc1.pcib->iclear = clr; - - /*LEON_Clear_interrupt( brd->irq );*/ -} diff --git a/c/src/lib/libbsp/sparc/leon2/console/console.c b/c/src/lib/libbsp/sparc/leon2/console/console.c index 91a1daa739..7f4d1cfdaf 100644 --- a/c/src/lib/libbsp/sparc/leon2/console/console.c +++ b/c/src/lib/libbsp/sparc/leon2/console/console.c @@ -69,7 +69,7 @@ int console_inbyte_nonblocking( int port ); * Buffers between task and ISRs */ -#include <ringbuf.h> +#include <rtems/ringbuf.h> Ring_buffer_t TX_Buffer[ 2 ]; bool Is_TX_active[ 2 ]; diff --git a/c/src/lib/libbsp/sparc/leon2/console/debugputs.c b/c/src/lib/libbsp/sparc/leon2/console/debugputs.c index ffd489b4a6..ae17248f12 100644 --- a/c/src/lib/libbsp/sparc/leon2/console/debugputs.c +++ b/c/src/lib/libbsp/sparc/leon2/console/debugputs.c @@ -29,14 +29,23 @@ void console_outbyte_polled( unsigned char ch ) { +send: if ( port == 0 ) { while ( (LEON_REG.UART_Status_1 & LEON_REG_UART_STATUS_THE) == 0 ); LEON_REG.UART_Channel_1 = (unsigned int) ch; + if ( ch == '\n' ) { + ch = '\r'; + goto send; + } return; } while ( (LEON_REG.UART_Status_2 & LEON_REG_UART_STATUS_THE) == 0 ); LEON_REG.UART_Channel_2 = (unsigned int) ch; + if ( ch == '\n' ) { + ch = '\r'; + goto send; + } } /* diff --git a/c/src/lib/libbsp/sparc/leon2/include/bsp.h b/c/src/lib/libbsp/sparc/leon2/include/bsp.h index 34e31ce206..a5aeeb3ba0 100644 --- a/c/src/lib/libbsp/sparc/leon2/include/bsp.h +++ b/c/src/lib/libbsp/sparc/leon2/include/bsp.h @@ -32,6 +32,7 @@ extern "C" { #include <leon.h> #include <rtems/clockdrv.h> #include <rtems/console.h> +#include <rtems/irq-extension.h> /* SPARC CPU variant: LEON2 */ #define LEON2 1 @@ -58,6 +59,9 @@ extern int rtems_smc91111_driver_attach_leon2( #define RTEMS_BSP_NETWORK_DRIVER_ATTACH_SMC91111 \ rtems_smc91111_driver_attach_leon2 +#define GRETH_SUPPORTED +#define GRETH_MEM_LOAD(addr) leon_r32_no_cache(addr) + /* * The synchronous trap is an arbitrarily chosen software trap. */ @@ -84,6 +88,10 @@ extern int end; /* last address in the program */ /* miscellaneous stuff assumed to exist */ +/* set_vec type */ +#define SET_VECTOR_RAW 0 /* Raw trap handler */ +#define SET_VECTOR_INT 1 /* Trap handler with _ISR_Handler interrupt handler */ + rtems_isr_entry set_vector( /* returns old vector */ rtems_isr_entry handler, /* isr routine */ rtems_vector_number vector, /* vector number */ @@ -94,6 +102,102 @@ void BSP_fatal_return( void ); void bsp_spurious_initialize( void ); +/* Allocate 8-byte aligned non-freeable pre-malloc memory */ +void *bsp_early_malloc(int size); + +/* Interrupt Service Routine (ISR) pointer */ +typedef void (*bsp_shared_isr)(void *arg); + +/* Initializes the Shared System Interrupt service */ +extern int BSP_shared_interrupt_init(void); + +/* Registers a shared IRQ handler, and enable it at IRQ controller. Multiple + * interrupt handlers may use the same IRQ number, all ISRs will be called + * when an interrupt on that line is fired. + * + * Arguments + * irq System IRQ number + * info Optional Name of IRQ source + * isr Function pointer to the ISR + * arg Second argument to function isr + */ +static __inline__ int BSP_shared_interrupt_register + ( + int irq, + const char *info, + bsp_shared_isr isr, + void *arg + ) +{ + return rtems_interrupt_handler_install(irq, info, + RTEMS_INTERRUPT_SHARED, isr, arg); +} + +/* Unregister previously registered shared IRQ handler. + * + * Arguments + * irq System IRQ number + * isr Function pointer to the ISR + * arg Second argument to function isr + */ +static __inline__ int BSP_shared_interrupt_unregister + ( + int irq, + bsp_shared_isr isr, + void *arg + ) +{ + return rtems_interrupt_handler_remove(irq, isr, arg); +} + +/* Clear interrupt pending on IRQ controller, this is typically done on a + * level triggered interrupt source such as PCI to avoid taking double IRQs. + * In such a case the interrupt source must be cleared first on LEON, before + * acknowledging the IRQ with this function. + * + * Arguments + * irq System IRQ number + */ +extern void BSP_shared_interrupt_clear(int irq); + +/* Enable Interrupt. This function will unmask the IRQ at the interrupt + * controller. This is normally done by _register(). Note that this will + * affect all ISRs on this IRQ. + * + * Arguments + * irq System IRQ number + */ +extern void BSP_shared_interrupt_unmask(int irq); + +/* Disable Interrupt. This function will mask one IRQ at the interrupt + * controller. This is normally done by _unregister(). Note that this will + * affect all ISRs on this IRQ. + * + * Arguments + * irq System IRQ number + */ +extern void BSP_shared_interrupt_mask(int irq); + +/* BSP PCI Interrupt support */ +#define BSP_PCI_shared_interrupt_register BSP_shared_interrupt_register +#define BSP_PCI_shared_interrupt_unregister BSP_shared_interrupt_unregister +#define BSP_PCI_shared_interrupt_unmask BSP_shared_interrupt_unmask +#define BSP_PCI_shared_interrupt_mask BSP_shared_interrupt_mask +#define BSP_PCI_shared_interrupt_clear BSP_shared_interrupt_clear + +/* AT697 has PCI defined as big endian */ +#define BSP_PCI_BIG_ENDIAN + +/* Common driver build-time configurations. On small systems undefine + * [DRIVER]_INFO_AVAIL to avoid info routines get dragged in. It is good + * for debugging and printing information about the system, but makes the + * image bigger. + */ +#define AMBAPPBUS_INFO_AVAIL /* AMBAPP Bus driver */ +#define GPTIMER_INFO_AVAIL /* GPTIMER Timer driver */ +#define GRETH_INFO_AVAIL /* GRETH Ethernet driver */ +#define GRTC_RMAP_INFO_AVAIL /* GRTC over SpaceWire/RMAP driver */ + #ifdef __cplusplus } #endif diff --git a/c/src/lib/libbsp/sparc/leon2/include/bsp/irq.h b/c/src/lib/libbsp/sparc/leon2/include/bsp/irq.h new file mode 100644 index 0000000000..709b5631aa --- /dev/null +++ b/c/src/lib/libbsp/sparc/leon2/include/bsp/irq.h @@ -0,0 +1,20 @@ +/* LEON2 generic shared IRQ setup + * + * Based on libbsp/shared/include/irq.h. + * + * 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 LIBBSP_LEON2_IRQ_CONFIG_H +#define LIBBSP_LEON2_IRQ_CONFIG_H + +#define BSP_INTERRUPT_VECTOR_MAX_STD 15 /* Standard IRQ controller */ +#define BSP_INTERRUPT_VECTOR_MIN 0 +#define BSP_INTERRUPT_VECTOR_MAX BSP_INTERRUPT_VECTOR_MAX_STD + +/* No extra check is needed */ +#undef BSP_INTERRUPT_CUSTOM_VALID_VECTOR + +#endif /* LIBBSP_LEON2_IRQ_CONFIG_H */ diff --git a/c/src/lib/libbsp/sparc/leon2/include/cchip.h b/c/src/lib/libbsp/sparc/leon2/include/cchip.h deleted file mode 100644 index 5136f9452c..0000000000 --- a/c/src/lib/libbsp/sparc/leon2/include/cchip.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * $Id$ - */ - -#ifndef __CCHIP_H__ -#define __CCHIP_H__ - -#include <b1553brm_pci.h> -#include <occan_pci.h> -#include <grspw_pci.h> -#include <apbuart_pci.h> - -#ifdef __cplusplus -extern "C" { -#endif - -#define CCHIP_IRQ 4 - -/* Register all drivers supported by the Companion Chip board */ -int cchip_register(void); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/c/src/lib/libbsp/sparc/leon2/include/leon.h b/c/src/lib/libbsp/sparc/leon2/include/leon.h index 168ebe5b52..4ff53060f8 100644 --- a/c/src/lib/libbsp/sparc/leon2/include/leon.h +++ b/c/src/lib/libbsp/sparc/leon2/include/leon.h @@ -271,6 +271,11 @@ typedef struct { extern LEON_Register_Map LEON_REG; +static __inline__ int leon_irq_fixup(int irq) +{ + return irq; +} + /* * Macros to manipulate the Interrupt Clear, Interrupt Force, Interrupt Mask, * and the Interrupt Pending Registers. @@ -369,6 +374,14 @@ extern LEON_Register_Map LEON_REG; #define LEON_REG_TIMER_COUNTER_DEFINED_MASK 0x00000003 #define LEON_REG_TIMER_COUNTER_CURRENT_MODE_MASK 0x00000003 +/* Load 32-bit word by forcing a cache-miss */ +static inline unsigned int leon_r32_no_cache(unsigned int addr) +{ + unsigned int tmp; + asm volatile (" lda [%1] 1, %0\n" : "=r"(tmp) : "r"(addr) ); + return tmp; +} + #endif /* !ASM */ #ifdef __cplusplus diff --git a/c/src/lib/libbsp/sparc/leon2/include/rasta.h b/c/src/lib/libbsp/sparc/leon2/include/rasta.h deleted file mode 100644 index 751ec3eb8e..0000000000 --- a/c/src/lib/libbsp/sparc/leon2/include/rasta.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * $Id$ - */ - -#ifndef __RASTA_H__ -#define __RASTA_H__ - -#include <bsp.h> - -#include <grcan.h> -#include <b1553brm_rasta.h> -#include <grspw.h> - -#ifdef __cplusplus -extern "C" { -#endif - -extern int rasta_register(void); - -/* Address of PCI bus on RASTA local AMBA bus */ -#define RASTA_PCI_BASE 0xe0000000 - -/* Address of SRAM on RASTA local AMBA bus */ -#define RASTA_LOCAL_SRAM 0x40000000 - -#define UART0_IRQNO 2 -#define UART1_IRQNO 3 -#define GRCAN_IRQNO 7 -#define SPW0_IRQNO 10 -#define SPW1_IRQNO 11 -#define SPW2_IRQNO 12 -#define BRM_IRQNO 13 - -#define GRCAN_IRQ (3<<GRCAN_IRQNO) -#define SPW0_IRQ (1<<SPW0_IRQNO) -#define SPW1_IRQ (1<<SPW1_IRQNO) -#define SPW2_IRQ (1<<SPW2_IRQNO) -#define SPW_IRQ (7<<SPW0_IRQNO) -#define BRM_IRQ (1<<BRM_IRQNO) -#define UART0_IRQ (1<<UART0_IRQNO) -#define UART1_IRQ (1<<UART1_IRQNO) - -/* - * The following defines the bits in the UART Control Registers. - * - */ -#define LEON_REG_UART_CONTROL_RTD 0x000000FF /* RX/TX data */ - -/* - * The following defines the bits in the LEON UART Status Registers. - */ -#define LEON_REG_UART_STATUS_DR 0x00000001 /* Data Ready */ -#define LEON_REG_UART_STATUS_TSE 0x00000002 /* TX Send Register Empty */ -#define LEON_REG_UART_STATUS_THE 0x00000004 /* TX Hold Register Empty */ -#define LEON_REG_UART_STATUS_BR 0x00000008 /* Break Error */ -#define LEON_REG_UART_STATUS_OE 0x00000010 /* RX Overrun Error */ -#define LEON_REG_UART_STATUS_PE 0x00000020 /* RX Parity Error */ -#define LEON_REG_UART_STATUS_FE 0x00000040 /* RX Framing Error */ -#define LEON_REG_UART_STATUS_ERR 0x00000078 /* Error Mask */ - - -/* - * The following defines the bits in the LEON UART Status Registers. - */ -#define LEON_REG_UART_CTRL_RE 0x00000001 /* Receiver enable */ -#define LEON_REG_UART_CTRL_TE 0x00000002 /* Transmitter enable */ -#define LEON_REG_UART_CTRL_RI 0x00000004 /* Receiver interrupt enable */ -#define LEON_REG_UART_CTRL_TI 0x00000008 /* Transmitter interrupt enable */ -#define LEON_REG_UART_CTRL_PS 0x00000010 /* Parity select */ -#define LEON_REG_UART_CTRL_PE 0x00000020 /* Parity enable */ -#define LEON_REG_UART_CTRL_FL 0x00000040 /* Flow control enable */ -#define LEON_REG_UART_CTRL_LB 0x00000080 /* Loop Back enable */ - -#define UART_SET_SCALER 0 -#define UART_SET_CTRL 1 -#define UART_GET_STAT 2 -#define UART_CLR_STAT 3 - -struct uart_reg { - volatile unsigned int data; /* 0x00 */ - volatile unsigned int status; /* 0x04 */ - volatile unsigned int ctrl; /* 0x08 */ - volatile unsigned int scaler; /* 0x0C */ -}; - - -void uart_register(unsigned int baseaddr); -rtems_device_driver uart_initialize(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); -rtems_device_driver uart_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); -rtems_device_driver uart_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); -rtems_device_driver uart_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); -rtems_device_driver uart_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); -rtems_device_driver uart_control(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); - - -struct gpio_reg { - volatile unsigned int in_data; /* 0x00 */ - volatile unsigned int out_data; /* 0x04 */ - volatile unsigned int dir; /* 0x08 */ - volatile unsigned int imask; /* 0x0C */ - volatile unsigned int ipol; /* 0x10 */ - volatile unsigned int iedge; /* 0x14 */ -}; - -extern struct gpio_reg *gpio0, *gpio1; - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/c/src/lib/libbsp/sparc/leon2/leon_smc91111/leon_smc91111.c b/c/src/lib/libbsp/sparc/leon2/leon_smc91111/leon_smc91111.c index 47b4d7a413..3a065f9ec1 100644 --- a/c/src/lib/libbsp/sparc/leon2/leon_smc91111/leon_smc91111.c +++ b/c/src/lib/libbsp/sparc/leon2/leon_smc91111/leon_smc91111.c @@ -26,12 +26,12 @@ #define SMC91111_BASE_ADDR (void*)0x20000300 -#define SMC91111_BASE_IRQ LEON_TRAP_TYPE(4) +#define SMC91111_BASE_IRQ 4 #define SMC91111_BASE_PIO 4 scmv91111_configuration_t leon_scmv91111_configuration = { SMC91111_BASE_ADDR, /* base address */ - SMC91111_BASE_IRQ, /* vector number */ + SMC91111_BASE_IRQ, /* IRQ number to IRQ funcs */ SMC91111_BASE_PIO, /* PIO */ 100, /* 100b */ 1, /* fulldx */ @@ -55,7 +55,7 @@ int rtems_smc91111_driver_attach_leon2(struct rtems_bsdnet_ifconfig *config) *((volatile unsigned int *)0x80000000) |= 0x10f80000; *((volatile unsigned int *)0x800000A8) |= (0xe0 | leon_scmv91111_configuration.pio) - << (8 * ((leon_scmv91111_configuration.vector & 0x0f) - 4)); + << (8 * (leon_scmv91111_configuration.irq - 4)); return _rtems_smc91111_driver_attach(config,&leon_scmv91111_configuration); diff --git a/c/src/lib/libbsp/sparc/leon2/pci/at697_pci.c b/c/src/lib/libbsp/sparc/leon2/pci/at697_pci.c new file mode 100644 index 0000000000..a2dc44b877 --- /dev/null +++ b/c/src/lib/libbsp/sparc/leon2/pci/at697_pci.c @@ -0,0 +1,658 @@ +/* LEON2 AT697 PCI Host Driver. + * + * COPYRIGHT (c) 2008. + * Aeroflex Gaisler AB. + * + * Configures the AT697 PCI core and initialize, + * - the PCI Library (pci.c) + * - the general part of the PCI Bus driver (pci_bus.c) + * + * System interrupt assigned to PCI interrupt (INTA#..INTD#) is by + * default taken from Plug and Play, but may be overridden by the + * driver resources INTA#..INTD#. + * + * 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-06, Daniel Hellstrom <daniel@gaisler.com> + * Created + * + * Configurable parameters + * ======================= + * INT[A..D]# Select system IRQ (can be tranlated into I/O interrupt) + * INT[A..D]#_PIO Select PIO used to generate I/O interrupt + * + * Notes + * ===== + * IRQ must not be enabled before all PCI boards have been enabled, the + * IRQ is therefore enabled first in init2. The init2() for this driver + * is assumed to be executed earlier that all boards and their devices + * driver's init2() function. + * + */ + +#include <rtems/bspIo.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <libcpu/byteorder.h> +#include <libcpu/access.h> +#include <pci.h> +#include <pci/cfg.h> + +#include <drvmgr/drvmgr.h> +#include <drvmgr/pci_bus.h> +#include <drvmgr/leon2_amba_bus.h> + +#include <leon.h> + +/* Configuration options */ + +#define SYSTEM_MAINMEM_START 0x40000000 +#define SYSTEM_MAINMEM_START2 0x60000000 + +/* Interrupt assignment. Set to other value than 0xff in order to + * override defaults and plug&play information + */ +#ifndef AT697_INTA_SYSIRQ + #define AT697_INTA_SYSIRQ 0xff +#endif +#ifndef AT697_INTB_SYSIRQ + #define AT697_INTB_SYSIRQ 0xff +#endif +#ifndef AT697_INTC_SYSIRQ + #define AT697_INTC_SYSIRQ 0xff +#endif +#ifndef AT697_INTD_SYSIRQ + #define AT697_INTD_SYSIRQ 0xff +#endif + +#ifndef AT697_INTA_PIO + #define AT697_INTA_PIO 0xff +#endif +#ifndef AT697_INTB_PIO + #define AT697_INTB_PIO 0xff +#endif +#ifndef AT697_INTC_PIO + #define AT697_INTC_PIO 0xff +#endif +#ifndef AT697_INTD_PIO + #define AT697_INTD_PIO 0xff +#endif + + +/* AT697 PCI */ +#define AT697_PCI_REG_ADR 0x80000100 + +/* PCI Window used */ +#define PCI_MEM_START 0xa0000000 +#define PCI_MEM_END 0xf0000000 +#define PCI_MEM_SIZE (PCI_MEM_END - PCI_MEM_START) + +/* #define DEBUG 1 */ + +#ifdef DEBUG +#define DBG(x...) printf(x) +#else +#define DBG(x...) +#endif + +#define PCI_INVALID_VENDORDEVICEID 0xffffffff +#define PCI_MULTI_FUNCTION 0x80 + +struct at697pci_regs { + volatile unsigned int pciid1; /* 0x80000100 - PCI Device identification register 1 */ + volatile unsigned int pcisc; /* 0x80000104 - PCI Status & Command */ + volatile unsigned int pciid2; /* 0x80000108 - PCI Device identification register 2 */ + volatile unsigned int pcibhlc; /* 0x8000010c - BIST, Header type, Cache line size register */ + volatile unsigned int mbar1; /* 0x80000110 - Memory Base Address Register 1 */ + volatile unsigned int mbar2; /* 0x80000114 - Memory Base Address Register 2 */ + volatile unsigned int iobar3; /* 0x80000118 - IO Base Address Register 3 */ + volatile unsigned int dummy1[4]; /* 0x8000011c - 0x80000128 */ + volatile unsigned int pcisid; /* 0x8000012c - Subsystem identification register */ + volatile unsigned int dummy2; /* 0x80000130 */ + volatile unsigned int pcicp; /* 0x80000134 - PCI capabilities pointer register */ + volatile unsigned int dummy3; /* 0x80000138 */ + volatile unsigned int pcili; /* 0x8000013c - PCI latency interrupt register */ + volatile unsigned int pcirt; /* 0x80000140 - PCI retry, trdy config */ + volatile unsigned int pcicw; /* 0x80000144 - PCI configuration write register */ + volatile unsigned int pcisa; /* 0x80000148 - PCI Initiator Start Address */ + volatile unsigned int pciiw; /* 0x8000014c - PCI Initiator Write Register */ + volatile unsigned int pcidma; /* 0x80000150 - PCI DMA configuration register */ + volatile unsigned int pciis; /* 0x80000154 - PCI Initiator Status Register */ + volatile unsigned int pciic; /* 0x80000158 - PCI Initiator Configuration */ + volatile unsigned int pcitpa; /* 0x8000015c - PCI Target Page Address Register */ + volatile unsigned int pcitsc; /* 0x80000160 - PCI Target Status-Command Register */ + volatile unsigned int pciite; /* 0x80000164 - PCI Interrupt Enable Register */ + volatile unsigned int pciitp; /* 0x80000168 - PCI Interrupt Pending Register */ + volatile unsigned int pciitf; /* 0x8000016c - PCI Interrupt Force Register */ + volatile unsigned int pcid; /* 0x80000170 - PCI Data Register */ + volatile unsigned int pcibe; /* 0x80000174 - PCI Burst End Register */ + volatile unsigned int pcidmaa; /* 0x80000178 - PCI DMA Address Register */ +}; + +/* PCI Interrupt assignment. Connects an PCI interrupt pin (INTA#..INTD#) + * to a system interrupt number. + */ +unsigned char at697_pci_irq_table[4] = +{ + /* INTA# */ AT697_INTA_SYSIRQ, + /* INTB# */ AT697_INTB_SYSIRQ, + /* INTC# */ AT697_INTC_SYSIRQ, + /* INTD# */ AT697_INTD_SYSIRQ +}; + +/* PCI Interrupt PIO assignment. Selects which GPIO pin will be used to + * generate the system IRQ. + * + * PCI IRQ -> GPIO -> 4 x I/O select -> System IRQ + * ^- pio_table ^- irq_select + */ +unsigned char at697_pci_irq_pio_table[4] = +{ + /* INTA# */ AT697_INTA_PIO, + /* INTB# */ AT697_INTB_PIO, + /* INTC# */ AT697_INTC_PIO, + /* INTD# */ AT697_INTD_PIO +}; + +/* Driver private data struture */ +struct at697pci_priv { + struct drvmgr_dev *dev; + struct at697pci_regs *regs; + int minor; + + uint32_t devVend; /* PCI Device and Vendor ID of Host */ + uint32_t bar1_pci_adr; + uint32_t bar2_pci_adr; + + struct drvmgr_map_entry maps_up[3]; + struct drvmgr_map_entry maps_down[2]; + struct pcibus_config config; +}; + +struct at697pci_priv *at697pcipriv = NULL; +static int at697pci_minor = 0; + +int at697pci_init1(struct drvmgr_dev *dev); +int at697pci_init2(struct drvmgr_dev *dev); + +/* AT697 PCI DRIVER */ + +struct drvmgr_drv_ops at697pci_ops = +{ + .init = {at697pci_init1, at697pci_init2, NULL, NULL}, + .remove = NULL, + .info = NULL +}; + +struct leon2_amba_dev_id at697pci_ids[] = +{ + {LEON2_AMBA_AT697PCI_ID}, + {0} /* Mark end of table */ +}; + +struct leon2_amba_drv_info at697pci_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_LEON2_AMBA_AT697PCI, /* Driver ID */ + "AT697PCI_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_LEON2_AMBA, /* Bus Type */ + &at697pci_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + sizeof(struct at697pci_priv), /* let drvmgr alloc private */ + }, + &at697pci_ids[0] +}; + +void at697pci_register_drv(void) +{ + DBG("Registering AT697 PCI driver\n"); + drvmgr_drv_register(&at697pci_info.general); +} + +/* The configuration access functions uses the DMA functionality of the + * AT697 pci controller to be able access all slots + */ + +int at697pci_cfg_r32(pci_dev_t dev, int offset, uint32_t *val) +{ + struct at697pci_regs *regs; + volatile unsigned int data = 0; + unsigned int address; + int bus = PCI_DEV_BUS(dev); + int slot = PCI_DEV_SLOT(dev); + int func = PCI_DEV_FUNC(dev); + int retval; + + if (slot > 21 || (offset & ~0xfc)) { + *val = 0xffffffff; + return PCISTS_EINVAL; + } + + regs = at697pcipriv->regs; + + regs->pciitp = 0xff; /* clear interrupts */ + + if ( bus == 0 ) { + /* PCI Access - TYPE 0 */ + address = (1<<(11+slot)) | (func << 8) | offset; + } else { + /* PCI access - TYPE 1 */ + address = ((bus & 0xff) << 16) | ((slot & 0x1f) << 11) | + (func << 8) | offset | 1; + } + regs->pcisa = address; + regs->pcidma = 0xa01; + regs->pcidmaa = (unsigned int) &data; + + while (regs->pciitp == 0) + ; + + regs->pciitp = 0xff; /* clear interrupts */ + + if (regs->pcisc & 0x20000000) { /* Master Abort */ + regs->pcisc |= 0x20000000; + *val = 0xffffffff; + retval = PCISTS_MSTABRT; + } else { + *val = data; + retval = PCISTS_OK; + } + + DBG("pci_read - bus: %d, dev: %d, fn: %d, off: %d => addr: %x, val: %x\n", + bus, slot, func, offset, address, *val); + + return retval; +} + +int at697pci_cfg_r16(pci_dev_t dev, int ofs, uint16_t *val) +{ + uint32_t v; + int retval; + + if (ofs & 1) + return PCISTS_EINVAL; + + retval = at697pci_cfg_r32(dev, ofs & ~0x3, &v); + *val = 0xffff & (v >> (8*(ofs & 0x3))); + + return retval; +} + +int at697pci_cfg_r8(pci_dev_t dev, int ofs, uint8_t *val) +{ + uint32_t v; + int retval; + + retval = at697pci_cfg_r32(dev, ofs & ~0x3, &v); + + *val = 0xff & (v >> (8*(ofs & 3))); + + return retval; +} + +int at697pci_cfg_w32(pci_dev_t dev, int offset, uint32_t val) +{ + struct at697pci_regs *regs; + volatile unsigned int tmp_val = val; + unsigned int address; + int bus = PCI_DEV_BUS(dev); + int slot = PCI_DEV_SLOT(dev); + int func = PCI_DEV_FUNC(dev); + int retval; + + if (slot > 21 || (offset & ~0xfc)) + return PCISTS_EINVAL; + + regs = at697pcipriv->regs; + + regs->pciitp = 0xff; /* clear interrupts */ + + if ( bus == 0 ) { + /* PCI Access - TYPE 0 */ + address = (1<<(11+slot)) | (func << 8) | offset; + } else { + /* PCI access - TYPE 1 */ + address = ((bus & 0xff) << 16) | ((slot & 0x1f) << 11) | + (func << 8) | offset | 1; + } + regs->pcisa = address; + regs->pcidma = 0xb01; + regs->pcidmaa = (unsigned int) &tmp_val; + + while (regs->pciitp == 0) + ; + + if (regs->pcisc & 0x20000000) { /* Master Abort */ + regs->pcisc |= 0x20000000; + retval = PCISTS_MSTABRT; + } else + retval = PCISTS_OK; + + regs->pciitp = 0xff; /* clear interrupts */ + + DBG("pci_write - bus: %d, dev: %d, fn: %d, off: %d => addr: %x, val: %x\n", + bus, slot, func, offset, address, val); + + return retval; +} + +int at697pci_cfg_w16(pci_dev_t dev, int ofs, uint16_t val) +{ + uint32_t v; + int retval; + + if (ofs & 1) + return PCISTS_EINVAL; + + retval = at697pci_cfg_r32(dev, ofs & ~0x3, &v); + if (retval != PCISTS_OK) + return retval; + + v = (v & ~(0xffff << (8*(ofs&3)))) | ((0xffff&val) << (8*(ofs&3))); + + return at697pci_cfg_w32(dev, ofs & ~0x3, v); +} + +int at697pci_cfg_w8(pci_dev_t dev, int ofs, uint8_t val) +{ + uint32_t v; + + at697pci_cfg_r32(dev, ofs & ~0x3, &v); + + v = (v & ~(0xff << (8*(ofs&3)))) | ((0xff&val) << (8*(ofs&3))); + + return at697pci_cfg_w32(dev, ofs & ~0x3, v); +} + +/* Return the assigned system IRQ number that corresponds to the PCI + * "Interrupt Pin" information from configuration space. + * + * The IRQ information is stored in the at697_pci_irq_table configurable + * by the user. + * + * Returns the "system IRQ" for the PCI INTA#..INTD# pin in irq_pin. Returns + * 0xff if not assigned. + */ +uint8_t at697pci_bus0_irq_map(pci_dev_t dev, int irq_pin) +{ + uint8_t sysIrqNr = 0; /* not assigned */ + int irq_group; + + if ( (irq_pin >= 1) && (irq_pin <= 4) ) { + /* Use default IRQ decoding on PCI BUS0 according slot numbering */ + irq_group = PCI_DEV_SLOT(dev) & 0x3; + irq_pin = ((irq_pin - 1) + irq_group) & 0x3; + /* Valid PCI "Interrupt Pin" number */ + sysIrqNr = at697_pci_irq_table[irq_pin]; + } + return sysIrqNr; +} + +int at697pci_translate(uint32_t *address, int type, int dir) +{ + /* No address translation implmented at this point */ + return 0; +} + +extern struct pci_memreg_ops pci_memreg_sparc_be_ops; + +/* AT697 Big-Endian PCI access routines */ +struct pci_access_drv at697pci_access_drv = { + .cfg = + { + at697pci_cfg_r8, + at697pci_cfg_r16, + at697pci_cfg_r32, + at697pci_cfg_w8, + at697pci_cfg_w16, + at697pci_cfg_w32, + }, + .io = + { /* AT697 only supports non-standard Big-Endian PCI Bus */ + _ld8, + _ld_be16, + _ld_be32, + _st8, + _st_be16, + _st_be32, + + }, + .memreg = &pci_memreg_sparc_be_ops, + .translate = at697pci_translate, +}; + +/* Initializes the AT697PCI core hardware + * + */ +int at697pci_hw_init(struct at697pci_priv *priv) +{ + struct at697pci_regs *regs = priv->regs; + unsigned short vendor = regs->pciid1 >> 16; + pci_dev_t host = PCI_DEV(0, 0, 0); + + /* Must match ATMEL or ESA ID */ + if ( !((vendor == 0x1202) || (vendor == 0x1E0F)) ) { + /* No AT697 PCI, quit */ + return -1; + } + + /* Reset PCI Core */ + regs->pciic = 0xffffffff; + + /* Mask PCI interrupts */ + regs->pciite = 0; + + /* Map parts of AT697 main memory into PCI (for DMA) */ + regs->mbar1 = priv->bar1_pci_adr; + regs->mbar2 = priv->bar2_pci_adr; + regs->pcitpa = (priv->bar1_pci_adr & 0xff000000) | + ((priv->bar2_pci_adr>>16) & 0xff00); + + /* Enable PCI master and target memory command response */ + regs->pcisc |= 0x40 | 0x6; + + /* Set latency timer to 64 */ + regs->pcibhlc = 0x00004000; + + /* Set Inititator configuration so that AHB slave accesses generate memory read/write commands */ + regs->pciic = 0x41; + + /* Get the AT697PCI Host PCI ID */ + at697pci_cfg_r32(host, PCI_VENDOR_ID, &priv->devVend); + + return 0; +} + +/* Initializes the AT697PCI core and driver, must be called before calling init_pci() + * + * Return values + * 0 Successful initalization + * -1 Error during initialization. + */ +int at697pci_init(struct at697pci_priv *priv) +{ + int pin; + union drvmgr_key_value *value; + char keyname_sysirq[6]; + char keyname_pio[10]; + + /* PCI core, init private structure */ + priv->regs = (struct at697pci_regs *) AT697_PCI_REG_ADR; + + /* Init PCI interrupt assignment table to all use the interrupt routed + * through the GPIO core. + * + * INT[A..D]# selects system IRQ (and I/O interrupt) + * INT[A..D]#_PIO selects PIO used to generate I/O interrupt + */ + strcpy(keyname_sysirq, "INTX#"); + strcpy(keyname_pio, "INTX#_PIO"); + for (pin=1; pin<5; pin++) { + if ( at697_pci_irq_table[pin-1] == 0xff ) { + /* User may override hardcoded IRQ setup */ + keyname_sysirq[3] = 'A' + (pin-1); + value = drvmgr_dev_key_get(priv->dev, + keyname_sysirq, KEY_TYPE_INT); + if ( value ) + at697_pci_irq_table[pin-1] = value->i; + } + if ( at697_pci_irq_pio_table[pin-1] == 0xff ) { + /* User may override hardcoded IRQ setup */ + keyname_pio[3] = 'A' + (pin-1); + value = drvmgr_dev_key_get(priv->dev, + keyname_pio, KEY_TYPE_INT); + if ( value ) + at697_pci_irq_pio_table[pin-1] = value->i; + } + } + + /* Use GRPCI target BAR1 and BAR2 to map CPU RAM to PCI, this is to + * make it possible for PCI peripherals to do DMA directly to CPU memory + * + * Defualt is to map system RAM at pci address 0x40000000 and system + * SDRAM to pci address 0x60000000 + */ + value = drvmgr_dev_key_get(priv->dev, "tgtbar1", KEY_TYPE_INT); + if (value) + priv->bar1_pci_adr = value->i; + else + priv->bar1_pci_adr = SYSTEM_MAINMEM_START; /* default */ + + value = drvmgr_dev_key_get(priv->dev, "tgtbar2", KEY_TYPE_INT); + if (value) + priv->bar2_pci_adr = value->i; + else + priv->bar2_pci_adr = SYSTEM_MAINMEM_START2; /* default */ + + /* Init the PCI Core */ + if ( at697pci_hw_init(priv) ) { + return -3; + } + + /* Down streams translation table */ + priv->maps_down[0].name = "AMBA -> PCI MEM Window"; + priv->maps_down[0].size = 0xF0000000 - 0xA0000000; + priv->maps_down[0].from_adr = (void *)0xA0000000; + priv->maps_down[0].to_adr = (void *)0xA0000000; + /* End table */ + priv->maps_down[1].size = 0; + + /* Up streams translation table, 2x16Mb mapped 1:1 */ + priv->maps_up[0].name = "Target BAR0 -> AMBA"; + priv->maps_up[0].size = 0x01000000; /* 16Mb BAR1 */ + priv->maps_up[0].from_adr = (void *)priv->bar1_pci_adr; + priv->maps_up[0].to_adr = (void *)priv->bar1_pci_adr; + priv->maps_up[1].name = "Target BAR1 -> AMBA"; + priv->maps_up[1].size = 0x01000000; /* 16Mb BAR2 */ + priv->maps_up[1].from_adr = (void *)priv->bar2_pci_adr; + priv->maps_up[1].to_adr = (void *)priv->bar2_pci_adr; + /* End table */ + priv->maps_up[2].size = 0; + + return 0; +} + +/* Called when a core is found with the AMBA device and vendor ID + * given in at697pci_ids[]. + */ +int at697pci_init1(struct drvmgr_dev *dev) +{ + struct at697pci_priv *priv; + struct pci_auto_setup at697pci_auto_cfg; + + DBG("AT697PCI[%d] on bus %s\n", dev->minor_drv, + dev->parent->dev->name); + + if ( at697pci_minor != 0 ) { + DBG("Driver only supports one PCI core\n"); + return DRVMGR_FAIL; + } + + at697pcipriv = priv = dev->priv; + if ( !priv ) + return DRVMGR_NOMEM; + + priv->dev = dev; + priv->minor = at697pci_minor++; + + if (at697pci_init(priv)) { + DBG("Failed to initialize at697pci driver\n"); + return DRVMGR_FAIL; + } + + /* Host is always Big-Endian */ + pci_endian = PCI_BIG_ENDIAN; + + if (pci_access_drv_register(&at697pci_access_drv)) { + /* Access routines registration failed */ + return DRVMGR_FAIL; + } + + /* Prepare memory MAP */ + at697pci_auto_cfg.options = 0; + at697pci_auto_cfg.mem_start = 0; + at697pci_auto_cfg.mem_size = 0; + at697pci_auto_cfg.memio_start = PCI_MEM_START; + at697pci_auto_cfg.memio_size = PCI_MEM_SIZE; + at697pci_auto_cfg.io_start = 0; + at697pci_auto_cfg.io_size = 0; + at697pci_auto_cfg.irq_map = at697pci_bus0_irq_map; + at697pci_auto_cfg.irq_route = NULL; /* use standard routing */ + pci_config_register(&at697pci_auto_cfg); + + if (pci_config_init()) { + /* PCI configuration failed */ + return DRVMGR_FAIL; + } + + priv->config.maps_down = &priv->maps_down[0]; + priv->config.maps_up = &priv->maps_up[0]; + return pcibus_register(dev, &priv->config); +} + +int at697pci_init2(struct drvmgr_dev *dev) +{ +#if 0 + struct at697pci_priv *priv = dev->priv; +#endif + int pin, irq, pio, ioport; + LEON_Register_Map *regs = (LEON_Register_Map *)0x80000000; + + /* Enable interrupts now that init1 has been reached for all devices + * on the bus. + */ + + for (pin=1; pin<5; pin++) { + irq = at697_pci_irq_table[pin-1]; + pio = at697_pci_irq_pio_table[pin-1]; + if ( (pio < 16) && (irq >= 4) && (irq <= 7) ) { + /* AT697 I/O IRQ, we know how to set up this + * + * IRQ 4 -> I/O 0 + * IRQ 5 -> I/O 1 + * IRQ 6 -> I/O 2 + * IRQ 7 -> I/O 3 + */ + ioport = irq - 4; + + /* First disable interrupts */ + regs->PIO_Interrupt &= ~(0xff << (ioport * 8)); + /* Set PIO as input pin */ + regs->PIO_Direction &= ~(1 << pio); + /* Set Low Level sensitivity */ + regs->PIO_Interrupt |= ((0x80 | pio) << (ioport * 8)); + } + } + + /* Unmask Interrupt */ + /*priv->regs->pciite = 0xff;*/ + + return DRVMGR_OK; +} diff --git a/c/src/lib/libbsp/sparc/leon2/pci/pci.c b/c/src/lib/libbsp/sparc/leon2/pci/pci.c deleted file mode 100644 index 5e48c4703f..0000000000 --- a/c/src/lib/libbsp/sparc/leon2/pci/pci.c +++ /dev/null @@ -1,674 +0,0 @@ -/* - * pci.c : this file contains basic PCI Io functions. - * - * Copyright (C) 1999 valette@crf.canon.fr - * - * This code is heavily inspired by the public specification of STREAM V2 - * that can be found at : - * - * <http://www.chorus.com/Documentation/index.html> by following - * the STREAM API Specification Document link. - * - * The license and distribution terms for this file may be - * found in found in the file LICENSE in this distribution or at - * http://www.rtems.com/license/LICENSE. - * - * $Id$ - * - * Till Straumann, <strauman@slac.stanford.edu>, 1/2002 - * - separated bridge detection code out of this file - * - * Adapted to LEON2 AT697 PCI - * Copyright (C) 2006 Gaisler Research - * - */ - -#include <pci.h> -#include <rtems/bspIo.h> -#include <stdlib.h> - -/* Define PCI_INFO to get a listing of configured devices at boot time */ -#define PCI_INFO 1 - -/* #define DEBUG 1 */ - -#ifdef DEBUG -#define DBG(x...) printk(x) -#else -#define DBG(x...) -#endif - -/* allow for overriding these definitions */ -#ifndef PCI_CONFIG_ADDR -#define PCI_CONFIG_ADDR 0xcf8 -#endif -#ifndef PCI_CONFIG_DATA -#define PCI_CONFIG_DATA 0xcfc -#endif - -#define PCI_INVALID_VENDORDEVICEID 0xffffffff -#define PCI_MULTI_FUNCTION 0x80 - -/* define a shortcut */ -#define pci BSP_pci_configuration - -/* - * Bit encode for PCI_CONFIG_HEADER_TYPE register - */ -unsigned char ucMaxPCIBus; - -typedef struct { - volatile unsigned int pciid1; /* 0x80000100 - PCI Device identification register 1 */ - volatile unsigned int pcisc; /* 0x80000104 - PCI Status & Command */ - volatile unsigned int pciid2; /* 0x80000108 - PCI Device identification register 2 */ - volatile unsigned int pcibhlc; /* 0x8000010c - BIST, Header type, Cache line size register */ - volatile unsigned int mbar1; /* 0x80000110 - Memory Base Address Register 1 */ - volatile unsigned int mbar2; /* 0x80000114 - Memory Base Address Register 2 */ - volatile unsigned int iobar3; /* 0x80000118 - IO Base Address Register 3 */ - volatile unsigned int dummy1[4]; /* 0x8000011c - 0x80000128 */ - volatile unsigned int pcisid; /* 0x8000012c - Subsystem identification register */ - volatile unsigned int dummy2; /* 0x80000130 */ - volatile unsigned int pcicp; /* 0x80000134 - PCI capabilities pointer register */ - volatile unsigned int dummy3; /* 0x80000138 */ - volatile unsigned int pcili; /* 0x8000013c - PCI latency interrupt register */ - volatile unsigned int pcirt; /* 0x80000140 - PCI retry, trdy config */ - volatile unsigned int pcicw; /* 0x80000144 - PCI configuration write register */ - volatile unsigned int pcisa; /* 0x80000148 - PCI Initiator Start Address */ - volatile unsigned int pciiw; /* 0x8000014c - PCI Initiator Write Register */ - volatile unsigned int pcidma; /* 0x80000150 - PCI DMA configuration register */ - volatile unsigned int pciis; /* 0x80000154 - PCI Initiator Status Register */ - volatile unsigned int pciic; /* 0x80000158 - PCI Initiator Configuration */ - volatile unsigned int pcitpa; /* 0x8000015c - PCI Target Page Address Register */ - volatile unsigned int pcitsc; /* 0x80000160 - PCI Target Status-Command Register */ - volatile unsigned int pciite; /* 0x80000164 - PCI Interrupt Enable Register */ - volatile unsigned int pciitp; /* 0x80000168 - PCI Interrupt Pending Register */ - volatile unsigned int pciitf; /* 0x8000016c - PCI Interrupt Force Register */ - volatile unsigned int pcid; /* 0x80000170 - PCI Data Register */ - volatile unsigned int pcibe; /* 0x80000174 - PCI Burst End Register */ - volatile unsigned int pcidmaa; /* 0x80000178 - PCI DMA Address Register */ -} AT697_PCI_Map; - -AT697_PCI_Map *pcic = (AT697_PCI_Map *) 0x80000100; - -#define PCI_MEM_START 0xa0000000 -#define PCI_MEM_END 0xf0000000 -#define PCI_MEM_SIZE (PCI_MEM_START - PCI_MEM_END) - - -struct pci_res { - unsigned int size; - unsigned char bar; - unsigned char devfn; -}; - -/* The configuration access functions uses the DMA functionality of the - * AT697 pci controller to be able access all slots - */ - -static int -BSP_pci_read_config_dword(unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, unsigned int *val) { - - volatile unsigned int data; - - if (offset & 3) return PCIBIOS_BAD_REGISTER_NUMBER; - - pcic->pciitp = 0xff; /* clear interrupts */ - - pcic->pcisa = ( 1<<(11+slot) ) | ((function & 7)<<8) | (offset&0x3f); - pcic->pcidma = 0xa01; - pcic->pcidmaa = (unsigned int) &data; - - while (pcic->pciitp == 0) - ; - - pcic->pciitp = 0xff; /* clear interrupts */ - - if (pcic->pcisc & 0x20000000) { /* Master Abort */ - pcic->pcisc |= 0x20000000; - *val = 0xffffffff; - } - else - *val = data; - - DBG("pci_read - bus: %d, dev: %d, fn: %d, off: %d => addr: %x, val: %x\n", bus, slot, function, offset, (1<<(11+slot) ) | ((function & 7)<<8) | (offset&0x3f), *val); - - return PCIBIOS_SUCCESSFUL; -} - - -static int -BSP_pci_read_config_word(unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, unsigned short *val) { - unsigned int v; - - if (offset & 1) return PCIBIOS_BAD_REGISTER_NUMBER; - - pci_read_config_dword(bus, slot, function, offset&~3, &v); - *val = 0xffff & (v >> (8*(offset & 3))); - - return PCIBIOS_SUCCESSFUL; -} - - -static int -BSP_pci_read_config_byte(unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, unsigned char *val) { - unsigned int v; - - pci_read_config_dword(bus, slot, function, offset&~3, &v); - - *val = 0xff & (v >> (8*(offset & 3))); - - return PCIBIOS_SUCCESSFUL; -} - - -static int -BSP_pci_write_config_dword(unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, unsigned int val) { - - if (offset & 3) return PCIBIOS_BAD_REGISTER_NUMBER; - - pcic->pciitp = 0xff; /* clear interrupts */ - - pcic->pcisa = ( 1<<(11+slot) ) | ((function & 7)<<8) | (offset&0x3f); - pcic->pcidma = 0xb01; - pcic->pcidmaa = (unsigned int) &val; - - while (pcic->pciitp == 0) - ; - - if (pcic->pcisc & 0x20000000) { /* Master Abort */ - pcic->pcisc |= 0x20000000; - } - - pcic->pciitp = 0xff; /* clear interrupts */ - -/* DBG("pci write - bus: %d, dev: %d, fn: %d, off: %d => addr: %x, val: %x\n", bus, slot, function, offset, (1<<(11+slot) ) | ((function & 7)<<8) | (offset&0x3f), val); */ - - return PCIBIOS_SUCCESSFUL; -} - - -static int -BSP_pci_write_config_word(unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, unsigned short val) { - unsigned int v; - - if (offset & 1) return PCIBIOS_BAD_REGISTER_NUMBER; - - pci_read_config_dword(bus, slot, function, offset&~3, &v); - - v = (v & ~(0xffff << (8*(offset&3)))) | ((0xffff&val) << (8*(offset&3))); - - return pci_write_config_dword(bus, slot, function, offset&~3, v); -} - - -static int -BSP_pci_write_config_byte(unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, unsigned char val) { - unsigned int v; - - pci_read_config_dword(bus, slot, function, offset&~3, &v); - - v = (v & ~(0xff << (8*(offset&3)))) | ((0xff&val) << (8*(offset&3))); - - return pci_write_config_dword(bus, slot, function, offset&~3, v); -} - - - -const pci_config_access_functions pci_access_functions = { - BSP_pci_read_config_byte, - BSP_pci_read_config_word, - BSP_pci_read_config_dword, - BSP_pci_write_config_byte, - BSP_pci_write_config_word, - BSP_pci_write_config_dword -}; - -rtems_pci_config_t BSP_pci_configuration = { - (volatile unsigned char*)PCI_CONFIG_ADDR, - (volatile unsigned char*)PCI_CONFIG_DATA, - &pci_access_functions -}; - - -void init_at697_pci(void) { - - /* Reset */ - pcic->pciic = 0xffffffff; - - /* Map system RAM at pci address 0x40000000 and system SDRAM to pci address 0x60000000 */ - pcic->mbar1 = 0x40000000; - pcic->mbar2 = 0x60000000; - pcic->pcitpa = 0x40006000; - - /* Enable PCI master and target memory command response */ - pcic->pcisc |= 0x40 | 0x6; - - /* Set latency timer to 64 */ - pcic->pcibhlc = 0x00004000; - - /* Set Inititator configuration so that AHB slave accesses generate memory read/write commands */ - pcic->pciic = 0x41; - - pcic->pciite = 0xff; - -} - -/* May not pass a 1k boundary */ -int dma_from_pci_1k(unsigned int addr, unsigned int paddr, unsigned char len) { - - int retval = 0; - - if (addr & 3) { - return -1; - } - - pcic->pciitp = 0xff; /* clear interrupts */ - - pcic->pcisa = paddr; - pcic->pcidma = 0xc00 | len; - pcic->pcidmaa = addr; - - while (pcic->pciitp == 0) - ; - - if (pcic->pciitp & 0x7F) { - retval = -1; - } - - pcic->pciitp = 0xff; /* clear interrupts */ - - if (pcic->pcisc & 0x20000000) { /* Master Abort */ - pcic->pcisc |= 0x20000000; - retval = -1; - } - - return retval; -} - -/* May not pass a 1k boundary */ -int dma_to_pci_1k(unsigned int addr, unsigned int paddr, unsigned char len) { - - int retval = 0; - - if (addr & 3) return -1; - - pcic->pciitp = 0xff; /* clear interrupts */ - - pcic->pcisa = paddr; - pcic->pcidma = 0x700 | len; - pcic->pcidmaa = addr; - - while (pcic->pciitp == 0) - ; - - if (pcic->pciitp & 0x7F) retval = -1; - - pcic->pciitp = 0xff; /* clear interrupts */ - - if (pcic->pcisc & 0x20000000) { /* Master Abort */ - pcic->pcisc |= 0x20000000; - retval = -1; - } - - return retval; -} - -/* Transfer len number of words from addr to paddr */ -int dma_to_pci(unsigned int addr, unsigned int paddr, unsigned int len) { - - int tmp_len; - - /* Align to 1k boundary */ - tmp_len = ((addr + 1024) & 0xfffffc00) - addr; - - tmp_len = (tmp_len/4 < len) ? tmp_len : (len*4); - - if (dma_to_pci_1k(addr, paddr, tmp_len/4) < 0) - return -1; - - addr += tmp_len; - paddr += tmp_len; - len -= tmp_len/4; - - /* Transfer all 1k blocks */ - while (len >= 128) { - - if (dma_to_pci_1k(addr, paddr, 128) < 0) - return -1; - - addr += 512; - paddr += 512; - len -= 128; - - } - - /* Transfer last words */ - if (len) return dma_to_pci_1k(addr, paddr, len); - - return 0; -} - -/* Transfer len number of words from paddr to addr */ -int dma_from_pci(unsigned int addr, unsigned int paddr, unsigned int len) { - - int tmp_len; - - /* Align to 1k boundary */ - tmp_len = ((addr + 1024) & 0xfffffc00) - addr; - - tmp_len = (tmp_len/4 < len) ? tmp_len : (len*4); - - if (dma_from_pci_1k(addr, paddr, tmp_len/4) < 0) - return -1; - - addr += tmp_len; - paddr += tmp_len; - len -= tmp_len/4; - - /* Transfer all 1k blocks */ - while (len >= 128) { - - if (dma_from_pci_1k(addr, paddr, 128) < 0) - return -1; - addr += 512; - paddr += 512; - len -= 128; - - } - - /* Transfer last words */ - if (len) return dma_from_pci_1k(addr, paddr, len); - - return 0; -} - -void pci_mem_enable(unsigned char bus, unsigned char slot, unsigned char function) { - unsigned int data; - - pci_read_config_dword(0, slot, function, PCI_COMMAND, &data); - pci_write_config_dword(0, slot, function, PCI_COMMAND, data | PCI_COMMAND_MEMORY); - -} - -void pci_master_enable(unsigned char bus, unsigned char slot, unsigned char function) { - unsigned int data; - - pci_read_config_dword(0, slot, function, PCI_COMMAND, &data); - pci_write_config_dword(0, slot, function, PCI_COMMAND, data | PCI_COMMAND_MASTER); - -} - -static inline void swap_res(struct pci_res **p1, struct pci_res **p2) { - - struct pci_res *tmp = *p1; - *p1 = *p2; - *p2 = tmp; - -} - -/* pci_allocate_resources - * - * This function scans the bus and assigns PCI addresses to all devices. It handles both - * single function and multi function devices. All allocated devices are enabled and - * latency timers are set to 40. - * - * NOTE that it only allocates PCI memory space devices. IO spaces are not enabled. - * Also, it does not handle pci-pci bridges. They are left disabled. - * - * -*/ -void pci_allocate_resources(void) { - - unsigned int slot, numfuncs, func, id, pos, size, tmp, i, swapped, addr, dev, fn; - unsigned char header; - struct pci_res **res; - - res = (struct pci_res **) malloc(sizeof(struct pci_res *)*32*8*6); - - for (i = 0; i < 32*8*6; i++) { - res[i] = (struct pci_res *) malloc(sizeof(struct pci_res)); - res[i]->size = 0; - res[i]->devfn = i; - } - - for(slot = 0; slot< PCI_MAX_DEVICES; slot++) { - - pci_read_config_dword(0, slot, 0, PCI_VENDOR_ID, &id); - - if(id == PCI_INVALID_VENDORDEVICEID || id == 0) { - /* - * This slot is empty - */ - continue; - } - - pci_read_config_byte(0, slot, 0, PCI_HEADER_TYPE, &header); - - if(header & PCI_MULTI_FUNCTION) { - numfuncs = PCI_MAX_FUNCTIONS; - } - else { - numfuncs = 1; - } - - for(func = 0; func < numfuncs; func++) { - - pci_read_config_dword(0, slot, func, PCI_VENDOR_ID, &id); - if(id == PCI_INVALID_VENDORDEVICEID || id == 0) { - continue; - } - - pci_read_config_dword(0, slot, func, PCI_CLASS_REVISION, &tmp); - tmp >>= 16; - if (tmp == PCI_CLASS_BRIDGE_PCI) { - continue; - } - - for (pos = 0; pos < 6; pos++) { - pci_write_config_dword(0, slot, func, PCI_BASE_ADDRESS_0 + (pos<<2), 0xffffffff); - pci_read_config_dword(0, slot, func, PCI_BASE_ADDRESS_0 + (pos<<2), &size); - - if (size == 0 || size == 0xffffffff || (size & 0xff) != 0) { - pci_write_config_dword(0, slot, func, PCI_BASE_ADDRESS_0 + (pos<<2), 0); - continue; - } - - else { - res[slot*8*6+func*6+pos]->size = ~size+1; - res[slot*8*6+func*6+pos]->devfn = slot*8 + func; - res[slot*8*6+func*6+pos]->bar = pos; - - DBG("Slot: %d, function: %d, bar%d size: %x\n", slot, func, pos, ~size+1); - } - } - } - } - - - /* Sort the resources in descending order */ - - swapped = 1; - while (swapped == 1) { - swapped = 0; - for (i = 0; i < 32*8*6-1; i++) { - if (res[i]->size < res[i+1]->size) { - swap_res(&res[i], &res[i+1]); - swapped = 1; - } - } - i++; - } - - /* Assign the BARs */ - - addr = PCI_MEM_START; - for (i = 0; i < 32*8*6; i++) { - - if (res[i]->size == 0) { - goto done; - } - if ( (addr + res[i]->size) > PCI_MEM_END) { - printk("Out of PCI memory space, all devices not configured.\n"); - goto done; - } - - dev = res[i]->devfn >> 3; - fn = res[i]->devfn & 7; - - DBG("Assigning PCI addr %x to device %d, function %d, bar %d\n", addr, dev, fn, res[i]->bar); - pci_write_config_dword(0, dev, fn, PCI_BASE_ADDRESS_0+res[i]->bar*4, addr); - addr += res[i]->size; - - /* Set latency timer to 64 */ - pci_read_config_dword(0, dev, fn, 0xC, &tmp); - pci_write_config_dword(0, dev, fn, 0xC, tmp|0x4000); - - pci_mem_enable(0, dev, fn); - - } - - - -done: - -#ifdef PCI_INFO - printk("\nPCI devices found and configured:\n"); - for (slot = 0; slot < PCI_MAX_DEVICES; slot++) { - - pci_read_config_byte(0, slot, 0, PCI_HEADER_TYPE, &header); - - if(header & PCI_MULTI_FUNCTION) { - numfuncs = PCI_MAX_FUNCTIONS; - } - else { - numfuncs = 1; - } - - for (func = 0; func < numfuncs; func++) { - - pci_read_config_dword(0, slot, func, PCI_COMMAND, &tmp); - - if (tmp & PCI_COMMAND_MEMORY) { - - pci_read_config_dword(0, slot, func, PCI_VENDOR_ID, &id); - - if (id == PCI_INVALID_VENDORDEVICEID || id == 0) continue; - - printk("\nSlot %d function: %d\nVendor id: 0x%x, device id: 0x%x\n", slot, func, id & 0xffff, id>>16); - - for (pos = 0; pos < 6; pos++) { - pci_read_config_dword(0, slot, func, PCI_BASE_ADDRESS_0 + pos*4, &tmp); - - if (tmp != 0 && tmp != 0xffffffff && (tmp & 0xff) == 0) { - - printk("\tBAR %d: %x\n", pos, tmp); - } - - } - printk("\n"); - - } - - } - } - printk("\n"); -#endif - - for (i = 0; i < 1536; i++) { - free(res[i]); - } - free(res); -} - - - - - - - -/* - * This routine determines the maximum bus number in the system - */ -int init_pci(void) -{ - unsigned char ucSlotNumber, ucFnNumber, ucNumFuncs; - unsigned char ucHeader; - unsigned char ucMaxSubordinate; - unsigned int ulClass, ulDeviceID; - - init_at697_pci(); - pci_allocate_resources(); - -/* - * Scan PCI bus 0 looking for PCI-PCI bridges - */ - for(ucSlotNumber=0;ucSlotNumber<PCI_MAX_DEVICES;ucSlotNumber++) { - (void)pci_read_config_dword(0, - ucSlotNumber, - 0, - PCI_VENDOR_ID, - &ulDeviceID); - if(ulDeviceID==PCI_INVALID_VENDORDEVICEID) { -/* - * This slot is empty - */ - continue; - } - (void)pci_read_config_byte(0, - ucSlotNumber, - 0, - PCI_HEADER_TYPE, - &ucHeader); - if(ucHeader&PCI_MULTI_FUNCTION) { - ucNumFuncs=PCI_MAX_FUNCTIONS; - } - else { - ucNumFuncs=1; - } - for(ucFnNumber=0;ucFnNumber<ucNumFuncs;ucFnNumber++) { - (void)pci_read_config_dword(0, - ucSlotNumber, - ucFnNumber, - PCI_VENDOR_ID, - &ulDeviceID); - if(ulDeviceID==PCI_INVALID_VENDORDEVICEID) { -/* - * This slot/function is empty - */ - continue; - } - -/* - * This slot/function has a device fitted. - */ - (void)pci_read_config_dword(0, - ucSlotNumber, - ucFnNumber, - PCI_CLASS_REVISION, - &ulClass); - ulClass >>= 16; - if (ulClass == PCI_CLASS_BRIDGE_PCI) { -/* - * We have found a PCI-PCI bridge - */ - (void)pci_read_config_byte(0, - ucSlotNumber, - ucFnNumber, - PCI_SUBORDINATE_BUS, - &ucMaxSubordinate); - if(ucMaxSubordinate>ucMaxPCIBus) { - ucMaxPCIBus=ucMaxSubordinate; - } - } - } - } - return 0; -} - -/* - * Return the number of PCI busses in the system - */ -unsigned char BusCountPCI(void) -{ - return(ucMaxPCIBus+1); -} diff --git a/c/src/lib/libbsp/sparc/leon2/preinstall.am b/c/src/lib/libbsp/sparc/leon2/preinstall.am index 4b7d4daf29..b53baabfe8 100644 --- a/c/src/lib/libbsp/sparc/leon2/preinstall.am +++ b/c/src/lib/libbsp/sparc/leon2/preinstall.am @@ -45,107 +45,221 @@ $(PROJECT_INCLUDE)/tm27.h: include/tm27.h $(PROJECT_INCLUDE)/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/tm27.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/tm27.h -$(PROJECT_INCLUDE)/rasta.h: include/rasta.h $(PROJECT_INCLUDE)/$(dirstamp) - $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rasta.h -PREINSTALL_FILES += $(PROJECT_INCLUDE)/rasta.h +$(PROJECT_INCLUDE)/debug_defs.h: ../../sparc/shared/include/debug_defs.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/debug_defs.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/debug_defs.h + +$(PROJECT_INCLUDE)/bspopts.h: include/bspopts.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bspopts.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bspopts.h + +$(PROJECT_INCLUDE)/bsp/bootcard.h: ../../shared/include/bootcard.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/bootcard.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/bootcard.h + +$(PROJECT_INCLUDE)/leon.h: include/leon.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/leon.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/leon.h + +$(PROJECT_INCLUDE)/coverhd.h: ../../shared/include/coverhd.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/coverhd.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/coverhd.h + +$(PROJECT_LIB)/start.$(OBJEXT): start.$(OBJEXT) $(PROJECT_LIB)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_LIB)/start.$(OBJEXT) +TMPINSTALL_FILES += $(PROJECT_LIB)/start.$(OBJEXT) + +$(PROJECT_LIB)/linkcmds: startup/linkcmds $(PROJECT_LIB)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_LIB)/linkcmds +PREINSTALL_FILES += $(PROJECT_LIB)/linkcmds + +$(PROJECT_LIB)/linkcmds.base: ../shared/startup/linkcmds.base $(PROJECT_LIB)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_LIB)/linkcmds.base +PREINSTALL_FILES += $(PROJECT_LIB)/linkcmds.base + +$(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 -$(PROJECT_INCLUDE)/cchip.h: include/cchip.h $(PROJECT_INCLUDE)/$(dirstamp) - $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/cchip.h -PREINSTALL_FILES += $(PROJECT_INCLUDE)/cchip.h +$(PROJECT_INCLUDE)/bsp/irq-info.h: ../../shared/include/irq-info.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/irq-info.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/irq-info.h + +$(PROJECT_INCLUDE)/bsp/irq.h: include/bsp/irq.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/irq.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/irq.h $(PROJECT_INCLUDE)/ambapp.h: ../../sparc/shared/include/ambapp.h $(PROJECT_INCLUDE)/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/ambapp.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/ambapp.h -$(PROJECT_INCLUDE)/grspw.h: ../../sparc/shared/include/grspw.h $(PROJECT_INCLUDE)/$(dirstamp) - $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/grspw.h -PREINSTALL_FILES += $(PROJECT_INCLUDE)/grspw.h +$(PROJECT_INCLUDE)/ambapp_ids.h: ../../sparc/shared/include/ambapp_ids.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/ambapp_ids.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/ambapp_ids.h + +$(PROJECT_INCLUDE)/grlib.h: ../../sparc/shared/include/grlib.h $(PROJECT_INCLUDE)/$(dirstamp) + $(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 + +$(PROJECT_INCLUDE)/grpci2.h: ../../sparc/shared/include/grpci2.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/grpci2.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/grpci2.h + +$(PROJECT_INCLUDE)/gr_701.h: ../../sparc/shared/include/gr_701.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/gr_701.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/gr_701.h + +$(PROJECT_INCLUDE)/gr_rasta_adcdac.h: ../../sparc/shared/include/gr_rasta_adcdac.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/gr_rasta_adcdac.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/gr_rasta_adcdac.h + +$(PROJECT_INCLUDE)/gr_rasta_io.h: ../../sparc/shared/include/gr_rasta_io.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/gr_rasta_io.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/gr_rasta_io.h + +$(PROJECT_INCLUDE)/gr_rasta_tmtc.h: ../../sparc/shared/include/gr_rasta_tmtc.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/gr_rasta_tmtc.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/gr_rasta_tmtc.h + +$(PROJECT_INCLUDE)/b1553brm.h: ../../sparc/shared/include/b1553brm.h $(PROJECT_INCLUDE)/$(dirstamp) + $(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)/grspw_pci.h: ../../sparc/shared/include/grspw_pci.h $(PROJECT_INCLUDE)/$(dirstamp) - $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/grspw_pci.h -PREINSTALL_FILES += $(PROJECT_INCLUDE)/grspw_pci.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)/grspw_rasta.h: ../../sparc/shared/include/grspw_rasta.h $(PROJECT_INCLUDE)/$(dirstamp) - $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/grspw_rasta.h -PREINSTALL_FILES += $(PROJECT_INCLUDE)/grspw_rasta.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 -$(PROJECT_INCLUDE)/occan_pci.h: ../../sparc/shared/include/occan_pci.h $(PROJECT_INCLUDE)/$(dirstamp) - $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/occan_pci.h -PREINSTALL_FILES += $(PROJECT_INCLUDE)/occan_pci.h - $(PROJECT_INCLUDE)/grcan.h: ../../sparc/shared/include/grcan.h $(PROJECT_INCLUDE)/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/grcan.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/grcan.h -$(PROJECT_INCLUDE)/grcan_rasta.h: ../../sparc/shared/include/grcan_rasta.h $(PROJECT_INCLUDE)/$(dirstamp) - $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/grcan_rasta.h -PREINSTALL_FILES += $(PROJECT_INCLUDE)/grcan_rasta.h +$(PROJECT_INCLUDE)/grspw.h: ../../sparc/shared/include/grspw.h $(PROJECT_INCLUDE)/$(dirstamp) + $(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)/rmap.h: ../../sparc/shared/include/rmap.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rmap.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rmap.h + +$(PROJECT_INCLUDE)/rmap_drv_grspw.h: ../../sparc/shared/include/rmap_drv_grspw.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rmap_drv_grspw.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rmap_drv_grspw.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 -$(PROJECT_INCLUDE)/apbuart_pci.h: ../../sparc/shared/include/apbuart_pci.h $(PROJECT_INCLUDE)/$(dirstamp) - $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/apbuart_pci.h -PREINSTALL_FILES += $(PROJECT_INCLUDE)/apbuart_pci.h +$(PROJECT_INCLUDE)/i2cmst.h: ../../sparc/shared/include/i2cmst.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/i2cmst.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/i2cmst.h -$(PROJECT_INCLUDE)/apbuart_rasta.h: ../../sparc/shared/include/apbuart_rasta.h $(PROJECT_INCLUDE)/$(dirstamp) - $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/apbuart_rasta.h -PREINSTALL_FILES += $(PROJECT_INCLUDE)/apbuart_rasta.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)/b1553brm.h: ../../sparc/shared/include/b1553brm.h $(PROJECT_INCLUDE)/$(dirstamp) - $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/b1553brm.h -PREINSTALL_FILES += $(PROJECT_INCLUDE)/b1553brm.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)/b1553brm_pci.h: ../../sparc/shared/include/b1553brm_pci.h $(PROJECT_INCLUDE)/$(dirstamp) - $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/b1553brm_pci.h -PREINSTALL_FILES += $(PROJECT_INCLUDE)/b1553brm_pci.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)/b1553brm_rasta.h: ../../sparc/shared/include/b1553brm_rasta.h $(PROJECT_INCLUDE)/$(dirstamp) - $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/b1553brm_rasta.h -PREINSTALL_FILES += $(PROJECT_INCLUDE)/b1553brm_rasta.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)/debug_defs.h: ../../sparc/shared/include/debug_defs.h $(PROJECT_INCLUDE)/$(dirstamp) - $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/debug_defs.h -PREINSTALL_FILES += $(PROJECT_INCLUDE)/debug_defs.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)/pci.h: ../../sparc/shared/include/pci.h $(PROJECT_INCLUDE)/$(dirstamp) - $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/pci.h -PREINSTALL_FILES += $(PROJECT_INCLUDE)/pci.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)/bspopts.h: include/bspopts.h $(PROJECT_INCLUDE)/$(dirstamp) - $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bspopts.h -PREINSTALL_FILES += $(PROJECT_INCLUDE)/bspopts.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)/bsp/bootcard.h: ../../shared/include/bootcard.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) - $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/bootcard.h -PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/bootcard.h +$(PROJECT_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)/leon.h: include/leon.h $(PROJECT_INCLUDE)/$(dirstamp) - $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/leon.h -PREINSTALL_FILES += $(PROJECT_INCLUDE)/leon.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)/coverhd.h: ../../shared/include/coverhd.h $(PROJECT_INCLUDE)/$(dirstamp) - $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/coverhd.h -PREINSTALL_FILES += $(PROJECT_INCLUDE)/coverhd.h +$(PROJECT_INCLUDE)/drvmgr/$(dirstamp): + @$(MKDIR_P) $(PROJECT_INCLUDE)/drvmgr + @: > $(PROJECT_INCLUDE)/drvmgr/$(dirstamp) +PREINSTALL_DIRS += $(PROJECT_INCLUDE)/drvmgr/$(dirstamp) -$(PROJECT_LIB)/start.$(OBJEXT): start.$(OBJEXT) $(PROJECT_LIB)/$(dirstamp) - $(INSTALL_DATA) $< $(PROJECT_LIB)/start.$(OBJEXT) -TMPINSTALL_FILES += $(PROJECT_LIB)/start.$(OBJEXT) +$(PROJECT_INCLUDE)/drvmgr/ambapp_bus.h: ../../sparc/shared/include/drvmgr/ambapp_bus.h $(PROJECT_INCLUDE)/drvmgr/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/drvmgr/ambapp_bus.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/drvmgr/ambapp_bus.h -$(PROJECT_LIB)/linkcmds: startup/linkcmds $(PROJECT_LIB)/$(dirstamp) - $(INSTALL_DATA) $< $(PROJECT_LIB)/linkcmds -PREINSTALL_FILES += $(PROJECT_LIB)/linkcmds +$(PROJECT_INCLUDE)/drvmgr/ambapp_bus_rmap.h: ../../sparc/shared/include/drvmgr/ambapp_bus_rmap.h $(PROJECT_INCLUDE)/drvmgr/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/drvmgr/ambapp_bus_rmap.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/drvmgr/ambapp_bus_rmap.h -$(PROJECT_LIB)/linkcmds.base: ../shared/startup/linkcmds.base $(PROJECT_LIB)/$(dirstamp) - $(INSTALL_DATA) $< $(PROJECT_LIB)/linkcmds.base -PREINSTALL_FILES += $(PROJECT_LIB)/linkcmds.base +$(PROJECT_INCLUDE)/drvmgr/leon2_amba_bus.h: ../../sparc/shared/include/drvmgr/leon2_amba_bus.h $(PROJECT_INCLUDE)/drvmgr/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/drvmgr/leon2_amba_bus.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/drvmgr/leon2_amba_bus.h -$(PROJECT_INCLUDE)/i2cmst.h: ../../sparc/shared/include/i2cmst.h $(PROJECT_INCLUDE)/$(dirstamp) - $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/i2cmst.h -PREINSTALL_FILES += $(PROJECT_INCLUDE)/i2cmst.h +$(PROJECT_INCLUDE)/drvmgr/spw_bus.h: ../../sparc/shared/include/drvmgr/spw_bus.h $(PROJECT_INCLUDE)/drvmgr/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/drvmgr/spw_bus.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/drvmgr/spw_bus.h +$(PROJECT_INCLUDE)/drvmgr/spw_bus_ids.h: ../../sparc/shared/include/drvmgr/spw_bus_ids.h $(PROJECT_INCLUDE)/drvmgr/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/drvmgr/spw_bus_ids.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/drvmgr/spw_bus_ids.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 +endif diff --git a/c/src/lib/libbsp/sparc/leon2/rasta/rasta.c b/c/src/lib/libbsp/sparc/leon2/rasta/rasta.c deleted file mode 100644 index 7b66b07246..0000000000 --- a/c/src/lib/libbsp/sparc/leon2/rasta/rasta.c +++ /dev/null @@ -1,388 +0,0 @@ -/* - * $Id$ - */ - -#include <rtems/bspIo.h> -#include <pci.h> -#include <rasta.h> -#include <ambapp.h> -#include <grcan_rasta.h> -#include <grspw_rasta.h> -#include <b1553brm_rasta.h> -#include <apbuart_rasta.h> - -#include <string.h> - -/* If RASTA_SRAM is defined SRAM will be used, else SDRAM */ -/*#define RASTA_SRAM 1*/ - -#define RASTA_IRQ 5 - -/* Offset from 0x80000000 (dual bus version) */ -#define AHB1_IOAREA_BASE_ADDR 0x80100000 -#define APB2_OFFSET 0x200000 -#define IRQ_OFFSET 0x200500 -#define GRHCAN_OFFSET 0x201000 -#define BRM_OFFSET 0x100000 -#define SPW_OFFSET 0xa00 -#define UART_OFFSET 0x200200 -#define GPIO0_OFF 0x200600 -#define GPIO1_OFF 0x200700 - -/* #define DEBUG 1 */ - -#ifdef DEBUG -#define DBG(x...) printk(x) -#else -#define DBG(x...) -#endif - -/* -typedef struct { - volatile unsigned int ilevel; - volatile unsigned int ipend; - volatile unsigned int iforce; - volatile unsigned int iclear; - volatile unsigned int mpstat; - volatile unsigned int notused01; - volatile unsigned int notused02; - volatile unsigned int notused03; - volatile unsigned int notused10; - volatile unsigned int notused11; - volatile unsigned int notused12; - volatile unsigned int notused13; - volatile unsigned int notused20; - volatile unsigned int notused21; - volatile unsigned int notused22; - volatile unsigned int notused23; - volatile unsigned int mask[16]; - volatile unsigned int force[16]; -} LEON3_IrqCtrl_Regs_Map; -*/ -static int bus, dev, fun; - -LEON3_IrqCtrl_Regs_Map *irq = NULL; -LEON_Register_Map *regs = (LEON_Register_Map *)0x80000000; - -struct gpio_reg *gpio0, *gpio1; - -/* static rtems_isr pci_interrupt_handler (rtems_vector_number v) { */ - -/* volatile unsigned int *pci_int = (volatile unsigned int *) 0x80000168; */ -/* volatile unsigned int *pci_mem = (volatile unsigned int *) 0xb0400000; */ - -/* if (*pci_int & 0x20) { */ - -/* *pci_int = 0x20; */ - -/* *pci_mem = 0; */ - -/* printk("pci died\n"); */ - -/* } */ - -/* } */ - -void *uart0_int_arg, *uart1_int_arg; -void *spw0_int_arg, *spw1_int_arg, *spw2_int_arg; -void *grcan_int_arg; -void *brm_int_arg; - -void (*uart0_int_handler)(int irq, void *arg) = NULL; -void (*uart1_int_handler)(int irq, void *arg) = NULL; -void (*spw0_int_handler)(int irq, void *arg) = NULL; -void (*spw1_int_handler)(int irq, void *arg) = NULL; -void (*spw2_int_handler)(int irq, void *arg) = NULL; -void (*grcan_int_handler)(int irq, void *arg) = NULL; -void (*brm_int_handler)(int irq, void *arg) = NULL; - -static rtems_isr rasta_interrupt_handler (rtems_vector_number v) -{ - unsigned int status; - - status = irq->ipend; - - if ( (status & GRCAN_IRQ) && grcan_int_handler ) { - grcan_int_handler(GRCAN_IRQNO,grcan_int_arg); - } - - if (status & SPW_IRQ) { - if ( (status & SPW0_IRQ) && spw0_int_handler ){ - spw0_int_handler(SPW0_IRQNO,spw0_int_arg); - } - - if ( (status & SPW1_IRQ) && spw1_int_handler ){ - spw1_int_handler(SPW1_IRQNO,spw1_int_arg); - } - - if ( (status & SPW2_IRQ) && spw2_int_handler ){ - spw2_int_handler(SPW2_IRQNO,spw2_int_arg); - } - } - if ((status & BRM_IRQ) && brm_int_handler ){ - brm_int_handler(BRM_IRQNO,brm_int_arg); - } - if ( (status & UART0_IRQ) && uart0_int_handler ) { - uart0_int_handler(UART0_IRQNO,uart0_int_arg); - } - if ( (status & UART1_IRQ) && uart1_int_handler) { - uart1_int_handler(UART1_IRQNO,uart1_int_arg); - } - - DBG("RASTA-IRQ: 0x%x\n",status); - irq->iclear = status; - -} - -void rasta_interrrupt_register(void *handler, int irqno, void *arg) -{ - DBG("RASTA: Registering irq %d\n",irqno); - if ( irqno == UART0_IRQNO ){ - DBG("RASTA: Registering uart0 handler: 0x%x, arg: 0x%x\n",handler,arg); - uart0_int_handler = handler; - uart0_int_arg = arg; - - /* unmask interrupt source */ - irq->iclear = UART0_IRQ; - irq->mask[0] |= UART0_IRQ; - } - - if ( irqno == UART1_IRQNO ){ - DBG("RASTA: Registering uart1 handler: 0x%x, arg: 0x%x\n",handler,arg); - uart1_int_handler = handler; - uart1_int_arg = arg; - - /* unmask interrupt source */ - irq->iclear = UART1_IRQ; - irq->mask[0] |= UART1_IRQ; - } - - if ( irqno == SPW0_IRQNO ){ - DBG("RASTA: Registering spw0 handler: 0x%x, arg: 0x%x\n",handler,arg); - spw0_int_handler = handler; - spw0_int_arg = arg; - - /* unmask interrupt source */ - irq->iclear = SPW0_IRQ; - irq->mask[0] |= SPW0_IRQ; - } - - if ( irqno == SPW1_IRQNO ){ - DBG("RASTA: Registering spw1 handler: 0x%x, arg: 0x%x\n",handler,arg); - spw1_int_handler = handler; - spw1_int_arg = arg; - - /* unmask interrupt source */ - irq->iclear = SPW1_IRQ; - irq->mask[0] |= SPW1_IRQ; - } - - if ( irqno == SPW2_IRQNO ){ - DBG("RASTA: Registering spw2 handler: 0x%x, arg: 0x%x\n",handler,arg); - spw2_int_handler = handler; - spw2_int_arg = arg; - - /* unmask interrupt source */ - irq->iclear = SPW2_IRQ; - irq->mask[0] |= SPW2_IRQ; - } - - if ( irqno == GRCAN_IRQNO ){ - DBG("RASTA: Registering GRCAN handler: 0x%x, arg: 0x%x\n",handler,arg); - grcan_int_handler = handler; - grcan_int_arg = arg; - - /* unmask interrupt source */ - irq->iclear = GRCAN_IRQ; - irq->mask[0] |= GRCAN_IRQ; - } - - if ( irqno == BRM_IRQNO ){ - DBG("RASTA: Registering BRM handler: 0x%x, arg: 0x%x\n",handler,arg); - brm_int_handler = handler; - brm_int_arg = arg; - - /* unmask interrupt source */ - irq->iclear = BRM_IRQ; - irq->mask[0] |= BRM_IRQ; - } -} - - -int rasta_get_gpio(amba_confarea_type *abus, int index, struct gpio_reg **regs, int *irq) -{ - amba_apb_device dev; - int cores; - - if ( !abus ) - return -1; - - /* Scan PnP info for GPIO port number 'index' */ - cores = amba_find_next_apbslv(abus,VENDOR_GAISLER,GAISLER_PIOPORT,&dev,index); - if ( cores < 1 ) - return -1; - - if ( regs ) - *regs = (struct gpio_reg *)dev.start; - - if ( irq ) - *irq = dev.irq; - - return 0; -} - -/* AMBA Plug&Play information */ -static amba_confarea_type abus; -static struct amba_mmap amba_maps[3]; - -int rasta_register(void) -{ - unsigned int bar0, bar1, data; - - unsigned int *page0 = NULL; - unsigned int *apb_base = NULL; - int found=0; - - - DBG("Searching for RASTA board ..."); - - /* Search PCI vendor/device id. */ - if (BSP_pciFindDevice(0x1AC8, 0x0010, 0, &bus, &dev, &fun) == 0) { - found = 1; - } - - /* Search old PCI vendor/device id. */ - if ( (!found) && (BSP_pciFindDevice(0x16E3, 0x0210, 0, &bus, &dev, &fun) == 0) ) { - found = 1; - } - - /* Did we find a RASTA board? */ - if ( !found ) - return -1; - - DBG(" found it (dev/fun: %d/%d).\n", dev, fun); - - pci_read_config_dword(bus, dev, fun, 0x10, &bar0); - pci_read_config_dword(bus, dev, fun, 0x14, &bar1); - - page0 = (unsigned int *)(bar0 + 0x400000); - *page0 = 0x80000000; /* Point PAGE0 to start of APB */ - - apb_base = (unsigned int *)(bar0+APB2_OFFSET); - -/* apb_base[0] = 0x000002ff; - apb_base[1] = 0x8a205260; - apb_base[2] = 0x00184000; */ - - /* Configure memory controller */ -#ifdef RASTA_SRAM - apb_base[0] = 0x000002ff; - apb_base[1] = 0x00001260; - apb_base[2] = 0x000e8000; -#else - apb_base[0] = 0x000002ff; - apb_base[1] = 0x82206000; - apb_base[2] = 0x000e8000; -#endif - /* Set up rasta irq controller */ - irq = (LEON3_IrqCtrl_Regs_Map *) (bar0+IRQ_OFFSET); - irq->iclear = 0xffff; - irq->ilevel = 0; - irq->mask[0] = 0xffff & ~(UART0_IRQ|UART1_IRQ|SPW0_IRQ|SPW1_IRQ|SPW2_IRQ|GRCAN_IRQ|BRM_IRQ); - - /* Configure AT697 ioport bit 7 to input pci irq */ - regs->PIO_Direction &= ~(1<<7); - regs->PIO_Interrupt |= (0x87<<8); /* level sensitive */ - - apb_base[0x100] |= 0x40000000; /* Set GRPCI mmap 0x4 */ - apb_base[0x104] = 0x40000000; /* 0xA0000000; Point PAGE1 to RAM */ - - - /* set parity error response */ - pci_read_config_dword(bus, dev, fun, 0x4, &data); - pci_write_config_dword(bus, dev, fun, 0x4, data|0x40); - - - pci_master_enable(bus, dev, fun); - - /* install PCI interrupt vector */ - /* set_vector(pci_interrupt_handler,14+0x10, 1); */ - - - /* install interrupt vector */ - set_vector(rasta_interrupt_handler, RASTA_IRQ+0x10, 1); - - /* Scan AMBA Plug&Play */ - - /* AMBA MAP bar0 (in CPU) ==> 0x80000000(remote amba address) */ - amba_maps[0].size = 0x10000000; - amba_maps[0].cpu_adr = bar0; - amba_maps[0].remote_amba_adr = 0x80000000; - - /* AMBA MAP bar1 (in CPU) ==> 0x40000000(remote amba address) */ - amba_maps[1].size = 0x10000000; - amba_maps[1].cpu_adr = bar1; - amba_maps[1].remote_amba_adr = 0x40000000; - - /* Mark end of table */ - amba_maps[2].size=0; - amba_maps[2].cpu_adr = 0; - amba_maps[2].remote_amba_adr = 0; - - memset(&abus,0,sizeof(abus)); - - /* Start AMBA PnP scan at first AHB bus */ - amba_scan(&abus,bar0+(AHB1_IOAREA_BASE_ADDR&~0xf0000000),&amba_maps[0]); - - printk("Registering RASTA GRCAN driver\n\r"); - - /*grhcan_register(bar0 + GRHCAN_OFFSET, bar1);*/ - grcan_rasta_int_reg=rasta_interrrupt_register; - if ( grcan_rasta_ram_register(&abus,bar1+0x20000) ){ - printk("Failed to register RASTA GRCAN driver\n\r"); - return -1; - } - - printk("Registering RASTA BRM driver\n\r"); - - /*brm_register(bar0 + BRM_OFFSET, bar1);*/ - /* register the BRM RASTA driver, use 128k on RASTA SRAM... */ - b1553brm_rasta_int_reg=rasta_interrrupt_register; - if ( b1553brm_rasta_register(&abus,2,0,3,bar1,0x40000000) ){ - printk("Failed to register BRM RASTA driver\n"); - return -1; - } - - /* provide the spacewire driver with AMBA Plug&Play - * info so that it can find the GRSPW cores. - */ - grspw_rasta_int_reg=rasta_interrrupt_register; - if ( grspw_rasta_register(&abus,bar1) ){ - printk("Failed to register RASTA GRSPW driver\n\r"); - return -1; - } - - /* provide the spacewire driver with AMBA Plug&Play - * info so that it can find the GRSPW cores. - */ - apbuart_rasta_int_reg=rasta_interrrupt_register; - if ( apbuart_rasta_register(&abus) ){ - printk("Failed to register RASTA APBUART driver\n\r"); - return -1; - } - - /* Find GPIO0 address */ - if ( rasta_get_gpio(&abus,0,&gpio0,NULL) ){ - printk("Failed to get address for RASTA GPIO0\n\r"); - return -1; - } - - /* Find GPIO1 address */ - if ( rasta_get_gpio(&abus,1,&gpio1,NULL) ){ - printk("Failed to get address for RASTA GPIO1\n\r"); - return -1; - } - - /* Successfully registered the RASTA board */ - return 0; -} diff --git a/c/src/lib/libbsp/sparc/leon2/startup/bsppredriver.c b/c/src/lib/libbsp/sparc/leon2/startup/bsppredriver.c new file mode 100644 index 0000000000..eef11540da --- /dev/null +++ b/c/src/lib/libbsp/sparc/leon2/startup/bsppredriver.c @@ -0,0 +1,80 @@ +/* Installs the BSP pre-driver hook + * + * COPYRIGHT (c) 2011 + * Aeroflex Gaisler + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include <bsp.h> + +/* If RTEMS_DRVMGR_STARTUP is defined extra code is added that + * registers the LEON2 AMBA bus driver as root driver into the + * driver manager. + * + * The structues here are declared weak so that the user can override + * the configuration and add custom cores in the RTEMS project. + */ +#ifdef RTEMS_DRVMGR_STARTUP +#include <drvmgr/leon2_amba_bus.h> + +/* All drivers included by BSP, this is overridden by the user by including + * the devmgr_confdefs.h. No specifc drivers needed by BSP since IRQ/TIMER/UART + * is not drvmgr drivers. + */ +struct drvmgr_drv_reg_func drvmgr_drivers[] __attribute__((weak)) = +{ + {NULL} /* End array with NULL */ +}; + +/* Defines what cores are avilable on the bus in addition to the standard + * LEON2 peripherals. + */ +struct leon2_core leon2_amba_custom_cores[] __attribute__((weak)) = +{ + EMPTY_LEON2_CORE +}; + +/* Configure LEON2 Root bus driver */ +struct leon2_bus leon2_bus_config __attribute__((weak)) = +{ + &leon2_std_cores[0], /* The standard cores, defined by driver */ + &leon2_amba_custom_cores[0], /* custom cores, defined by us */ + DRVMGR_TRANSLATE_ONE2ONE, + DRVMGR_TRANSLATE_ONE2ONE, +}; + +/* Driver resources on LEON2 AMBA bus. Used to set options for particular + * LEON2 cores, it is up to the driver to look at the configuration paramters + * once started. + */ +struct drvmgr_bus_res leon2_amba_res __attribute__((weak)) = +{ + .next = NULL, + .resource = { + RES_EMPTY + }, +}; +#endif + +/* + * bsp_predriver_hook + * + * BSP predriver hook. Called just before drivers are initialized. + * Is used to initialize shared interrupt handling. + */ +void bsp_predriver_hook( void ) +{ + /* Initialize shared interrupt handling, must be done after IRQ + * controller has been found and initialized. + */ + BSP_shared_interrupt_init(); + +#ifdef RTEMS_DRVMGR_STARTUP + leon2_root_register(&leon2_bus_config, &leon2_amba_res); +#endif +} diff --git a/c/src/lib/libbsp/sparc/leon2/startup/setvec.c b/c/src/lib/libbsp/sparc/leon2/startup/setvec.c index c7b8af6616..028b17a26a 100644 --- a/c/src/lib/libbsp/sparc/leon2/startup/setvec.c +++ b/c/src/lib/libbsp/sparc/leon2/startup/setvec.c @@ -42,7 +42,7 @@ rtems_isr_entry set_vector( /* returns old vector */ uint32_t real_trap; uint32_t source; - if ( type ) + if ( type == SET_VECTOR_INT ) rtems_interrupt_catch( handler, vector, &previous_isr ); else _CPU_ISR_install_raw_handler( vector, handler, (void *)&previous_isr ); diff --git a/c/src/lib/libbsp/sparc/leon3/Makefile.am b/c/src/lib/libbsp/sparc/leon3/Makefile.am index ebe8ffbf41..a9d4fea088 100644 --- a/c/src/lib/libbsp/sparc/leon3/Makefile.am +++ b/c/src/lib/libbsp/sparc/leon3/Makefile.am @@ -31,7 +31,10 @@ noinst_LIBRARIES = libbspstart.a libbspstart_a_SOURCES = ../../sparc/shared/start.S project_lib_DATA = start.$(OBJEXT) -dist_project_lib_DATA += startup/linkcmds ../shared/startup/linkcmds.base +dist_project_lib_DATA += \ + startup/linkcmds \ + startup/linkcmds_ngmp \ + ../shared/startup/linkcmds.base noinst_LIBRARIES += libbsp.a libbsp_a_SOURCES = @@ -39,55 +42,196 @@ libbsp_a_SOURCES = # startup libbsp_a_SOURCES += ../../shared/bspclean.c ../../shared/bsplibc.c \ ../../shared/bsppost.c ../../shared/bootcard.c startup/bspstart.c \ - ../../sparc/shared/bsppretaskinghook.c ../../shared/bsppredriverhook.c \ + ../../sparc/shared/bsppretaskinghook.c startup/bsppredriver.c \ ../../sparc/shared/bspgetworkarea.c ../../shared/sbrk.c startup/setvec.c \ + ../../sparc/shared/startup/early_malloc.c \ startup/spurious.c startup/bspidle.S # gnatsupp libbsp_a_SOURCES += gnatsupp/gnatsupp.c ../../sparc/shared/gnatcommon.c -# amba +# AMBA bus include_HEADERS += include/amba.h include_HEADERS += ../../sparc/shared/include/ambapp.h -libbsp_a_SOURCES += amba/amba.c ../../sparc/shared/amba/ambapp.c +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 +libbsp_a_SOURCES += ../../sparc/shared/amba/ambapp_count.c +libbsp_a_SOURCES += ../../sparc/shared/amba/ambapp_depth.c +libbsp_a_SOURCES += ../../sparc/shared/amba/ambapp_find_by_idx.c +libbsp_a_SOURCES += ../../sparc/shared/amba/ambapp_freq.c +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 +libbsp_a_SOURCES += ../../sparc/shared/timer/gptimer.c +libbsp_a_SOURCES += ../../sparc/shared/timer/tlib.c +libbsp_a_SOURCES += ../../sparc/shared/timer/tlib_ckinit.c +# non-Driver Manager Clock Implementation +libbsp_a_SOURCES += clock/ckinit.c ../../../shared/clockdrv_shell.h + # console libbsp_a_SOURCES += console/console.c +libbsp_a_SOURCES += ../../sparc/shared/uart/cons.c +libbsp_a_SOURCES += ../../sparc/shared/uart/apbuart_cons.c +include_HEADERS += ../../sparc/shared/include/cons.h # debugio libbsp_a_SOURCES += console/debugputs.c -# clock -libbsp_a_SOURCES += clock/ckinit.c ../../../shared/clockdrv_shell.h + +# 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 \ + ../../shared/src/irq-generic.c \ + ../../shared/src/irq-legacy.c \ + ../../shared/src/irq-info.c \ + ../../shared/src/irq-shell.c \ + ../../shared/src/irq-server.c + # PCI -include_HEADERS += ../../sparc/shared/include/pci.h -libbsp_a_SOURCES += pci/pci.c ../../sparc/shared/pci/pcifinddevice.c +include_HEADERS += ../../sparc/shared/include/grpci2.h +libbsp_a_SOURCES += ../../sparc/shared/pci/grpci.c +libbsp_a_SOURCES += ../../sparc/shared/pci/grpci2.c +libbsp_a_SOURCES += ../../sparc/shared/pci/pcif.c +libbsp_a_SOURCES += ../../sparc/shared/pci/pci_memreg_sparc_le.c +libbsp_a_SOURCES += ../../sparc/shared/pci/pci_memreg_sparc_be.c + +# PCI target devices +include_HEADERS += ../../sparc/shared/include/gr_701.h +include_HEADERS += ../../sparc/shared/include/gr_rasta_adcdac.h +include_HEADERS += ../../sparc/shared/include/gr_rasta_io.h +include_HEADERS += ../../sparc/shared/include/gr_rasta_tmtc.h +include_HEADERS += ../../sparc/shared/include/gr_tmtc_1553.h +libbsp_a_SOURCES += ../../sparc/shared/pci/gr_701.c +libbsp_a_SOURCES += ../../sparc/shared/pci/gr_rasta_adcdac.c +libbsp_a_SOURCES += ../../sparc/shared/pci/gr_rasta_io.c +libbsp_a_SOURCES += ../../sparc/shared/pci/gr_rasta_spw_router.c +libbsp_a_SOURCES += ../../sparc/shared/pci/gr_rasta_tmtc.c +libbsp_a_SOURCES += ../../sparc/shared/pci/gr_tmtc_1553.c # B1553BRM -include_HEADERS += ../../sparc/shared/include/b1553brm.h \ - ../../sparc/shared/include/b1553brm_pci.h -libbsp_a_SOURCES += ../../sparc/shared/1553/b1553brm.c \ - ../../sparc/shared/1553/b1553brm_pci.c +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 \ - ../../sparc/shared/include/occan_pci.h ../../sparc/shared/include/grcan.h + ../../sparc/shared/include/grcan.h libbsp_a_SOURCES += ../../sparc/shared/can/occan.c \ - ../../sparc/shared/can/occan_pci.c ../../sparc/shared/can/grcan.c + ../../sparc/shared/can/grcan.c # SpaceWire -include_HEADERS += ../../sparc/shared/include/grspw.h \ - ../../sparc/shared/include/grspw_pci.h -libbsp_a_SOURCES += ../../sparc/shared/spw/grspw.c \ - ../../sparc/shared/spw/grspw_pci.c +include_HEADERS += ../../sparc/shared/include/grspw.h +include_HEADERS += ../../sparc/shared/include/grspw_pkt.h +include_HEADERS += ../../sparc/shared/include/grspw_router.h +include_HEADERS += ../../sparc/shared/include/rmap.h +include_HEADERS += ../../sparc/shared/include/rmap_drv_grspw.h +libbsp_a_SOURCES += ../../sparc/shared/spw/grspw.c +libbsp_a_SOURCES += ../../sparc/shared/spw/grspw_pkt.c +libbsp_a_SOURCES += ../../sparc/shared/spw/grspw_router.c +libbsp_a_SOURCES += ../../sparc/shared/spw/rmap.c +libbsp_a_SOURCES += ../../sparc/shared/spw/rmap_drv_grspw.c # UART -include_HEADERS += ../../sparc/shared/include/apbuart.h \ - ../../sparc/shared/include/apbuart_pci.h -libbsp_a_SOURCES += ../../sparc/shared/uart/apbuart.c \ - ../../sparc/shared/uart/apbuart_pci.c +include_HEADERS += ../../sparc/shared/include/apbuart.h +libbsp_a_SOURCES += ../../sparc/shared/uart/apbuart.c # I2CMST 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 + +# GRAES +include_HEADERS += ../../sparc/shared/include/graes.h +libbsp_a_SOURCES += ../../sparc/shared/graes/graes.c + +# GRPWRX +include_HEADERS += ../../sparc/shared/include/grpwrx.h +libbsp_a_SOURCES += ../../sparc/shared/grpwrx/grpwrx.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 +libbsp_a_SOURCES += ../../sparc/shared/mem/mctrl_rmap.c + # timer libbsp_a_SOURCES += timer/timer.c +libbsp_a_SOURCES += timer/watchdog.c + +# 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/grtc_rmap.c +libbsp_a_SOURCES += ../../sparc/shared/tmtc/grtm.c +libbsp_a_SOURCES += ../../sparc/shared/tmtc/grtm_rmap.c + +# Driver Manager +include_drvmgrdir = $(includedir)/drvmgr +include_drvmgr_HEADERS = ../../sparc/shared/include/drvmgr/ambapp_bus_grlib.h +include_drvmgr_HEADERS += ../../sparc/shared/include/drvmgr/ambapp_bus_rmap.h +include_drvmgr_HEADERS += ../../sparc/shared/include/drvmgr/ambapp_bus.h +include_drvmgr_HEADERS += ../../sparc/shared/include/drvmgr/spw_bus.h +include_drvmgr_HEADERS += ../../sparc/shared/include/drvmgr/spw_bus_ids.h +libbsp_a_SOURCES += ../../sparc/shared/drvmgr/ambapp_bus.c +libbsp_a_SOURCES += ../../sparc/shared/drvmgr/ambapp_bus_grlib.c +libbsp_a_SOURCES += ../../sparc/shared/drvmgr/ambapp_bus_rmap.c +libbsp_a_SOURCES += ../../sparc/shared/drvmgr/spw_bus.c if HAS_NETWORKING noinst_PROGRAMS += leon_smc91111.rel @@ -107,12 +251,20 @@ 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) endif +# BSP Network configuration +if HAS_NETWORKING +include_HEADERS += ../../sparc/shared/include/network_interface_add.h +libbsp_a_SOURCES += ../../sparc/shared/net/network_interface_add.c +endif + EXTRA_DIST = shmsupp/README if HAS_MP # shmsupp @@ -120,7 +272,9 @@ libbsp_a_SOURCES += shmsupp/addrconv.c shmsupp/getcfg.c shmsupp/lock.c \ shmsupp/mpisr.c endif -libbsp_a_LIBADD = ../../../libcpu/@RTEMS_CPU@/cache.rel \ +libbsp_a_LIBADD = \ + ../../../libcpu/@RTEMS_CPU@/access.rel \ + ../../../libcpu/@RTEMS_CPU@/cache.rel \ ../../../libcpu/@RTEMS_CPU@/reg_win.rel \ ../../../libcpu/@RTEMS_CPU@/syscall.rel diff --git a/c/src/lib/libbsp/sparc/leon3/amba/amba.c b/c/src/lib/libbsp/sparc/leon3/amba/amba.c index 6f86e27ded..a439300d27 100644 --- a/c/src/lib/libbsp/sparc/leon3/amba/amba.c +++ b/c/src/lib/libbsp/sparc/leon3/amba/amba.c @@ -1,10 +1,10 @@ /* - * AMBA Plag & Play Bus Driver + * AMBA Plug & Play Bus Driver * * This driver hook performs bus scanning. * - * COPYRIGHT (c) 2004. - * Gaisler Research + * COPYRIGHT (c) 2011. + * Aeroflex Gaisler * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at @@ -14,14 +14,60 @@ */ #include <bsp.h> +#include <ambapp.h> -/* Structure containing address to devices found on the Amba Plug&Play bus */ -amba_confarea_type amba_conf; +/* AMBA Plug&Play information description. + * + * After software has scanned AMBA PnP it builds a tree to make + * it easier for drivers to work with the bus architecture. + */ +struct ambapp_bus ambapp_plb; -/* Pointers to Interrupt Controller configuration registers */ -volatile LEON3_IrqCtrl_Regs_Map *LEON3_IrqCtrl_Regs; +/* If RTEMS_DRVMGR_STARTUP is defined extra code is added that + * registers the GRLIB AMBA PnP bus driver as root driver. + */ +#ifdef RTEMS_DRVMGR_STARTUP +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus_grlib.h> + +extern void gptimer_register_drv (void); +extern void apbuart_cons_register_drv(void); +/* All drivers included by BSP, this is overridden by the user by including + * the drvmgr_confdefs.h. By default the Timer and UART driver are included. + */ +struct drvmgr_drv_reg_func drvmgr_drivers[] __attribute__((weak)) = +{ + {gptimer_register_drv}, + {apbuart_cons_register_drv}, + {NULL} /* End array with NULL */ +}; + +/* Driver resources configuration for AMBA root bus. It is declared weak + * so that the user may override it, if the defualt settings are not + * enough. + */ +struct drvmgr_bus_res grlib_drv_resources __attribute__((weak)) = +{ + .next = NULL, + .resource = + { + RES_EMPTY, + } +}; + +/* GRLIB AMBA bus configuration (the LEON3 root bus configuration) */ +struct grlib_config grlib_bus_config = +{ + &ambapp_plb, /* AMBAPP bus setup */ + &grlib_drv_resources, /* Driver configuration */ +}; +#endif -int LEON3_Cpu_Index = 0; +/* GRLIB extended IRQ controller register */ +extern void leon3_ext_irq_init(void); + +/* Pointers to Interrupt Controller configuration registers */ +volatile struct irqmp_regs *LEON3_IrqCtrl_Regs; /* * amba_initialize @@ -33,45 +79,67 @@ int LEON3_Cpu_Index = 0; * amba_ahb_masters, amba_ahb_slaves and amba. */ -unsigned int getasr17(void); - -asm(" .text \n" - "getasr17: \n" - "retl \n" - "mov %asr17, %o0\n" -); +void amba_initialize(void) +{ + int icsel; + struct ambapp_dev *adev; + /* Scan AMBA Plug&Play read-only information. The routine builds a PnP + * tree into ambapp_plb in RAM, after this we never access the PnP + * information in hardware directly any more. + * Since on Processor Local Bus (PLB) memory mapping is 1:1 + */ + ambapp_scan(&ambapp_plb, LEON3_IO_AREA, NULL, NULL); -extern rtems_configuration_table Configuration; -extern int scan_uarts(void); + /* Find LEON3 Interrupt controller */ + adev = (void *)ambapp_for_each(&ambapp_plb, (OPTIONS_ALL|OPTIONS_APB_SLVS), + VENDOR_GAISLER, GAISLER_IRQMP, + ambapp_find_by_idx, NULL); + if (adev == NULL) { + /* PANIC IRQ controller not found! + * + * What else can we do but stop ... + */ + asm volatile( "mov 1, %g1; ta 0x0" ); + } -void amba_initialize(void) -{ - int i; - amba_apb_device dev; - - /* Scan the AMBA Plug&Play info at the default LEON3 area */ - amba_scan(&amba_conf,LEON3_IO_AREA,NULL); - - /* Find LEON3 Interrupt controler */ - i = amba_find_apbslv(&amba_conf,VENDOR_GAISLER,GAISLER_IRQMP,&dev); - if ( i > 0 ){ - /* Found APB IRQ_MP Interrupt Controller */ - LEON3_IrqCtrl_Regs = (volatile LEON3_IrqCtrl_Regs_Map *) dev.start; -#if defined(RTEMS_MULTIPROCESSING) - if (rtems_configuration_get_user_multiprocessing_table() != NULL) { - unsigned int tmp = getasr17(); - LEON3_Cpu_Index = (tmp >> 28) & 3; - } -#endif + LEON3_IrqCtrl_Regs = (volatile struct irqmp_regs *)DEV_TO_APB(adev)->start; + if ((LEON3_IrqCtrl_Regs->ampctrl >> 28) > 0) { + /* IRQ Controller has support for multiple IRQ Controllers, each + * CPU can be routed to different Controllers, we find out which + * controller by looking at the IRQCTRL Select Register for this CPU. + * Each Controller is located at a 4KByte offset. + */ + icsel = LEON3_IrqCtrl_Regs->icsel[LEON3_Cpu_Index/8]; + icsel = (icsel >> ((7 - (LEON3_Cpu_Index & 0x7)) * 4)) & 0xf; + LEON3_IrqCtrl_Regs += icsel; + LEON3_IrqCtrl_Regs->mask[LEON3_Cpu_Index] = 0; + LEON3_IrqCtrl_Regs->force[LEON3_Cpu_Index] = 0; + LEON3_IrqCtrl_Regs->iclear = 0xffffffff; } + /* Init Extended IRQ controller if available */ + leon3_ext_irq_init(); + + /* If we are running without Driver Manager at startup, we must still + * assure that Timer and Console UART is working. So we can not + * depend on the DrvMgr capable Timer and Console UART drivers, + * instead we use the small-footprint drivers. + */ +#ifndef RTEMS_DRVMGR_STARTUP /* find GP Timer */ - i = amba_find_apbslv(&amba_conf,VENDOR_GAISLER,GAISLER_GPTIMER,&dev); - if ( i > 0 ){ - LEON3_Timer_Regs = (volatile LEON3_Timer_Regs_Map *) dev.start; - } + adev = (void *)ambapp_for_each(&ambapp_plb, (OPTIONS_ALL|OPTIONS_APB_SLVS), + VENDOR_GAISLER, GAISLER_GPTIMER, + ambapp_find_by_idx, NULL); + if (adev) { + LEON3_Timer_Regs = (volatile struct gptimer_regs *)DEV_TO_APB(adev)->start; - /* find UARTS */ - scan_uarts(); + /* Register AMBA Bus Frequency */ + ambapp_freq_init(&ambapp_plb, adev, + (LEON3_Timer_Regs->scaler_reload + 1) * 1000000); + } +#else + /* Register Root bus, Use GRLIB AMBA PnP bus as root bus for LEON3 */ + ambapp_grlib_root_register(&grlib_bus_config); +#endif } diff --git a/c/src/lib/libbsp/sparc/leon3/clock/ckinit.c b/c/src/lib/libbsp/sparc/leon3/clock/ckinit.c index 5bd41bb7cd..dd197370ac 100644 --- a/c/src/lib/libbsp/sparc/leon3/clock/ckinit.c +++ b/c/src/lib/libbsp/sparc/leon3/clock/ckinit.c @@ -23,6 +23,16 @@ #include <bsp.h> #include <bspopts.h> +/* The LEON3 BSP Timer driver can rely on the Driver Manager if the + * DrvMgr is initialized during startup. Otherwise the classic driver + * must be used. + * + * The DrvMgr Clock driver is located in the shared/timer directory + */ +#ifndef RTEMS_DRVMGR_STARTUP + +#include <ambapp.h> + #if SIMSPARC_FAST_IDLE==1 #define CLOCK_DRIVER_USE_FAST_IDLE #endif @@ -39,7 +49,7 @@ #endif -volatile LEON3_Timer_Regs_Map *LEON3_Timer_Regs = 0; +volatile struct gptimer_regs *LEON3_Timer_Regs = 0; static int clkirq; #define CLOCK_VECTOR LEON_TRAP_TYPE( clkirq ) @@ -54,20 +64,21 @@ static int clkirq; } \ } while(0) #else - #define Adjust_clkirq_for_node() + #define Adjust_clkirq_for_node() do { clkirq += LEON3_CLOCK_INDEX; } while(0) #endif #define Clock_driver_support_find_timer() \ do { \ - int cnt; \ - amba_apb_device dev; \ + struct ambapp_dev *adev; \ \ - /* Find LEON3 GP Timer */ \ - cnt = amba_find_apbslv(&amba_conf,VENDOR_GAISLER,GAISLER_GPTIMER,&dev); \ - if ( cnt > 0 ){ \ + /* Find first LEON3 GP Timer */ \ + adev = (void *)ambapp_for_each(&ambapp_plb, (OPTIONS_ALL|OPTIONS_APB_SLVS),\ + VENDOR_GAISLER, GAISLER_GPTIMER, ambapp_find_by_idx, NULL); \ + if ( adev ) { \ /* Found APB GPTIMER Timer */ \ - LEON3_Timer_Regs = (volatile LEON3_Timer_Regs_Map *) dev.start; \ - clkirq = (LEON3_Timer_Regs->status & 0xfc) >> 3; \ + LEON3_Timer_Regs = (volatile struct gptimer_regs *) \ + DEV_TO_APB(adev)->start; \ + clkirq = (LEON3_Timer_Regs->cfg & 0xf8) >> 3; \ \ Adjust_clkirq_for_node(); \ } \ @@ -83,7 +94,7 @@ static int clkirq; LEON3_Timer_Regs->timer[LEON3_CLOCK_INDEX].reload = \ rtems_configuration_get_microseconds_per_tick() - 1; \ \ - LEON3_Timer_Regs->timer[LEON3_CLOCK_INDEX].conf = \ + LEON3_Timer_Regs->timer[LEON3_CLOCK_INDEX].ctrl = \ LEON3_GPTIMER_EN | LEON3_GPTIMER_RL | \ LEON3_GPTIMER_LD | LEON3_GPTIMER_IRQEN; \ } while (0) @@ -91,7 +102,7 @@ static int clkirq; #define Clock_driver_support_shutdown_hardware() \ do { \ LEON_Mask_interrupt(LEON_TRAP_TYPE(clkirq)); \ - LEON3_Timer_Regs->timer[LEON3_CLOCK_INDEX].conf = 0; \ + LEON3_Timer_Regs->timer[LEON3_CLOCK_INDEX].ctrl = 0; \ } while (0) uint32_t bsp_clock_nanoseconds_since_last_tick(void) @@ -102,10 +113,10 @@ uint32_t bsp_clock_nanoseconds_since_last_tick(void) if ( !LEON3_Timer_Regs ) return 0; - clicks = LEON3_Timer_Regs->timer[0].value; + clicks = LEON3_Timer_Regs->timer[LEON3_CLOCK_INDEX].value; if ( LEON_Is_interrupt_pending( clkirq ) ) { - clicks = LEON3_Timer_Regs->timer[0].value; + clicks = LEON3_Timer_Regs->timer[LEON3_CLOCK_INDEX].value; usecs = (2*rtems_configuration_get_microseconds_per_tick() - clicks); } else { usecs = (rtems_configuration_get_microseconds_per_tick() - clicks); @@ -118,3 +129,5 @@ uint32_t bsp_clock_nanoseconds_since_last_tick(void) bsp_clock_nanoseconds_since_last_tick #include "../../../shared/clockdrv_shell.h" + +#endif diff --git a/c/src/lib/libbsp/sparc/leon3/console/console.c b/c/src/lib/libbsp/sparc/leon3/console/console.c index fee8721dee..0ac17be696 100644 --- a/c/src/lib/libbsp/sparc/leon3/console/console.c +++ b/c/src/lib/libbsp/sparc/leon3/console/console.c @@ -17,6 +17,17 @@ * $Id$ */ +/* Define CONSOLE_USE_INTERRUPTS to enable APBUART interrupt handling instead + * of polling mode. + * + * Note that it is not possible to use the interrupt mode of the driver + * together with the "old" APBUART and -u to GRMON. However the new + * APBUART core (from GRLIB 1.0.17-b2710) has the GRMON debug bit and can + * handle interrupts. + * + * NOTE: This can be defined in the make/custom/leon3.cfg file. + */ + #include <bsp.h> #include <rtems/libio.h> #include <stdlib.h> @@ -24,59 +35,281 @@ #include <rtems/bspIo.h> #include <amba.h> -/* - * Should we use a polled or interrupt drived console? - * - * NOTE: This is defined in the custom/leon.cfg file. +#ifndef RTEMS_DRVMGR_STARTUP + +/* Let user override which on-chip APBUART will be debug UART + * 0 = Default APBUART. On MP system CPU0=APBUART0, CPU1=APBUART1... + * 1 = APBUART[0] + * 2 = APBUART[1] + * 3 = APBUART[2] + * ... */ +int syscon_uart_index __attribute__((weak)) = 0; /* - * console_outbyte_polled + * apbuart_outbyte_polled * * This routine transmits a character using polling. */ -void console_outbyte_polled( - int port, - char ch -); +extern void apbuart_outbyte_polled( + struct apbuart_regs *regs, + unsigned char ch, + int do_cr_on_newline, + int wait_sent); + /* body is in debugputs.c */ /* - * console_inbyte_nonblocking + * apbuart_inbyte_nonblocking * * This routine polls for a character. */ -int console_inbyte_nonblocking( int port ); +extern int apbuart_inbyte_nonblocking(struct apbuart_regs *regs); /* body is in debugputs.c */ +struct apbuart_priv { + struct apbuart_regs *regs; + unsigned int freq_hz; +#if CONSOLE_USE_INTERRUPTS + int irq; + void *cookie; + volatile int sending; + char *buf; +#endif +}; +static struct apbuart_priv apbuarts[BSP_NUMBER_OF_TERMIOS_PORTS]; +static int uarts = 0; + +#if CONSOLE_USE_INTERRUPTS + +/* Handle UART interrupts */ +void console_isr(void *arg) +{ + struct apbuart_priv *uart = arg; + unsigned int status; + char data; + + /* Get all received characters */ + while ((status=uart->regs->status) & LEON_REG_UART_STATUS_DR) { + /* Data has arrived, get new data */ + data = uart->regs->data; + + /* Tell termios layer about new character */ + rtems_termios_enqueue_raw_characters(uart->cookie, &data, 1); + } + + if (status & LEON_REG_UART_STATUS_THE) { + /* Sent the one char, we disable TX interrupts */ + uart->regs->ctrl &= ~LEON_REG_UART_CTRL_TI; + + /* Tell close that we sent everything */ + uart->sending = 0; + + /* write_interrupt will get called from this function */ + rtems_termios_dequeue_characters(uart->cookie, 1); + } +} + +/* + * Console Termios Write-Buffer Support Entry Point + * + */ + +int console_write_interrupt (int minor, const char *buf, int len) +{ + struct apbuart_priv *uart; + unsigned int oldLevel; + + if (minor == 0) + uart = &apbuarts[syscon_uart_index]; + else + uart = &apbuarts[minor - 1]; + + /* Remember what position in buffer */ + + rtems_interrupt_disable(oldLevel); + + /* Enable TX interrupt */ + uart->regs->ctrl |= LEON_REG_UART_CTRL_TI; + + /* start UART TX, this will result in an interrupt when done */ + uart->regs->data = *buf; + + uart->sending = 1; + + rtems_interrupt_enable(oldLevel); + + return 0; +} + +#else /* * Console Termios Support Entry Points * */ -ssize_t console_write_support (int minor, const char *buf, size_t len) +ssize_t console_write_polled (int minor, const char *buf, size_t len) { - int nwrite = 0; + int nwrite = 0, port; + + if (minor == 0) + port = syscon_uart_index; + else + port = minor - 1; while (nwrite < len) { - console_outbyte_polled( minor, *buf++ ); + apbuart_outbyte_polled(apbuarts[port].regs, *buf++, 1, 0 ); nwrite++; } return nwrite; } +int console_pollRead(int minor) +{ + int port; + + if (minor == 0) + port = syscon_uart_index; + else + port = minor - 1; + + return apbuart_inbyte_nonblocking(apbuarts[port].regs); +} + +#endif + +int console_set_attributes(int minor, const struct termios *t) +{ + unsigned int scaler; + unsigned int ctrl; + int baud; + struct apbuart_priv *uart; + + switch(t->c_cflag & CSIZE) { + default: + case CS5: + case CS6: + case CS7: + /* Hardware doesn't support other than CS8 */ + return -1; + case CS8: + break; + } + + if (minor == 0) + uart = &apbuarts[syscon_uart_index]; + else + uart = &apbuarts[minor - 1]; + + /* Read out current value */ + ctrl = uart->regs->ctrl; + + switch(t->c_cflag & (PARENB|PARODD)){ + case (PARENB|PARODD): + /* Odd parity */ + ctrl |= LEON_REG_UART_CTRL_PE|LEON_REG_UART_CTRL_PS; + break; + + case PARENB: + /* Even parity */ + ctrl &= ~LEON_REG_UART_CTRL_PS; + ctrl |= LEON_REG_UART_CTRL_PE; + break; + + default: + case 0: + case PARODD: + /* No Parity */ + ctrl &= ~(LEON_REG_UART_CTRL_PS|LEON_REG_UART_CTRL_PE); + } + + if ( !(t->c_cflag & CLOCAL) ){ + ctrl |= LEON_REG_UART_CTRL_FL; + }else{ + ctrl &= ~LEON_REG_UART_CTRL_FL; + } + + /* Update new settings */ + uart->regs->ctrl = ctrl; + + /* Baud rate */ + switch(t->c_cflag & CBAUD){ + default: baud = -1; break; + case B50: baud = 50; break; + case B75: baud = 75; break; + case B110: baud = 110; break; + case B134: baud = 134; break; + case B150: baud = 150; break; + case B200: baud = 200; break; + case B300: baud = 300; break; + case B600: baud = 600; break; + case B1200: baud = 1200; break; + case B1800: baud = 1800; break; + case B2400: baud = 2400; break; + case B4800: baud = 4800; break; + case B9600: baud = 9600; break; + case B19200: baud = 19200; break; + case B38400: baud = 38400; break; + case B57600: baud = 57600; break; + case B115200: baud = 115200; break; + case B230400: baud = 230400; break; + case B460800: baud = 460800; break; + } + + if ( baud > 0 ){ + /* Calculate Baud rate generator "scaler" number */ + scaler = (((uart->freq_hz * 10)/(baud * 8)) - 5) / 10; + + /* Set new baud rate by setting scaler */ + uart->regs->scaler = scaler; + } + + return 0; +} + +/* AMBA PP find routine. Extract AMBA PnP information into data structure. */ +int find_matching_apbuart(struct ambapp_dev *dev, int index, void *arg) +{ + struct ambapp_apb_info *apb = (struct ambapp_apb_info *)dev->devinfo; + + /* Extract needed information of one APBUART */ + apbuarts[uarts].regs = (struct apbuart_regs *)apb->start; +#if CONSOLE_USE_INTERRUPTS + apbuarts[uarts].irq = apb->irq; +#endif + /* Get APBUART core frequency, it is assumed that it is the same + * as Bus frequency where the UART is situated + */ + apbuarts[uarts].freq_hz = ambapp_freq_get(&ambapp_plb, dev); + uarts++; + + if (uarts >= BSP_NUMBER_OF_TERMIOS_PORTS) + return 1; /* Satisfied number of UARTs, stop search */ + else + return 0; /* Continue searching for more UARTs */ +} + +/* Find all UARTs */ +int console_scan_uarts(void) +{ + memset(apbuarts, 0, sizeof(apbuarts)); + + /* Find APBUART cores */ + ambapp_for_each(&ambapp_plb, (OPTIONS_ALL|OPTIONS_APB_SLVS), VENDOR_GAISLER, + GAISLER_APBUART, find_matching_apbuart, NULL); + + return uarts; +} /* * Console Device Driver Entry Points * */ -int uarts = 0; -volatile LEON3_UART_Regs_Map *LEON3_Console_Uart[LEON3_APBUARTS]; rtems_device_driver console_initialize( rtems_device_major_number major, @@ -85,45 +318,50 @@ rtems_device_driver console_initialize( ) { rtems_status_code status; - int i, uart0; + int i; char console_name[16]; rtems_termios_initialize(); - /* default console to zero and override if multiprocessing */ - uart0 = 0; - #if defined(RTEMS_MULTIPROCESSING) - if (rtems_configuration_get_user_multiprocessing_table() != NULL) - uart0 = LEON3_Cpu_Index; - #endif + /* Find UARTs */ + console_scan_uarts(); - /* Register Device Names */ - if (uarts && (uart0 < uarts)) { + /* Update syscon_uart_index to index used as /dev/console + * Let user select System console by setting syscon_uart_index. If the + * BSP is to provide the default UART (syscon_uart_index==0): + * non-MP: APBUART[0] is system console + * MP: LEON CPU index select UART + */ + if (syscon_uart_index == 0) { +#if defined(RTEMS_MULTIPROCESSING) + syscon_uart_index = LEON3_Cpu_Index; +#else + syscon_uart_index = 0; +#endif + } else { + syscon_uart_index = syscon_uart_index - 1; /* User selected sys-console */ + } + + /* Register Device Names + * + * 0 /dev/console - APBUART[USER-SELECTED, DEFAULT=APBUART[0]] + * 1 /dev/console_a - APBUART[0] (by default not present because is console) + * 2 /dev/console_b - APBUART[1] + * ... + * + * On a MP system one should not open UARTs that other OS instances use. + */ + if (syscon_uart_index < uarts) { status = rtems_io_register_name( "/dev/console", major, 0 ); if (status != RTEMS_SUCCESSFUL) rtems_fatal_error_occurred(status); - - strcpy(console_name,"/dev/console_a"); - for (i = uart0+1; i < uarts; i++) { - console_name[13]++; - status = rtems_io_register_name( console_name, major, i); - } } - - /* - * Initialize Hardware if ONLY CPU or first CPU in MP system - */ - - #if defined(RTEMS_MULTIPROCESSING) - if (rtems_configuration_get_user_multiprocessing_table()->node == 1) - #endif - { - for (i = uart0; i < uarts; i++) - { - LEON3_Console_Uart[i]->ctrl |= - LEON_REG_UART_CTRL_RE | LEON_REG_UART_CTRL_TE; - LEON3_Console_Uart[i]->status = 0; - } + strcpy(console_name,"/dev/console_a"); + for (i = 0; i < uarts; i++) { + if (i == syscon_uart_index) + continue; /* skip UART that is registered as /dev/console */ + console_name[13] = 'a' + i; + status = rtems_io_register_name( console_name, major, i+1); } return RTEMS_SUCCESSFUL; @@ -136,25 +374,70 @@ rtems_device_driver console_open( ) { rtems_status_code sc; + struct apbuart_priv *uart; +#if CONSOLE_USE_INTERRUPTS + rtems_libio_open_close_args_t *priv = arg; - static const rtems_termios_callbacks pollCallbacks = { + /* Interrupt mode routines */ + static const rtems_termios_callbacks Callbacks = { + NULL, /* firstOpen */ + NULL, /* lastClose */ + NULL, /* pollRead */ + console_write_interrupt, /* write */ + console_set_attributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + 1 /* outputUsesInterrupts */ + }; +#else + /* Polling mode routines */ + static const rtems_termios_callbacks Callbacks = { NULL, /* firstOpen */ NULL, /* lastClose */ - console_inbyte_nonblocking, /* pollRead */ - console_write_support, /* write */ - NULL, /* setAttributes */ + console_pollRead, /* pollRead */ + console_write_polled, /* write */ + console_set_attributes, /* setAttributes */ NULL, /* stopRemoteTx */ NULL, /* startRemoteTx */ 0 /* outputUsesInterrupts */ }; +#endif - - assert( minor <= LEON3_APBUARTS ); - if ( minor > LEON3_APBUARTS ) + assert(minor <= uarts); + if (minor > uarts || minor == (syscon_uart_index + 1)) return RTEMS_INVALID_NUMBER; - sc = rtems_termios_open (major, minor, arg, &pollCallbacks); - + sc = rtems_termios_open (major, minor, arg, &Callbacks); + if (sc != RTEMS_SUCCESSFUL) + return sc; + + if (minor == 0) + uart = &apbuarts[syscon_uart_index]; + else + uart = &apbuarts[minor - 1]; + +#if CONSOLE_USE_INTERRUPTS + if (priv && priv->iop) + uart->cookie = priv->iop->data1; + else + uart->cookie = NULL; + + /* Register Interrupt handler */ + sc = rtems_interrupt_handler_install(uart->irq, "console", + RTEMS_INTERRUPT_SHARED, console_isr, + uart); + if (sc != RTEMS_SUCCESSFUL) + return sc; + + uart->sending = 0; + /* Enable Receiver and transmitter and Turn on RX interrupts */ + uart->regs->ctrl |= LEON_REG_UART_CTRL_RE | LEON_REG_UART_CTRL_TE | + LEON_REG_UART_CTRL_RI; +#else + /* Initialize UART on opening */ + uart->regs->ctrl |= LEON_REG_UART_CTRL_RE | LEON_REG_UART_CTRL_TE; +#endif + uart->regs->status = 0; return RTEMS_SUCCESSFUL; } @@ -165,6 +448,25 @@ rtems_device_driver console_close( void * arg ) { +#if CONSOLE_USE_INTERRUPTS + struct apbuart_priv *uart; + + if (minor == 0) + uart = &apbuarts[syscon_uart_index]; + else + uart = &apbuarts[minor - 1]; + + /* Turn off RX interrupts */ + uart->regs->ctrl &= ~(LEON_REG_UART_CTRL_RI); + + /**** Flush device ****/ + while (uart->sending) { + /* Wait until all data has been sent */ + } + + /* uninstall ISR */ + rtems_interrupt_handler_remove(uart->irq, console_isr, uart); +#endif return rtems_termios_close (arg); } @@ -195,3 +497,4 @@ rtems_device_driver console_control( return rtems_termios_ioctl (arg); } +#endif diff --git a/c/src/lib/libbsp/sparc/leon3/console/debugputs.c b/c/src/lib/libbsp/sparc/leon3/console/debugputs.c index 2cd0d136e1..b9b9022b5f 100644 --- a/c/src/lib/libbsp/sparc/leon3/console/debugputs.c +++ b/c/src/lib/libbsp/sparc/leon3/console/debugputs.c @@ -7,8 +7,8 @@ * On-Line Applications Research Corporation (OAR). * * Modified for LEON3 BSP. - * COPYRIGHT (c) 2004. - * Gaisler Research. + * COPYRIGHT (c) 2011. + * Aeroflex Gaisler. * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at @@ -21,86 +21,131 @@ #include <rtems/libio.h> #include <stdlib.h> #include <assert.h> - -/* - * Number of uarts on AMBA bus +#include <stdio.h> + +/* Let user override which on-chip APBUART will be debug UART + * 0 = Default APBUART. On MP system CPU0=APBUART0, CPU1=APBUART1... + * 1 = APBUART[0] + * 2 = APBUART[1] + * 3 = APBUART[2] + * ... */ -extern int uarts; +int debug_uart_index __attribute__((weak)) = 0; +struct apbuart_regs *dbg_uart = NULL; -static int isinit = 0; +/* Before UART driver has registered (or when no UART is available), calls to + * printk that gets to bsp_out_char() will be filling data into the + * pre_printk_dbgbuf[] buffer, hopefully the buffer can help debugging the + * early BSP boot.. At least the last printk() will be caught. + */ +char pre_printk_dbgbuf[32] = {0}; +int pre_printk_pos = 0; -/* - * Scan for UARTS in configuration +/* Initialize the BSP system debug console layer. It will scan AMBA Plu&Play + * for a debug APBUART and enable RX/TX for that UART. */ -int scan_uarts(void) +int bsp_debug_uart_init(void) { int i; - amba_apb_device apbuarts[LEON3_APBUARTS]; - - if (isinit == 0) { - i = 0; - uarts = 0; - - uarts = amba_find_apbslvs( - &amba_conf, VENDOR_GAISLER, GAISLER_APBUART, apbuarts, LEON3_APBUARTS); - for(i=0; i<uarts; i++) { - LEON3_Console_Uart[i] = (volatile LEON3_UART_Regs_Map *)apbuarts[i].start; - } - - /* initialize uart 0 if present for printk */ - if ( uarts ) { - LEON3_Console_Uart[0]->ctrl |= - LEON_REG_UART_CTRL_RE | LEON_REG_UART_CTRL_TE; - LEON3_Console_Uart[0]->status = 0; - } - isinit = 1; + struct ambapp_dev *adev; + struct ambapp_apb_info *apb; + + /* Update debug_uart_index to index used as debug console. + * Let user select Debug console by setting debug_uart_index. If the + * BSP is to provide the default UART (debug_uart_index==0): + * non-MP: APBUART[0] is debug console + * MP: LEON CPU index select UART + */ + if (debug_uart_index == 0) { +#if defined(RTEMS_MULTIPROCESSING) + debug_uart_index = LEON3_Cpu_Index; +#else + debug_uart_index = 0; +#endif + } else { + debug_uart_index = debug_uart_index - 1; /* User selected dbg-console */ } - return uarts; + /* Find APBUART core for System Debug Console */ + i = debug_uart_index; + adev = (void *)ambapp_for_each(&ambapp_plb, (OPTIONS_ALL|OPTIONS_APB_SLVS), + VENDOR_GAISLER, GAISLER_APBUART, + ambapp_find_by_idx, (void *)&i); + if (adev) { + /* Found a matching debug console, initialize debug uart if present + * for printk + */ + apb = (struct ambapp_apb_info *)adev->devinfo; + dbg_uart = (struct apbuart_regs *)apb->start; + dbg_uart->ctrl |= LEON_REG_UART_CTRL_RE | LEON_REG_UART_CTRL_TE; + dbg_uart->status = 0; + return 1; + } else + return 0; } /* - * console_outbyte_polled + * apbuart_outbyte_polled * * This routine transmits a character using polling. */ -void console_outbyte_polled( - int port, - unsigned char ch +void apbuart_outbyte_polled( + struct apbuart_regs *regs, + unsigned char ch, + int do_cr_on_newline, + int wait_sent ) { - if ((port >= 0) && (port < uarts)) { - int u = LEON3_Cpu_Index+port; - while ( (LEON3_Console_Uart[u]->status & LEON_REG_UART_STATUS_THE) == 0 ); - LEON3_Console_Uart[u]->data = (unsigned int) ch; +send: + while ( (regs->status & LEON_REG_UART_STATUS_THE) == 0 ) { + /* Lower bus utilization while waiting for UART */ + asm volatile ("nop"::); asm volatile ("nop"::); + asm volatile ("nop"::); asm volatile ("nop"::); + asm volatile ("nop"::); asm volatile ("nop"::); + asm volatile ("nop"::); asm volatile ("nop"::); + } + regs->data = (unsigned int) ch; + + if ((ch == '\n') && do_cr_on_newline) { + ch = '\r'; + goto send; + } + + /* Wait until the character has been sent? */ + if (wait_sent) { + while ((regs->status & LEON_REG_UART_STATUS_THE) == 0) + ; } } /* - * console_inbyte_nonblocking + * apbuart_inbyte_nonblocking * * This routine polls for a character. */ -int console_inbyte_nonblocking( int port ) +int apbuart_inbyte_nonblocking(struct apbuart_regs *regs) { - if ((port >= 0) && (port < uarts)) { - int u = LEON3_Cpu_Index+port; - if (LEON3_Console_Uart[u]->status & LEON_REG_UART_STATUS_ERR) - LEON3_Console_Uart[u]->status = ~LEON_REG_UART_STATUS_ERR; - - if ((LEON3_Console_Uart[u]->status & LEON_REG_UART_STATUS_DR) == 0) - return -1; - return (int) LEON3_Console_Uart[u]->data; - } else { - assert( 0 ); - } - return -1; + /* Clear errors */ + if (regs->status & LEON_REG_UART_STATUS_ERR) + regs->status = ~LEON_REG_UART_STATUS_ERR; + + if ((regs->status & LEON_REG_UART_STATUS_DR) == 0) + return EOF; + else + return (int) regs->data; } /* putchar/getchar for printk */ static void bsp_out_char(char c) { - console_outbyte_polled(0, c); + if (dbg_uart == NULL) { + /* Local debug buffer when UART driver has not registered */ + pre_printk_dbgbuf[pre_printk_pos++] = c; + pre_printk_pos = pre_printk_pos & (sizeof(pre_printk_dbgbuf)-1); + return; + } + + apbuart_outbyte_polled(dbg_uart, c, 1, 1); } /* @@ -115,7 +160,11 @@ static int bsp_in_char(void) { int tmp; - while ((tmp = console_inbyte_nonblocking(0)) < 0); + if (dbg_uart == NULL) + return EOF; + + while ((tmp = apbuart_inbyte_nonblocking(dbg_uart)) < 0) + ; return tmp; } diff --git a/c/src/lib/libbsp/sparc/leon3/include/amba.h b/c/src/lib/libbsp/sparc/leon3/include/amba.h index 9167ff111f..a4a0ac9bce 100644 --- a/c/src/lib/libbsp/sparc/leon3/include/amba.h +++ b/c/src/lib/libbsp/sparc/leon3/include/amba.h @@ -25,16 +25,16 @@ #define LEON3_AHB_MASTERS 64 #define LEON3_AHB_SLAVES 64 #define LEON3_APB_SLAVES 16 -#define LEON3_APBUARTS 8 #include <ambapp.h> +#include <grlib.h> #ifdef __cplusplus extern "C" { #endif /* The AMBA Plug&Play info of the bus that the LEON3 sits on */ -extern amba_confarea_type amba_conf; +extern struct ambapp_bus ambapp_plb; #ifdef __cplusplus } diff --git a/c/src/lib/libbsp/sparc/leon3/include/bsp.h b/c/src/lib/libbsp/sparc/leon3/include/bsp.h index 89b36d47f5..a37574675c 100644 --- a/c/src/lib/libbsp/sparc/leon3/include/bsp.h +++ b/c/src/lib/libbsp/sparc/leon3/include/bsp.h @@ -32,6 +32,7 @@ extern "C" { #include <leon.h> #include <rtems/clockdrv.h> #include <rtems/console.h> +#include <rtems/irq-extension.h> /* SPARC CPU variant: LEON3 */ #define LEON3 1 @@ -42,6 +43,9 @@ extern "C" { void *bsp_idle_thread( uintptr_t ignored ); #define BSP_IDLE_TASK_BODY bsp_idle_thread +/* Maximum supported APBUARTs by BSP */ +#define BSP_NUMBER_OF_TERMIOS_PORTS 8 + /* * Network driver configuration */ @@ -71,6 +75,10 @@ extern int rtems_leon_greth_driver_attach( #define RTEMS_BSP_NETWORK_DRIVER_ATTACH RTEMS_BSP_NETWORK_DRIVER_ATTACH_GRETH #endif +/* Configure GRETH driver */ +#define GRETH_SUPPORTED +#define GRETH_MEM_LOAD(addr) leon_r32_no_cache(addr) + extern int CPU_SPARC_HAS_SNOOPING; @@ -94,6 +102,10 @@ extern int end; /* last address in the program */ /* miscellaneous stuff assumed to exist */ +/* set_vec type */ +#define SET_VECTOR_RAW 0 /* Raw trap handler */ +#define SET_VECTOR_INT 1 /* Trap handler with _ISR_Handler interrupt handler */ + rtems_isr_entry set_vector( /* returns old vector */ rtems_isr_entry handler, /* isr routine */ rtems_vector_number vector, /* vector number */ @@ -104,6 +116,125 @@ void BSP_fatal_return( void ); void bsp_spurious_initialize( void ); +/* Allocate 8-byte aligned non-freeable pre-malloc memory */ +void *bsp_early_malloc(int size); + +/* Interrupt Service Routine (ISR) pointer */ +typedef void (*bsp_shared_isr)(void *arg); + +/* Initializes the Shared System Interrupt service */ +extern int BSP_shared_interrupt_init(void); + +/* Registers a shared IRQ handler, and enable it at IRQ controller. Multiple + * interrupt handlers may use the same IRQ number, all ISRs will be called + * when an interrupt on that line is fired. + * + * Arguments + * irq System IRQ number + * info Optional Name of IRQ source + * isr Function pointer to the ISR + * arg Second argument to function isr + */ +static __inline__ int BSP_shared_interrupt_register + ( + int irq, + const char *info, + bsp_shared_isr isr, + void *arg + ) +{ + return rtems_interrupt_handler_install(irq, info, + RTEMS_INTERRUPT_SHARED, isr, arg); +} + +/* Unregister previously registered shared IRQ handler. + * + * Arguments + * irq System IRQ number + * isr Function pointer to the ISR + * arg Second argument to function isr + */ +static __inline__ int BSP_shared_interrupt_unregister + ( + int irq, + bsp_shared_isr isr, + void *arg + ) +{ + return rtems_interrupt_handler_remove(irq, isr, arg); +} + +/* Clear interrupt pending on IRQ controller, this is typically done on a + * level triggered interrupt source such as PCI to avoid taking double IRQs. + * In such a case the interrupt source must be cleared first on LEON, before + * acknowledging the IRQ with this function. + * + * Arguments + * irq System IRQ number + */ +extern void BSP_shared_interrupt_clear(int irq); + +/* Enable Interrupt. This function will unmask the IRQ at the interrupt + * controller. This is normally done by _register(). Note that this will + * affect all ISRs on this IRQ. + * + * Arguments + * irq System IRQ number + */ +extern void BSP_shared_interrupt_unmask(int irq); + +/* Disable Interrupt. This function will mask one IRQ at the interrupt + * controller. This is normally done by _unregister(). Note that this will + * affect all ISRs on this IRQ. + * + * Arguments + * irq System IRQ number + */ +extern void BSP_shared_interrupt_mask(int irq); + +/* BSP PCI Interrupt support */ +#define BSP_PCI_shared_interrupt_register BSP_shared_interrupt_register +#define BSP_PCI_shared_interrupt_unregister BSP_shared_interrupt_unregister +#define BSP_PCI_shared_interrupt_unmask BSP_shared_interrupt_unmask +#define BSP_PCI_shared_interrupt_mask BSP_shared_interrupt_mask +#define BSP_PCI_shared_interrupt_clear BSP_shared_interrupt_clear + +/* Initialize BSP watchdog routines. Returns number of watchdog timers found. + * Currently only one is supported. + */ +extern int bsp_watchdog_init(void); + +/* Reload watchdog (last timer on the first GPTIMER core), all systems does not + * feature a watchdog, it is expected that if this function is called the + * user knows that there is a watchdog available. + * + * The prescaler is normally set to number of MHz of system, this is to + * make the system clock tick be stable. + * + * Arguments + * watchdog - Always 0 for now + * reload_value - Number of timer clocks (after prescaler) to count before + * watchdog is woken. + */ +extern void bsp_watchdog_reload(int watchdog, unsigned int reload_value); + +/* Stop watchdog timer */ +extern void bsp_watchdog_stop(int watchdog); + +/* Use watchdog0 timer to reset the system */ +extern void bsp_watchdog_system_reset(void); + +/* Common driver build-time configurations. On small systems undefine + * [DRIVER]_INFO_AVAIL to avoid info routines get dragged in. It is good + * for debugging and printing information about the system, but makes the + * image bigger. + */ +#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 */ +#define GRTC_RMAP_INFO_AVAIL /* GRTC over SpaceWire/RMAP driver */ + #ifdef __cplusplus } #endif diff --git a/c/src/lib/libbsp/sparc/leon3/include/bsp/irq.h b/c/src/lib/libbsp/sparc/leon3/include/bsp/irq.h new file mode 100644 index 0000000000..71c0df3c5d --- /dev/null +++ b/c/src/lib/libbsp/sparc/leon3/include/bsp/irq.h @@ -0,0 +1,36 @@ +/* LEON3 generic shared IRQ setup + * + * Based on libbsp/shared/include/irq.h. + * + * 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 LIBBSP_LEON3_IRQ_CONFIG_H +#define LIBBSP_LEON3_IRQ_CONFIG_H + +#define BSP_INTERRUPT_VECTOR_MAX_STD 15 /* Standard IRQ controller */ +#define BSP_INTERRUPT_VECTOR_MAX_EXT 31 /* Extended IRQ controller */ + +#define BSP_INTERRUPT_VECTOR_MIN 0 +#define BSP_INTERRUPT_VECTOR_MAX BSP_INTERRUPT_VECTOR_MAX_EXT + +/* The check is different depending on IRQ controller, runtime detected */ +#define BSP_INTERRUPT_CUSTOM_VALID_VECTOR + +extern int LEON3_IrqCtrl_EIrq; + +/** + * @brief Returns true if the interrupt vector with number @a vector is valid. + */ +static inline bool bsp_interrupt_is_valid_vector(rtems_vector_number vector) +{ + return (rtems_vector_number) BSP_INTERRUPT_VECTOR_MIN <= vector + && ((vector <= (rtems_vector_number) BSP_INTERRUPT_VECTOR_MAX_STD && + LEON3_IrqCtrl_EIrq == 0) || + (vector <= (rtems_vector_number) BSP_INTERRUPT_VECTOR_MAX_EXT && + LEON3_IrqCtrl_EIrq != 0)); +} + +#endif /* LIBBSP_LEON3_IRQ_CONFIG_H */ diff --git a/c/src/lib/libbsp/sparc/leon3/include/leon.h b/c/src/lib/libbsp/sparc/leon3/include/leon.h index ee5ae30af8..864536d73a 100644 --- a/c/src/lib/libbsp/sparc/leon3/include/leon.h +++ b/c/src/lib/libbsp/sparc/leon3/include/leon.h @@ -46,97 +46,6 @@ extern "C" { ( (_trap) >= 0x11 && \ (_trap) <= 0x1F ) -/* - * Structure for LEON memory mapped registers. - * - * Source: Section 6.1 - On-chip registers - * - * NOTE: There is only one of these structures per CPU, its base address - * is 0x80000000, and the variable LEON_REG is placed there by the - * linkcmds file. - */ - -/* Leon uses dynamic register mapping using amba configuration records, - * LEON_Register_Map is obsolete - */ -/* - typedef struct { - volatile unsigned int Memory_Config_1; - volatile unsigned int Memory_Config_2; - volatile unsigned int Edac_Control; - volatile unsigned int Failed_Address; - volatile unsigned int Memory_Status; - volatile unsigned int Cache_Control; - volatile unsigned int Power_Down; - volatile unsigned int Write_Protection_1; - volatile unsigned int Write_Protection_2; - volatile unsigned int Leon_Configuration; - volatile unsigned int dummy2; - volatile unsigned int dummy3; - volatile unsigned int dummy4; - volatile unsigned int dummy5; - volatile unsigned int dummy6; - volatile unsigned int dummy7; - volatile unsigned int Timer_Counter_1; - volatile unsigned int Timer_Reload_1; - volatile unsigned int Timer_Control_1; - volatile unsigned int Watchdog; - volatile unsigned int Timer_Counter_2; - volatile unsigned int Timer_Reload_2; - volatile unsigned int Timer_Control_2; - volatile unsigned int dummy8; - volatile unsigned int Scaler_Counter; - volatile unsigned int Scaler_Reload; - volatile unsigned int dummy9; - volatile unsigned int dummy10; - volatile unsigned int UART_Channel_1; - volatile unsigned int UART_Status_1; - volatile unsigned int UART_Control_1; - volatile unsigned int UART_Scaler_1; - volatile unsigned int UART_Channel_2; - volatile unsigned int UART_Status_2; - volatile unsigned int UART_Control_2; - volatile unsigned int UART_Scaler_2; - volatile unsigned int Interrupt_Mask; - volatile unsigned int Interrupt_Pending; - volatile unsigned int Interrupt_Force; - volatile unsigned int Interrupt_Clear; - volatile unsigned int PIO_Data; - volatile unsigned int PIO_Direction; - volatile unsigned int PIO_Interrupt; -} LEON_Register_Map; -*/ - -typedef struct { - volatile unsigned int data; - volatile unsigned int status; - volatile unsigned int ctrl; -} LEON3_UART_Regs_Map; - -typedef struct { - volatile unsigned int value; - volatile unsigned int reload; - volatile unsigned int conf; - volatile unsigned int notused; -} LEON3_Timer_SubType; - -typedef struct { - volatile unsigned int scaler_value; /* common timer registers */ - volatile unsigned int scaler_reload; - volatile unsigned int status; - volatile unsigned int notused; - LEON3_Timer_SubType timer[8]; -} LEON3_Timer_Regs_Map; - -typedef struct { - volatile unsigned int iodata; - volatile unsigned int ioout; - volatile unsigned int iodir; - volatile unsigned int irqmask; - volatile unsigned int irqpol; - volatile unsigned int irqedge; -} LEON3_IOPORT_Regs_Map; - /* /\* */ /* * This is used to manipulate the on-chip registers. */ /* * */ @@ -191,9 +100,9 @@ typedef struct { #define LEON_REG_UART_STATUS_OE 0x00000010 /* RX Overrun Error */ #define LEON_REG_UART_STATUS_PE 0x00000020 /* RX Parity Error */ #define LEON_REG_UART_STATUS_FE 0x00000040 /* RX Framing Error */ +#define LEON_REG_UART_STATUS_TF 0x00000200 /* FIFO Full */ #define LEON_REG_UART_STATUS_ERR 0x00000078 /* Error Mask */ - /* * The following defines the bits in the LEON UART Status Registers. */ @@ -206,13 +115,34 @@ typedef struct { #define LEON_REG_UART_CTRL_PE 0x00000020 /* Parity enable */ #define LEON_REG_UART_CTRL_FL 0x00000040 /* Flow control enable */ #define LEON_REG_UART_CTRL_LB 0x00000080 /* Loop Back enable */ +#define LEON_REG_UART_CTRL_DB 0x00000800 /* Debug FIFO enable */ +#define LEON_REG_UART_CTRL_SI 0x00004000 /* TX shift register empty IRQ enable */ +#define LEON_REG_UART_CTRL_FA 0x80000000 /* FIFO Available */ +#define LEON_REG_UART_CTRL_FA_BIT 31 -extern volatile LEON3_IrqCtrl_Regs_Map *LEON3_IrqCtrl_Regs; /* LEON3 Interrupt Controller */ -extern volatile LEON3_Timer_Regs_Map *LEON3_Timer_Regs; /* LEON3 GP Timer */ -extern volatile LEON3_UART_Regs_Map *LEON3_Console_Uart[LEON3_APBUARTS]; +extern volatile struct irqmp_regs *LEON3_IrqCtrl_Regs; /* LEON3 Interrupt Controller */ +extern volatile struct gptimer_regs *LEON3_Timer_Regs; /* LEON3 GP Timer */ +/* LEON3 CPU Index of boot CPU */ extern int LEON3_Cpu_Index; +/* The external IRQ number, -1 if not external interrupts */ +extern int LEON3_IrqCtrl_EIrq; + +static __inline__ int leon_irq_fixup(int irq) +{ + int eirq; + + if (LEON3_IrqCtrl_EIrq != 0 && irq == LEON3_IrqCtrl_EIrq) { + /* Get interrupt number from IRQ controller */ + eirq = LEON3_IrqCtrl_Regs->intid[LEON3_Cpu_Index] & 0x1f; + if (eirq & 0x10) + irq = eirq; + } + + return irq; +} + /* Macros used for manipulating bits in LEON3 GP Timer Control Register */ #define LEON3_GPTIMER_EN 1 @@ -252,7 +182,6 @@ extern int LEON3_Cpu_Index; (LEON3_IrqCtrl_Regs->mask[LEON3_Cpu_Index] & (1 << (_source))); \ } while (0) - #define LEON_Mask_interrupt( _source ) \ do { \ uint32_t _level; \ @@ -322,6 +251,14 @@ extern int LEON3_Cpu_Index; #define LEON_REG_TIMER_COUNTER_DEFINED_MASK 0x00000003 #define LEON_REG_TIMER_COUNTER_CURRENT_MODE_MASK 0x00000003 +/* Load 32-bit word by forcing a cache-miss */ +static inline unsigned int leon_r32_no_cache(uintptr_t addr) +{ + unsigned int tmp; + asm volatile (" lda [%1] 1, %0\n" : "=r"(tmp) : "r"(addr)); + return tmp; +} + #endif /* !ASM */ #ifdef __cplusplus diff --git a/c/src/lib/libbsp/sparc/leon3/leon_greth/leon_greth.c b/c/src/lib/libbsp/sparc/leon3/leon_greth/leon_greth.c index 341bd2792e..95a0c2b689 100644 --- a/c/src/lib/libbsp/sparc/leon3/leon_greth/leon_greth.c +++ b/c/src/lib/libbsp/sparc/leon3/leon_greth/leon_greth.c @@ -31,29 +31,31 @@ int rtems_leon_greth_driver_attach( int attach ) { - int device_found = 0; unsigned int base_addr = 0; /* avoid warnings */ unsigned int eth_irq = 0; /* avoid warnings */ - amba_apb_device apbgreth; + struct ambapp_dev *adev; + struct ambapp_apb_info *apb; /* Scan for MAC AHB slave interface */ - device_found = amba_find_apbslv(&amba_conf,VENDOR_GAISLER,GAISLER_ETHMAC,&apbgreth); - if (device_found == 1) - { - base_addr = apbgreth.start; - eth_irq = apbgreth.irq + 0x10; + adev = (void *)ambapp_for_each(&ambapp_plb, (OPTIONS_ALL|OPTIONS_APB_SLVS), + VENDOR_GAISLER, GAISLER_ETHMAC, + ambapp_find_by_idx, NULL); + if (adev) { + apb = DEV_TO_APB(adev); + base_addr = apb->start; + eth_irq = apb->irq; /* clear control register and reset NIC */ *(volatile int *) base_addr = 0; *(volatile int *) base_addr = GRETH_CTRL_RST; *(volatile int *) base_addr = 0; leon_greth_configuration.base_address = base_addr; - leon_greth_configuration.vector = eth_irq; + leon_greth_configuration.irq = eth_irq; leon_greth_configuration.txd_count = TDA_COUNT; leon_greth_configuration.rxd_count = RDA_COUNT; if (rtems_greth_driver_attach( config, &leon_greth_configuration )) { - LEON_Clear_interrupt(leon_greth_configuration.vector); - LEON_Unmask_interrupt(leon_greth_configuration.vector); + LEON_Clear_interrupt(eth_irq); + LEON_Unmask_interrupt(eth_irq); } } return 0; diff --git a/c/src/lib/libbsp/sparc/leon3/leon_open_eth/leon_open_eth.c b/c/src/lib/libbsp/sparc/leon3/leon_open_eth/leon_open_eth.c index 3d42d9f53b..a6bb848dbb 100644 --- a/c/src/lib/libbsp/sparc/leon3/leon_open_eth/leon_open_eth.c +++ b/c/src/lib/libbsp/sparc/leon3/leon_open_eth/leon_open_eth.c @@ -31,44 +31,41 @@ int rtems_leon_open_eth_driver_attach( int attach ) { - int device_found = 0; - int i; - unsigned int conf, iobar; unsigned int base_addr = 0; /* avoid warnings */ unsigned int eth_irq = 0; /* avoid warnings */ - + struct ambapp_dev *adev; + struct ambapp_ahb_info *ahb; /* Scan for MAC AHB slave interface */ - for (i = 0; i < amba_conf.ahbslv.devnr; i++) - { - conf = amba_get_confword(amba_conf.ahbslv, i, 0); - if (((amba_vendor(conf) == VENDOR_OPENCORES) && (amba_device(conf) == OPENCORES_ETHMAC)) || - ((amba_vendor(conf) == VENDOR_GAISLER) && (amba_device(conf) == GAISLER_ETHAHB))) - { - iobar = amba_ahb_get_membar(amba_conf.ahbslv, i, 0); - base_addr = amba_iobar_start(LEON3_IO_AREA, iobar); - eth_irq = amba_irq(conf) + 0x10; - device_found = 1; - break; - } + adev = (void *)ambapp_for_each(&ambapp_plb, (OPTIONS_ALL|OPTIONS_AHB_SLVS), + VENDOR_OPENCORES, OPENCORES_ETHMAC, + ambapp_find_by_idx, NULL); + if (!adev) { + adev = (void *)ambapp_for_each(&ambapp_plb, (OPTIONS_ALL|OPTIONS_AHB_SLVS), + VENDOR_GAISLER, GAISLER_ETHAHB, + ambapp_find_by_idx, NULL); } - - if (device_found) + if (adev) { + ahb = DEV_TO_AHB(adev); + base_addr = ahb->start[0]; + eth_irq = ahb->irq; + /* clear control register and reset NIC */ *(volatile int *) base_addr = 0; *(volatile int *) base_addr = 0x800; *(volatile int *) base_addr = 0; leon_open_eth_configuration.base_address = base_addr; - leon_open_eth_configuration.vector = eth_irq; + leon_open_eth_configuration.vector = eth_irq + 0x10; leon_open_eth_configuration.txd_count = TDA_COUNT; leon_open_eth_configuration.rxd_count = RDA_COUNT; /* enable 100 MHz operation only if cpu frequency >= 50 MHz */ - if (LEON3_Timer_Regs->scaler_reload >= 49) leon_open_eth_configuration.en100MHz = 1; + if (LEON3_Timer_Regs->scaler_reload >= 49) + leon_open_eth_configuration.en100MHz = 1; if (rtems_open_eth_driver_attach( config, &leon_open_eth_configuration )) { - LEON_Clear_interrupt(leon_open_eth_configuration.vector); - LEON_Unmask_interrupt(leon_open_eth_configuration.vector); + LEON_Clear_interrupt(eth_irq); + LEON_Unmask_interrupt(eth_irq); } } return 0; diff --git a/c/src/lib/libbsp/sparc/leon3/leon_smc91111/leon_smc91111.c b/c/src/lib/libbsp/sparc/leon3/leon_smc91111/leon_smc91111.c index 5ebdc12cfc..babd414473 100644 --- a/c/src/lib/libbsp/sparc/leon3/leon_smc91111/leon_smc91111.c +++ b/c/src/lib/libbsp/sparc/leon3/leon_smc91111/leon_smc91111.c @@ -5,7 +5,7 @@ #include <bsp.h> #include <libchip/smc91111exp.h> #include <rtems/bspIo.h> - +#include <ambapp.h> #define SMC91111_BASE_ADDR (void*)0x20000300 #define SMC91111_BASE_IRQ 4 @@ -13,9 +13,9 @@ scmv91111_configuration_t leon_scmv91111_configuration = { SMC91111_BASE_ADDR, /* base address */ - LEON_TRAP_TYPE (SMC91111_BASE_IRQ), /* vector number */ + SMC91111_BASE_IRQ, /* IRQ number */ SMC91111_BASE_PIO, /* PIO */ - 100, /* 100b */ + 100, /* 100b */ 1, /* fulldx */ 1 /* autoneg */ }; @@ -31,28 +31,31 @@ rtems_smc91111_driver_attach_leon3 (struct rtems_bsdnet_ifconfig *config, int attach) { unsigned long addr_mctrl = 0; - LEON3_IOPORT_Regs_Map *io; - - amba_apb_device apbpio; - amba_ahb_device apbmctrl; + struct grgpio_regs *io; + struct ambapp_apb_info apbpio; + struct ambapp_apb_info apbmctrl; - if ( amba_find_apbslv(&amba_conf,VENDOR_GAISLER,GAISLER_PIOPORT,&apbpio) != 1 ){ + if (ambapp_find_apbslv(&ambapp_plb,VENDOR_GAISLER,GAISLER_GPIO,&apbpio) != 1) { printk("SMC9111_leon3: didn't find PIO\n"); return 0; } /* Find LEON2 memory controller */ - if ( amba_find_ahbslv(&amba_conf,VENDOR_ESA,ESA_MCTRL,&apbmctrl) != 1 ){ + if (ambapp_find_apbslv(&ambapp_plb,VENDOR_ESA,ESA_MCTRL,&apbmctrl) != 1) { /* LEON2 memory controller not found, search for fault tolerant memory controller */ - if ( amba_find_ahbslv(&amba_conf,VENDOR_GAISLER,GAISLER_FTMCTRL,&apbmctrl) != 1 ) { - printk("SMC9111_leon3: didn't find any memory controller\n"); - return 0; + if (ambapp_find_apbslv(&ambapp_plb,VENDOR_GAISLER,GAISLER_FTMCTRL,&apbmctrl) != 1) { + if (ambapp_find_apbslv(&ambapp_plb,VENDOR_GAISLER,GAISLER_FTSRCTRL,&apbmctrl) != 1) { + if (ambapp_find_apbslv(&ambapp_plb,VENDOR_GAISLER,GAISLER_FTSRCTRL8,&apbmctrl) != 1) { + printk("SMC9111_leon3: didn't find any memory controller\n"); + return 0; + } + } } } /* Get controller address */ addr_mctrl = (unsigned long) apbmctrl.start; - io = (LEON3_IOPORT_Regs_Map *) apbpio.start; + io = (struct grgpio_regs *) apbpio.start; printk( "Activating Leon3 io port for smsc_lan91cxx (pio:%x mctrl:%x)\n", @@ -60,14 +63,13 @@ rtems_smc91111_driver_attach_leon3 (struct rtems_bsdnet_ifconfig *config, (unsigned int)addr_mctrl); /* Setup PIO IRQ */ - io->irqmask |= (1 << leon_scmv91111_configuration.pio); - io->irqpol |= (1 << leon_scmv91111_configuration.pio); - io->irqedge |= (1 << leon_scmv91111_configuration.pio); - io->iodir &= ~(1 << leon_scmv91111_configuration.pio); + io->imask |= (1 << leon_scmv91111_configuration.pio); + io->ipol |= (1 << leon_scmv91111_configuration.pio); + io->iedge |= (1 << leon_scmv91111_configuration.pio); + io->dir &= ~(1 << leon_scmv91111_configuration.pio); /* Setup memory controller I/O waitstates */ *((volatile unsigned int *) addr_mctrl) |= 0x10f80000; /* enable I/O area access */ - return _rtems_smc91111_driver_attach (config, - &leon_scmv91111_configuration); + return _rtems_smc91111_driver_attach (config, &leon_scmv91111_configuration); }; diff --git a/c/src/lib/libbsp/sparc/leon3/pci/pci.c b/c/src/lib/libbsp/sparc/leon3/pci/pci.c deleted file mode 100644 index 9eedec3f25..0000000000 --- a/c/src/lib/libbsp/sparc/leon3/pci/pci.c +++ /dev/null @@ -1,586 +0,0 @@ -/* - * pci.c : this file contains basic PCI Io functions. - * - * Copyright (C) 1999 valette@crf.canon.fr - * - * This code is heavily inspired by the public specification of STREAM V2 - * that can be found at : - * - * <http://www.chorus.com/Documentation/index.html> by following - * the STREAM API Specification Document link. - * - * The license and distribution terms for this file may be - * found in found in the file LICENSE in this distribution or at - * http://www.rtems.com/license/LICENSE. - * - * pci.c,v 1.2.4.4 2004/11/10 22:15:01 joel Exp - * - * Till Straumann, <strauman@slac.stanford.edu>, 1/2002 - * - separated bridge detection code out of this file - * - * - * Adapted to GRPCI - * Copyright (C) 2006 Gaisler Research - * - */ - -#include <pci.h> -#include <stdlib.h> -#include <rtems/bspIo.h> - -#define PCI_ADDR 0x80000400 -#define DMAPCI_ADDR 0x80000500 -#define PCI_CONF 0xfff50000 -#define PCI_MEM_START 0xe0000000 -#define PCI_MEM_END 0xf0000000 -#define PCI_MEM_SIZE (PCI_MEM_START - PCI_MEM_END) - -/* If uncommented byte twisting is enabled */ -/*#define BT_ENABLED 1*/ - -/* Define PCI_INFO to get a listing of configured devices at boot time */ -#define PCI_INFO 1 - -#define DEBUG 1 - -#ifdef DEBUG -#define DBG(x...) printk(x) -#else -#define DBG(x...) -#endif - -/* allow for overriding these definitions */ -#ifndef PCI_CONFIG_ADDR -#define PCI_CONFIG_ADDR 0xcf8 -#endif -#ifndef PCI_CONFIG_DATA -#define PCI_CONFIG_DATA 0xcfc -#endif - -#define PCI_INVALID_VENDORDEVICEID 0xffffffff -#define PCI_MULTI_FUNCTION 0x80 - -/* define a shortcut */ -#define pci BSP_pci_configuration - -/* - * Bit encode for PCI_CONFIG_HEADER_TYPE register - */ -unsigned char ucMaxPCIBus; -typedef struct { - volatile unsigned int cfg_stat; - volatile unsigned int bar0; - volatile unsigned int page0; - volatile unsigned int bar1; - volatile unsigned int page1; - volatile unsigned int iomap; - volatile unsigned int stat_cmd; -} LEON3_GRPCI_Regs_Map; - -LEON3_GRPCI_Regs_Map *pcic = (LEON3_GRPCI_Regs_Map *) PCI_ADDR; -unsigned int *pcidma = (unsigned int *)DMAPCI_ADDR; - -struct pci_res { - unsigned int size; - unsigned char bar; - unsigned char devfn; -}; - -static inline unsigned int flip_dword (unsigned int l) -{ - return ((l&0xff)<<24) | (((l>>8)&0xff)<<16) | (((l>>16)&0xff)<<8)| ((l>>24)&0xff); -} - - -/* The configuration access functions uses the DMA functionality of the - * AT697 pci controller to be able access all slots - */ - - -static int -BSP_pci_read_config_dword( - unsigned char bus, - unsigned char slot, - unsigned char function, - unsigned char offset, - unsigned int *val -) -{ - volatile unsigned int *pci_conf; - - if (offset & 3) return PCIBIOS_BAD_REGISTER_NUMBER; - - if (slot > 21) { - *val = 0xffffffff; - return PCIBIOS_SUCCESSFUL; - } - - pci_conf = (volatile unsigned int *) (PCI_CONF + - ((slot<<11) | (function<<8) | offset)); - -#ifdef BT_ENABLED - *val = flip_dword(*pci_conf); -#else - *val = *pci_conf; -#endif - - if (pcic->cfg_stat & 0x100) { - *val = 0xffffffff; - } - - DBG("pci_read - bus: %d, dev: %d, fn: %d, off: %d => addr: %x, val: %x\n", bus, slot, function, offset, (1<<(11+slot) ) | ((function & 7)<<8) | (offset&0x3f), *val); - - return PCIBIOS_SUCCESSFUL; -} - - -static int -BSP_pci_read_config_word(unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, unsigned short *val) { - unsigned int v; - - if (offset & 1) return PCIBIOS_BAD_REGISTER_NUMBER; - - pci_read_config_dword(bus, slot, function, offset&~3, &v); - *val = 0xffff & (v >> (8*(offset & 3))); - - return PCIBIOS_SUCCESSFUL; -} - - -static int -BSP_pci_read_config_byte(unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, unsigned char *val) { - unsigned int v; - - pci_read_config_dword(bus, slot, function, offset&~3, &v); - - *val = 0xff & (v >> (8*(offset & 3))); - - return PCIBIOS_SUCCESSFUL; -} - - -static int -BSP_pci_write_config_dword(unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, unsigned int val) { - - volatile unsigned int *pci_conf; - unsigned int value; - - if (offset & 3 || bus != 0) return PCIBIOS_BAD_REGISTER_NUMBER; - - - pci_conf = (volatile unsigned int *) (PCI_CONF + - ((slot<<11) | (function<<8) | (offset & ~3))); - -#ifdef BT_ENABLED - value = flip_dword(val); -#else - value = val; -#endif - - *pci_conf = value; - - DBG("pci write - bus: %d, dev: %d, fn: %d, off: %d => addr: %x, val: %x\n", bus, slot, function, offset, (1<<(11+slot) ) | ((function & 7)<<8) | (offset&0x3f), value); - - return PCIBIOS_SUCCESSFUL; -} - - -static int -BSP_pci_write_config_word(unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, unsigned short val) { - unsigned int v; - - if (offset & 1) return PCIBIOS_BAD_REGISTER_NUMBER; - - pci_read_config_dword(bus, slot, function, offset&~3, &v); - - v = (v & ~(0xffff << (8*(offset&3)))) | ((0xffff&val) << (8*(offset&3))); - - return pci_write_config_dword(bus, slot, function, offset&~3, v); -} - - -static int -BSP_pci_write_config_byte(unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, unsigned char val) { - unsigned int v; - - pci_read_config_dword(bus, slot, function, offset&~3, &v); - - v = (v & ~(0xff << (8*(offset&3)))) | ((0xff&val) << (8*(offset&3))); - - return pci_write_config_dword(bus, slot, function, offset&~3, v); -} - - - -const pci_config_access_functions pci_access_functions = { - BSP_pci_read_config_byte, - BSP_pci_read_config_word, - BSP_pci_read_config_dword, - BSP_pci_write_config_byte, - BSP_pci_write_config_word, - BSP_pci_write_config_dword -}; - -rtems_pci_config_t BSP_pci_configuration = { - (volatile unsigned char*)PCI_CONFIG_ADDR, - (volatile unsigned char*)PCI_CONFIG_DATA, - &pci_access_functions -}; - - -int init_grpci(void) { - - volatile unsigned int *page0 = (unsigned volatile int *) PCI_MEM_START; - unsigned int data, addr; - -#ifndef BT_ENABLED - pci_write_config_dword(0,0,0,0x10, 0xffffffff); - pci_read_config_dword(0,0,0,0x10, &addr); - pci_write_config_dword(0,0,0,0x10, flip_dword(0x10000000)); /* Setup bar0 to nonzero value (grpci considers BAR==0 as invalid) */ - addr = (~flip_dword(addr)+1)>>1; /* page0 is accessed through upper half of bar0 */ - pcic->cfg_stat |= 0x10000000; /* Setup mmap reg so we can reach bar0 */ - page0[addr/4] = 0; /* Disable bytetwisting ... */ -#endif - - /* set 1:1 mapping between AHB -> PCI memory */ - pcic->cfg_stat = (pcic->cfg_stat & 0x0fffffff) | PCI_MEM_START; - - /* and map system RAM at pci address 0x40000000 */ - pci_write_config_dword(0, 0, 0, 0x14, 0x40000000); - pcic->page1 = 0x40000000; - - /* set as bus master and enable pci memory responses */ - pci_read_config_dword(0, 0, 0, 0x4, &data); - pci_write_config_dword(0, 0, 0, 0x4, data | 0x6); - - return 0; -} - -/* DMA functions which uses GRPCIs optional DMA controller (len in words) */ -int dma_to_pci(unsigned int ahb_addr, unsigned int pci_addr, unsigned int len) { - int ret = 0; - - pcidma[0] = 0x82; - pcidma[1] = ahb_addr; - pcidma[2] = pci_addr; - pcidma[3] = len; - pcidma[0] = 0x83; - - while ( (pcidma[0] & 0x4) == 0) - ; - - if (pcidma[0] & 0x8) { /* error */ - ret = -1; - } - - pcidma[0] |= 0xC; - return ret; - -} - -int dma_from_pci(unsigned int ahb_addr, unsigned int pci_addr, unsigned int len) { - int ret = 0; - - pcidma[0] = 0x80; - pcidma[1] = ahb_addr; - pcidma[2] = pci_addr; - pcidma[3] = len; - pcidma[0] = 0x81; - - while ( (pcidma[0] & 0x4) == 0) - ; - - if (pcidma[0] & 0x8) { /* error */ - ret = -1; - } - - pcidma[0] |= 0xC; - return ret; - -} - - -void pci_mem_enable(unsigned char bus, unsigned char slot, unsigned char function) { - unsigned int data; - - pci_read_config_dword(0, slot, function, PCI_COMMAND, &data); - pci_write_config_dword(0, slot, function, PCI_COMMAND, data | PCI_COMMAND_MEMORY); - -} - -void pci_master_enable(unsigned char bus, unsigned char slot, unsigned char function) { - unsigned int data; - - pci_read_config_dword(0, slot, function, PCI_COMMAND, &data); - pci_write_config_dword(0, slot, function, PCI_COMMAND, data | PCI_COMMAND_MASTER); - -} - -static inline void swap_res(struct pci_res **p1, struct pci_res **p2) { - - struct pci_res *tmp = *p1; - *p1 = *p2; - *p2 = tmp; - -} - -/* pci_allocate_resources - * - * This function scans the bus and assigns PCI addresses to all devices. It handles both - * single function and multi function devices. All allocated devices are enabled and - * latency timers are set to 40. - * - * NOTE that it only allocates PCI memory space devices (that are at least 1 KB). - * IO spaces are not enabled. Also, it does not handle pci-pci bridges. They are left disabled. - * - * -*/ -void pci_allocate_resources(void) { - - unsigned int slot, numfuncs, func, id, pos, size, tmp, i, swapped, addr, dev, fn; - unsigned char header; - struct pci_res **res; - - res = (struct pci_res **) malloc(sizeof(struct pci_res *)*32*8*6); - - for (i = 0; i < 32*8*6; i++) { - res[i] = (struct pci_res *) malloc(sizeof(struct pci_res)); - res[i]->size = 0; - res[i]->devfn = i; - } - - for(slot = 1; slot < PCI_MAX_DEVICES; slot++) { - - pci_read_config_dword(0, slot, 0, PCI_VENDOR_ID, &id); - - if(id == PCI_INVALID_VENDORDEVICEID || id == 0) { - /* - * This slot is empty - */ - continue; - } - - pci_read_config_byte(0, slot, 0, PCI_HEADER_TYPE, &header); - - if(header & PCI_MULTI_FUNCTION) { - numfuncs = PCI_MAX_FUNCTIONS; - } - else { - numfuncs = 1; - } - - for(func = 0; func < numfuncs; func++) { - - pci_read_config_dword(0, slot, func, PCI_VENDOR_ID, &id); - if(id == PCI_INVALID_VENDORDEVICEID || id == 0) { - continue; - } - - pci_read_config_dword(0, slot, func, PCI_CLASS_REVISION, &tmp); - tmp >>= 16; - if (tmp == PCI_CLASS_BRIDGE_PCI) { - continue; - } - - for (pos = 0; pos < 6; pos++) { - pci_write_config_dword(0, slot, func, PCI_BASE_ADDRESS_0 + (pos<<2), 0xffffffff); - pci_read_config_dword(0, slot, func, PCI_BASE_ADDRESS_0 + (pos<<2), &size); - - if (size == 0 || size == 0xffffffff || (size & 0x3f1) != 0){ - pci_write_config_dword(0, slot, func, PCI_BASE_ADDRESS_0 + (pos<<2), 0); - continue; - - }else { - size &= 0xfffffff0; - res[slot*8*6+func*6+pos]->size = ~size+1; - res[slot*8*6+func*6+pos]->devfn = slot*8 + func; - res[slot*8*6+func*6+pos]->bar = pos; - - DBG("Slot: %d, function: %d, bar%d size: %x\n", slot, func, pos, ~size+1); - } - } - } - } - - - /* Sort the resources in descending order */ - swapped = 1; - while (swapped == 1) { - swapped = 0; - for (i = 0; i < 32*8*6-1; i++) { - if (res[i]->size < res[i+1]->size) { - swap_res(&res[i], &res[i+1]); - swapped = 1; - } - } - i++; - } - - /* Assign the BARs */ - addr = PCI_MEM_START; - for (i = 0; i < 32*8*6; i++) { - - if (res[i]->size == 0) { - goto done; - } - if ( (addr + res[i]->size) > PCI_MEM_END) { - printk("Out of PCI memory space, all devices not configured.\n"); - goto done; - } - - dev = res[i]->devfn >> 3; - fn = res[i]->devfn & 7; - - DBG("Assigning PCI addr %x to device %d, function %d, bar %d\n", addr, dev, fn, res[i]->bar); - pci_write_config_dword(0, dev, fn, PCI_BASE_ADDRESS_0+res[i]->bar*4, addr); - addr += res[i]->size; - - /* Set latency timer to 64 */ - pci_read_config_dword(0, dev, fn, 0xC, &tmp); - pci_write_config_dword(0, dev, fn, 0xC, tmp|0x4000); - - pci_mem_enable(0, dev, fn); - - } - - - -done: - -#ifdef PCI_INFO - printk("\nPCI devices found and configured:\n"); - for (slot = 1; slot < PCI_MAX_DEVICES; slot++) { - - pci_read_config_byte(0, slot, 0, PCI_HEADER_TYPE, &header); - - if(header & PCI_MULTI_FUNCTION) { - numfuncs = PCI_MAX_FUNCTIONS; - } - else { - numfuncs = 1; - } - - for (func = 0; func < numfuncs; func++) { - - pci_read_config_dword(0, slot, func, PCI_COMMAND, &tmp); - - if (tmp & PCI_COMMAND_MEMORY) { - - pci_read_config_dword(0, slot, func, PCI_VENDOR_ID, &id); - - if (id == PCI_INVALID_VENDORDEVICEID || id == 0) continue; - - printk("\nSlot %d function: %d\nVendor id: 0x%x, device id: 0x%x\n", slot, func, id & 0xffff, id>>16); - - for (pos = 0; pos < 6; pos++) { - pci_read_config_dword(0, slot, func, PCI_BASE_ADDRESS_0 + pos*4, &tmp); - - if (tmp != 0 && tmp != 0xffffffff && (tmp & 0x3f1) == 0) { - - printk("\tBAR %d: %x\n", pos, tmp); - } - - } - printk("\n"); - - } - - } - } - printk("\n"); -#endif - - for (i = 0; i < 1536; i++) { - free(res[i]); - } - free(res); -} - - - -int init_pci(void) -{ - unsigned char ucSlotNumber, ucFnNumber, ucNumFuncs; - unsigned char ucHeader; - unsigned char ucMaxSubordinate; - unsigned int ulClass, ulDeviceID; - - DBG("Initializing PCI\n"); - if ( init_grpci() ) { - return -1; - } - pci_allocate_resources(); - DBG("PCI resource allocation done\n"); -/* - * Scan PCI bus 0 looking for PCI-PCI bridges - */ - for(ucSlotNumber=0;ucSlotNumber<PCI_MAX_DEVICES;ucSlotNumber++) { - (void)pci_read_config_dword(0, - ucSlotNumber, - 0, - PCI_VENDOR_ID, - &ulDeviceID); - if(ulDeviceID==PCI_INVALID_VENDORDEVICEID) { -/* - * This slot is empty - */ - continue; - } - (void)pci_read_config_byte(0, - ucSlotNumber, - 0, - PCI_HEADER_TYPE, - &ucHeader); - if(ucHeader&PCI_MULTI_FUNCTION) { - ucNumFuncs=PCI_MAX_FUNCTIONS; - } - else { - ucNumFuncs=1; - } - for(ucFnNumber=0;ucFnNumber<ucNumFuncs;ucFnNumber++) { - (void)pci_read_config_dword(0, - ucSlotNumber, - ucFnNumber, - PCI_VENDOR_ID, - &ulDeviceID); - if(ulDeviceID==PCI_INVALID_VENDORDEVICEID) { -/* - * This slot/function is empty - */ - continue; - } - -/* - * This slot/function has a device fitted. - */ - (void)pci_read_config_dword(0, - ucSlotNumber, - ucFnNumber, - PCI_CLASS_REVISION, - &ulClass); - ulClass >>= 16; - if (ulClass == PCI_CLASS_BRIDGE_PCI) { -/* - * We have found a PCI-PCI bridge - */ - (void)pci_read_config_byte(0, - ucSlotNumber, - ucFnNumber, - PCI_SUBORDINATE_BUS, - &ucMaxSubordinate); - if(ucMaxSubordinate>ucMaxPCIBus) { - ucMaxPCIBus=ucMaxSubordinate; - } - } - } - } - return 0; -} - -/* - * Return the number of PCI busses in the system - */ -unsigned char BusCountPCI(void) -{ - return(ucMaxPCIBus+1); -} diff --git a/c/src/lib/libbsp/sparc/leon3/preinstall.am b/c/src/lib/libbsp/sparc/leon3/preinstall.am index b948529704..00eeb53aa1 100644 --- a/c/src/lib/libbsp/sparc/leon3/preinstall.am +++ b/c/src/lib/libbsp/sparc/leon3/preinstall.am @@ -73,6 +73,10 @@ $(PROJECT_LIB)/linkcmds: startup/linkcmds $(PROJECT_LIB)/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_LIB)/linkcmds PREINSTALL_FILES += $(PROJECT_LIB)/linkcmds +$(PROJECT_LIB)/linkcmds_ngmp: startup/linkcmds_ngmp $(PROJECT_LIB)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_LIB)/linkcmds_ngmp +PREINSTALL_FILES += $(PROJECT_LIB)/linkcmds_ngmp + $(PROJECT_LIB)/linkcmds.base: ../shared/startup/linkcmds.base $(PROJECT_LIB)/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_LIB)/linkcmds.base PREINSTALL_FILES += $(PROJECT_LIB)/linkcmds.base @@ -85,26 +89,98 @@ $(PROJECT_INCLUDE)/ambapp.h: ../../sparc/shared/include/ambapp.h $(PROJECT_INCLU $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/ambapp.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/ambapp.h -$(PROJECT_INCLUDE)/pci.h: ../../sparc/shared/include/pci.h $(PROJECT_INCLUDE)/$(dirstamp) - $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/pci.h -PREINSTALL_FILES += $(PROJECT_INCLUDE)/pci.h +$(PROJECT_INCLUDE)/ambapp_ids.h: ../../sparc/shared/include/ambapp_ids.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/ambapp_ids.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/ambapp_ids.h + +$(PROJECT_INCLUDE)/grlib.h: ../../sparc/shared/include/grlib.h $(PROJECT_INCLUDE)/$(dirstamp) + $(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 + +$(PROJECT_INCLUDE)/cons.h: ../../sparc/shared/include/cons.h $(PROJECT_INCLUDE)/$(dirstamp) + $(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 + +$(PROJECT_INCLUDE)/bsp/irq-info.h: ../../shared/include/irq-info.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/irq-info.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/irq-info.h + +$(PROJECT_INCLUDE)/bsp/irq.h: include/bsp/irq.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/irq.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/irq.h + +$(PROJECT_INCLUDE)/grpci2.h: ../../sparc/shared/include/grpci2.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/grpci2.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/grpci2.h + +$(PROJECT_INCLUDE)/gr_701.h: ../../sparc/shared/include/gr_701.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/gr_701.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/gr_701.h + +$(PROJECT_INCLUDE)/gr_rasta_adcdac.h: ../../sparc/shared/include/gr_rasta_adcdac.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/gr_rasta_adcdac.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/gr_rasta_adcdac.h + +$(PROJECT_INCLUDE)/gr_rasta_io.h: ../../sparc/shared/include/gr_rasta_io.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/gr_rasta_io.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/gr_rasta_io.h + +$(PROJECT_INCLUDE)/gr_rasta_tmtc.h: ../../sparc/shared/include/gr_rasta_tmtc.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/gr_rasta_tmtc.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/gr_rasta_tmtc.h + +$(PROJECT_INCLUDE)/gr_tmtc_1553.h: ../../sparc/shared/include/gr_tmtc_1553.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/gr_tmtc_1553.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/gr_tmtc_1553.h $(PROJECT_INCLUDE)/b1553brm.h: ../../sparc/shared/include/b1553brm.h $(PROJECT_INCLUDE)/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/b1553brm.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/b1553brm.h -$(PROJECT_INCLUDE)/b1553brm_pci.h: ../../sparc/shared/include/b1553brm_pci.h $(PROJECT_INCLUDE)/$(dirstamp) - $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/b1553brm_pci.h -PREINSTALL_FILES += $(PROJECT_INCLUDE)/b1553brm_pci.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 -$(PROJECT_INCLUDE)/occan_pci.h: ../../sparc/shared/include/occan_pci.h $(PROJECT_INCLUDE)/$(dirstamp) - $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/occan_pci.h -PREINSTALL_FILES += $(PROJECT_INCLUDE)/occan_pci.h - $(PROJECT_INCLUDE)/grcan.h: ../../sparc/shared/include/grcan.h $(PROJECT_INCLUDE)/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/grcan.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/grcan.h @@ -113,19 +189,122 @@ $(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_pci.h: ../../sparc/shared/include/grspw_pci.h $(PROJECT_INCLUDE)/$(dirstamp) - $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/grspw_pci.h -PREINSTALL_FILES += $(PROJECT_INCLUDE)/grspw_pci.h +$(PROJECT_INCLUDE)/grspw_pkt.h: ../../sparc/shared/include/grspw_pkt.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/grspw_pkt.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/grspw_pkt.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)/rmap.h: ../../sparc/shared/include/rmap.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rmap.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rmap.h + +$(PROJECT_INCLUDE)/rmap_drv_grspw.h: ../../sparc/shared/include/rmap_drv_grspw.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rmap_drv_grspw.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rmap_drv_grspw.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 -$(PROJECT_INCLUDE)/apbuart_pci.h: ../../sparc/shared/include/apbuart_pci.h $(PROJECT_INCLUDE)/$(dirstamp) - $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/apbuart_pci.h -PREINSTALL_FILES += $(PROJECT_INCLUDE)/apbuart_pci.h - $(PROJECT_INCLUDE)/i2cmst.h: ../../sparc/shared/include/i2cmst.h $(PROJECT_INCLUDE)/$(dirstamp) $(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)/graes.h: ../../sparc/shared/include/graes.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/graes.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/graes.h + +$(PROJECT_INCLUDE)/grpwrx.h: ../../sparc/shared/include/grpwrx.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/grpwrx.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/grpwrx.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)/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) +PREINSTALL_DIRS += $(PROJECT_INCLUDE)/drvmgr/$(dirstamp) + +$(PROJECT_INCLUDE)/drvmgr/ambapp_bus_grlib.h: ../../sparc/shared/include/drvmgr/ambapp_bus_grlib.h $(PROJECT_INCLUDE)/drvmgr/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/drvmgr/ambapp_bus_grlib.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/drvmgr/ambapp_bus_grlib.h + +$(PROJECT_INCLUDE)/drvmgr/ambapp_bus_rmap.h: ../../sparc/shared/include/drvmgr/ambapp_bus_rmap.h $(PROJECT_INCLUDE)/drvmgr/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/drvmgr/ambapp_bus_rmap.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/drvmgr/ambapp_bus_rmap.h + +$(PROJECT_INCLUDE)/drvmgr/ambapp_bus.h: ../../sparc/shared/include/drvmgr/ambapp_bus.h $(PROJECT_INCLUDE)/drvmgr/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/drvmgr/ambapp_bus.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/drvmgr/ambapp_bus.h + +$(PROJECT_INCLUDE)/drvmgr/spw_bus.h: ../../sparc/shared/include/drvmgr/spw_bus.h $(PROJECT_INCLUDE)/drvmgr/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/drvmgr/spw_bus.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/drvmgr/spw_bus.h + +$(PROJECT_INCLUDE)/drvmgr/spw_bus_ids.h: ../../sparc/shared/include/drvmgr/spw_bus_ids.h $(PROJECT_INCLUDE)/drvmgr/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/drvmgr/spw_bus_ids.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/drvmgr/spw_bus_ids.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 +endif diff --git a/c/src/lib/libbsp/sparc/leon3/shmsupp/getcfg.c b/c/src/lib/libbsp/sparc/leon3/shmsupp/getcfg.c index 609c2b1d98..55b5659155 100644 --- a/c/src/lib/libbsp/sparc/leon3/shmsupp/getcfg.c +++ b/c/src/lib/libbsp/sparc/leon3/shmsupp/getcfg.c @@ -76,8 +76,12 @@ extern rtems_mpci_entry Shm_Send_packet( #define INTERRUPT 0 /* XXX: */ #define POLLING 1 /* XXX: fix me -- is polling ONLY!!! */ - -shm_config_table BSP_shm_cfgtbl; +/* Let user override this configuration by declaring this a weak variable */ +shm_config_table BSP_shm_cfgtbl __attribute__((weak)) = +{ + (vol_u32 *)0x40000000, + 0x00001000, +}; void Shm_Get_configuration( uint32_t localnode, @@ -88,8 +92,6 @@ void Shm_Get_configuration( int i; unsigned int tmp; - BSP_shm_cfgtbl.base = 0x40000000; - BSP_shm_cfgtbl.length = 0x00001000; BSP_shm_cfgtbl.format = SHM_BIG; /* @@ -107,14 +109,14 @@ void Shm_Get_configuration( BSP_shm_cfgtbl.poll_intr = INTR_MODE; BSP_shm_cfgtbl.Intr.address = - (vol_u32) &(LEON3_IrqCtrl_Regs->force[LEON3_Cpu_Index]); + (vol_u32 *) &(LEON3_IrqCtrl_Regs->force[LEON3_Cpu_Index]); BSP_shm_cfgtbl.Intr.value = 1 << LEON3_MP_IRQ ; BSP_shm_cfgtbl.Intr.length = 4; if (LEON3_Cpu_Index == 0) { tmp = 0; for (i = 1; - i < (Configuration.User_multiprocessing_table)->maximum_nodes+1; i++) + i < (Configuration.User_multiprocessing_table)->maximum_nodes; i++) tmp |= (1 << i); LEON3_IrqCtrl_Regs->mpstat = tmp; } diff --git a/c/src/lib/libbsp/sparc/leon3/startup/bspidle.S b/c/src/lib/libbsp/sparc/leon3/startup/bspidle.S index 24be8c0cb0..cb832677bf 100644 --- a/c/src/lib/libbsp/sparc/leon3/startup/bspidle.S +++ b/c/src/lib/libbsp/sparc/leon3/startup/bspidle.S @@ -24,6 +24,7 @@ PUBLIC(bsp_idle_thread) SYM(bsp_idle_thread): pwdloop: mov %g0, %asr19 + lda [%sp] 1, %g0 ! Needed for UT699 and GR712 ba pwdloop nop retl diff --git a/c/src/lib/libbsp/sparc/leon3/startup/bsppredriver.c b/c/src/lib/libbsp/sparc/leon3/startup/bsppredriver.c new file mode 100644 index 0000000000..ff633fee98 --- /dev/null +++ b/c/src/lib/libbsp/sparc/leon3/startup/bsppredriver.c @@ -0,0 +1,27 @@ +/* Installs the BSP pre-driver hook + * + * COPYRIGHT (c) 2011 + * Aeroflex Gaisler + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include <bsp.h> + +/* + * bsp_predriver_hook + * + * BSP predriver hook. Called just before drivers are initialized. + * Is used to initialize shared interrupt handling. + */ +void bsp_predriver_hook( void ) +{ + /* Initialize shared interrupt handling, must be done after IRQ + * controller has been found and initialized. + */ + BSP_shared_interrupt_init(); +} diff --git a/c/src/lib/libbsp/sparc/leon3/startup/bspstart.c b/c/src/lib/libbsp/sparc/leon3/startup/bspstart.c index fbe6b876ed..9e957020ba 100644 --- a/c/src/lib/libbsp/sparc/leon3/startup/bspstart.c +++ b/c/src/lib/libbsp/sparc/leon3/startup/bspstart.c @@ -27,25 +27,37 @@ */ int CPU_SPARC_HAS_SNOOPING; +/* Index of CPU, in an AMP system CPU-index may be non-zero */ +int LEON3_Cpu_Index = 0; + extern void amba_initialize(void); +extern void bsp_debug_uart_init(void); /* * set_snooping * * Read the data cache configuration register to determine if - * bus snooping is available. This is needed for some drivers so - * that they can select the most efficient copy routines. + * bus snooping is available and enabled. This is needed for some + * drivers so that they can select the most efficient copy routines. * */ static inline int set_snooping(void) { int tmp; - asm(" lda [%1] 2, %0 " + asm(" lda [%%g0] 2, %0 " : "=r"(tmp) - : "r"(0xC) + : ); - return (tmp >> 27) & 1; + return (tmp >> 23) & 1; +} + +/* ASM-function used to get the CPU-Index on calling LEON3 CPUs */ +static inline unsigned int get_asr17(void) +{ + unsigned int reg; + __asm__ (" mov %%asr17, %0 " : "=r"(reg) :); + return reg; } /* @@ -57,6 +69,19 @@ void bsp_start( void ) { CPU_SPARC_HAS_SNOOPING = set_snooping(); - /* Find UARTs */ + /* Get the LEON3 CPU index, normally 0, but for MP systems we do + * _not_ assume that this is CPU0. One may run another OS on CPU0 + * and RTEMS on this CPU, and AMP system with mixed operating + * systems + */ + LEON3_Cpu_Index = (get_asr17() >> 28) & 3; + + /* Scan AMBA Plug&Play and parse it into a RAM description (ambapp_plb), + * find GPTIMER for bus frequency, find IRQ Controller and initialize + * interrupt support + */ amba_initialize(); + + /* find debug UART for printk() */ + bsp_debug_uart_init(); } diff --git a/c/src/lib/libbsp/sparc/leon3/startup/eirq.c b/c/src/lib/libbsp/sparc/leon3/startup/eirq.c new file mode 100644 index 0000000000..68089a3d0c --- /dev/null +++ b/c/src/lib/libbsp/sparc/leon3/startup/eirq.c @@ -0,0 +1,27 @@ +/* + * GRLIB/LEON3 extended interrupt controller + * + * COPYRIGHT (c) 2011 + * Aeroflex Gaisler + * + * 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> + +extern int LEON3_Cpu_Index; + +/* GRLIB extended IRQ controller IRQ number */ +int LEON3_IrqCtrl_EIrq = -1; + +/* Initialize Exteneded Interrupt controller */ +void leon3_ext_irq_init(void) +{ + if ( (LEON3_IrqCtrl_Regs->mpstat >> 16) & 0xf ) { + /* Extended IRQ controller available */ + LEON3_IrqCtrl_EIrq = (LEON3_IrqCtrl_Regs->mpstat >> 16) & 0xf; + } +} diff --git a/c/src/lib/libbsp/sparc/leon3/startup/linkcmds_ngmp b/c/src/lib/libbsp/sparc/leon3/startup/linkcmds_ngmp new file mode 100644 index 0000000000..b0acd650c7 --- /dev/null +++ b/c/src/lib/libbsp/sparc/leon3/startup/linkcmds_ngmp @@ -0,0 +1,210 @@ +/* linkcmds + * + * $Id: linkcmds,v 1.8 2008/08/08 15:54:59 joel Exp $ + */ + +OUTPUT_ARCH(sparc) +__DYNAMIC = 0; + +/* + * The memory map looks like this: + * +--------------------+ <- low memory + * | .text | + * | etext | + * | ctor list | the ctor and dtor lists are for + * | dtor list | C++ support + * | _endtext | + * +--------------------+ + * | .data | initialized data goes here + * | _sdata | + * | _edata | + * +--------------------+ + * | .bss | + * | __bss_start | start of bss, cleared by crt0 + * | _end | start of heap, used by sbrk() + * +--------------------+ + * | heap space | + * | _ENDHEAP | + * | stack space | + * | __stack | top of stack + * +--------------------+ <- high memory + */ + + +/* + * User modifiable values: + * + * _CLOCK_SPEED in Mhz (used to program the counter/timers) + * + * _PROM_SIZE size of PROM (permissible values are 128K, 256K, + * 512K, 1M, 2M, 4M, 8M and 16M) + * _RAM_SIZE size of RAM (permissible values are 256K, 512K, + * 1M, 2M, 4M, 8M, 16M, and 32M) + * + */ + +/* Default values, can be overridden */ + +/*_PROM_SIZE = 2M;*/ +_RAM_SIZE = 64M; + +_RAM_START = 0x00000000; +_RAM_END = _RAM_START + _RAM_SIZE; + +/*_PROM_START = 0xC0000000; +_PROM_END = _PROM_START + _PROM_SIZE;*/ + +/* + * Alternate names without leading _. + */ + +/*PROM_START = _PROM_START; +PROM_SIZE = _PROM_SIZE; +PROM_END = _PROM_END;*/ + +RAM_START = _RAM_START; +RAM_SIZE = _RAM_SIZE; +RAM_END = _RAM_END; + +/* these are the maximum values */ + +MEMORY +{ + rom : ORIGIN = 0xC0000000, LENGTH = 256M + ram : ORIGIN = 0x00000000, LENGTH = 2048M + sram : ORIGIN = 0xD0000000, LENGTH = 256M +} + +/* + * SPARC monitor assumes this is present to provide proper RTEMS awareness. + */ +EXTERN(rtems_get_version_string); + +/* + * stick everything in ram (of course) + */ +SECTIONS +{ + .text : + { + CREATE_OBJECT_SYMBOLS + text_start = .; + _text_start = .; + *(.text*) + . = ALIGN (16); + + /* + * Special FreeBSD sysctl sections. + */ + . = ALIGN (16); + __start_set_sysctl_set = .; + *(set_sysctl_*); + __stop_set_sysctl_set = ABSOLUTE(.); + *(set_domain_*); + *(set_pseudo_*); + + *(.eh_frame) + . = ALIGN (16); + + *(.gnu.linkonce.t*) + + /* + * C++ constructors + */ + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + + _rodata_start = . ; + *(.rodata*) + *(.gnu.linkonce.r*) + _erodata = ALIGN( 0x10 ) ; + + etext = ALIGN(0x10); + _etext = .; + *(.init) + *(.fini) + *(.lit) + *(.shdata) + . = ALIGN (16); + _endtext = .; + } > ram + .rela.dyn : + { + *(.rela.init) + *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) + *(.rela.fini) + *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) + *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) + *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) + *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) + *(.rela.ctors) + *(.rela.dtors) + *(.rela.got) + *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) + } >ram + .data : + { + data_start = .; + _data_start = .; + _sdata = . ; + *(.data*) + *(.gnu.linkonce.d*) + *(.gcc_except_table*) + . = ALIGN(0x10); + edata = .; + _edata = .; + } > ram + .dynamic : { *(.dynamic) } >ram + .jcr : { *(.jcr) } >ram + .got : { *(.got) } >ram + .plt : { *(.plt) } >ram + .hash : { *(.hash) } >ram + .dynrel : { *(.dynrel) } >ram + .dynsym : { *(.dynsym) } >ram + .dynstr : { *(.dynstr) } >ram + .hash : { *(.hash) } >ram + .shbss : + { + *(.shbss) + } > ram + .bss : + { + __bss_start = ALIGN(0x8); + _bss_start = .; + bss_start = .; + *(.bss .bss* .gnu.linkonce.b*) + *(COMMON) + end = .; + _end = ALIGN(0x8); + __end = ALIGN(0x8); + } > ram + .stab . (NOLOAD) : + { + [ .stab ] + } + .stabstr . (NOLOAD) : + { + [ .stabstr ] + } +} diff --git a/c/src/lib/libbsp/sparc/leon3/startup/setvec.c b/c/src/lib/libbsp/sparc/leon3/startup/setvec.c index c7b8af6616..028b17a26a 100644 --- a/c/src/lib/libbsp/sparc/leon3/startup/setvec.c +++ b/c/src/lib/libbsp/sparc/leon3/startup/setvec.c @@ -42,7 +42,7 @@ rtems_isr_entry set_vector( /* returns old vector */ uint32_t real_trap; uint32_t source; - if ( type ) + if ( type == SET_VECTOR_INT ) rtems_interrupt_catch( handler, vector, &previous_isr ); else _CPU_ISR_install_raw_handler( vector, handler, (void *)&previous_isr ); diff --git a/c/src/lib/libbsp/sparc/leon3/startup/spurious.c b/c/src/lib/libbsp/sparc/leon3/startup/spurious.c index 62d38d3f0c..c7b63eb6a4 100644 --- a/c/src/lib/libbsp/sparc/leon3/startup/spurious.c +++ b/c/src/lib/libbsp/sparc/leon3/startup/spurious.c @@ -38,7 +38,7 @@ rtems_isr bsp_spurious_handler( real_trap = SPARC_REAL_TRAP_NUMBER(trap); - printk( "Unexpected trap (%2d) at address 0x%08x\n", real_trap, isf->tpc); + printk( "Unexpected trap (0x%02x) at address 0x%08x\n", real_trap, isf->tpc); switch (real_trap) { diff --git a/c/src/lib/libbsp/sparc/leon3/timer/timer.c b/c/src/lib/libbsp/sparc/leon3/timer/timer.c index 47de8339dc..b16c970b2f 100644 --- a/c/src/lib/libbsp/sparc/leon3/timer/timer.c +++ b/c/src/lib/libbsp/sparc/leon3/timer/timer.c @@ -34,7 +34,7 @@ bool benchmark_timer_find_average_overhead; bool benchmark_timer_is_initialized = false; -extern volatile LEON3_Timer_Regs_Map *LEON3_Timer_Regs; +extern volatile struct gptimer_regs *LEON3_Timer_Regs; void benchmark_timer_initialize(void) { @@ -49,7 +49,7 @@ void benchmark_timer_initialize(void) } else { benchmark_timer_is_initialized = true; } - LEON3_Timer_Regs->timer[LEON3_TIMER_INDEX].conf = LEON3_GPTIMER_EN | LEON3_GPTIMER_LD; + LEON3_Timer_Regs->timer[LEON3_TIMER_INDEX].ctrl = LEON3_GPTIMER_EN | LEON3_GPTIMER_LD; } } diff --git a/c/src/lib/libbsp/sparc/leon3/timer/watchdog.c b/c/src/lib/libbsp/sparc/leon3/timer/watchdog.c new file mode 100644 index 0000000000..47a5a8051a --- /dev/null +++ b/c/src/lib/libbsp/sparc/leon3/timer/watchdog.c @@ -0,0 +1,91 @@ +/* GPTIMER Watchdog timer routines. On some systems the first GPTIMER + * core's last Timer instance underflow signal is connected to system + * reset. + * + * COPYRIGHT (c) 2011 + * Aeroflex Gaisler + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include <bsp.h> +#include <grlib.h> + +extern volatile struct gptimer_regs *LEON3_Timer_Regs; + +struct gptimer_watchdog_priv { + struct gptimer_regs *regs; + struct gptimer_timer_regs *timer; + int timerno; +}; + +struct gptimer_watchdog_priv bsp_watchdogs[1]; +int bsp_watchdog_count = 0; + +int bsp_watchdog_init(void) +{ + int timercnt; + + if (!LEON3_Timer_Regs) + return 0; + + /* Get Watchdogs in system, this is implemented for one GPTIMER core + * only. + * + * First watchdog is a special case, we can get the first timer core by + * looking at LEON3_Timer_Regs, the watchdog within a timer core is + * always the last timer. Unfortunately we can not know it the watchdog + * functionality is available or not, we assume that it is if we + * reached this function. + */ + bsp_watchdogs[0].regs = (struct gptimer_regs *)LEON3_Timer_Regs; + + /* Find Timer that has watchdog functionality */ + timercnt = bsp_watchdogs[0].regs->cfg & 0x7; + if (timercnt < 2) /* First timer system clock timer */ + return 0; + + bsp_watchdogs[0].timerno = timercnt - 1; + bsp_watchdogs[0].timer = &bsp_watchdogs[0].regs->timer[bsp_watchdogs[0].timerno]; + + bsp_watchdog_count = 1; + return bsp_watchdog_count; +} + +void bsp_watchdog_reload(int watchdog, unsigned int reload_value) +{ + if (bsp_watchdog_count == 0) + bsp_watchdog_init(); + + if (bsp_watchdog_count <= watchdog) + return; + + /* Kick watchdog, and clear interrupt pending bit */ + bsp_watchdogs[watchdog].timer->reload = reload_value; + bsp_watchdogs[watchdog].timer->ctrl = + (LEON3_GPTIMER_LD | LEON3_GPTIMER_EN) | + (bsp_watchdogs[watchdog].timer->ctrl & ~(1<<4)); +} + +void bsp_watchdog_stop(int watchdog) +{ + if (bsp_watchdog_count == 0) + bsp_watchdog_init(); + + if (bsp_watchdog_count <= watchdog) + return; + + /* Stop watchdog timer */ + bsp_watchdogs[watchdog].timer->ctrl = 0; +} + +/* Use watchdog timer to reset system */ +void bsp_watchdog_system_reset(void) +{ + sparc_disable_interrupts(); + bsp_watchdog_reload(0, 1); +} diff --git a/c/src/lib/libbsp/sparc/shared/1553/b1553brm.c b/c/src/lib/libbsp/sparc/shared/1553/b1553brm.c index 5eac447450..ca81a929a6 100644 --- a/c/src/lib/libbsp/sparc/shared/1553/b1553brm.c +++ b/c/src/lib/libbsp/sparc/shared/1553/b1553brm.c @@ -1,51 +1,23 @@ /* - * BRM driver + * BRM driver * - * COPYRIGHT (c) 2006. - * Gaisler Research. + * COPYRIGHT (c) 2008. + * Aeroflex Gaisler. * * 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. * + * 2008-12-10, Daniel Hellstrom <daniel@gaisler.com> + * Converted to support driver manager */ /********** Set defaults **********/ -/* basic bus/interface of device, - * Default to direct accessed AMBA bus. - */ -#ifndef B1553BRM_NO_AMBA - #define B1553BRM_AMBA - #undef B1553BRM_PCI -#endif - -/* default name to /dev/brm */ -#if !defined(B1553BRM_DEVNAME) || !defined(B1553BRM_DEVNAME_NO) - #undef B1553BRM_DEVNAME - #undef B1553BRM_DEVNAME_NO - #define B1553BRM_DEVNAME "/dev/brm0" - #define B1553BRM_DEVNAME_NO(devstr,no) ((devstr)[8]='0'+(no)) -#endif - -#ifndef B1553BRM_PREFIX - #define B1553BRM_PREFIX(name) b1553brm##name -#endif - -/* default to no translation */ -#ifndef B1553BRM_ADR_TO - #define memarea_to_hw(x) ((unsigned int)(x)) -#endif - -#ifndef B1553BRM_REG_INT - #define B1553BRM_REG_INT(handler,irqno,arg) set_vector(handler,(irqno)+0x10,1) - #undef B1553BRM_DEFINE_INTHANDLER - #define B1553BRM_DEFINE_INTHANDLER -#endif - -/* default to 128K memory layout */ -#if !defined(DMA_MEM_16K) - #define DMA_MEM_128K +/* default to 16K memory layout */ +#define DMA_MEM_128K +#if !defined(DMA_MEM_128K) + #define DMA_MEM_16K #endif #include <bsp.h> @@ -57,8 +29,10 @@ #include <ctype.h> #include <rtems/bspIo.h> +#include <drvmgr/drvmgr.h> #include <b1553brm.h> #include <ambapp.h> +#include <drvmgr/ambapp_bus.h> /* Uncomment for debug output */ /*#define DEBUG 1 @@ -68,28 +42,28 @@ /* EVENT_QUEUE_SIZE sets the size of the event queue */ -#define EVENT_QUEUE_SIZE 1024 +#define EVENT_QUEUE_SIZE 1024 #define INDEX(x) ( x&(EVENT_QUEUE_SIZE-1) ) -#ifdef DEBUG +#if 0 #define DBG(x...) printk(x) #else -#define DBG(x...) +#define DBG(x...) #endif #ifdef FUNCDEBUG #define FUNCDBG(x...) printk(x) #else -#define FUNCDBG(x...) +#define FUNCDBG(x...) #endif -#define READ_REG(address) _BRM_REG_READ16((unsigned int)address) +#define READ_REG(address) (*(volatile unsigned int *)address) #define READ_DMA(address) _BRM_REG_READ16((unsigned int)address) static __inline__ unsigned short _BRM_REG_READ16(unsigned int addr) { unsigned short tmp; - asm(" lduha [%1]1, %0 " + __asm__(" lduha [%1]1, %0 " : "=r"(tmp) : "r"(addr) ); @@ -105,7 +79,7 @@ static rtems_device_driver brm_control(rtems_device_major_number major, rtems_de #define BRM_DRIVER_TABLE_ENTRY { brm_initialize, brm_open, brm_close, brm_read, brm_write, brm_control } -static rtems_driver_address_table brm_driver = BRM_DRIVER_TABLE_ENTRY; +static rtems_driver_address_table b1553brm_driver = BRM_DRIVER_TABLE_ENTRY; struct msg { unsigned short miw; @@ -132,165 +106,450 @@ struct irq_log_list { volatile unsigned short iaw; }; -typedef struct { - - unsigned int memarea_base; - struct brm_reg *regs; - - /* BRM descriptors */ - struct desc_table { - - volatile unsigned short ctrl; - volatile unsigned short top; - volatile unsigned short cur; - volatile unsigned short bot; - - } *desc; - - volatile unsigned short *mem; - /* bc mem struct */ +typedef struct { + + struct drvmgr_dev *dev; /* Driver manager device */ + char devName[32]; /* Device Name */ + struct brm_reg *regs; + + unsigned int memarea_base; + unsigned int memarea_base_remote; + unsigned int cfg_clksel; + unsigned int cfg_clkdiv; + unsigned int cfg_freq; + + /* BRM descriptors */ + struct desc_table { + volatile unsigned short ctrl; + volatile unsigned short top; + volatile unsigned short cur; + volatile unsigned short bot; + } *desc; + + volatile unsigned short *mem; + /* bc mem struct */ + struct { + /* BC Descriptors */ + struct { + unsigned short ctrl; /* control */ + unsigned short cw1; /* Command word 1*/ + unsigned short cw2; /* Command word 1*/ + unsigned short dptr; /* Data pointer in halfword offset from bcmem */ + unsigned short tsw[2]; /* status word 1 & 2 */ + unsigned short ba; /* branch address */ + unsigned short timer; /* timer value */ + } descs[128]; /* 2k (1024 half words) */ + + /* message data */ struct { - /* BC Descriptors */ - struct { - unsigned short ctrl; /* control */ - unsigned short cw1; /* Command word 1*/ - unsigned short cw2; /* Command word 1*/ - unsigned short dptr; /* Data pointer in halfword offset from bcmem */ - unsigned short tsw[2]; /* status word 1 & 2 */ - unsigned short ba; /* branch address */ - unsigned short timer; /* timer value */ - } descs[128]; /* 2k (1024 half words) */ - - /* message data */ - struct { - unsigned short data[32]; /* 1 message's data */ - } msg_data[128]; /* 8k */ + unsigned short data[32]; /* 1 message's data */ + } msg_data[128]; /* 8k */ #if defined(DMA_MEM_128K) - /* offset to last 64bytes of 128k */ - unsigned short unused[(64*1024-(128*8+128*32))-16*2]; + /* offset to last 64bytes of 128k */ + unsigned short unused[(64*1024-(128*8+128*32))-16*2]; #elif defined(DMA_MEM_16K) - unsigned short unused[(8*1024-(128*8+128*32))-16*2]; + unsigned short unused[(8*1024-(128*8+128*32))-16*2]; #endif - /* interrupt log at 64 bytes from end */ - struct irq_log_list irq_logs[16]; - } *bcmem; + /* interrupt log at 64 bytes from end */ + struct irq_log_list irq_logs[16]; + } *bcmem; #if defined(DMA_MEM_128K) - /* Memory structure of a RT being inited, just used - * for RT initialization. - * - * *mesgs[32] fit each minimally 8 messages per sub address. - */ - struct { - /* RX Sub Address descriptors */ - struct desc_table rxsubs[32]; - /* TX Sub Address descriptors */ - struct desc_table txsubs[32]; - /* RX mode code descriptors */ - struct desc_table rxmodes[32]; - /* TX mode code descriptors */ - struct desc_table txmodes[32]; - - /* RX Sub Address messages */ - struct circ_buf rxsuba_msgs[32]; - /* TX Sub Address messages */ - struct circ_buf txsuba_msgs[32]; - /* RX Mode Code messages */ - struct circ_buf rxmode_msgs[32]; - /* RX Mode Code messages */ - struct circ_buf txmode_msgs[32]; - - - /* offset to last 64bytes of 128k: tot-used-needed */ - unsigned short unused[(64*1024-(4*32*4+4*32*9*34))-16*2]; - - /* interrupt log at 64 bytes from end */ - struct irq_log_list irq_logs[16]; - } *rtmem; + /* Memory structure of a RT being inited, just used + * for RT initialization. + * + * *mesgs[32] fit each minimally 8 messages per sub address. + */ + struct { + /* RX Sub Address descriptors */ + struct desc_table rxsubs[32]; + /* TX Sub Address descriptors */ + struct desc_table txsubs[32]; + /* RX mode code descriptors */ + struct desc_table rxmodes[32]; + /* TX mode code descriptors */ + struct desc_table txmodes[32]; + + /* RX Sub Address messages */ + struct circ_buf rxsuba_msgs[32]; + /* TX Sub Address messages */ + struct circ_buf txsuba_msgs[32]; + /* RX Mode Code messages */ + struct circ_buf rxmode_msgs[32]; + /* RX Mode Code messages */ + struct circ_buf txmode_msgs[32]; + + /* offset to last 64bytes of 128k: tot-used-needed */ + unsigned short unused[(64*1024-(4*32*4+4*32*9*34))-16*2]; + + /* interrupt log at 64 bytes from end */ + struct irq_log_list irq_logs[16]; + } *rtmem; #elif defined(DMA_MEM_16K) - /* Memory structure of a RT being inited, just used - * for RT initialization. - * - * circ_buf_2 *mesgs[32] fit each minimally 2 messages per queue. - * circ_buf_1 *mesgs[32] fit each minimally 1 messages per queue. - */ - struct { - /* RX Sub Address descriptors */ - struct desc_table rxsubs[32]; - /* TX Sub Address descriptors */ - struct desc_table txsubs[32]; - /* RX mode code descriptors */ - struct desc_table rxmodes[32]; - /* TX mode code descriptors */ - struct desc_table txmodes[32]; - - /* RX Sub Address messages */ - struct circ_buf_2 rxsuba_msgs[32]; - /* TX Sub Address messages */ - struct circ_buf_2 txsuba_msgs[32]; - /* RX Mode Code messages */ - struct circ_buf_2 rxmode_msgs[32]; - /* RX Mode Code messages */ - struct circ_buf_1 txmode_msgs[32]; - - - /* offset to last 64bytes of 16k: tot-used-needed */ - unsigned short unused[8*1024 -(4*32*4 +3*32*2*34 +1*32*1*34) -16*2]; - - /* interrupt log at 64 bytes from end */ - struct irq_log_list irq_logs[16]; - } *rtmem; + /* Memory structure of a RT being inited, just used + * for RT initialization. + * + * circ_buf_2 *mesgs[32] fit each minimally 2 messages per queue. + * circ_buf_1 *mesgs[32] fit each minimally 1 messages per queue. + */ + struct { + /* RX Sub Address descriptors */ + struct desc_table rxsubs[32]; + /* TX Sub Address descriptors */ + struct desc_table txsubs[32]; + /* RX mode code descriptors */ + struct desc_table rxmodes[32]; + /* TX mode code descriptors */ + struct desc_table txmodes[32]; + + /* RX Sub Address messages */ + struct circ_buf_2 rxsuba_msgs[32]; + /* TX Sub Address messages */ + struct circ_buf_2 txsuba_msgs[32]; + /* RX Mode Code messages */ + struct circ_buf_2 rxmode_msgs[32]; + /* RX Mode Code messages */ + struct circ_buf_1 txmode_msgs[32]; + + /* offset to last 64bytes of 16k: tot-used-needed */ + unsigned short unused[8*1024 -(4*32*4 +3*32*2*34 +1*32*1*34) -16*2]; + + /* interrupt log at 64 bytes from end */ + struct irq_log_list irq_logs[16]; + } *rtmem; #else #error You must define one DMA_MEM_???K #endif - /* Interrupt log list */ - struct irq_log_list *irq_log; - unsigned int irq; + /* Interrupt log list */ + struct irq_log_list *irq_log; + unsigned int irq; - /* Received events waiting to be read */ - struct rt_msg *rt_event; - struct bm_msg *bm_event; + /* Received events waiting to be read */ + struct rt_msg *rt_event; + struct bm_msg *bm_event; - unsigned int head, tail; + unsigned int head, tail; - unsigned int last_read[128]; - unsigned int written[32]; + unsigned int last_read[128]; + unsigned int written[32]; - struct bc_msg *cur_list; + struct bc_msg *cur_list; - int tx_blocking, rx_blocking; + int tx_blocking, rx_blocking; - rtems_id rx_sem, tx_sem, dev_sem; - int minor; - int irqno; - unsigned int mode; -#ifdef DEBUG - unsigned int log[EVENT_QUEUE_SIZE*4]; - unsigned int log_i; + rtems_id rx_sem, tx_sem, dev_sem; + int minor; + int irqno; + unsigned int mode; +#ifdef DEBUG + unsigned int log[EVENT_QUEUE_SIZE*4]; + unsigned int log_i; #endif - rtems_id event_id; /* event that may be signalled upon errors, needs to be set through ioctl command BRM_SET_EVENTID */ - unsigned int status; - int bc_list_fail; + rtems_id event_id; /* event that may be signalled upon errors, needs to be set through ioctl command BRM_SET_EVENTID */ + unsigned int status; + int bc_list_fail; } brm_priv; -static int brm_cores; -static unsigned int allbrm_memarea; -static brm_priv *brms; -static amba_confarea_type *amba_bus; -static unsigned int allbrm_cfg_clksel; -static unsigned int allbrm_cfg_clkdiv; -static unsigned int allbrm_cfg_freq; - -static void brm_interrupt(brm_priv *brm); -#ifdef B1553BRM_DEFINE_INTHANDLER -static void b1553brm_interrupt_handler(rtems_vector_number v); -#endif +static void b1553brm_interrupt(void *arg); +static rtems_device_driver rt_init(brm_priv *brm); #define OFS(ofs) (((unsigned int)&ofs & 0x1ffff)>>1) +static int b1553brm_driver_io_registered = 0; +static rtems_device_major_number b1553brm_driver_io_major = 0; + +/******************* Driver manager interface ***********************/ + +/* Driver prototypes */ +int b1553brm_register_io(rtems_device_major_number *m); +int b1553brm_device_init(brm_priv *pDev); + +int b1553brm_init2(struct drvmgr_dev *dev); +int b1553brm_init3(struct drvmgr_dev *dev); +int b1553brm_remove(struct drvmgr_dev *dev); + +struct drvmgr_drv_ops b1553brm_ops = +{ + .init = {NULL, b1553brm_init2, b1553brm_init3, NULL}, + .remove = b1553brm_remove, + .info = NULL +}; + +struct amba_dev_id b1553brm_ids[] = +{ + {VENDOR_GAISLER, GAISLER_B1553BRM}, + {0, 0} /* Mark end of table */ +}; + +struct amba_drv_info b1553brm_drv_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_GAISLER_B1553BRM_ID, /* Driver ID */ + "B1553BRM_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &b1553brm_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + 0, + }, + &b1553brm_ids[0] +}; + +void b1553brm_register_drv (void) +{ + DBG("Registering B1553BRM driver\n"); + drvmgr_drv_register(&b1553brm_drv_info.general); +} + +int b1553brm_init2(struct drvmgr_dev *dev) +{ + brm_priv *priv; + + DBG("B1553BRM[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name); + priv = dev->priv = malloc(sizeof(brm_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 b1553brm_init3(struct drvmgr_dev *dev) +{ + brm_priv *priv; + char prefix[32]; + rtems_status_code status; + + priv = dev->priv; + + /* Do initialization */ + + if ( b1553brm_driver_io_registered == 0) { + /* Register the I/O driver only once for all cores */ + if ( b1553brm_register_io(&b1553brm_driver_io_major) ) { + /* Failed to register I/O driver */ + dev->priv = NULL; + return DRVMGR_FAIL; + } + + b1553brm_driver_io_registered = 1; + } + + /* I/O system registered and initialized + * Now we take care of device initialization. + */ + + if ( b1553brm_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/b1553brm%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/%sb1553brm%d", prefix, dev->minor_bus); + } + + /* Register Device */ + status = rtems_io_register_name(priv->devName, b1553brm_driver_io_major, dev->minor_drv); + if (status != RTEMS_SUCCESSFUL) { + return DRVMGR_FAIL; + } + + return DRVMGR_OK; +} + +int b1553brm_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_OK; +} + +/******************* Driver Implementation ***********************/ + +int b1553brm_register_io(rtems_device_major_number *m) +{ + rtems_status_code r; + + if ((r = rtems_io_register_driver(0, &b1553brm_driver, m)) == RTEMS_SUCCESSFUL) { + DBG("B1553BRM driver successfully registered, major: %d\n", *m); + } else { + switch(r) { + case RTEMS_TOO_MANY: + printk("B1553BRM rtems_io_register_driver failed: RTEMS_TOO_MANY\n"); + return -1; + case RTEMS_INVALID_NUMBER: + printk("B1553BRM rtems_io_register_driver failed: RTEMS_INVALID_NUMBER\n"); + return -1; + case RTEMS_RESOURCE_IN_USE: + printk("B1553BRM rtems_io_register_driver failed: RTEMS_RESOURCE_IN_USE\n"); + return -1; + default: + printk("B1553BRM rtems_io_register_driver failed\n"); + return -1; + } + } + return 0; +} + +int b1553brm_device_init(brm_priv *pDev) +{ + struct amba_dev_info *ambadev; + struct ambapp_core *pnpinfo; + union drvmgr_key_value *value; + char *mem; + + /* 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; + /* Two versions of the BRM core. One where the registers are accessed using the AHB bus + * and one where the APB bus is used + */ + if ( pnpinfo->ahb_slv ) { + /* Registers accessed over AHB */ + pDev->regs = (struct brm_reg *)pnpinfo->ahb_slv->start[0]; + } else { + /* Registers accessed over APB */ + pDev->regs = (struct brm_reg *)pnpinfo->apb_slv->start; + } + pDev->minor = pDev->dev->minor_drv; +#ifdef DEBUG + pDev->log_i = 0; + memset(pDev->log,0,sizeof(pDev->log)); +#endif + + /* Get memory configuration from bus resources */ + value = drvmgr_dev_key_get(pDev->dev, "dmaBaseAdr", KEY_TYPE_POINTER); + if ( value ) { + mem = value->ptr; + if ( (unsigned int)mem & 1 ) { + /* Remote address, address as BRM looks at it. */ + + /* Translate the base address into an address that the the CPU can understand */ + mem = (char *)((unsigned int)mem & ~1); + drvmgr_translate(pDev->dev, 1, 1, (void *)mem, (void **)&mem); + } + } else { + /* Use dynamically allocated memory */ + mem = (char *)malloc(128 * 1024 * 2); /* 128k DMA memory + 128k for alignment */ + if ( !mem ){ + printk("BRM: Failed to allocate HW memory\n\r"); + return -1; + } + } + /* align memory to 128k boundary */ + mem = (char *)(((unsigned int)mem+0x1ffff) & ~0x1ffff); + + /* clear the used memory */ +#ifdef DMA_MEM_128K + memset(mem, 0, (128 * 1024)); +#else + memset(mem, 0, (16 * 1024)); +#endif + + /* Set base address of all descriptors */ + pDev->memarea_base = (unsigned int)mem; + pDev->desc = (struct desc_table *) pDev->memarea_base; + pDev->mem = (volatile unsigned short *) pDev->memarea_base; + pDev->irq_log = (struct irq_log_list *)(pDev->memarea_base + (0xFFE0<<1)); /* last 64byte */ + + /* Translate the base address into an address that the BRM core can understand */ + drvmgr_translate(pDev->dev, 0, 0, (void *)mem, (void **)&pDev->memarea_base_remote); + + pDev->bm_event = NULL; + pDev->rt_event = NULL; + + pDev->cfg_clksel = 0; + pDev->cfg_clkdiv = 0; + pDev->cfg_freq = BRM_FREQ_24MHZ; + + value = drvmgr_dev_key_get(pDev->dev, "clkSel", KEY_TYPE_INT); + if ( value ) { + pDev->cfg_clksel = value->i & CLKSEL_MASK; + } + + value = drvmgr_dev_key_get(pDev->dev, "clkDiv", KEY_TYPE_INT); + if ( value ) { + pDev->cfg_clkdiv = value->i & CLKDIV_MASK; + } + + value = drvmgr_dev_key_get(pDev->dev, "coreFreq", KEY_TYPE_INT); + if ( value ) { + pDev->cfg_freq = value->i & BRM_FREQ_MASK; + } + + /* Sel clock so that we can write to BRM's registers */ + pDev->regs->w_ctrl = (pDev->cfg_clksel<<9) | (pDev->cfg_clkdiv<<5); + /* Reset BRM core */ + pDev->regs->w_ctrl = 1<<10 | READ_REG(&pDev->regs->w_ctrl); + + /* RX Semaphore created with count = 0 */ + if ( rtems_semaphore_create(rtems_build_name('B', 'M', 'R', '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("BRM: Failed to create rx semaphore\n"); + return RTEMS_INTERNAL_ERROR; + } + + /* TX Semaphore created with count = 1 */ + if ( rtems_semaphore_create(rtems_build_name('B', 'M', 'T', '0' + pDev->minor), + 1, + RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE|RTEMS_NO_INHERIT_PRIORITY|RTEMS_LOCAL|RTEMS_NO_PRIORITY_CEILING, + 0, + &pDev->tx_sem) != RTEMS_SUCCESSFUL ){ + printk("BRM: Failed to create tx semaphore\n"); + return RTEMS_INTERNAL_ERROR; + } + + /* Device Semaphore created with count = 1 */ + if ( rtems_semaphore_create(rtems_build_name('B', 'M', 'D', '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("BRM: 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; @@ -298,12 +557,11 @@ static int odd_parity(unsigned int data) { { i++; data &= (data - 1); - } + } return !(i&1); } - static void start_operation(brm_priv *brm) { unsigned int ctrl = READ_REG(&brm->regs->ctrl); brm->regs->ctrl = ctrl | 0x8000; @@ -318,72 +576,6 @@ static int is_executing(brm_priv *brm) { return ((ctrl>>15) & 1); } -#ifdef LEON3 -#ifndef DONT_DEF_RAMON -int brm_register_leon3_ramon_fpga(void){ - /* Clock div & Clock sel is NOT available. - * The BRM is always clocked with 24MHz. - * 3 in BRM enhanced register will select 24MHz - */ - return b1553brm_register(&amba_conf,0,0,3); -} - -int brm_register_leon3_ramon_asic(void){ - /* Clock div & Clock sel is available. - * Clkdiv only matter when clksel is 1. - * clksel=2, clkdiv=don't care, brm_frq=24MHz - * - * 3 in BRM enhanced register will select 24MHz - */ - return b1553brm_register(&amba_conf,2,0,3); -} -#endif -#endif - -int B1553BRM_PREFIX(_register)(amba_confarea_type *bus, unsigned int clksel, unsigned int clkdiv, unsigned int brm_freq) -{ - rtems_status_code r; - rtems_device_major_number m; - - FUNCDBG("brm_register:\n\r"); - - /* save amba bus pointer */ - amba_bus = bus; - if ( !bus ){ - printk("brm_register: bus is NULL\n\r"); - return 1; - } - -#ifdef B1553BRM_LOCAL_MEM - allbrm_memarea = B1553BRM_LOCAL_MEM_ADR; -#else - allbrm_memarea = 0; -#endif - - /* Save clksel, clkdiv and brm_freq for later use */ - allbrm_cfg_clksel = clksel & CLKSEL_MASK; - allbrm_cfg_clkdiv = clkdiv & CLKDIV_MASK; - allbrm_cfg_freq = brm_freq & BRM_FREQ_MASK; - - if ((r = rtems_io_register_driver(0, &brm_driver, &m)) == RTEMS_SUCCESSFUL) { - DBG("BRM: driver successfully registered, major: %d\n",m); - - } else { - switch(r) { - case RTEMS_TOO_MANY: - printk("BRM rtems_io_register_driver failed: RTEMS_TOO_MANY\n"); break; - case RTEMS_INVALID_NUMBER: - printk("BRM rtems_io_register_driver failed: RTEMS_INVALID_NUMBER\n"); break; - case RTEMS_RESOURCE_IN_USE: - printk("BRM rtems_io_register_driver failed: RTEMS_RESOURCE_IN_USE\n"); break; - default: - printk("BRM rtems_io_register_driver failed\n"); - } - return 1; - } - return 0; -} - static void clr_int_logs(struct irq_log_list *logs){ int i; for(i=0; i<16; i++){ @@ -398,18 +590,18 @@ static rtems_device_driver rt_init(brm_priv *brm) { brm->head = brm->tail = 0; brm->rx_blocking = brm->tx_blocking = 1; - if ( brm->bm_event ) + if ( brm->bm_event ) free(brm->bm_event); brm->bm_event = NULL; - if ( brm->rt_event ) + if ( brm->rt_event ) free(brm->rt_event); - + brm->bcmem = NULL; brm->rtmem = (void *)brm->mem; brm->rt_event = (struct rt_msg *) malloc(EVENT_QUEUE_SIZE*sizeof(struct rt_msg)); - + if (brm->rt_event == NULL) { DBG("BRM driver failed to allocated memory."); return RTEMS_NO_MEMORY; @@ -422,11 +614,11 @@ static rtems_device_driver rt_init(brm_priv *brm) { brm->regs->imask = BRM_RT_ILLCMD_IRQ|BRM_SUBAD_IRQ|BRM_TAPF_IRQ|BRM_DMAF_IRQ|BRM_WRAPF_IRQ|BRM_MERR_IRQ; brm->regs->dpoint = 0; brm->regs->ipoint = OFS(brm->rtmem->irq_logs[0]); - brm->regs->enhanced = 0x0000 | allbrm_cfg_freq; /* BRM clocked with freq = 12,16,20 or 24MHz */ - brm->regs->w_ctrl = (allbrm_cfg_clksel<<9) | (allbrm_cfg_clkdiv<<5) | 1; + brm->regs->enhanced = 0x0000 | brm->cfg_freq; /* BRM clocked with freq = 12,16,20 or 24MHz */ + brm->regs->w_ctrl = (brm->cfg_clksel<<9) | (brm->cfg_clkdiv<<5) | 1; brm->regs->w_irqctrl = 6; - brm->regs->w_ahbaddr = (unsigned int) memarea_to_hw(brm->memarea_base); - + brm->regs->w_ahbaddr = brm->memarea_base_remote; + clr_int_logs(brm->irq_log); /* Legalize all commands */ @@ -434,19 +626,19 @@ static rtems_device_driver rt_init(brm_priv *brm) { brm->regs->rt_cmd_leg[i] = 0; } - /* Init descriptor table - * + /* Init descriptor table + * * Each circular buffer has room for 8 messages with up to 34 (32 data + miw + time) words (16b) in each. * The buffers must separated by 34 words. */ - + /* RX Sub-address 0 - 31 */ for (i = 0; i < 32; i++) { brm->rtmem->rxsubs[i].ctrl = 0x00E0; /* Interrupt: INTX, IWA, and IBRD */ brm->rtmem->rxsubs[i].top = OFS(brm->rtmem->rxsuba_msgs[i]); /* Top address */ brm->rtmem->rxsubs[i].cur = OFS(brm->rtmem->rxsuba_msgs[i]); /* Current address */ - brm->rtmem->rxsubs[i].bot = OFS(brm->rtmem->rxsuba_msgs[i+1]) - sizeof(struct msg)/2; /* Bottom address */ + brm->rtmem->rxsubs[i].bot = OFS(brm->rtmem->rxsuba_msgs[i+1]) - sizeof(struct msg)/2; /* Bottom address */ brm->last_read[i] = OFS(brm->rtmem->rxsuba_msgs[i]); } /* TX Sub-address 0 - 31 */ @@ -465,7 +657,7 @@ static rtems_device_driver rt_init(brm_priv *brm) { brm->rtmem->rxmodes[i].cur = OFS(brm->rtmem->rxmode_msgs[i]); /* Current address */ brm->rtmem->rxmodes[i].bot = OFS(brm->rtmem->rxmode_msgs[i+1]) - sizeof(struct msg)/2; /* Bottom address */ brm->last_read[i+64] = OFS(brm->rtmem->rxmode_msgs[i]); - } + } /* TX mode code 0 - 31 */ for (i = 0; i < 32; i++) { brm->rtmem->txmodes[i].ctrl = 0x0060; /* Interrupt: IWA and IBRD */ @@ -475,6 +667,12 @@ static rtems_device_driver rt_init(brm_priv *brm) { brm->last_read[i+96] = OFS(brm->rtmem->txmode_msgs[i]); } +#ifdef DEBUG + printk("b1553BRM DMA_AREA: 0x%x\n", (unsigned int)brm->rtmem); + printk("LOG: 0x%x\n", &brm->log[0]); + printk("LOG_I: 0x%x\n", &brm->log_i); +#endif + brm->mode = BRM_MODE_RT; return RTEMS_SUCCESSFUL; @@ -482,36 +680,35 @@ static rtems_device_driver rt_init(brm_priv *brm) { static rtems_device_driver bc_init(brm_priv *brm){ - if ( brm->bm_event ) + if ( brm->bm_event ) free(brm->bm_event); brm->bm_event = NULL; - if ( brm->rt_event ) + if ( brm->rt_event ) free(brm->rt_event); brm->rt_event = NULL; - + brm->bcmem = (void *)brm->mem; brm->rtmem = NULL; brm->irq_log = (struct irq_log_list *)&brm->bcmem->irq_logs[0]; - + brm->head = brm->tail = 0; brm->rx_blocking = brm->tx_blocking = 1; - + brm->regs->ctrl = 0x0006; /* ping pong enable and enable interrupt log */ brm->regs->oper = 0x0800; /* configure as BC */ brm->regs->imask = BRM_EOL_IRQ|BRM_BC_ILLCMD_IRQ|BRM_ILLOP_IRQ|BRM_DMAF_IRQ|BRM_WRAPF_IRQ|BRM_MERR_IRQ; brm->regs->dpoint = 0; - printk("Set BC interrupt log: 0x%lx, 0x%lx, 0x%lx\n",OFS(brm->bcmem->irq_logs[0]),&brm->bcmem->irq_logs[0],brm->bcmem); brm->regs->ipoint = OFS(brm->bcmem->irq_logs[0]); - brm->regs->enhanced = 0x0000 | (allbrm_cfg_freq&0x3); /* freq = 24 */ - brm->regs->w_ctrl = (allbrm_cfg_clksel<<9) | (allbrm_cfg_clkdiv<<5) | 1; + brm->regs->enhanced = 0x0000 | (brm->cfg_freq&BRM_FREQ_MASK); /* freq = 24 */ + brm->regs->w_ctrl = (brm->cfg_clksel<<9) | (brm->cfg_clkdiv<<5) | 1; brm->regs->w_irqctrl = 6; - brm->regs->w_ahbaddr = (unsigned int) memarea_to_hw(brm->memarea_base); - + brm->regs->w_ahbaddr = brm->memarea_base_remote; + clr_int_logs(brm->irq_log); - + brm->mode = BRM_MODE_BC; - + return RTEMS_SUCCESSFUL; } @@ -521,18 +718,18 @@ static rtems_device_driver bm_init(brm_priv *brm) { brm->head = brm->tail = 0; brm->rx_blocking = brm->tx_blocking = 1; - if ( brm->rt_event ) + if ( brm->rt_event ) free(brm->rt_event); brm->rt_event = NULL; - if ( brm->bm_event ) + if ( brm->bm_event ) free(brm->bm_event); - + brm->bcmem = NULL; brm->rtmem = NULL; - + brm->bm_event = (struct bm_msg *) malloc(EVENT_QUEUE_SIZE*sizeof(struct bm_msg)); - + if (brm->bm_event == NULL) { DBG("BRM driver failed to allocated memory."); return RTEMS_NO_MEMORY; @@ -543,760 +740,634 @@ static rtems_device_driver bm_init(brm_priv *brm) { brm->regs->ctrl = 0x0006; /* ping pong enable and enable interrupt log */ brm->regs->oper = 0x0A00; /* configure as BM */ - brm->regs->imask = BRM_MBC_IRQ|BRM_MERR_IRQ|BRM_DMAF_IRQ|BRM_MERR_IRQ; + brm->regs->imask = BRM_MBC_IRQ|BRM_MERR_IRQ|BRM_DMAF_IRQ; brm->regs->dpoint = 0; brm->regs->ipoint = OFS(brm->mem[8*1024-16*2]); brm->regs->mcpoint = 0; /* Command pointer */ brm->regs->mdpoint = 0x100; /* Data pointer */ brm->regs->mbc = 1; /* Block count */ - brm->regs->enhanced = 0x0000 | (allbrm_cfg_freq&0x3); /* freq = 24 */ - brm->regs->w_ctrl = (allbrm_cfg_clksel<<9) | (allbrm_cfg_clkdiv<<5) | 1; + brm->regs->enhanced = 0x0000 | (brm->cfg_freq&BRM_FREQ_MASK); /* freq = 24 */ + brm->regs->w_ctrl = (brm->cfg_clksel<<9) | (brm->cfg_clkdiv<<5) | 1; brm->regs->w_irqctrl = 6; - brm->regs->w_ahbaddr = (unsigned int) memarea_to_hw(brm->memarea_base); - + brm->regs->w_ahbaddr = brm->memarea_base_remote; + clr_int_logs(brm->irq_log); - + brm->mode = BRM_MODE_BM; - + return RTEMS_SUCCESSFUL; } static rtems_device_driver brm_initialize(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) { - rtems_status_code status; - int dev_cnt; - char fs_name[20]; - brm_priv *brm; - amba_ahb_device ambadev; - char *mem; - - FUNCDBG("brm_initialize\n"); + return RTEMS_SUCCESSFUL; +} - brm_cores = 0; - strcpy(fs_name,B1553BRM_DEVNAME); +static rtems_device_driver brm_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) { + brm_priv *brm; + struct drvmgr_dev *dev; - /* Find all BRM devices */ - dev_cnt = amba_get_number_ahbslv_devices(amba_bus,VENDOR_GAISLER,GAISLER_BRM); - if ( dev_cnt < 1 ){ - /* Failed to find any CAN cores! */ - printk("BRM: Failed to find any BRM cores\n\r"); - return -1; - } + FUNCDBG("brm_open\n"); - /* allocate & zero memory for the brm devices */ - brms = (brm_priv *)malloc(sizeof(*brms)*dev_cnt); - if ( !brms ){ - printk("BRM: Failed to allocate SW memory\n\r"); - return -1; + if ( drvmgr_get_dev(&b1553brm_drv_info.general, minor, &dev) ) { + DBG("Wrong minor %d\n", minor); + return RTEMS_UNSATISFIED; } - memset(brms,0,sizeof(*brms)*dev_cnt); - - /* Allocate memory for all device's descriptors, - * they must be aligned to a XXX byte boundary. - */ - #define BRM_DESCS_PER_CTRL 128 - if ( allbrm_memarea ){ - mem = (char *)allbrm_memarea; - }else{ - /* sizeof(struct desc_table) * BRM_DESCS_PER_CTRL * dev_cnt */ - mem = (char *)malloc( (128*1024) * (dev_cnt+1)); /* 128k per core + 128k for alignment */ - if ( !mem ){ - free(brms); - printk("BRM: Failed to allocate HW memory\n\r"); - return -1; - } + brm = (brm_priv *)dev->priv; - /* align memory to 128k boundary */ - mem = (char *)(((unsigned int)mem+0x1ffff) & ~0x1ffff); + if (rtems_semaphore_obtain(brm->dev_sem, RTEMS_NO_WAIT, RTEMS_NO_TIMEOUT) != RTEMS_SUCCESSFUL) { + DBG("brm_open: resource in use\n"); + return RTEMS_RESOURCE_IN_USE; /* EBUSY */ } - /* clear the used memory */ - memset(mem,0,(128*1024) * dev_cnt); - - /* initialize each brm device, one at a time */ - for(minor=0; minor<dev_cnt; minor++){ - brm = &brms[minor]; + /* Set defaults */ + brm->event_id = 0; - /* Get AMBA AHB device info from Plug&Play */ - amba_find_next_ahbslv(amba_bus,VENDOR_GAISLER,GAISLER_BRM,&ambadev,minor); + start_operation(brm); - /* Copy Basic HW info */ - brm->regs = (void *)ambadev.start[0]; - brm->irqno = ambadev.irq; - brm->minor = minor; - brm->irq = 0; -#ifdef DEBUG - brm->log_i = 0; - memset(brm->log,0,sizeof(brm->log)); -#endif - - /* Set unique name */ - B1553BRM_DEVNAME_NO(fs_name,minor); - - DBG("Registering BRM core at [0x%x] irq %d, minor %d as %s\n",brm->regs,brm->irqno,minor,fs_name); - - /* Bind filesystem name to device number (minor) */ - status = rtems_io_register_name(fs_name, major, minor); - if (status != RTEMS_SUCCESSFUL) - rtems_fatal_error_occurred(status); - - /* RX Semaphore created with count = 0 */ - if ( rtems_semaphore_create(rtems_build_name('B', 'M', 'R', '0'+minor), - 0, - RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE|RTEMS_NO_INHERIT_PRIORITY|RTEMS_LOCAL|RTEMS_NO_PRIORITY_CEILING, - 0, - &brm->rx_sem) != RTEMS_SUCCESSFUL ){ - printk("BRM: Failed to create rx semaphore\n"); - return RTEMS_INTERNAL_ERROR; - } - - /* TX Semaphore created with count = 1 */ - if ( rtems_semaphore_create(rtems_build_name('B', 'M', 'T', '0'+minor), - 1, - RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE|RTEMS_NO_INHERIT_PRIORITY|RTEMS_LOCAL|RTEMS_NO_PRIORITY_CEILING, - 0, - &brm->tx_sem) != RTEMS_SUCCESSFUL ){ - printk("BRM: Failed to create tx semaphore\n"); - return RTEMS_INTERNAL_ERROR; - } - - /* Device Semaphore created with count = 1 */ - if ( rtems_semaphore_create(rtems_build_name('B', 'M', 'D', '0'+minor), - 1, - RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE|RTEMS_NO_INHERIT_PRIORITY|RTEMS_LOCAL|RTEMS_NO_PRIORITY_CEILING, - 0, - &brm->dev_sem) != RTEMS_SUCCESSFUL ){ - printk("BRM: Failed to create device semaphore\n"); - return RTEMS_INTERNAL_ERROR; - } - - - /* Set base address of all descriptors */ - brm->memarea_base = (unsigned int)&mem[(128*1024) * minor]; - brm->desc = (struct desc_table *) brm->memarea_base; - brm->mem = (volatile unsigned short *) brm->memarea_base; - brm->irq_log = (struct irq_log_list *)(brm->memarea_base + (0xFFE0<<1)); /* last 64byte */ - - brm->bm_event = NULL; - brm->rt_event = NULL; - - /* Sel clock so that we can write to BRM's registers */ - brm->regs->w_ctrl = (allbrm_cfg_clksel<<9) | (allbrm_cfg_clkdiv<<5); - /* Reset BRM core */ - brm->regs->w_ctrl = 1<<10 | READ_REG(&brm->regs->w_ctrl); - - /* Register interrupt handler */ - B1553BRM_REG_INT(B1553BRM_PREFIX(_interrupt_handler), brm->irqno, brm); - - rt_init(brm); - - DBG("BRM: LOG: 0x%lx, 0x%lx\n\r",brm->log,brm); + /* Register interrupt routine */ + if ( drvmgr_interrupt_register(brm->dev, 0, "b1553brm", b1553brm_interrupt, brm) ) { + rtems_semaphore_release(brm->dev_sem); + return RTEMS_UNSATISFIED; } - /* save number of BRM cores found */ - brm_cores = dev_cnt; - - DBG("BRM initialisation done.\n"); - return RTEMS_SUCCESSFUL; } - -static rtems_device_driver brm_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) { - brm_priv *brm; - - FUNCDBG("brm_open\n"); - - if (minor >= brm_cores) { - DBG("Wrong minor %d\n", minor); - return RTEMS_UNSATISFIED; /* ENODEV */ - } - - brm = &brms[minor]; - - if (rtems_semaphore_obtain(brm->dev_sem, RTEMS_NO_WAIT, RTEMS_NO_TIMEOUT) != RTEMS_SUCCESSFUL) { - DBG("brm_open: resource in use\n"); - return RTEMS_RESOURCE_IN_USE; /* EBUSY */ - } - - /* Set defaults */ - brm->event_id = 0; - - start_operation(brm); - - return RTEMS_SUCCESSFUL; -} - + static rtems_device_driver brm_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) { - brm_priv *brm = &brms[minor]; + brm_priv *brm; + struct drvmgr_dev *dev; + FUNCDBG("brm_close"); + + if ( drvmgr_get_dev(&b1553brm_drv_info.general, minor, &dev) ) { + return RTEMS_UNSATISFIED; + } + brm = (brm_priv *)dev->priv; + + drvmgr_interrupt_unregister(brm->dev, 0, b1553brm_interrupt, brm); stop_operation(brm); rtems_semaphore_release(brm->dev_sem); return RTEMS_SUCCESSFUL; } + +static int get_rt_messages(brm_priv *brm, void *buf, unsigned int msg_count) +{ + struct rt_msg *dest = (struct rt_msg *) buf; + int count = 0; -static int get_rt_messages(brm_priv *brm, void *buf, unsigned int msg_count) { - - struct rt_msg *dest = (struct rt_msg *) buf; - int count = 0; - - if (brm->head == brm->tail) { - return 0; - } - - do { - - DBG("rt read - head: %d, tail: %d\n", brm->head, brm->tail); - dest[count++] = brm->rt_event[INDEX(brm->tail++)]; + if (brm->head == brm->tail) { + return 0; + } - } while (brm->head != brm->tail && count < msg_count); + do { - return count; + DBG("rt read - head: %d, tail: %d\n", brm->head, brm->tail); + dest[count++] = brm->rt_event[INDEX(brm->tail++)]; + } while (brm->head != brm->tail && count < msg_count); + return count; } -static int get_bm_messages(brm_priv *brm, void *buf, unsigned int msg_count) { - - struct bm_msg *dest = (struct bm_msg *) buf; - int count = 0; - - if (brm->head == brm->tail) { - return 0; - } +static int get_bm_messages(brm_priv *brm, void *buf, unsigned int msg_count) +{ + struct bm_msg *dest = (struct bm_msg *) buf; + int count = 0; - do { + if (brm->head == brm->tail) { + return 0; + } - DBG("bm read - head: %d, tail: %d\n", brm->head, brm->tail); - dest[count++] = brm->bm_event[INDEX(brm->tail++)]; + do { - } while (brm->head != brm->tail && count < msg_count); + DBG("bm read - head: %d, tail: %d\n", brm->head, brm->tail); + dest[count++] = brm->bm_event[INDEX(brm->tail++)]; - return count; + } while (brm->head != brm->tail && count < msg_count); + return count; } static rtems_device_driver brm_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) { - rtems_libio_rw_args_t *rw_args; - int count = 0; - brm_priv *brm = &brms[minor]; - int (*get_messages)(brm_priv *brm, void *buf, unsigned int count); - - if ( ! (brm->mode & (BRM_MODE_RT | BRM_MODE_BM)) ){ - return RTEMS_INVALID_NAME; - } - - rw_args = (rtems_libio_rw_args_t *) arg; - - if ( ((READ_REG(&brm->regs->oper)>>8) & 3) == 1 ) { /* RT */ - get_messages = get_rt_messages; - } - else { /* BM */ - get_messages = get_bm_messages; - } + rtems_libio_rw_args_t *rw_args; + int count = 0; + brm_priv *brm; + struct drvmgr_dev *dev; + int (*get_messages)(brm_priv *brm, void *buf, unsigned int count); + if ( drvmgr_get_dev(&b1553brm_drv_info.general, minor, &dev) ) { + return RTEMS_UNSATISFIED; + } + brm = (brm_priv *)dev->priv; - FUNCDBG("brm_read [%i,%i]: buf: 0x%x len: %i\n",major, minor, (unsigned int)rw_args->buffer,rw_args->count); + if ( ! (brm->mode & (BRM_MODE_RT | BRM_MODE_BM)) ){ + return RTEMS_INVALID_NAME; + } - while ( (count=get_messages(brm,rw_args->buffer, rw_args->count)) == 0 ) { + rw_args = (rtems_libio_rw_args_t *) arg; - if (brm->rx_blocking) { - rtems_semaphore_obtain(brm->rx_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT); - } - else { - /* Translates to EBUSY */ - return RTEMS_RESOURCE_IN_USE; - } + if ( ((READ_REG(&brm->regs->oper)>>8) & 3) == 1 ) { /* RT */ + get_messages = get_rt_messages; + } else { /* BM */ + get_messages = get_bm_messages; + } - } + FUNCDBG("brm_read [%i,%i]: buf: 0x%x len: %i\n",major, minor, (unsigned int)rw_args->buffer,rw_args->count); - rw_args->bytes_moved = count; - return RTEMS_SUCCESSFUL; + while ( (count=get_messages(brm,rw_args->buffer, rw_args->count)) == 0 ) { + if (brm->rx_blocking) { + rtems_semaphore_obtain(brm->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 brm_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) { - rtems_libio_rw_args_t *rw_args; - struct rt_msg *source; - unsigned int count=0, current, next, descriptor, wc, suba; - brm_priv *brm = &brms[minor]; - - if ( ! (brm->mode & BRM_MODE_RT) ){ - return RTEMS_INVALID_NAME; - } - - rw_args = (rtems_libio_rw_args_t *) arg; - source = (struct rt_msg *) rw_args->buffer; - - FUNCDBG("brm_write [%i,%i]: buf: 0x%x len: %i\n",major, minor, (unsigned int)rw_args->buffer,rw_args->count); + rtems_libio_rw_args_t *rw_args; + struct rt_msg *source; + unsigned int count=0, current, next, descriptor, wc, suba; + brm_priv *brm; + struct drvmgr_dev *dev; + + if ( drvmgr_get_dev(&b1553brm_drv_info.general, minor, &dev) ) { + return RTEMS_UNSATISFIED; + } + brm = (brm_priv *)dev->priv; + + if ( ! (brm->mode & BRM_MODE_RT) ){ + return RTEMS_INVALID_NAME; + } + + rw_args = (rtems_libio_rw_args_t *) arg; + source = (struct rt_msg *) rw_args->buffer; - do { + FUNCDBG("brm_write [%i,%i]: buf: 0x%x len: %i\n",major, minor, (unsigned int)rw_args->buffer,rw_args->count); - descriptor = source[count].desc & 0x7F; - suba = descriptor-32; - wc = source[count].miw >> 11; - wc = wc ? wc : 32; + do { - /* Only subaddress transmission is allowed with write */ - if (descriptor < 32 || descriptor >= 64) - return RTEMS_INVALID_NAME; + descriptor = source[count].desc & 0x7F; + suba = descriptor-32; + wc = source[count].miw >> 11; + wc = wc ? wc : 32; - current = brm->desc[descriptor].cur; - next = brm->written[suba] + 2 + wc; + /* Only subaddress transmission is allowed with write */ + if (descriptor < 32 || descriptor >= 64) + return RTEMS_INVALID_NAME; - if (brm->written[suba] < current) { + current = brm->desc[descriptor].cur; + next = brm->written[suba] + 2 + wc; - if (next > current) { + if (brm->written[suba] < current) { - /* No room in transmission buffer */ + if (next > current) { - if (brm->tx_blocking && count == 0) { - rtems_semaphore_obtain(brm->tx_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT); - } - else if ( brm->tx_blocking && (count != 0) ){ - /* return the number of messages sent so far */ - break; - } - else { - /* Translates to posix EBUSY */ - return RTEMS_RESOURCE_IN_USE; - } - } - } + /* No room in transmission buffer */ + if (brm->tx_blocking && count == 0) { + rtems_semaphore_obtain(brm->tx_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + } else if ( count > 0 ) { + /* return the number of messages sent so far */ + break; + } else { + /* Translates to posix EBUSY */ + return RTEMS_RESOURCE_IN_USE; + } + } + } - memcpy((void *)&brm->mem[brm->written[suba]], &source[count], (2+wc)*2); + memcpy((void *)&brm->mem[brm->written[suba]], &source[count], (2+wc)*2); - count++; + count++; - if (next >= brm->desc[descriptor].bot) { - next = brm->desc[descriptor].top; - } - brm->written[suba] = next; + if (next >= brm->desc[descriptor].bot) { + next = brm->desc[descriptor].top; + } + brm->written[suba] = next; - } while (count < rw_args->count); + } while (count < rw_args->count); - rw_args->bytes_moved = count; + rw_args->bytes_moved = count; - if (count >= 0) { - return RTEMS_SUCCESSFUL; - } - return RTEMS_UNSATISFIED; + if (count >= 0) { + return RTEMS_SUCCESSFUL; + } + return RTEMS_UNSATISFIED; } static rtems_device_driver brm_control(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) { + + unsigned int i=0; + unsigned short ctrl, oper, cw1, cw2; + rtems_libio_ioctl_args_t *ioarg = (rtems_libio_ioctl_args_t *) arg; + unsigned int *data = ioarg->buffer; + struct bc_msg *cmd_list = (struct bc_msg *) ioarg->buffer; + brm_priv *brm; + struct drvmgr_dev *dev; + rtems_device_driver ret; + int len, msglen; + + FUNCDBG("brm_control[%d]: [%i,%i]\n", minor, major, minor); + + if ( drvmgr_get_dev(&b1553brm_drv_info.general, minor, &dev) ) { + return RTEMS_UNSATISFIED; + } + brm = (brm_priv *)dev->priv; - unsigned int i=0; - unsigned short ctrl, oper, cw1, cw2; - rtems_libio_ioctl_args_t *ioarg = (rtems_libio_ioctl_args_t *) arg; - unsigned int *data = ioarg->buffer; - struct bc_msg *cmd_list = (struct bc_msg *) ioarg->buffer; - brm_priv *brm = &brms[minor]; - rtems_device_driver ret; - int len; - - FUNCDBG("brm_control[%d]: [%i,%i]\n",minor,major, minor); - - if (!ioarg) { - DBG("brm_control: invalid argument\n"); - return RTEMS_INVALID_NAME; - } - - ioarg->ioctl_return = 0; - switch(ioarg->command) { - - case BRM_SET_MODE: - if ( data[0] > 2 ) - return RTEMS_INVALID_NAME; - stop_operation(brm); - if (data[0] == 0) { - ret = bc_init(brm); - } - else if (data[0] == 1) { - ret = rt_init(brm); - } - else if (data[0] == 2) { - ret = bm_init(brm); - }else - ret = RTEMS_INVALID_NAME; - - if ( ret != RTEMS_SUCCESSFUL) - return ret; - - if ( brm->mode & (BRM_MODE_RT | BRM_MODE_BM ) ) - start_operation(brm); - break; - - case BRM_SET_BUS: - stop_operation(brm); - ctrl = READ_REG(&brm->regs->ctrl); - ctrl &= 0xE7FF; /* Clear bit 12-11 ... */ - ctrl |= (data[0]&0x3)<<11; /* ... OR in new bus status */ - brm->regs->ctrl = ctrl; - start_operation(brm); - break; - - case BRM_SET_MSGTO: - stop_operation(brm); - ctrl = READ_REG(&brm->regs->ctrl); - ctrl &= 0xFDFF; /* Clear bit 9 ... */ - ctrl |= (data[0]&1)<<9; /* ... OR in new MSGTO */ - brm->regs->ctrl = ctrl; - start_operation(brm); - break; - - case BRM_SET_RT_ADDR: - stop_operation(brm); - oper = READ_REG(&brm->regs->oper); - oper &= 0x03FF; /* Clear bit 15-10 ... */ - oper |= (data[0]&0x1f)<<11; /* ... OR in new address */ - oper |= odd_parity(data[0]&0x1f)<<10; /* ... OR in parity */ - brm->regs->oper = oper; - start_operation(brm); - break; - - case BRM_SET_STD: - stop_operation(brm); - ctrl = READ_REG(&brm->regs->ctrl); - ctrl &= 0xFF7F; /* Clear bit 7 ... */ - ctrl |= (data[0]&1)<<7; /* ... OR in new ABSTD (1=A) */ - brm->regs->ctrl = ctrl; - start_operation(brm); - break; - - case BRM_SET_BCE: - stop_operation(brm); - ctrl = READ_REG(&brm->regs->ctrl); - ctrl &= 0xFFEF; /* Clear bit 4 ... */ - ctrl |= (data[0]&1)<<4; /* ... OR in new BCE */ - brm->regs->ctrl = ctrl; - start_operation(brm); - break; - - case BRM_TX_BLOCK: - brm->tx_blocking = data[0]; - break; - - case BRM_RX_BLOCK: - brm->rx_blocking = data[0]; - break; - - case BRM_DO_LIST: - - if ( brm->mode != BRM_MODE_BC ){ - return RTEMS_INVALID_NAME; - } - - /* Check if we are bus controller */ - if ( ((READ_REG(&brm->regs->oper)>>8) & 3) != 0 ) { - return RTEMS_INVALID_NAME; - } - - /* Already processing list? */ - if (is_executing(brm)) { - return RTEMS_RESOURCE_IN_USE; - } - - /* clear any earlier releases */ - rtems_semaphore_obtain(brm->tx_sem, RTEMS_NO_WAIT, RTEMS_NO_TIMEOUT); - - brm->bc_list_fail = 0; - brm->cur_list = cmd_list; - brm->regs->dpoint = 0; - - i = 0; - while ( (cmd_list[i].ctrl & BC_EOL) == 0) { + if (!ioarg) { + DBG("brm_control: invalid argument\n"); + return RTEMS_INVALID_NAME; + } - ctrl = (4<<12) | (((cmd_list[i].ctrl&BC_BUSA)==BC_BUSA)<<9) | (((cmd_list[i].ctrl&BC_RTRT)==BC_RTRT)<<8); + ioarg->ioctl_return = 0; + switch (ioarg->command) { - if (cmd_list[i].ctrl&BC_RTRT) { - cw1 = (cmd_list[i].rtaddr[0]<<11) | (0<<10) | (cmd_list[i].subaddr[0]<<5) | (cmd_list[i].wc & 0x1f); /* receive cw */ - cw2 = (cmd_list[i].rtaddr[1]<<11) | (1<<10) | (cmd_list[i].subaddr[1]<<5) | (cmd_list[i].wc & 0x1f); /* transmit cw */ - } - else { - cw1 = (cmd_list[i].rtaddr[0]<<11) | (((cmd_list[i].ctrl&BC_TR)==BC_TR)<<10) | (cmd_list[i].subaddr[0]<<5) | (cmd_list[i].wc&0x1f); - cw2 = 0; - } + case BRM_SET_MODE: + if ( data[0] > 2 ) + return RTEMS_INVALID_NAME; + stop_operation(brm); + if (data[0] == 0) { + ret = bc_init(brm); + } else if (data[0] == 1) { + ret = rt_init(brm); + } else if (data[0] == 2) { + ret = bm_init(brm); + } else { + ret = RTEMS_INVALID_NAME; + } + if ( ret != RTEMS_SUCCESSFUL) + return ret; + + if ( brm->mode & (BRM_MODE_RT | BRM_MODE_BM ) ) + start_operation(brm); + break; + + case BRM_SET_BUS: + stop_operation(brm); + ctrl = READ_REG(&brm->regs->ctrl); + ctrl &= 0xE7FF; /* Clear bit 12-11 ... */ + ctrl |= (data[0]&0x3)<<11; /* ... OR in new bus status */ + brm->regs->ctrl = ctrl; + start_operation(brm); + break; + + case BRM_SET_MSGTO: + stop_operation(brm); + ctrl = READ_REG(&brm->regs->ctrl); + ctrl &= 0xFDFF; /* Clear bit 9 ... */ + ctrl |= (data[0]&1)<<9; /* ... OR in new MSGTO */ + brm->regs->ctrl = ctrl; + start_operation(brm); + break; + + case BRM_SET_RT_ADDR: + stop_operation(brm); + oper = READ_REG(&brm->regs->oper); + oper &= 0x03FF; /* Clear bit 15-10 ... */ + oper |= (data[0]&0x1f)<<11; /* ... OR in new address */ + oper |= odd_parity(data[0]&0x1f)<<10; /* ... OR in parity */ + brm->regs->oper = oper; + start_operation(brm); + break; + + case BRM_SET_STD: + stop_operation(brm); + ctrl = READ_REG(&brm->regs->ctrl); + ctrl &= 0xFF7F; /* Clear bit 7 ... */ + ctrl |= (data[0]&1)<<7; /* ... OR in new ABSTD (1=A) */ + brm->regs->ctrl = ctrl; + start_operation(brm); + break; + + case BRM_SET_BCE: + stop_operation(brm); + ctrl = READ_REG(&brm->regs->ctrl); + ctrl &= 0xFFEF; /* Clear bit 4 ... */ + ctrl |= (data[0]&1)<<4; /* ... OR in new BCE */ + brm->regs->ctrl = ctrl; + start_operation(brm); + break; + + case BRM_TX_BLOCK: + brm->tx_blocking = data[0]; + break; + + case BRM_RX_BLOCK: + brm->rx_blocking = data[0]; + break; + + case BRM_DO_LIST: + if ( brm->mode != BRM_MODE_BC ){ + return RTEMS_INVALID_NAME; + } + /* Check if we are bus controller */ + if ( ((READ_REG(&brm->regs->oper)>>8) & 3) != 0 ) { + return RTEMS_INVALID_NAME; + } - /* Set up command block */ - brm->bcmem->descs[i].ctrl = ctrl; - brm->bcmem->descs[i].cw1 = cw1; - brm->bcmem->descs[i].cw2 = cw2; - /* data pointer: - * (&brm->bcmem->msg_data[i].data[0] & 0x1ffff) / 2 - */ - brm->bcmem->descs[i].dptr = 1024+i*32; /* data pointer */ - brm->bcmem->descs[i].tsw[0] = 0; - brm->bcmem->descs[i].tsw[1] = 0; - brm->bcmem->descs[i].ba = 0; - brm->bcmem->descs[i].timer = 0; + /* Already processing list? */ + if (is_executing(brm)) { + return RTEMS_RESOURCE_IN_USE; + } - memcpy((void *)&brm->bcmem->msg_data[i].data[0], &cmd_list[i].data[0], cmd_list[i].wc*2); + /* clear any earlier releases */ + rtems_semaphore_obtain(brm->tx_sem, RTEMS_NO_WAIT, RTEMS_NO_TIMEOUT); + + brm->bc_list_fail = 0; + brm->cur_list = cmd_list; + brm->regs->dpoint = 0; + + i = 0; + while ( (cmd_list[i].ctrl & BC_EOL) == 0) { + + ctrl = (4<<12) | (((cmd_list[i].ctrl&BC_BUSA)==BC_BUSA)<<9) | (((cmd_list[i].ctrl&BC_RTRT)==BC_RTRT)<<8); + + if (cmd_list[i].ctrl&BC_RTRT) { + cw1 = (cmd_list[i].rtaddr[0]<<11) | (0<<10) | (cmd_list[i].subaddr[0]<<5) | (cmd_list[i].wc & 0x1f); /* receive cw */ + cw2 = (cmd_list[i].rtaddr[1]<<11) | (1<<10) | (cmd_list[i].subaddr[1]<<5) | (cmd_list[i].wc & 0x1f); /* transmit cw */ + } else { + cw1 = (cmd_list[i].rtaddr[0]<<11) | (((cmd_list[i].ctrl&BC_TR)==BC_TR)<<10) | (cmd_list[i].subaddr[0]<<5) | (cmd_list[i].wc&0x1f); + cw2 = 0; + } + + /* Set up command block */ + brm->bcmem->descs[i].ctrl = ctrl; + brm->bcmem->descs[i].cw1 = cw1; + brm->bcmem->descs[i].cw2 = cw2; + /* data pointer: + * (&brm->bcmem->msg_data[i].data[0] & 0x1ffff) / 2 + */ + brm->bcmem->descs[i].dptr = 1024+i*32; /* data pointer */ + brm->bcmem->descs[i].tsw[0] = 0; + brm->bcmem->descs[i].tsw[1] = 0; + brm->bcmem->descs[i].ba = 0; + brm->bcmem->descs[i].timer = 0; + + msglen = cmd_list[i].wc; + if ( msglen == 0 ) + msglen = 32; + memcpy((void *)&brm->bcmem->msg_data[i].data[0], &cmd_list[i].data[0], msglen*2); + + i++; + } - i++; - } + brm->bcmem->descs[i].ctrl = 0; /* end of list */ - brm->bcmem->descs[i].ctrl = 0; /* end of list */ + start_operation(brm); + break; - start_operation(brm); + case BRM_LIST_DONE: - break; + if ( brm->mode != BRM_MODE_BC ){ + return RTEMS_INVALID_NAME; + } + + /* Check if we are bus controller */ + if ( ((READ_REG(&brm->regs->oper)>>8) & 3) != 0 ) { + return RTEMS_INVALID_NAME; + } - case BRM_LIST_DONE: + if (is_executing(brm)) { - if ( brm->mode != BRM_MODE_BC ){ - return RTEMS_INVALID_NAME; + data[0] = 0; + if (brm->tx_blocking) { + rtems_semaphore_obtain(brm->tx_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + data[0] = 1; + if ( brm->bc_list_fail ){ + return RTEMS_INVALID_NAME; } + } else { + return RTEMS_RESOURCE_IN_USE; + } + } else { + data[0] = 1; /* done */ + } - /* Check if we are bus controller */ - if ( ((READ_REG(&brm->regs->oper)>>8) & 3) != 0 ) { - return RTEMS_INVALID_NAME; - } - - if (is_executing(brm)) { - - data[0] = 0; - if (brm->tx_blocking) { - rtems_semaphore_obtain(brm->tx_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT); - data[0] = 1; - if ( brm->bc_list_fail ){ - return RTEMS_INVALID_NAME; - } - }else{ - return RTEMS_RESOURCE_IN_USE; - } - - - } - else { - data[0] = 1; /* done */ - } - - /* copy finished list results back into bc_msg array */ - i = 0; - while ( (brm->cur_list[i].ctrl & BC_EOL) == 0) { - - if (READ_DMA(&brm->bcmem->descs[i].ctrl) & 1) { - brm->cur_list[i].ctrl |= 0x8000; /* Set BAME */ - } - if (brm->cur_list[i].ctrl & BC_TR) { - /* RT Transmit command, copy received data */ - len = brm->cur_list[i].wc; - while( len-- > 0){ - brm->cur_list[i].data[len] = READ_DMA(&brm->bcmem->msg_data[i].data[len]); - } - } - brm->cur_list[i].tsw[0] = READ_DMA(&brm->bcmem->descs[i].tsw[0]); - brm->cur_list[i].tsw[1] = READ_DMA(&brm->bcmem->descs[i].tsw[1]); - - i++; - } - break; - - - case BRM_CLR_STATUS: - brm->status = 0; - break; + /* copy finished list results back into bc_msg array */ + i = 0; + while ( (brm->cur_list[i].ctrl & BC_EOL) == 0) { + if (READ_DMA(&brm->bcmem->descs[i].ctrl) & 1) { + brm->cur_list[i].ctrl |= 0x8000; /* Set BAME */ + } + if (brm->cur_list[i].ctrl & BC_TR) { + /* RT Transmit command, copy received data */ + len = brm->cur_list[i].wc; + if ( len == 0 ) + len = 32; + while ( len-- > 0) { + brm->cur_list[i].data[len] = READ_DMA(&brm->bcmem->msg_data[i].data[len]); + } + } + brm->cur_list[i].tsw[0] = READ_DMA(&brm->bcmem->descs[i].tsw[0]); + brm->cur_list[i].tsw[1] = READ_DMA(&brm->bcmem->descs[i].tsw[1]); - case BRM_GET_STATUS: /* copy status */ + i++; + } + break; - if ( !ioarg->buffer ) - return RTEMS_INVALID_NAME; + case BRM_CLR_STATUS: + brm->status = 0; + break; - *(unsigned int *)ioarg->buffer = brm->status; - break; + case BRM_GET_STATUS: /* copy status */ + if ( !ioarg->buffer ) + return RTEMS_INVALID_NAME; + *(unsigned int *)ioarg->buffer = brm->status; + break; + case BRM_SET_EVENTID: - brm->event_id = (rtems_id)ioarg->buffer; - break; - - default: - return RTEMS_NOT_DEFINED; - } - return RTEMS_SUCCESSFUL; -} + brm->event_id = (rtems_id)ioarg->buffer; + break; -#ifdef B1553BRM_DEFINE_INTHANDLER -static void b1553brm_interrupt_handler(rtems_vector_number v){ - int i; - /* find minor */ - for(i=0; i<brm_cores; i++){ - if ( (brms[i].irqno+0x10) == v ){ - brm_interrupt(&brms[i]); - return; - } + default: + return RTEMS_NOT_DEFINED; } + return RTEMS_SUCCESSFUL; } -#endif -static void brm_interrupt(brm_priv *brm) { - unsigned short descriptor, current, pending, miw, wc, tmp; +static void b1553brm_interrupt(void *arg) +{ + brm_priv *brm = arg; + unsigned short descriptor, current, pending, miw, wc, tmp, ctrl; unsigned short msgadr, iaw, iiw; int len; - int signal_event=0; + int signal_event=0, wake_rx_task=0, wake_tx_task=0; unsigned int event_status=0; + int accessed; #define SET_ERROR_DESCRIPTOR(descriptor) (event_status = (event_status & 0x0000ffff) | descriptor<<16) - - while( (iiw=READ_REG(&brm->irq_log[brm->irq].iiw)) != 0xffff ){ - iaw=READ_REG(&brm->irq_log[brm->irq].iaw); - + + while( (iiw=READ_DMA(&brm->irq_log[brm->irq].iiw)) != 0xffff ){ + iaw=READ_DMA(&brm->irq_log[brm->irq].iaw); + /* indicate that the interrupt log entry has been processed */ brm->irq_log[brm->irq].iiw = 0xffff; /* Interpret interrupt log entry */ - descriptor = iaw >> 2; - pending = iiw; - brm->irq = (brm->irq + 1) % 16; - + descriptor = iaw >> 2; + pending = iiw; + brm->irq = (brm->irq + 1) % 16; + /* Clear the log so that we */ - /* Subaddress accessed irq (RT only) - * - * Can be either a receive or transmit command - * as well as a mode code. - */ - if (pending & BRM_SUBAD_IRQ) { - - /* Pointer to next free message in circular buffer */ - current = READ_DMA(&brm->desc[descriptor].cur); - - while ( (msgadr=brm->last_read[descriptor]) != current) { - - /* Get word count */ - miw = READ_DMA(&brm->mem[msgadr]); - wc = miw >> 11; - - /* Data received */ - if (descriptor < 32) { - wc = wc ? wc : 32; - } - /* Data transmitted */ - else if (descriptor < 64) { - wc = wc ? wc : 32; - rtems_semaphore_release(brm->tx_sem); - } - /* RX Mode code */ - else if (descriptor < 96) { - wc = (wc>>4); - } - /* TX Mode code */ - else if (descriptor < 128) { - wc = (wc>>4); - } - + /* Subaddress accessed irq (RT only) + * + * Can be either a receive or transmit command + * as well as a mode code. + */ + if (pending & BRM_SUBAD_IRQ) { + + /* Pointer to next free message in circular buffer */ + current = READ_DMA(&brm->desc[descriptor].cur); + ctrl = READ_DMA(&brm->desc[descriptor].ctrl); #ifdef DEBUG - brm->log[brm->log_i++ % EVENT_QUEUE_SIZE] = (descriptor << 16) | wc; - brm->log[brm->log_i++ % EVENT_QUEUE_SIZE] = current; - brm->log[brm->log_i++ % EVENT_QUEUE_SIZE] = msgadr; + brm->log[brm->log_i++ % EVENT_QUEUE_SIZE] = (0xff<<16); + brm->log[brm->log_i++ % EVENT_QUEUE_SIZE] = current; + brm->log[brm->log_i++ % EVENT_QUEUE_SIZE] = ctrl; + brm->log[brm->log_i++ % EVENT_QUEUE_SIZE] = 0; #endif + accessed = ctrl & 0x10; + /* Note that current may be equal to bot and top when + * circular buffer one can handle one message. + */ + if ( accessed ) + do { + msgadr = brm->last_read[descriptor]; + + /* Get word count */ + miw = READ_DMA(&brm->mem[msgadr]); + wc = miw >> 11; + + /* Data received */ + if (descriptor < 32) { + wc = wc ? wc : 32; + } + /* Data transmitted */ + else if (descriptor < 64) { + wc = wc ? wc : 32; + wake_tx_task=1; + } + /* RX Mode code */ + else if (descriptor < 96) { + wc = (wc>>4); + } + /* TX Mode code */ + else if (descriptor < 128) { + wc = (wc>>4); + } - /* If there is room in the event queue, copy the event there */ - if (brm->head - brm->tail != EVENT_QUEUE_SIZE) { - - /* Copy to event queue */ - brm->rt_event[INDEX(brm->head)].miw = READ_DMA(&brm->mem[msgadr]); - brm->rt_event[INDEX(brm->head)].time = READ_DMA(&brm->mem[msgadr+1]); - len = wc; - while( len-- > 0){ - brm->rt_event[INDEX(brm->head)].data[len] = READ_DMA(&brm->mem[msgadr+2+len]); - } - brm->rt_event[INDEX(brm->head)].desc = descriptor; - brm->head++; +#ifdef DEBUG + brm->log[brm->log_i++ % EVENT_QUEUE_SIZE] = (descriptor << 16) | wc; + brm->log[brm->log_i++ % EVENT_QUEUE_SIZE] = current; + brm->log[brm->log_i++ % EVENT_QUEUE_SIZE] = msgadr; +#endif - } - else { - /* Indicate overrun */ - brm->rt_event[INDEX(brm->head)].desc |= 0x8000; - } + /* If there is room in the event queue, copy the event there */ + if (brm->head - brm->tail != EVENT_QUEUE_SIZE) { + + /* Copy to event queue */ + brm->rt_event[INDEX(brm->head)].miw = READ_DMA(&brm->mem[msgadr]); + brm->rt_event[INDEX(brm->head)].time = READ_DMA(&brm->mem[msgadr+1]); + len = wc; + while( len-- > 0){ + brm->rt_event[INDEX(brm->head)].data[len] = READ_DMA(&brm->mem[msgadr+2+len]); + } + brm->rt_event[INDEX(brm->head)].desc = descriptor; + brm->head++; + } + else { + /* Indicate overrun */ + brm->rt_event[INDEX(brm->head)].desc |= 0x8000; + } - msgadr += (2+wc); + msgadr += (2+wc); - if (msgadr >= brm->desc[descriptor].bot) { - msgadr = brm->desc[descriptor].top; - } - brm->last_read[descriptor] = msgadr; + if (msgadr >= READ_DMA(&brm->desc[descriptor].bot)) { + msgadr = READ_DMA(&brm->desc[descriptor].top); + } + brm->last_read[descriptor] = msgadr; #ifdef DEBUG - brm->log[brm->log_i++ % EVENT_QUEUE_SIZE] = msgadr; + brm->log[brm->log_i++ % EVENT_QUEUE_SIZE] = msgadr; #endif + wake_rx_task = 1; + } while ( (msgadr=brm->last_read[descriptor]) != current ); + } - /* Wake any blocked rx thread */ - rtems_semaphore_release(brm->rx_sem); - - } - - } - - if (pending & BRM_EOL_IRQ) { - rtems_semaphore_release(brm->tx_sem); - } - - if (pending & BRM_BC_ILLCMD_IRQ) { - brm->bc_list_fail = 1; - rtems_semaphore_release(brm->tx_sem); - SET_ERROR_DESCRIPTOR(descriptor); - FUNCDBG("BRM: ILLCMD IRQ\n\r"); - } - - /* Monitor irq */ - if (pending & BRM_MBC_IRQ) { - - stop_operation(brm); - brm->regs->mbc = 1; - start_operation(brm); - - /* If there is room in the event queue, copy the event there */ - if (brm->head - brm->tail != EVENT_QUEUE_SIZE) { - - /* Copy to event queue */ + if (pending & BRM_EOL_IRQ) { + wake_tx_task = 1; + } - brm->bm_event[INDEX(brm->head)].miw = READ_DMA(&brm->mem[0]); - brm->bm_event[INDEX(brm->head)].cw1 = READ_DMA(&brm->mem[1]); - brm->bm_event[INDEX(brm->head)].cw2 = READ_DMA(&brm->mem[2]); - brm->bm_event[INDEX(brm->head)].sw1 = READ_DMA(&brm->mem[4]); - brm->bm_event[INDEX(brm->head)].sw2 = READ_DMA(&brm->mem[5]); - brm->bm_event[INDEX(brm->head)].time = READ_DMA(&brm->mem[6]); + if (pending & BRM_BC_ILLCMD_IRQ) { + brm->bc_list_fail = 1; + wake_tx_task = 1; + SET_ERROR_DESCRIPTOR(descriptor); + FUNCDBG("BRM: ILLCMD IRQ\n\r"); + } - len = 32; - while ( len-- ){ - brm->bm_event[INDEX(brm->head)].data[len] = READ_DMA(&brm->mem[0x100+len]); - len--; - brm->bm_event[INDEX(brm->head)].data[len] = READ_DMA(&brm->mem[0x100+len]); - len--; - brm->bm_event[INDEX(brm->head)].data[len] = READ_DMA(&brm->mem[0x100+len]); - len--; - brm->bm_event[INDEX(brm->head)].data[len] = READ_DMA(&brm->mem[0x100+len]); - } -/* memcpy((void *)brm->bm_event[INDEX(brm->head)].data, &brm->mem[0x100], 32);*/ + /* Monitor irq */ + if (pending & BRM_MBC_IRQ) { + + stop_operation(brm); + brm->regs->mbc = 1; + start_operation(brm); + + /* If there is room in the event queue, copy the event there */ + if (brm->head - brm->tail != EVENT_QUEUE_SIZE) { + + /* Copy to event queue */ + + brm->bm_event[INDEX(brm->head)].miw = READ_DMA(&brm->mem[0]); + brm->bm_event[INDEX(brm->head)].cw1 = READ_DMA(&brm->mem[1]); + brm->bm_event[INDEX(brm->head)].cw2 = READ_DMA(&brm->mem[2]); + brm->bm_event[INDEX(brm->head)].sw1 = READ_DMA(&brm->mem[4]); + brm->bm_event[INDEX(brm->head)].sw2 = READ_DMA(&brm->mem[5]); + brm->bm_event[INDEX(brm->head)].time = READ_DMA(&brm->mem[6]); + + len = 32; + while ( len-- ){ + brm->bm_event[INDEX(brm->head)].data[len] = READ_DMA(&brm->mem[0x100+len]); + len--; + brm->bm_event[INDEX(brm->head)].data[len] = READ_DMA(&brm->mem[0x100+len]); + len--; + brm->bm_event[INDEX(brm->head)].data[len] = READ_DMA(&brm->mem[0x100+len]); + len--; + brm->bm_event[INDEX(brm->head)].data[len] = READ_DMA(&brm->mem[0x100+len]); + } +/* memcpy((void *)brm->bm_event[INDEX(brm->head)].data, &brm->mem[0x100], 32);*/ #ifdef DEBUG - brm->log[brm->log_i++ % EVENT_QUEUE_SIZE] = READ_REG(&brm->regs->mbc); - brm->log[brm->log_i++ % EVENT_QUEUE_SIZE] = READ_DMA(&brm->mem[0]); - brm->log[brm->log_i++ % EVENT_QUEUE_SIZE] = READ_DMA(&brm->mem[1]); - brm->log[brm->log_i++ % EVENT_QUEUE_SIZE] = READ_DMA(&brm->mem[4]); + brm->log[brm->log_i++ % EVENT_QUEUE_SIZE] = READ_REG(&brm->regs->mbc) & 0xffff; + brm->log[brm->log_i++ % EVENT_QUEUE_SIZE] = READ_DMA(&brm->mem[0]); + brm->log[brm->log_i++ % EVENT_QUEUE_SIZE] = READ_DMA(&brm->mem[1]); + brm->log[brm->log_i++ % EVENT_QUEUE_SIZE] = READ_DMA(&brm->mem[4]); #endif - brm->head++; + brm->head++; - } - else { - /* Indicate overrun */ - brm->rt_event[INDEX(brm->head)].miw |= 0x8000; - } + } + else { + /* Indicate overrun */ + brm->bm_event[INDEX(brm->head)].miw |= 0x8000; + } - /* Wake any blocking thread */ - rtems_semaphore_release(brm->rx_sem); - - } + /* Wake any blocking thread */ + wake_rx_task = 1; + } - /* The reset of the interrupts + /* The reset of the interrupts * cause a event to be signalled * so that user can handle error. */ @@ -1311,9 +1382,9 @@ static void brm_interrupt(brm_priv *brm) { if ( pending & BRM_ILLOP_IRQ){ FUNCDBG("BRM: BRM_ILLOP_IRQ\n\r"); brm->bc_list_fail = 1; - rtems_semaphore_release(brm->tx_sem); + wake_tx_task = 1; event_status |= BRM_ILLOP_IRQ; - SET_ERROR_DESCRIPTOR(descriptor); + SET_ERROR_DESCRIPTOR(descriptor); signal_event=1; } @@ -1323,10 +1394,15 @@ static void brm_interrupt(brm_priv *brm) { SET_ERROR_DESCRIPTOR(descriptor); signal_event=1; } - /* Clear Block Accessed Bit */ - tmp = READ_REG(&brm->desc[descriptor].ctrl); - brm->desc[descriptor].ctrl = tmp & ~0x10; - + /* Clear Block Accessed Bit */ + tmp = READ_DMA(&brm->desc[descriptor].ctrl); + brm->desc[descriptor].ctrl = tmp & ~0x10; +#ifdef DEBUG + brm->log[brm->log_i++ % EVENT_QUEUE_SIZE] = (0xfe<<16); + brm->log[brm->log_i++ % EVENT_QUEUE_SIZE] = 0; + brm->log[brm->log_i++ % EVENT_QUEUE_SIZE] = tmp & ~0x10; + brm->log[brm->log_i++ % EVENT_QUEUE_SIZE] = tmp; +#endif } /* While */ /* clear interrupt flags & handle Hardware errors */ @@ -1357,9 +1433,88 @@ static void brm_interrupt(brm_priv *brm) { brm->status |= event_status; } + /* Wake any blocked rx thread only on receive interrupts */ + if ( wake_rx_task ) { + rtems_semaphore_release(brm->rx_sem); + } + + /* Wake any blocked tx thread only on transmit interrupts */ + if ( wake_tx_task ) { + rtems_semaphore_release(brm->tx_sem); + } + /* signal event once */ if ( signal_event && (brm->event_id!=0) ){ rtems_event_send(brm->event_id, event_status); } } + +void b1553brm_print_dev(struct drvmgr_dev *dev, int options) +{ + brm_priv *pDev = dev->priv; + struct amba_dev_info *devinfo; + struct brm_reg *regs = pDev->regs; + + devinfo = (struct amba_dev_info *)pDev->dev->businfo; + + /* Print */ + printf("--- B1553BRM[%d] %s ---\n", pDev->minor, pDev->devName); + printf(" REGS: 0x%x\n", (unsigned int)pDev->regs); + printf(" IRQ: %d\n", pDev->irqno); + switch (pDev->mode) { + case BRM_MODE_BC: + printf(" MODE: BC\n"); + printf(" DESCS: 0x%x\n", (unsigned int)&pDev->bcmem->descs[0]); + printf(" DATA: 0x%x\n", (unsigned int)&pDev->bcmem->msg_data[0].data[0]); + printf(" IRQLOG: 0x%x\n", (unsigned int)&pDev->bcmem->irq_logs[0]); + break; + case BRM_MODE_BM: + printf(" MODE: BM\n"); + break; + case BRM_MODE_RT: + printf(" MODE: RT\n"); + printf(" RXSUBS: 0x%x\n", (unsigned int)&pDev->rtmem->rxsubs[0]); + printf(" TXSUBS: 0x%x\n", (unsigned int)&pDev->rtmem->txsubs[0]); + printf(" RXMODES: 0x%x\n", (unsigned int)&pDev->rtmem->rxmodes[0]); + printf(" TXOMODES: 0x%x\n", (unsigned int)&pDev->rtmem->txmodes[0]); + printf(" RXSUBS MSGS: 0x%x\n", (unsigned int)&pDev->rtmem->rxsuba_msgs[0]); + printf(" TXSUBS MSGS: 0x%x\n", (unsigned int)&pDev->rtmem->txsuba_msgs[0]); + printf(" RXMODES MSGS: 0x%x\n", (unsigned int)&pDev->rtmem->rxmode_msgs[0]); + printf(" TXMODES MSGS: 0x%x\n", (unsigned int)&pDev->rtmem->txmode_msgs[0]); + printf(" IRQLOG: 0x%x\n", (unsigned int)&pDev->rtmem->irq_logs[0]); + break; + } + printf(" CTRL: 0x%x\n", regs->ctrl); + printf(" OPER: 0x%x\n", regs->oper); + printf(" CUR_CMD: 0x%x\n", regs->cur_cmd); + printf(" IMASK: 0x%x\n", regs->imask); + printf(" IPEND: 0x%x\n", regs->ipend); + printf(" IPOINT: 0x%x\n", regs->ipoint); + printf(" BIT_REG: 0x%x\n", regs->bit_reg); + printf(" TTAG: 0x%x\n", regs->ttag); + printf(" DPOINT: 0x%x\n", regs->dpoint); + printf(" SW: 0x%x\n", regs->sw); + printf(" INITCOUNT: 0x%x\n", regs->initcount); + printf(" MCPOINT: 0x%x\n", regs->mcpoint); + printf(" MDPOINT: 0x%x\n", regs->mdpoint); + printf(" MBC: 0x%x\n", regs->mbc); + printf(" MFILTA: 0x%x\n", regs->mfilta); + printf(" MFILTB: 0x%x\n", regs->mfiltb); + printf(" ENHANCED: 0x%x\n", regs->enhanced); + printf(" W_CTRL: 0x%x\n", regs->w_ctrl); + printf(" W_IRQCTRL: 0x%x\n", regs->w_irqctrl); + printf(" W_AHBADDR: 0x%x\n", regs->w_ahbaddr); +} + +void b1553brm_print(int options) +{ + struct amba_drv_info *drv = &b1553brm_drv_info; + struct drvmgr_dev *dev; + + dev = drv->general.dev; + while(dev) { + b1553brm_print_dev(dev, options); + dev = dev->next_in_drv; + } +} diff --git a/c/src/lib/libbsp/sparc/shared/1553/b1553brm_pci.c b/c/src/lib/libbsp/sparc/shared/1553/b1553brm_pci.c deleted file mode 100644 index 76c9f2e56a..0000000000 --- a/c/src/lib/libbsp/sparc/shared/1553/b1553brm_pci.c +++ /dev/null @@ -1,132 +0,0 @@ -/* Select PCI driver */ -#define B1553BRM_NO_AMBA -#define B1553BRM_PCI - -#undef B1553BRM_MAXDEVS - -/* Use only 16K memory */ -#define DMA_MEM_16K - -/* Malloced memory or - * Card local memory - */ -#define B1553BRM_LOCAL_MEM - -#define DONT_DEF_RAMON - -/* memory must be aligned to a 128k boundary */ -unsigned int brmpci_memarea_address; -#define B1553BRM_LOCAL_MEM_ADR brmpci_memarea_address - -/* We have custom address tranlation for HW addresses */ -#define B1553BRM_ADR_TO - -/* No custom MEMAREA=>CPU used since BRM Core work with offsets - * in it's descriptors. - */ -#undef B1553BRM_ADR_FROM - -/* Set registered device name */ -#define B1553BRM_DEVNAME "/dev/brmpci0" -#define B1553BRM_DEVNAME_NO(devstr,no) ((devstr)[11]='0'+(no)) - -/* Any non-static function will begin with */ -#define B1553BRM_PREFIX(name) b1553brmpci##name - -/* do nothing, assume that the interrupt handler is called - * setup externally calling b1553_interrupt_handler. - */ -#define B1553BRM_REG_INT(handler,irq,arg) \ - if ( b1553brm_pci_int_reg ) \ - b1553brm_pci_int_reg(handler,irq,arg); - - -#ifdef B1553BRM_ADR_TO -/* Translate a address within the Memory Region (memarea) into an Hardware - * device address. This address is put into hardware registers or descriptors - * so that the hardware can access the Memory Region. - * Example: - * An local AMBA access at 0xe0000000 will translate into PCI address 0x40000000, - * the PCI address 0x40000000 will translate into CPU-AMBA address 0x40000000. - */ -unsigned int brmpci_hw_address; -static inline unsigned int memarea_to_hw(unsigned int addr) { - /* don't translate? */ - if ( brmpci_hw_address == 0xffffffff ) - return addr; - return ((addr & 0x000fffff) | brmpci_hw_address); -} -#endif - -/* not used since BRM Core work with offsets */ -#ifdef B1553BRM_ADR_FROM -unsigned int brmpci_cpu_access_address; -static inline unsigned int hw_to_cpu(unsigned int addr) { - /* don't translate? */ - if ( brmpci_cpu_address == 0xffffffff ) - return addr; - return ((addr & 0x0fffffff) | brmpci_cpu_address); -} -#endif - -void (*b1553brm_pci_int_reg)(void *handler, int irq, void *arg) = 0; - -static void b1553brmpci_interrupt_handler(int irq, void *arg); - -#include "b1553brm.c" - -/* - * - * memarea = preallocated memory somewhere, pointer to start of memory. - * hw_address = how to translate a memarea address into an HW device AMBA address. - */ - -int b1553brm_pci_register( - amba_confarea_type *bus, - unsigned int clksel, - unsigned int clkdiv, - unsigned int brm_freq, - unsigned int memarea, - unsigned int hw_address - ) -{ - /* Setup configuration */ - - /* if zero malloc will be used */ - brmpci_memarea_address = memarea; - - brmpci_hw_address = hw_address; - -#ifdef B1553BRM_ADR_FROM - brmpci_cpu_address = memarea & 0xf0000000; -#endif - - /* Register the driver */ - return B1553BRM_PREFIX(_register)(bus,clksel,clkdiv,brm_freq); -} - -/* Call this from PCI interrupt handler - * irq = the irq number of the HW device local to that IRQMP controller - * - */ -static void b1553brmpci_interrupt_handler(int irq, void *arg){ - brm_interrupt(arg); -} - -#if 0 -int b1553brm_pci_interrupt_handler(int irqmask){ - int i; - unsigned int mask=0; - /* find minor */ - for(i=0; i<brm_cores; i++){ - if ( (1<<brms[i].irqno) & irqmask ){ - mask |= 1<<brms[i].irqno; - brm_interrupt(&brms[i]); - /* more interrupts to scan for? */ - if ( irqmask & ~mask ) - return mask; /* handled */ - } - } - return mask; -} -#endif diff --git a/c/src/lib/libbsp/sparc/shared/1553/b1553brm_rasta.c b/c/src/lib/libbsp/sparc/shared/1553/b1553brm_rasta.c deleted file mode 100644 index 10adb0aec5..0000000000 --- a/c/src/lib/libbsp/sparc/shared/1553/b1553brm_rasta.c +++ /dev/null @@ -1,115 +0,0 @@ -/* Select PCI driver */ -#define B1553BRM_NO_AMBA -#define B1553BRM_PCI - -#undef B1553BRM_MAXDEVS - -/* Use only 16K memory */ -#define DMA_MEM_128K - -/* Malloced memory or - * Card local memory - */ -#define B1553BRM_LOCAL_MEM - -#define DONT_DEF_RAMON - -/* memory must be aligned to a 128k boundary */ -unsigned int brmrasta_memarea_address; -#define B1553BRM_LOCAL_MEM_ADR brmrasta_memarea_address - -/* We have custom address tranlation for HW addresses */ -#define B1553BRM_ADR_TO - -/* No custom MEMAREA=>CPU used since BRM Core work with offsets - * in it's descriptors. - */ -#undef B1553BRM_ADR_FROM - -/* Set registered device name */ -#define B1553BRM_DEVNAME "/dev/brmrasta0" -#define B1553BRM_DEVNAME_NO(devstr,no) ((devstr)[13]='0'+(no)) - -/* Any non-static function will begin with */ -#define B1553BRM_PREFIX(name) b1553brmrasta##name - -/* do nothing, assume that the interrupt handler is called - * setup externally calling b1553_interrupt_handler. - */ -#define B1553BRM_REG_INT(handler,irq,arg) \ - if ( b1553brm_rasta_int_reg ) \ - b1553brm_rasta_int_reg(handler,irq,arg); - - -#ifdef B1553BRM_ADR_TO -/* Translate a address within the Memory Region (memarea) into an Hardware - * device address. This address is put into hardware registers or descriptors - * so that the hardware can access the Memory Region. - * Example: - * An local AMBA access at 0xe0000000 will translate into PCI address 0x40000000, - * the PCI address 0x40000000 will translate into CPU-AMBA address 0x40000000. - */ -unsigned int brmrasta_hw_address; -static inline unsigned int memarea_to_hw(unsigned int addr) { - /* don't translate? */ - if ( brmrasta_hw_address == 0xffffffff ) - return addr; - return ((addr & 0x0fffffff) | brmrasta_hw_address); -} -#endif - -/* not used since BRM Core work with offsets */ -#ifdef B1553BRM_ADR_FROM -unsigned int brmrasta_cpu_access_address; -static inline unsigned int hw_to_cpu(unsigned int addr) { - /* don't translate? */ - if ( brmrasta_cpu_address == 0xffffffff ) - return addr; - return ((addr & 0x0fffffff) | brmrasta_cpu_address); -} -#endif - -void (*b1553brm_rasta_int_reg)(void *handler, int irq, void *arg) = 0; - -static void b1553brmrasta_interrupt_handler(int irq, void *arg); - -#include "b1553brm.c" - -/* - * - * memarea = preallocated memory somewhere, pointer to start of memory. - * hw_address = how to translate a memarea address into an HW device AMBA address. - */ - -int b1553brm_rasta_register( - amba_confarea_type *bus, - unsigned int clksel, - unsigned int clkdiv, - unsigned int brm_freq, - unsigned int memarea, - unsigned int hw_address - ) -{ - /* Setup configuration */ - - /* if zero the malloc will be used */ - brmrasta_memarea_address = memarea; - - brmrasta_hw_address = hw_address; - -#ifdef B1553BRM_ADR_FROM - brmrasta_cpu_address = memarea & 0xf0000000; -#endif - - /* Register the driver */ - return B1553BRM_PREFIX(_register)(bus,clksel,clkdiv,brm_freq); -} - -/* Call this from RASTA interrupt handler - * irq = the irq number of the HW device local to that IRQMP controller - * - */ -static void b1553brmrasta_interrupt_handler(int irq, void *arg){ - brm_interrupt(arg); -} - 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..acb9379650 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/1553/b1553rt.c @@ -0,0 +1,851 @@ +/* + * B1553RT driver implmenetation + * + * COPYRIGHT (c) 2009. + * Aeroflex Gaisler. + * + * 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; + char *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 = value->ptr; + if ( (unsigned int)mem & 1 ) { + /* Remote address, address as RT looks at it. */ + + /* Translate the base address into an address that the the CPU can understand */ + mem = (char *)((unsigned int)mem & ~1); + drvmgr_translate(pDev->dev, 1, 1, (void *)mem, (void **)&mem); + } + } else { + /* Use dynamically allocated memory */ + mem = (char *)malloc(4 * 1024 * 2); /* 4k DMA memory + 4k for alignment */ + if ( !mem ){ + printk("RT: Failed to allocate HW memory\n\r"); + return -1; + } + } + + /* align memory to 4k boundary */ + mem = (char *)(((unsigned int)mem+0xfff) & ~0xfff); + + /* clear the used memory */ + memset(mem, 0, (4 * 1024)); + + /* Set base address of all descriptors */ + pDev->memarea_base = (unsigned int)mem; + pDev->mem = (volatile unsigned short *) pDev->memarea_base; + + /* Translate the base address into an address that the RT core can understand */ + drvmgr_translate(pDev->dev, 0, 0, (void *)mem, (void **)&pDev->memarea_base_remote); + + 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..3c86349202 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/1553/gr1553b.c @@ -0,0 +1,313 @@ +/* GR1553B driver, used by BC, RT and/or BM driver + * + * COPYRIGHT (c) 2010. + * Aeroflex Gaisler. + * + * 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. + * + * 2010-03-15, Daniel Hellstrom <daniel@gaisler.com> + * Created + * + * + * OVERVIEW + * ======== + * See header file + */ + +#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..d409761994 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/1553/gr1553bc.c @@ -0,0 +1,1682 @@ +/* GR1553B BC driver + * + * COPYRIGHT (c) 2010. + * Aeroflex Gaisler. + * + * 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. + * + * 2010-03-15, Daniel Hellstrom <daniel@gaisler.com> + * Created + * + * + * OVERVIEW + * ======== + * See header file and list header file. + * + */ + +#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; + + if ( bdtab_custom == NULL ) { + /* Get Size required for descriptors */ + size = gr1553bc_list_table_size(list); + + /* Allocate descriptors */ + list->_table = malloc(size + (GR1553BC_BD_ALIGN-1)); + if ( list->_table == NULL ) + return -1; + } else if ( (unsigned int)bdtab_custom & 0x1 ) { + /* Address given in Hardware accessible address, we + * convert it into CPU-accessible address. + */ + drvmgr_translate( + *bcpriv->pdev, + 1, + 1, + (void *)((unsigned int)bdtab_custom & ~0x1), + (void **)&list->_table + ); + } else { + /* Custom address, given in CPU-accessible address */ + list->_table = bdtab_custom; + } + + /* 128-bit Alignment required by HW */ + table = (unsigned int)list->_table; + table = (table + (GR1553BC_BD_ALIGN-1)) & ~(GR1553BC_BD_ALIGN-1); + list->table_cpu = table; + list->table_hw = table; + + /* We got CPU accessible descriptor table address, now we translate + * that into an address which the Hardware can understand + */ + if ( bcpriv ) { + drvmgr_translate( + *bcpriv->pdev, + 0, + 0, + (void *)list->table_cpu, + (void **)&list->table_hw + ); + } + + /* 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. + */ + 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, + 0, + 0, + (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, + 0, + 0, + (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( + *priv->pdev, + 0, + 0, + (void *)priv->irq_log_base, + (void **)&priv->irq_log_base_hw + ); + 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..2ceb2a9046 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/1553/gr1553bm.c @@ -0,0 +1,526 @@ +/* GR1553B BM driver + * + * COPYRIGHT (c) 2010. + * Aeroflex Gaisler. + * + * 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. + * + * 2010-03-15, Daniel Hellstrom <daniel@gaisler.com> + * Created + * + * OVERVIEW + * ======== + * See header file + */ + +#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 ( cfg->buffer_custom == NULL ) { + /* Allocate new buffer dynamically */ + priv->buffer = malloc(priv->buffer_size + 8); + if ( priv->buffer == NULL ) + return -1; + } else { + 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 + */ + drvmgr_translate( + *priv->pdev, + 1, + 1, + (void *)((unsigned int)cfg->buffer_custom & ~1), + (void **)&priv->buffer + ); + } 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( + *priv->pdev, + 0, + 0, + (void *)priv->buffer_base, + (void **)&priv->buffer_base_hw + ); + + /* 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..7ada4d1ac7 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/1553/gr1553rt.c @@ -0,0 +1,1250 @@ +/* GR1553B RT driver + * + * COPYRIGHT (c) 2010. + * Aeroflex Gaisler. + * + * 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. + * + * 2010-03-15, Daniel Hellstrom <daniel@gaisler.com> + * Created + * + * + * OVERVIEW + * ======== + * See header file. + */ + + +#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 ) { + *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, + 0, + 0, + (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, + 0, + 0, + (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 ( priv->cfg.evlog_buffer == NULL ) { + priv->evlog_buffer = malloc(priv->cfg.evlog_size * 2); + } else if ( (unsigned int)priv->cfg.evlog_buffer & 1 ) { + /* Translate Address from HARDWARE (REMOTE) to CPU-LOCAL */ + drvmgr_translate( + *priv->pdev, + 1, + 1, + (void *)((unsigned int)priv->cfg.evlog_buffer & ~0x1), + (void **)&priv->evlog_buffer + ); + } else { + /* Addess already CPU-LOCAL */ + priv->evlog_buffer = priv->cfg.evlog_buffer; + } + + /* Allocate Transfer Descriptors */ + if ( priv->cfg.bd_buffer == NULL ) { + size = priv->cfg.bd_count * sizeof(struct gr1553rt_bd) + 0xf; + priv->bd_buffer = malloc(size); + } else if ( (unsigned int)priv->cfg.bd_buffer & 1 ) { + /* Translate Address from HARDWARE (REMOTE) to CPU-LOCAL */ + drvmgr_translate( + *priv->pdev, + 1, + 1, + (void *)((unsigned int)priv->cfg.bd_buffer & ~0x1), + (void **)&priv->bd_buffer + ); + } else { + /* Addess already CPU-LOCAL */ + priv->bd_buffer = priv->cfg.bd_buffer; + } + +#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 ( priv->cfg.satab_buffer == NULL ) { + priv->satab_buffer = malloc((16 * 32) * 2); + } else if ( (unsigned int)priv->cfg.satab_buffer & 1 ) { + /* Translate Address from HARDWARE (REMOTE) to CPU-LOCAL */ + drvmgr_translate( + *priv->pdev, + 1, + 1, + (void *)((unsigned int)priv->cfg.satab_buffer & ~0x1), + (void **)&priv->satab_buffer + ); + } else { + /* Addess already CPU-LOCAL */ + priv->satab_buffer = priv->cfg.satab_buffer; + } + + if ( !priv->evlog_buffer || !priv->bd_buffer || !priv->satab_buffer ) + return -1; + + return 0; +} + +void gr1553rt_sw_init(struct gr1553rt_priv *priv) +{ + unsigned int buf; + int i; + + /* Align to 512 bytes boundary */ + buf = (unsigned int)priv->satab_buffer; + buf = (buf + 0x1ff) & ~0x1ff; + priv->sas_cpu = (struct gr1553rt_sa *)buf; + /* Translate Address from CPU-LOCAL to HARDWARE (REMOTE) */ + drvmgr_translate( + *priv->pdev, + 0, + 0, + (void *)buf, + (void **)&priv->sas_hw + ); + memset(priv->sas_cpu, 0, 512); + + /* Align to 16 bytes boundary */ + buf = (unsigned int)priv->bd_buffer; + buf = (buf + 0xf) & ~0xf; + priv->bds_cpu = (struct gr1553rt_bd *)buf; + /* Translate from CPU address to hardware address */ + drvmgr_translate( + *priv->pdev, + 0, + 0, + (void *)buf, + (void **)&priv->bds_hw + ); + priv->bds_cnt = priv->cfg.bd_count; + memset(priv->bds_cpu, 0, priv->bds_cnt * 16); + + /* Align to SIZE bytes boundary */ + buf = (unsigned int)priv->evlog_buffer; + buf = (buf + (priv->cfg.evlog_size-1)) & ~(priv->cfg.evlog_size-1); + priv->evlog_cpu_base = (unsigned int *)buf; + /* Translate from CPU address to hardware address */ + drvmgr_translate( + *priv->pdev, + 0, + 0, + (void *)buf, + (void **)&priv->evlog_hw_base + ); + priv->evlog_cpu_end = priv->evlog_cpu_base + + priv->cfg.evlog_size/sizeof(unsigned int *); + 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..6f659fa71e --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/amba/ahbstat.c @@ -0,0 +1,196 @@ +#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/amba/ambapp.c b/c/src/lib/libbsp/sparc/shared/amba/ambapp.c index 814ee8c28e..62c96a0f7f 100644 --- a/c/src/lib/libbsp/sparc/shared/amba/ambapp.c +++ b/c/src/lib/libbsp/sparc/shared/amba/ambapp.c @@ -1,499 +1,475 @@ /* - * AMBA Plag & Play Bus Driver + * AMBA Plug & Play routines * - * This driver hook performs bus scanning. - * - * COPYRIGHT (c) 2004. - * Gaisler Research + * COPYRIGHT (c) 2011. + * Aeroflex Gaisler. * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.com/license/LICENSE. * - * $Id$ + * $Id: ambapp.c,v 1.2 2009/11/29 15:33:27 ralf Exp $ + * + * + * 2008-12-01, Daniel Hellstrom <daniel@gaisler.com> + * Created */ -#include <bsp.h> -#include <rtems/bspIo.h> +#include <string.h> +#include <stdlib.h> +#include <string.h> + #include <ambapp.h> +#include <bsp.h> + +#define AMBA_CONF_AREA 0xff000 +#define AMBA_AHB_SLAVE_CONF_AREA (1 << 11) +#define AMBA_APB_SLAVES 16 -#define amba_insert_device(tab, address) \ -{ \ - if (*(address)) \ - { \ - (tab)->addr[(tab)->devnr] = (address); \ - (tab)->devnr ++; \ - } \ -} while(0) - -#define amba_insert_apb_device(tab, address, apbmst) \ -{ \ - if (*(address)) \ - { \ - (tab)->addr[(tab)->devnr] = (address); \ - (tab)->apbmst[(tab)->devnr] = (apbmst); \ - (tab)->devnr ++; \ - } \ -} while(0) - -static unsigned int -addr_from (struct amba_mmap *mmaps, unsigned int address) +/* Allocate one AMBA device */ +struct ambapp_dev *ambapp_alloc_dev_struct(int dev_type) { - /* no translation? */ - if (!mmaps) - return address; - - while (mmaps->size) { - if ((address >= mmaps->remote_amba_adr) - && (address <= (mmaps->remote_amba_adr + (mmaps->size - 1)))) { - return (address - mmaps->remote_amba_adr) + mmaps->cpu_adr; - } - mmaps++; - } - return 1; + int size = sizeof(struct ambapp_dev); + struct ambapp_dev *dev; + + if (dev_type == DEV_APB_SLV) + size += sizeof(struct ambapp_apb_info); + else + size += sizeof(struct ambapp_ahb_info); /* AHB */ + dev = (struct ambapp_dev *)bsp_early_malloc(size); + if (dev == NULL) + return NULL; + memset(dev, 0 , size); + dev->dev_type = dev_type; + return dev; } - -void -amba_scan (amba_confarea_type * amba_conf, unsigned int ioarea, - struct amba_mmap *mmaps) +unsigned int +ambapp_addr_from (struct ambapp_mmap *mmaps, unsigned int address) { - unsigned int *cfg_area; /* address to configuration area */ - unsigned int mbar, conf, custom; - int i, j; - unsigned int apbmst; - int maxloops = amba_conf->notroot ? 16 : 64; /* scan first bus for 64 devices, rest for 16 devices */ - - amba_conf->ahbmst.devnr = 0; - amba_conf->ahbslv.devnr = 0; - amba_conf->apbslv.devnr = 0; - cfg_area = (unsigned int *) (ioarea | AMBA_CONF_AREA); - amba_conf->ioarea = ioarea; - amba_conf->mmaps = mmaps; - - for (i = 0; i < maxloops; i++) { - amba_insert_device (&amba_conf->ahbmst, cfg_area); - cfg_area += AMBA_AHB_CONF_WORDS; - } - - cfg_area = - (unsigned int *) (ioarea | AMBA_CONF_AREA | AMBA_AHB_SLAVE_CONF_AREA); - for (i = 0; i < maxloops; i++) { - amba_insert_device (&amba_conf->ahbslv, cfg_area); - cfg_area += AMBA_AHB_CONF_WORDS; - } - - for (i = 0; i < amba_conf->ahbslv.devnr; i++){ - conf = amba_get_confword(amba_conf->ahbslv, i, 0); - mbar = amba_ahb_get_membar(amba_conf->ahbslv, i, 0); - if ( (amba_vendor(conf) == VENDOR_GAISLER) && (amba_device(conf) == GAISLER_AHB2AHB) ){ - /* Found AHB->AHB bus bridge, scan it if more free amba_confarea_type:s available - * Custom config 1 contain ioarea. - */ - custom = amba_ahb_get_custom(amba_conf->ahbslv,i,1); - - if ( amba_ver(conf) && amba_conf->next ){ - amba_conf->next->notroot = 1; - amba_scan(amba_conf->next,custom,mmaps); - } - }else if ((amba_vendor(conf) == VENDOR_GAISLER) && (amba_device(conf) == GAISLER_APBMST)) - { - apbmst = amba_membar_start(mbar); - if ( (apbmst=addr_from(mmaps,apbmst)) == 1 ) - continue; /* no available memory translation available, will not be able to access - * Plug&Play information for this AHB/APB bridge. Skip it. - */ - cfg_area = (unsigned int *)( apbmst | AMBA_CONF_AREA); - for (j=0; (amba_conf->apbslv.devnr<AMBA_APB_SLAVES) && (j<AMBA_APB_SLAVES); j++){ - amba_insert_apb_device(&amba_conf->apbslv, cfg_area, apbmst); - cfg_area += AMBA_APB_CONF_WORDS; - } - } - } + /* no translation? */ + if (!mmaps) + return address; + + while (mmaps->size) { + if ((address >= mmaps->remote_adr) && + (address <= (mmaps->remote_adr + (mmaps->size - 1)))) { + return (address - mmaps->remote_adr) + mmaps->local_adr; + } + mmaps++; + } + return 1; } -void -amba_print_dev(int devno, unsigned int conf){ - int irq = amba_irq(conf); - if ( irq > 0 ){ - printk("%x.%x.%x: irq %d\n",devno,amba_vendor(conf),amba_device(conf),irq); - }else{ - printk("%x.%x.%x: no irq\n",devno,amba_vendor(conf),amba_device(conf)); +void ambapp_ahb_dev_init( + unsigned int ioarea, + struct ambapp_mmap *mmaps, + struct ambapp_pnp_ahb *ahb, + struct ambapp_dev *dev, + int ahbidx + ) +{ + int bar; + struct ambapp_ahb_info *ahb_info; + unsigned int addr, mask, mbar; + + /* Setup device struct */ + dev->vendor = ambapp_pnp_vendor(ahb->id); + dev->device = ambapp_pnp_device(ahb->id); + ahb_info = DEV_TO_AHB(dev); + ahb_info->ver = ambapp_pnp_ver(ahb->id); + ahb_info->irq = ambapp_pnp_irq(ahb->id); + ahb_info->ahbidx = ahbidx; + ahb_info->custom[0] = (unsigned int)ahb->custom[0]; + ahb_info->custom[1] = (unsigned int)ahb->custom[1]; + ahb_info->custom[2] = (unsigned int)ahb->custom[2]; + + /* Memory BARs */ + for (bar=0; bar<4; bar++) { + mbar = ahb->mbar[bar]; + if (mbar == 0) { + addr = 0; + mask = 0; + } else { + addr = ambapp_pnp_start(mbar); + if (ambapp_pnp_mbar_type(mbar) == AMBA_TYPE_AHBIO) { + /* AHB I/O area is releative IO_AREA */ + addr = AMBA_TYPE_AHBIO_ADDR(addr, ioarea); + mask = (((unsigned int) + (ambapp_pnp_mbar_mask((~mbar))<<8) | + 0xff)) + 1; + } else { + /* AHB memory area, absolute address */ + addr = ambapp_addr_from(mmaps, addr); + mask = (~((unsigned int) + (ambapp_pnp_mbar_mask(mbar)<<20))) + 1; + } + } + ahb_info->start[bar] = addr; + ahb_info->mask[bar] = mask; + ahb_info->type[bar] = ambapp_pnp_mbar_type(mbar); } } -void -amba_apb_print_dev(int devno, unsigned int conf, unsigned int address){ - int irq = amba_irq(conf); - if ( irq > 0 ){ - printk("%x.%x.%x: irq %d, apb: 0x%lx\n",devno,amba_vendor(conf),amba_device(conf),irq,address); - }else{ - printk("%x.%x.%x: no irq, apb: 0x%lx\n",devno,amba_vendor(conf),amba_device(conf),address); - } +void ambapp_apb_dev_init( + unsigned int base, + struct ambapp_mmap *mmaps, + struct ambapp_pnp_apb *apb, + struct ambapp_dev *dev, + int ahbidx + ) +{ + struct ambapp_apb_info *apb_info; + + /* Setup device struct */ + dev->vendor = ambapp_pnp_vendor(apb->id); + dev->device = ambapp_pnp_device(apb->id); + apb_info = DEV_TO_APB(dev); + apb_info->ver = ambapp_pnp_ver(apb->id); + apb_info->irq = ambapp_pnp_irq(apb->id); + apb_info->ahbidx = ahbidx; + apb_info->start = ambapp_pnp_apb_start(apb->iobar, base); + apb_info->mask = ambapp_pnp_apb_mask(apb->iobar); } -/* Print AMBA Plug&Play info on terminal */ -void -amba_print_conf (amba_confarea_type * amba_conf) +int ambapp_add_ahbbus( + struct ambapp_bus *abus, + unsigned int ioarea + ) { - int i,base=0; - unsigned int conf, iobar, address; - unsigned int apbmst; - - /* print all ahb masters */ - printk("--- AMBA AHB Masters ---\n"); - for(i=0; i<amba_conf->ahbmst.devnr; i++){ - conf = amba_get_confword(amba_conf->ahbmst, i, 0); - amba_print_dev(i,conf); + int i; + for (i=0; i<AHB_BUS_MAX; i++) { + if (abus->ahbs[i].ioarea == 0) { + abus->ahbs[i].ioarea = ioarea; + return i; + } else if (abus->ahbs[i].ioarea == ioarea) { + /* Bus already added */ + return -1; + } } + return -1; +} - /* print all ahb slaves */ - printk("--- AMBA AHB Slaves ---\n"); - for(i=0; i<amba_conf->ahbslv.devnr; i++){ - conf = amba_get_confword(amba_conf->ahbslv, i, 0); - amba_print_dev(i,conf); +/* Internal AMBA Scanning Function */ +static int ambapp_scan2( + struct ambapp_bus *abus, + unsigned int ioarea, + ambapp_memcpy_t memfunc, + struct ambapp_dev *parent, + struct ambapp_dev **root + ) +{ + struct ambapp_pnp_ahb *ahb, ahb_buf; + struct ambapp_pnp_apb *apb, apb_buf; + struct ambapp_dev *dev, *prev, *prevapb, *apbdev; + struct ambapp_ahb_info *ahb_info; + int maxloops = 64; + unsigned int apbbase, bridge_adr; + int i, j, ahbidx; + + *root = NULL; + + if (parent) { + /* scan first bus for 64 devices, rest for 16 devices */ + maxloops = 16; } - /* print all apb slaves */ - apbmst = 0; - for(i=0; i<amba_conf->apbslv.devnr; i++){ - if ( apbmst != amba_conf->apbslv.apbmst[i] ){ - apbmst = amba_conf->apbslv.apbmst[i]; - printk("--- AMBA APB Slaves on 0x%x ---\n",apbmst); - base=i; - } - conf = amba_get_confword(amba_conf->apbslv, i, 0); - iobar = amba_apb_get_membar(amba_conf->apbslv, i); - address = amba_iobar_start(amba_conf->apbslv.apbmst[i], iobar); - amba_apb_print_dev(i-base,conf,address); + ahbidx = ambapp_add_ahbbus(abus, ioarea); + if (ahbidx < 0) { + /* Bus already scanned, stop */ + return 0; } -} -/**** APB Slaves ****/ + prev = parent; -/* Return number of APB Slave devices which has given vendor and device */ -int -amba_get_number_apbslv_devices (amba_confarea_type * amba_conf, int vendor, - int device) -{ - unsigned int conf; - int cnt, i; - - for (cnt = i = 0; i < amba_conf->apbslv.devnr; i++) { - conf = amba_get_confword (amba_conf->apbslv, i, 0); - if ((amba_vendor (conf) == vendor) && (amba_device (conf) == device)) - cnt++; - } - return cnt; -} + /* AHB MASTERS */ + ahb = (struct ambapp_pnp_ahb *) (ioarea | AMBA_CONF_AREA); + for (i = 0; i < maxloops; i++, ahb++) { + memfunc(&ahb_buf, ahb, sizeof(struct ambapp_pnp_ahb), abus); + if (ahb_buf.id == 0) + continue; -/* Get First APB Slave device of this vendor&device id */ -int -amba_find_apbslv (amba_confarea_type * amba_conf, int vendor, int device, - amba_apb_device * dev) -{ - unsigned int conf, iobar; - int i; - - for (i = 0; i < amba_conf->apbslv.devnr; i++) { - conf = amba_get_confword (amba_conf->apbslv, i, 0); - if ((amba_vendor (conf) == vendor) && (amba_device (conf) == device)) { - iobar = amba_apb_get_membar (amba_conf->apbslv, i); - dev->start = amba_iobar_start (amba_conf->apbslv.apbmst[i], iobar); - dev->irq = amba_irq (conf); - return 1; - } - } - return 0; -} + /* An AHB device present here */ + dev = ambapp_alloc_dev_struct(DEV_AHB_MST); + if (!dev) + return -1; -/* Get APB Slave device of this vendor&device id. (setting nr to 0 is eqivalent to calling amba_find_apbslv() ) */ -int -amba_find_next_apbslv (amba_confarea_type * amba_conf, int vendor, int device, - amba_apb_device * dev, int index) -{ - unsigned int conf, iobar; - int cnt, i; - - for (cnt = i = 0; i < amba_conf->apbslv.devnr; i++) { - conf = amba_get_confword (amba_conf->apbslv, i, 0); - if ((amba_vendor (conf) == vendor) && (amba_device (conf) == device)) { - if (cnt == index) { - /* found device */ - iobar = amba_apb_get_membar (amba_conf->apbslv, i); - dev->start = amba_iobar_start (amba_conf->apbslv.apbmst[i], iobar); - dev->irq = amba_irq (conf); - return 1; - } - cnt++; - } - } - return 0; -} + ambapp_ahb_dev_init(ioarea, abus->mmaps, &ahb_buf, dev, ahbidx); -/* Get first nr APB Slave devices, put them into dev (which is an array of nr length) */ -int -amba_find_apbslvs (amba_confarea_type * amba_conf, int vendor, int device, - amba_apb_device * devs, int maxno) -{ - unsigned int conf, iobar; - int cnt, i; - - for (cnt = i = 0; (i < amba_conf->apbslv.devnr) && (cnt < maxno); i++) { - conf = amba_get_confword (amba_conf->apbslv, i, 0); - if ((amba_vendor (conf) == vendor) && (amba_device (conf) == device)) { - /* found device */ - iobar = amba_apb_get_membar (amba_conf->apbslv, i); - devs[cnt].start = amba_iobar_start (amba_conf->apbslv.apbmst[i], iobar); - devs[cnt].irq = amba_irq (conf); - cnt++; - } - } - return cnt; -} + if (*root == NULL) + *root = dev; -/***** AHB SLAVES *****/ + if (prev != parent) + prev->next = dev; + dev->prev = prev; + prev = dev; + } -/* Return number of AHB Slave devices which has given vendor and device */ -int -amba_get_number_ahbslv_devices (amba_confarea_type * amba_conf, int vendor, - int device) -{ - unsigned int conf; - int cnt, i; - - for (cnt = i = 0; i < amba_conf->ahbslv.devnr; i++) { - conf = amba_get_confword (amba_conf->ahbslv, i, 0); - if ((amba_vendor (conf) == vendor) && (amba_device (conf) == device)) - cnt++; - } - return cnt; -} + /* AHB SLAVES */ + ahb = (struct ambapp_pnp_ahb *) + (ioarea | AMBA_CONF_AREA | AMBA_AHB_SLAVE_CONF_AREA); + for (i = 0; i < maxloops; i++, ahb++) { + memfunc(&ahb_buf, ahb, sizeof(struct ambapp_pnp_ahb), abus); + if (ahb_buf.id == 0) + continue; + + /* An AHB device present here */ + dev = ambapp_alloc_dev_struct(DEV_AHB_SLV); + if (!dev) + return -1; + + ambapp_ahb_dev_init(ioarea, abus->mmaps, &ahb_buf, dev, ahbidx); + + if (*root == NULL) + *root = dev; + + if (prev != parent) + prev->next = dev; + dev->prev = prev; + prev = dev; + + ahb_info = DEV_TO_AHB(dev); + + /* Is it a AHB/AHB Bridge ? */ + if (((dev->device == GAISLER_AHB2AHB) && + (dev->vendor == VENDOR_GAISLER) && (ahb_info->ver>0)) || + ((dev->device == GAISLER_L2CACHE) && + (dev->vendor == VENDOR_GAISLER)) || + ((dev->device == GAISLER_GRIOMMU) && + (dev->vendor == VENDOR_GAISLER))) { + /* AHB/AHB Bridge Found, recurse down the + * Bridge + */ + if (ahb_info->custom[1] != 0) { + bridge_adr = ambapp_addr_from(abus->mmaps, + ahb_info->custom[1]); + /* Scan next bus if not already scanned */ + if (ambapp_scan2(abus, bridge_adr, memfunc, dev, + &dev->children)) + return -1; + } + } else if ((dev->device == GAISLER_APBMST) && + (dev->vendor == VENDOR_GAISLER)) { + /* AHB/APB Bridge Found, add the APB devices to this + * AHB Slave's children + */ + prevapb = dev; + apbbase = ahb_info->start[0]; + + /* APB SLAVES */ + apb = (struct ambapp_pnp_apb *) + (apbbase | AMBA_CONF_AREA); + for (j=0; j<AMBA_APB_SLAVES; j++, apb++) { + memfunc(&apb_buf, apb, sizeof(*apb), abus); + if (apb_buf.id == 0) + continue; + + apbdev = ambapp_alloc_dev_struct(DEV_APB_SLV); + if (!dev) + return -1; + + ambapp_apb_dev_init(apbbase, abus->mmaps, + &apb_buf, apbdev, ahbidx); + + if (prevapb != dev) + prevapb->next = apbdev; + else + dev->children = apbdev; + apbdev->prev = prevapb; + prevapb = apbdev; + } + } + } -/* Get First AHB Slave device of this vendor&device id */ -int -amba_find_ahbslv (amba_confarea_type * amba_conf, int vendor, int device, - amba_ahb_device * dev) -{ - unsigned int conf, mbar, addr; - int j, i; - - for (i = 0; i < amba_conf->ahbslv.devnr; i++) { - conf = amba_get_confword (amba_conf->ahbslv, i, 0); - if ((amba_vendor (conf) == vendor) && (amba_device (conf) == device)) { - for (j = 0; j < 4; j++) { - mbar = amba_ahb_get_membar (amba_conf->ahbslv, i, j); - addr = amba_membar_start (mbar); - if (amba_membar_type (mbar) == AMBA_TYPE_AHBIO) { - addr = AMBA_TYPE_AHBIO_ADDR (addr, amba_conf->ioarea); - } else { /* convert address if needed */ - if ((addr = addr_from (amba_conf->mmaps, addr)) == 1) { - addr = 0; /* no available memory translation available, will not be able to access - * Plug&Play information for this AHB address. Skip it. - */ - } - } - dev->start[j] = addr; - } - dev->irq = amba_irq (conf); - dev->ver = amba_ver (conf); - return 1; - } - } - return 0; -} + /* Remember first AHB MST/SLV device on bus and Parent Bridge */ + abus->ahbs[ahbidx].dev = *root; + abus->ahbs[ahbidx].bridge = parent; -/* Get AHB Slave device of this vendor&device id. (setting nr to 0 is eqivalent to calling amba_find_ahbslv() ) */ -int -amba_find_next_ahbslv (amba_confarea_type * amba_conf, int vendor, int device, - amba_ahb_device * dev, int index) -{ - unsigned int conf, mbar, addr; - int i, j, cnt; - - for (cnt = i = 0; i < amba_conf->ahbslv.devnr; i++) { - conf = amba_get_confword (amba_conf->ahbslv, i, 0); - if ((amba_vendor (conf) == vendor) && (amba_device (conf) == device)) { - if (cnt == index) { - for (j = 0; j < 4; j++) { - mbar = amba_ahb_get_membar (amba_conf->ahbslv, i, j); - addr = amba_membar_start (mbar); - if (amba_membar_type (mbar) == AMBA_TYPE_AHBIO) { - addr = AMBA_TYPE_AHBIO_ADDR (addr, amba_conf->ioarea); - } else { - /* convert address if needed */ - if ((addr = addr_from (amba_conf->mmaps, addr)) == 1) { - addr = 0; /* no available memory translation available, will not be able to access - * Plug&Play information for this AHB address. Skip it. - */ - } - } - dev->start[j] = addr; - } - dev->irq = amba_irq (conf); - dev->ver = amba_ver (conf); - return 1; - } - cnt++; - } - } - return 0; + return 0; } -/* Get first nr AHB Slave devices, put them into dev (which is an array of nr length) */ -int -amba_find_ahbslvs (amba_confarea_type * amba_conf, int vendor, int device, - amba_ahb_device * devs, int maxno) +/* Build AMBA Plug & Play device graph */ +int ambapp_scan( + struct ambapp_bus *abus, + unsigned int ioarea, + ambapp_memcpy_t memfunc, + struct ambapp_mmap *mmaps + ) { - unsigned int conf, mbar, addr; - int i, j, cnt; - - for (cnt = i = 0; (i < amba_conf->ahbslv.devnr) && (maxno < cnt); i++) { - conf = amba_get_confword (amba_conf->ahbslv, i, 0); - if ((amba_vendor (conf) == vendor) && (amba_device (conf) == device)) { - for (j = 0; j < 4; j++) { - mbar = amba_ahb_get_membar (amba_conf->ahbslv, i, j); - addr = amba_membar_start (mbar); - if (amba_membar_type (mbar) == AMBA_TYPE_AHBIO) { - addr = AMBA_TYPE_AHBIO_ADDR (addr, amba_conf->ioarea); - } else { - /* convert address if needed */ - if ((addr = addr_from (amba_conf->mmaps, addr)) == 1) { - addr = 0; /* no available memory translation available, will not be able to access - * Plug&Play information for this AHB address. Skip it. - */ - } - } - devs[cnt].start[j] = addr; - } - devs[cnt].irq = amba_irq (conf); - devs[cnt].ver = amba_ver (conf); - cnt++; - } - } - return cnt; -} + memset(abus, 0, sizeof(*abus)); + abus->mmaps = mmaps; + /* Default to memcpy() */ + if (!memfunc) + memfunc = (ambapp_memcpy_t)memcpy; -/***** AHB Masters *****/ + return ambapp_scan2(abus, ioarea, memfunc, NULL, &abus->root); +} -/* Return number of AHB Slave devices which has given vendor and device */ -int -amba_get_number_ahbmst_devices (amba_confarea_type * amba_conf, int vendor, - int device) +/* Match search options againt device */ +int ambapp_dev_match_options(struct ambapp_dev *dev, unsigned int options, int vendor, int device) { - unsigned int conf; - int cnt, i; - - for (cnt = i = 0; i < amba_conf->ahbmst.devnr; i++) { - conf = amba_get_confword (amba_conf->ahbmst, i, 0); - if ((amba_vendor (conf) == vendor) && (amba_device (conf) == device)) - cnt++; - } - return cnt; + if ((((options & (OPTIONS_ALL_DEVS)) == OPTIONS_ALL_DEVS) || /* TYPE */ + ((options & OPTIONS_AHB_MSTS) && (dev->dev_type == DEV_AHB_MST)) || + ((options & OPTIONS_AHB_SLVS) && (dev->dev_type == DEV_AHB_SLV)) || + ((options & OPTIONS_APB_SLVS) && (dev->dev_type == DEV_APB_SLV))) && + ((vendor == -1) || (vendor == dev->vendor)) && /* VENDOR/DEV ID */ + ((device == -1) || (device == dev->device)) && + (((options & OPTIONS_ALL) == OPTIONS_ALL) || /* Allocated State */ + ((options & OPTIONS_FREE) && DEV_IS_FREE(dev)) || + ((options & OPTIONS_ALLOCATED) && DEV_IS_ALLOCATED(dev)))) { + return 1; + } + return 0; } -/* Get First AHB Slave device of this vendor&device id */ -int -amba_find_ahbmst (amba_confarea_type * amba_conf, int vendor, int device, - amba_ahb_device * dev) +/* If device is an APB bridge all devices on the APB bridge is processed */ +static int ambapp_for_each_apb( + struct ambapp_dev *dev, + unsigned int options, + int vendor, + int device, + ambapp_func_t func, + void *arg) { - unsigned int conf, mbar, addr; - int j, i; - - for (i = 0; i < amba_conf->ahbmst.devnr; i++) { - conf = amba_get_confword (amba_conf->ahbslv, i, 0); - if ((amba_vendor (conf) == vendor) && (amba_device (conf) == device)) { - for (j = 0; j < 4; j++) { - mbar = amba_ahb_get_membar (amba_conf->ahbmst, i, j); - addr = amba_membar_start (mbar); - if (amba_membar_type (mbar) == AMBA_TYPE_AHBIO) { - addr = AMBA_TYPE_AHBIO_ADDR (addr, amba_conf->ioarea); - } else { - /* convert address if needed */ - if ((addr = addr_from (amba_conf->mmaps, addr)) == 1) { - addr = 0; /* no available memory translation available, will not be able to access - * Plug&Play information for this AHB address. Skip it. - */ - } - } - dev->start[j] = addr; - } - dev->irq = amba_irq (conf); - dev->ver = amba_ver (conf); - return 1; - } - } - return 0; + int index, ret; + struct ambapp_dev *apbslv; + + ret = 0; + if (dev->children && (dev->children->dev_type == DEV_APB_SLV)) { + /* Found a APB Bridge */ + index = 0; + apbslv = dev->children; + while (apbslv) { + if (ambapp_dev_match_options(apbslv, options, + vendor, device) == 1) { + ret = func(apbslv, index, arg); + if (ret != 0) + break; /* Signalled stopped */ + } + index++; + apbslv = apbslv->next; + } + } + + return ret; } -/* Get AHB Slave device of this vendor&device id. (setting nr to 0 is eqivalent to calling amba_find_ahbslv() ) */ -int -amba_find_next_ahbmst (amba_confarea_type * amba_conf, int vendor, int device, - amba_ahb_device * dev, int index) +/* Traverse the prescanned device information */ +static int ambapp_for_each_dev( + struct ambapp_dev *root, + unsigned int options, + int vendor, + int device, + ambapp_func_t func, + void *arg) { - unsigned int conf, mbar, addr; - int i, j, cnt; - - for (cnt = i = 0; i < amba_conf->ahbmst.devnr; i++) { - conf = amba_get_confword (amba_conf->ahbmst, i, 0); - if ((amba_vendor (conf) == vendor) && (amba_device (conf) == device)) { - if (cnt == index) { - for (j = 0; j < 4; j++) { - mbar = amba_ahb_get_membar (amba_conf->ahbmst, i, j); - addr = amba_membar_start (mbar); - if (amba_membar_type (mbar) == AMBA_TYPE_AHBIO) { - addr = AMBA_TYPE_AHBIO_ADDR (addr, amba_conf->ioarea); - } else { - /* convert address if needed */ - if ((addr = addr_from (amba_conf->mmaps, addr)) == 1) { - addr = 0; /* no available memory translation available, will not be able to access - * Plug&Play information for this AHB address. Skip it. - */ - } - } - dev->start[j] = addr; - } - dev->irq = amba_irq (conf); - dev->ver = amba_ver (conf); - return 1; - } - cnt++; - } - } - return 0; + struct ambapp_dev *dev; + int ahb_slave = 0; + int index, ret; + + /* Start at device 'root' and process downwards. + * + * Breadth first search, search order + * 1. AHB MSTS + * 2. AHB SLVS + * 3. APB SLVS on primary bus + * 4. AHB/AHB secondary... -> step to 1. + */ + + /* AHB MST / AHB SLV */ + if (options & (OPTIONS_AHB_MSTS|OPTIONS_AHB_SLVS|OPTIONS_DEPTH_FIRST)) { + index = 0; + dev = root; + while (dev) { + if ((dev->dev_type == DEV_AHB_SLV) && !ahb_slave) { + /* First AHB Slave */ + ahb_slave = 1; + index = 0; + } + + /* Conditions must be fullfilled for function to be + * called + */ + if (ambapp_dev_match_options(dev, options, vendor, + device) == 1) { + /* Correct device and vendor ID */ + ret = func(dev, index, arg); + if (ret != 0) + return ret; /* Signalled stopped */ + } + + if ((options & OPTIONS_DEPTH_FIRST) && + (options & OPTIONS_APB_SLVS)) { + /* Check is APB bridge, and process all APB + * Slaves in that case + */ + ret = ambapp_for_each_apb(dev, options, vendor, + device, func, arg); + if (ret != 0) + return ret; /* Signalled stopped */ + } + + if (options & OPTIONS_DEPTH_FIRST) { + if (dev->children && + (dev->children->dev_type != DEV_APB_SLV)) { + /* Found AHB Bridge, recurse */ + ret = ambapp_for_each_dev(dev->children, + options, vendor, + device, func, arg); + if (ret != 0) + return ret; + } + } + + index++; + dev = dev->next; + } + } + + /* Find APB Bridges */ + if ((options & OPTIONS_APB_SLVS) && !(options & OPTIONS_DEPTH_FIRST)) { + dev = root; + while (dev) { + /* Check is APB bridge, and process all APB Slaves in + * that case + */ + ret = ambapp_for_each_apb(dev, options, vendor, device, + func, arg); + if (ret != 0) + return ret; /* Signalled stopped */ + dev = dev->next; + } + } + + /* Find AHB Bridges */ + if (!(options & OPTIONS_DEPTH_FIRST)) { + dev = root; + while (dev) { + if (dev->children && + (dev->children->dev_type != DEV_APB_SLV)) { + /* Found AHB Bridge, recurse */ + ret = ambapp_for_each_dev(dev->children, + options, vendor, + device, func, arg); + if (ret != 0) + return ret; + } + dev = dev->next; + } + } + + return 0; } -/* Get first nr AHB Slave devices, put them into dev (which is an array of nr length) */ -int -amba_find_ahbmsts (amba_confarea_type * amba_conf, int vendor, int device, - amba_ahb_device * devs, int maxno) +int ambapp_for_each( + struct ambapp_bus *abus, + unsigned int options, + int vendor, + int device, + ambapp_func_t func, + void *arg) { - unsigned int conf, mbar, addr; - int i, j, cnt; - - for (cnt = i = 0; (i < amba_conf->ahbmst.devnr) && (maxno < cnt); i++) { - conf = amba_get_confword (amba_conf->ahbslv, i, 0); - if ((amba_vendor (conf) == vendor) && (amba_device (conf) == device)) { - for (j = 0; j < 4; j++) { - mbar = amba_ahb_get_membar (amba_conf->ahbmst, i, j); - addr = amba_membar_start (mbar); - if (amba_membar_type (mbar) == AMBA_TYPE_AHBIO) { - addr = AMBA_TYPE_AHBIO_ADDR (addr, amba_conf->ioarea); - } else { - /* convert address if needed */ - if ((addr = addr_from (amba_conf->mmaps, addr)) == 1) { - addr = 0; /* no available memory translation available, will not be able to access - * Plug&Play information for this AHB address. Skip it. - */ - } - } - devs[cnt].start[j] = addr; - } - devs[cnt].irq = amba_irq (conf); - devs[cnt].ver = amba_ver (conf); - cnt++; - } - } - return cnt; + return ambapp_for_each_dev(abus->root, options, vendor, device, func, + arg); } diff --git a/c/src/lib/libbsp/sparc/shared/amba/ambapp_alloc.c b/c/src/lib/libbsp/sparc/shared/amba/ambapp_alloc.c new file mode 100644 index 0000000000..0188267fe9 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/amba/ambapp_alloc.c @@ -0,0 +1,28 @@ +/* + * AMBA Plug & Play routines + * + * COPYRIGHT (c) 2011 + * Aeroflex Gaisler + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id: ambapp_alloc.c,v + * + */ + +#include <ambapp.h> + +int ambapp_alloc_dev(struct ambapp_dev *dev, void *owner) +{ + if (dev->owner) + return -1; + dev->owner = owner; + return 0; +} + +void ambapp_free_dev(struct ambapp_dev *dev) +{ + dev->owner = 0; +} diff --git a/c/src/lib/libbsp/sparc/shared/amba/ambapp_count.c b/c/src/lib/libbsp/sparc/shared/amba/ambapp_count.c new file mode 100644 index 0000000000..5be46079ec --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/amba/ambapp_count.c @@ -0,0 +1,27 @@ +/* + * AMBA Plug & Play routines + * + * COPYRIGHT (c) 2011 + * Aeroflex Gaisler + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id: ambapp_count.c,v + * + */ + +#include <ambapp.h> + +/* Get number of devices matching search options */ +int ambapp_dev_count(struct ambapp_bus *abus, unsigned int options, + int vendor, int device) +{ + int count = 10000; + + ambapp_for_each(abus, options, vendor, device, ambapp_find_by_idx, + &count); + + return 10000 - count; +} diff --git a/c/src/lib/libbsp/sparc/shared/amba/ambapp_depth.c b/c/src/lib/libbsp/sparc/shared/amba/ambapp_depth.c new file mode 100644 index 0000000000..cd1a7b48e6 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/amba/ambapp_depth.c @@ -0,0 +1,27 @@ +/* + * AMBA Plug & Play routines + * + * COPYRIGHT (c) 2011 + * Aeroflex Gaisler + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id: ambapp_depth.c,v + * + */ + +#include <ambapp.h> + +int ambapp_depth(struct ambapp_dev *dev) +{ + int depth = 0; + + do { + dev = ambapp_find_parent(dev); + depth++; + } while (dev); + + return depth - 1; +} diff --git a/c/src/lib/libbsp/sparc/shared/amba/ambapp_find_by_idx.c b/c/src/lib/libbsp/sparc/shared/amba/ambapp_find_by_idx.c new file mode 100644 index 0000000000..9d078c0e11 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/amba/ambapp_find_by_idx.c @@ -0,0 +1,42 @@ +/* + * AMBA Plug & Play routines + * + * COPYRIGHT (c) 2011 + * Aeroflex Gaisler + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id: ambapp_find_by_idx.c,v + * + */ + +#include <ambapp.h> + +/* AMBAPP helper routine to find a device by index. The function is given to + * ambapp_for_each, the argument may be NULL (find first device) or a pointer + * to a index which is downcounted until 0 is reached. If the int-pointer + * points to a value of: + * 0 - first device is returned + * 1 - second device is returned + * ... + * + * The matching device is returned, which will stop the ambapp_for_each search. + * If zero is returned from ambapp_for_each no device matching the index was + * found + */ +int ambapp_find_by_idx(struct ambapp_dev *dev, int index, void *pcount) +{ + int *pi = pcount; + + if (pi) { + if (*pi-- == 0) + return (int)dev; + else + return 0; + } else { + /* Satisfied with first matching device, stop search */ + return (int)dev; + } +} diff --git a/c/src/lib/libbsp/sparc/shared/amba/ambapp_freq.c b/c/src/lib/libbsp/sparc/shared/amba/ambapp_freq.c new file mode 100644 index 0000000000..d3f0e7878d --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/amba/ambapp_freq.c @@ -0,0 +1,114 @@ +/* + * AMBA Plug & Play routines + * + * COPYRIGHT (c) 2011 + * Aeroflex Gaisler + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id: ambapp_freq.c,v + * + */ + +#include <ambapp.h> + +/* Calculate AHB Bus frequency of + * - Bus[0] (inverse=1), relative to the frequency of Bus[ahbidx] + * NOTE: set freq_hz to frequency of Bus[ahbidx]. + * or + * - Bus[ahbidx] (inverse=0), relative to the frequency of Bus[0] + * NOTE: set freq_hz to frequency of Bus[0]. + * + * If a unsupported bridge is found the invalid frequncy of 0Hz is + * returned. + */ +unsigned int ambapp_freq_calc( + struct ambapp_bus *abus, + int ahbidx, + unsigned int freq_hz, + int inverse) +{ + struct ambapp_ahb_info *ahb; + struct ambapp_dev *bridge; + unsigned char ffact; + int dir; + + /* Found Bus0? */ + bridge = abus->ahbs[ahbidx].bridge; + if (!bridge) + return freq_hz; + + /* Find this bus frequency relative to freq_hz */ + if ((bridge->vendor == VENDOR_GAISLER) && + ((bridge->device == GAISLER_AHB2AHB) || + (bridge->device == GAISLER_L2CACHE))) { + ahb = DEV_TO_AHB(bridge); + ffact = (ahb->custom[0] & AMBAPP_FLAG_FFACT) >> 4; + if (ffact != 0) { + dir = ahb->custom[0] & AMBAPP_FLAG_FFACT_DIR; + + /* Calculate frequency by dividing or + * multiplying system frequency + */ + if ((dir && !inverse) || (!dir && inverse)) + freq_hz = freq_hz * ffact; + else + freq_hz = freq_hz / ffact; + } + return ambapp_freq_calc(abus, ahb->ahbidx, freq_hz, inverse); + } else { + /* Unknown bridge, impossible to calc frequency */ + return 0; + } +} + +/* Find the frequency of all AHB Buses from knowing the frequency of one + * particular APB/AHB Device. + */ +void ambapp_freq_init( + struct ambapp_bus *abus, + struct ambapp_dev *dev, + unsigned int freq_hz) +{ + struct ambapp_common_info *info; + int i; + + for (i=0; i<AHB_BUS_MAX; i++) + abus->ahbs[i].freq_hz = 0; + + /* Register Frequency at the AHB bus that the device the user gave us + * is located at. + */ + if (dev) { + info = DEV_TO_COMMON(dev); + abus->ahbs[info->ahbidx].freq_hz = freq_hz; + + /* Find Frequency of Bus 0 */ + abus->ahbs[0].freq_hz = + ambapp_freq_calc(abus, info->ahbidx, freq_hz, 1); + } else { + abus->ahbs[0].freq_hz = freq_hz; + } + + /* Find Frequency of all except for Bus0 and the bus which frequency + * was reported at + */ + for (i=1; i<AHB_BUS_MAX; i++) { + if (abus->ahbs[i].ioarea == 0) + break; + if (abus->ahbs[i].freq_hz != 0) + continue; + abus->ahbs[i].freq_hz = + ambapp_freq_calc(abus, i, abus->ahbs[0].freq_hz, 0); + } +} + +/* Assign a AMBA Bus a frequency but reporting the frequency of a + * particular AHB/APB device */ +unsigned int ambapp_freq_get(struct ambapp_bus *abus, struct ambapp_dev *dev) +{ + struct ambapp_common_info *info = DEV_TO_COMMON(dev); + return abus->ahbs[info->ahbidx].freq_hz; +} diff --git a/c/src/lib/libbsp/sparc/shared/amba/ambapp_names.c b/c/src/lib/libbsp/sparc/shared/amba/ambapp_names.c new file mode 100644 index 0000000000..1fe65537aa --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/amba/ambapp_names.c @@ -0,0 +1,395 @@ +/* + * AMBA Plug & Play Device and Vendor name database + * + * COPYRIGHT (c) 2009. + * Aeroflex Gaisler. + * + * The device and vendor definitions are extracted with a script from + * GRLIB. + * + * 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. + * + * 2009-01-27, Daniel Hellstrom <daniel@gaisler.com> + * Created from GRLIB 3386. + */ + +#include <ambapp_ids.h> +#include <string.h> + +#ifndef NULL +#define NULL 0 +#endif + +typedef struct { + int device_id; + char *name; +} ambapp_device_name; + +typedef struct { + unsigned int vendor_id; + char *name; + ambapp_device_name *devices; +} ambapp_vendor_devnames; + +/**************** AUTO GENERATED FROM devices.vhd ****************/ +static ambapp_device_name GAISLER_devices[] = +{ + {GAISLER_LEON2DSU, "LEON2DSU"}, + {GAISLER_LEON3, "LEON3"}, + {GAISLER_LEON3DSU, "LEON3DSU"}, + {GAISLER_ETHAHB, "ETHAHB"}, + {GAISLER_APBMST, "APBMST"}, + {GAISLER_AHBUART, "AHBUART"}, + {GAISLER_SRCTRL, "SRCTRL"}, + {GAISLER_SDCTRL, "SDCTRL"}, + {GAISLER_SSRCTRL, "SSRCTRL"}, + {GAISLER_APBUART, "APBUART"}, + {GAISLER_IRQMP, "IRQMP"}, + {GAISLER_AHBRAM, "AHBRAM"}, + {GAISLER_AHBDPRAM, "AHBDPRAM"}, + {GAISLER_GPTIMER, "GPTIMER"}, + {GAISLER_PCITRG, "PCITRG"}, + {GAISLER_PCISBRG, "PCISBRG"}, + {GAISLER_PCIFBRG, "PCIFBRG"}, + {GAISLER_PCITRACE, "PCITRACE"}, + {GAISLER_DMACTRL, "DMACTRL"}, + {GAISLER_AHBTRACE, "AHBTRACE"}, + {GAISLER_DSUCTRL, "DSUCTRL"}, + {GAISLER_CANAHB, "CANAHB"}, + {GAISLER_GPIO, "GPIO"}, + {GAISLER_AHBROM, "AHBROM"}, + {GAISLER_AHBJTAG, "AHBJTAG"}, + {GAISLER_ETHMAC, "ETHMAC"}, + {GAISLER_SWNODE, "SWNODE"}, + {GAISLER_SPW, "SPW"}, + {GAISLER_AHB2AHB, "AHB2AHB"}, + {GAISLER_USBDC, "USBDC"}, + {GAISLER_USB_DCL, "USB_DCL"}, + {GAISLER_DDRMP, "DDRMP"}, + {GAISLER_ATACTRL, "ATACTRL"}, + {GAISLER_DDRSP, "DDRSP"}, + {GAISLER_EHCI, "EHCI"}, + {GAISLER_UHCI, "UHCI"}, + {GAISLER_I2CMST, "I2CMST"}, + {GAISLER_SPW2, "SPW2"}, + {GAISLER_AHBDMA, "AHBDMA"}, + {GAISLER_NUHOSP3, "NUHOSP3"}, + {GAISLER_CLKGATE, "CLKGATE"}, + {GAISLER_SPICTRL, "SPICTRL"}, + {GAISLER_DDR2SP, "DDR2SP"}, + {GAISLER_SLINK, "SLINK"}, + {GAISLER_GRTM, "GRTM"}, + {GAISLER_GRTC, "GRTC"}, + {GAISLER_GRPW, "GRPW"}, + {GAISLER_GRCTM, "GRCTM"}, + {GAISLER_GRHCAN, "GRHCAN"}, + {GAISLER_GRFIFO, "GRFIFO"}, + {GAISLER_GRADCDAC, "GRADCDAC"}, + {GAISLER_GRPULSE, "GRPULSE"}, + {GAISLER_GRTIMER, "GRTIMER"}, + {GAISLER_AHB2PP, "AHB2PP"}, + {GAISLER_GRVERSION, "GRVERSION"}, + {GAISLER_APB2PW, "APB2PW"}, + {GAISLER_PW2APB, "PW2APB"}, + {GAISLER_GRCAN, "GRCAN"}, + {GAISLER_I2CSLV, "I2CSLV"}, + {GAISLER_U16550, "U16550"}, + {GAISLER_AHBMST_EM, "AHBMST_EM"}, + {GAISLER_AHBSLV_EM, "AHBSLV_EM"}, + {GAISLER_GRTESTMOD, "GRTESTMOD"}, + {GAISLER_ASCS, "ASCS"}, + {GAISLER_IPMVBCTRL, "IPMVBCTRL"}, + {GAISLER_SPIMCTRL, "SPIMCTRL"}, + {GAISLER_LEON4, "LEON4"}, + {GAISLER_LEON4DSU, "LEON4DSU"}, + {GAISLER_GRPWM, "GRPWM"}, + {GAISLER_FTAHBRAM, "FTAHBRAM"}, + {GAISLER_FTSRCTRL, "FTSRCTRL"}, + {GAISLER_AHBSTAT, "AHBSTAT"}, + {GAISLER_LEON3FT, "LEON3FT"}, + {GAISLER_FTMCTRL, "FTMCTRL"}, + {GAISLER_FTSDCTRL, "FTSDCTRL"}, + {GAISLER_FTSRCTRL8, "FTSRCTRL8"}, + {GAISLER_MEMSCRUB, "MEMSCRUB"}, + {GAISLER_APBPS2, "APBPS2"}, + {GAISLER_VGACTRL, "VGACTRL"}, + {GAISLER_LOGAN, "LOGAN"}, + {GAISLER_SVGACTRL, "SVGACTRL"}, + {GAISLER_T1AHB, "T1AHB"}, + {GAISLER_MP7WRAP, "MP7WRAP"}, + {GAISLER_GRSYSMON, "GRSYSMON"}, + {GAISLER_GRACECTRL, "GRACECTRL"}, + {GAISLER_B1553BC, "B1553BC"}, + {GAISLER_B1553RT, "B1553RT"}, + {GAISLER_B1553BRM, "B1553BRM"}, + {GAISLER_SATCAN, "SATCAN"}, + {GAISLER_CANMUX, "CANMUX"}, + {GAISLER_GRTMRX, "GRTMRX"}, + {GAISLER_GRTCTX, "GRTCTX"}, + {GAISLER_GRTMDESC, "GRTMDESC"}, + {GAISLER_GRTMVC, "GRTMVC"}, + {GAISLER_GEFFE, "GEFFE"}, + {GAISLER_AES, "AES"}, + {GAISLER_GRAESDMA, "GRAESDMA"}, + {GAISLER_ECC, "ECC"}, + {GAISLER_PCIF, "PCIF"}, + {GAISLER_CLKMOD, "CLKMOD"}, + {GAISLER_HAPSTRAK, "HAPSTRAK"}, + {GAISLER_TEST_1X2, "TEST_1X2"}, + {GAISLER_WILD2AHB, "WILD2AHB"}, + {GAISLER_BIO1, "BIO1"}, + {GAISLER_GR1553B, "GR1553B"}, + {GAISLER_L2CACHE, "L2CACHE"}, + {GAISLER_L4STAT, "L4STAT"}, + {GAISLER_GRPCI2, "GRPCI2"}, + {GAISLER_GRPCI2_DMA, "GRPCI2_DMA"}, + {GAISLER_GRIOMMU, "GRIOMMU"}, + {GAISLER_SPW2_DMA, "SPW2_DMA"}, + {GAISLER_SPW_ROUTER, "SPW_ROUTER"}, + {0, NULL} +}; + +static ambapp_device_name PENDER_devices[] = +{ + {0, NULL} +}; + +static ambapp_device_name ESA_devices[] = +{ + {ESA_LEON2, "LEON2"}, + {ESA_LEON2APB, "LEON2APB"}, + {ESA_IRQ, "IRQ"}, + {ESA_TIMER, "TIMER"}, + {ESA_UART, "UART"}, + {ESA_CFG, "CFG"}, + {ESA_IO, "IO"}, + {ESA_MCTRL, "MCTRL"}, + {ESA_PCIARB, "PCIARB"}, + {ESA_HURRICANE, "HURRICANE"}, + {ESA_SPW_RMAP, "SPW_RMAP"}, + {ESA_AHBUART, "AHBUART"}, + {ESA_SPWA, "SPWA"}, + {ESA_BOSCHCAN, "BOSCHCAN"}, + {ESA_IRQ2, "IRQ2"}, + {ESA_AHBSTAT, "AHBSTAT"}, + {ESA_WPROT, "WPROT"}, + {ESA_WPROT2, "WPROT2"}, + {ESA_PDEC3AMBA, "PDEC3AMBA"}, + {ESA_PTME3AMBA, "PTME3AMBA"}, + {0, NULL} +}; + +static ambapp_device_name ASTRIUM_devices[] = +{ + {0, NULL} +}; + +static ambapp_device_name OPENCHIP_devices[] = +{ + {OPENCHIP_APBGPIO, "APBGPIO"}, + {OPENCHIP_APBI2C, "APBI2C"}, + {OPENCHIP_APBSPI, "APBSPI"}, + {OPENCHIP_APBCHARLCD, "APBCHARLCD"}, + {OPENCHIP_APBPWM, "APBPWM"}, + {OPENCHIP_APBPS2, "APBPS2"}, + {OPENCHIP_APBMMCSD, "APBMMCSD"}, + {OPENCHIP_APBNAND, "APBNAND"}, + {OPENCHIP_APBLPC, "APBLPC"}, + {OPENCHIP_APBCF, "APBCF"}, + {OPENCHIP_APBSYSACE, "APBSYSACE"}, + {OPENCHIP_APB1WIRE, "APB1WIRE"}, + {OPENCHIP_APBJTAG, "APBJTAG"}, + {OPENCHIP_APBSUI, "APBSUI"}, + {0, NULL} +}; + +static ambapp_device_name OPENCORES_devices[] = +{ + {0, NULL} +}; + +static ambapp_device_name CONTRIB_devices[] = +{ + {CONTRIB_CORE1, "CORE1"}, + {CONTRIB_CORE2, "CORE2"}, + {0, NULL} +}; + +static ambapp_device_name EONIC_devices[] = +{ + {0, NULL} +}; + +static ambapp_device_name RADIONOR_devices[] = +{ + {0, NULL} +}; + +static ambapp_device_name GLEICHMANN_devices[] = +{ + {GLEICHMANN_CUSTOM, "CUSTOM"}, + {GLEICHMANN_GEOLCD01, "GEOLCD01"}, + {GLEICHMANN_DAC, "DAC"}, + {GLEICHMANN_HPI, "HPI"}, + {GLEICHMANN_SPI, "SPI"}, + {GLEICHMANN_HIFC, "HIFC"}, + {GLEICHMANN_ADCDAC, "ADCDAC"}, + {GLEICHMANN_SPIOC, "SPIOC"}, + {GLEICHMANN_AC97, "AC97"}, + {0, NULL} +}; + +static ambapp_device_name MENTA_devices[] = +{ + {0, NULL} +}; + +static ambapp_device_name SUN_devices[] = +{ + {SUN_T1, "SUN_T1"}, + {SUN_S1, "SUN_S1"}, + {0, NULL} +}; + +static ambapp_device_name MOVIDIA_devices[] = +{ + {0, NULL} +}; + +static ambapp_device_name ORBITA_devices[] = +{ + {ORBITA_1553B, "1553B"}, + {ORBITA_429, "429"}, + {ORBITA_SPI, "SPI"}, + {ORBITA_I2C, "I2C"}, + {ORBITA_SMARTCARD, "SMARTCARD"}, + {ORBITA_SDCARD, "SDCARD"}, + {ORBITA_UART16550, "UART16550"}, + {ORBITA_CRYPTO, "CRYPTO"}, + {ORBITA_SYSIF, "SYSIF"}, + {ORBITA_PIO, "PIO"}, + {ORBITA_RTC, "RTC"}, + {ORBITA_COLORLCD, "COLORLCD"}, + {ORBITA_PCI, "PCI"}, + {ORBITA_DSP, "DSP"}, + {ORBITA_USBHOST, "USBHOST"}, + {ORBITA_USBDEV, "USBDEV"}, + {0, NULL} +}; + +static ambapp_device_name SYNOPSYS_devices[] = +{ + {0, NULL} +}; + +static ambapp_device_name NASA_devices[] = +{ + {NASA_EP32, "EP32"}, + {0, NULL} +}; + +static ambapp_device_name CAL_devices[] = +{ + {CAL_DDRCTRL, "DDRCTRL"}, + {0, NULL} +}; + +static ambapp_device_name EMBEDDIT_devices[] = +{ + {0, NULL} +}; + +static ambapp_device_name CETON_devices[] = +{ + {0, NULL} +}; + +static ambapp_device_name ACTEL_devices[] = +{ + {ACTEL_COREMP7, "COREMP7"}, + {0, NULL} +}; + +static ambapp_vendor_devnames vendors[] = +{ + {VENDOR_GAISLER, "GAISLER", GAISLER_devices}, + {VENDOR_PENDER, "PENDER", PENDER_devices}, + {VENDOR_ESA, "ESA", ESA_devices}, + {VENDOR_ASTRIUM, "ASTRIUM", ASTRIUM_devices}, + {VENDOR_OPENCHIP, "OPENCHIP", OPENCHIP_devices}, + {VENDOR_OPENCORES, "OPENCORES", OPENCORES_devices}, + {VENDOR_CONTRIB, "CONTRIB", CONTRIB_devices}, + {VENDOR_EONIC, "EONIC", EONIC_devices}, + {VENDOR_RADIONOR, "RADIONOR", RADIONOR_devices}, + {VENDOR_GLEICHMANN, "GLEICHMANN", GLEICHMANN_devices}, + {VENDOR_MENTA, "MENTA", MENTA_devices}, + {VENDOR_SUN, "SUN", SUN_devices}, + {VENDOR_MOVIDIA, "MOVIDIA", MOVIDIA_devices}, + {VENDOR_ORBITA, "ORBITA", ORBITA_devices}, + {VENDOR_SYNOPSYS, "SYNOPSYS", SYNOPSYS_devices}, + {VENDOR_NASA, "NASA", NASA_devices}, + {VENDOR_CAL, "CAL", CAL_devices}, + {VENDOR_EMBEDDIT, "EMBEDDIT", EMBEDDIT_devices}, + {VENDOR_CETON, "CETON", CETON_devices}, + {VENDOR_ACTEL, "ACTEL", ACTEL_devices}, + {0, NULL, NULL} +}; + +/*****************************************************************/ + +static char *ambapp_get_devname(ambapp_device_name *devs, int id) +{ + while (devs->device_id > 0) { + if (devs->device_id == id) + return devs->name; + devs++; + } + return NULL; +} + +char *ambapp_device_id2str(int vendor, int id) +{ + ambapp_vendor_devnames *ven = &vendors[0]; + + while (ven->vendor_id > 0) { + if (ven->vendor_id == vendor) + return ambapp_get_devname(ven->devices,id); + ven++; + } + return NULL; +} + +char *ambapp_vendor_id2str(int vendor) +{ + ambapp_vendor_devnames *ven = &vendors[0]; + + while (ven->vendor_id > 0) { + if (ven->vendor_id == vendor) + return ven->name; + ven++; + } + return NULL; +} + +int ambapp_vendev_id2str(int vendor, int id, char *buf) +{ + char *dstr, *vstr; + + *buf = '\0'; + + vstr = ambapp_vendor_id2str(vendor); + if (vstr == NULL) + return 0; + + dstr = ambapp_device_id2str(vendor, id); + if (dstr == NULL) + return 0; + + strcpy(buf, vstr); + strcat(buf, "_"); + strcat(buf, dstr); + + return strlen(buf); +} diff --git a/c/src/lib/libbsp/sparc/shared/amba/ambapp_old.c b/c/src/lib/libbsp/sparc/shared/amba/ambapp_old.c new file mode 100644 index 0000000000..1b543abde9 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/amba/ambapp_old.c @@ -0,0 +1,106 @@ +/* Old AMBA scanning Interface provided for backwards compability */ +#include <ambapp.h> + +struct ambapp_dev_find_match_arg { + int index; + int count; + int type; + void *dev; +}; + +/* AMBA PP find routines */ +int ambapp_dev_find_match(struct ambapp_dev *dev, int index, void *arg) +{ + struct ambapp_dev_find_match_arg *p = arg; + + if (p->index == 0) { + /* Found controller, stop */ + if (p->type == DEV_APB_SLV) { + *(struct ambapp_apb_info *)p->dev = *DEV_TO_APB(dev); + p->dev = ((struct ambapp_apb_info *)p->dev)+1; + } else { + *(struct ambapp_ahb_info *)p->dev = *DEV_TO_AHB(dev); + p->dev = ((struct ambapp_ahb_info *)p->dev)+1; + } + p->count--; + if (p->count < 1) + return 1; + } else { + p->index--; + } + return 0; +} + +int ambapp_find_apbslvs_next(struct ambapp_bus *abus, int vendor, int device, struct ambapp_apb_info *dev, int index, int maxno) +{ + struct ambapp_dev_find_match_arg arg; + + arg.index = index; + arg.count = maxno; + arg.type = DEV_APB_SLV; /* APB */ + arg.dev = dev; + + ambapp_for_each(abus, (OPTIONS_ALL|OPTIONS_APB_SLVS), vendor, device, + ambapp_dev_find_match, &arg); + + return maxno - arg.count; +} + +int ambapp_find_apbslv(struct ambapp_bus *abus, int vendor, int device, struct ambapp_apb_info *dev) +{ + return ambapp_find_apbslvs_next(abus, vendor, device, dev, 0, 1); +} + +int ambapp_find_apbslv_next(struct ambapp_bus *abus, int vendor, int device, struct ambapp_apb_info *dev, int index) +{ + return ambapp_find_apbslvs_next(abus, vendor, device, dev, index, 1); +} + +int ambapp_find_apbslvs(struct ambapp_bus *abus, int vendor, int device, struct ambapp_apb_info *dev, int maxno) +{ + return ambapp_find_apbslvs_next(abus, vendor, device, dev, 0, maxno); +} + +int ambapp_get_number_apbslv_devices(struct ambapp_bus *abus, int vendor, int device) +{ + return ambapp_dev_count(abus, + (OPTIONS_ALL|OPTIONS_APB_SLVS), + vendor, device); +} + +int ambapp_find_ahbslvs_next(struct ambapp_bus *abus, int vendor, int device, struct ambapp_ahb_info *dev, int index, int maxno) +{ + struct ambapp_dev_find_match_arg arg; + + arg.index = index; + arg.count = maxno; + arg.type = DEV_AHB_SLV; /* AHB SLV */ + arg.dev = dev; + + ambapp_for_each(abus, (OPTIONS_ALL|OPTIONS_AHB_SLVS), vendor, device, + ambapp_dev_find_match, &arg); + + return maxno - arg.count; +} + +int ambapp_find_ahbslv_next(struct ambapp_bus *abus, int vendor, int device, struct ambapp_ahb_info *dev, int index) +{ + return ambapp_find_ahbslvs_next(abus, vendor, device, dev, index, 1); +} + +int ambapp_find_ahbslv(struct ambapp_bus *abus, int vendor, int device, struct ambapp_ahb_info *dev) +{ + return ambapp_find_ahbslvs_next(abus, vendor, device, dev, 0, 1); +} + +int ambapp_find_ahbslvs(struct ambapp_bus *abus, int vendor, int device, struct ambapp_ahb_info *dev, int maxno) +{ + return ambapp_find_ahbslvs_next(abus, vendor, device, dev, 0, maxno); +} + +int ambapp_get_number_ahbslv_devices(struct ambapp_bus *abus, int vendor, int device) +{ + return ambapp_dev_count(abus, + (OPTIONS_ALL|OPTIONS_AHB_SLVS), + vendor, device); +} diff --git a/c/src/lib/libbsp/sparc/shared/amba/ambapp_parent.c b/c/src/lib/libbsp/sparc/shared/amba/ambapp_parent.c new file mode 100644 index 0000000000..44d3b26ea7 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/amba/ambapp_parent.c @@ -0,0 +1,26 @@ +/* + * AMBA Plug & Play routines + * + * COPYRIGHT (c) 2011 + * Aeroflex Gaisler + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id: ambapp_parent.c,v + * + */ + +#include <stdlib.h> +#include <ambapp.h> + +struct ambapp_dev *ambapp_find_parent(struct ambapp_dev *dev) +{ + while (dev->prev) { + if (dev == dev->prev->children) + return dev->prev; + dev = dev->prev; + } + return NULL; +} diff --git a/c/src/lib/libbsp/sparc/shared/amba/ambapp_show.c b/c/src/lib/libbsp/sparc/shared/amba/ambapp_show.c new file mode 100644 index 0000000000..eecca406e9 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/amba/ambapp_show.c @@ -0,0 +1,76 @@ +/* AMBA Plug & Play device information printing. + * + * COPYRIGHT (c) 2009. + * Aeroflex Gaisler. + * + * Prints a short description of APB and AHB devices. + * + * 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. + * + * 2008-12-01, Daniel Hellstrom <daniel@gaisler.com> + * Created + */ + +#include <stdio.h> +#include <ambapp.h> + +extern char *ambapp_device_id2str(int vendor, int id); +extern char *ambapp_vendor_id2str(int vendor); + +struct ambapp_dev_print_arg { + int show_depth; +}; + +static char *unknown = "unknown"; + +int ambapp_dev_print(struct ambapp_dev *dev, int index, void *arg) +{ + char *dev_str, *ven_str, *type_str; + struct ambapp_dev_print_arg *p = arg; + char dp[32]; + int i=0; + unsigned int basereg; + + if (p->show_depth) { + for (i=0; i<ambapp_depth(dev)*2; i+=2) { + dp[i] = ' '; + dp[i+1] = ' '; + } + } + dp[i] = '\0'; + + ven_str = ambapp_vendor_id2str(dev->vendor); + if (!ven_str) { + ven_str = unknown; + dev_str = unknown; + } else { + dev_str = ambapp_device_id2str(dev->vendor,dev->device); + if (!dev_str) + dev_str = unknown; + } + if (dev->dev_type == DEV_APB_SLV) { + /* APB */ + basereg = DEV_TO_APB(dev)->start; + type_str = "apb"; + } else { + /* AHB */ + basereg = DEV_TO_AHB(dev)->start[0]; + type_str = "ahb"; + } + printf("%s |-> 0x%x:0x%x:0x%x: %s_%s, %s: 0x%x, 0x%x (OWNER: 0x%x)\n", + dp, index, dev->vendor, dev->device, ven_str, dev_str, type_str, + basereg, (unsigned int)dev, (unsigned int)dev->owner); + + return 0; +} + +void ambapp_print(struct ambapp_bus *abus, int show_depth) +{ + struct ambapp_dev_print_arg arg; + arg.show_depth = show_depth; + ambapp_for_each(abus, + (OPTIONS_ALL_DEVS|OPTIONS_ALL|OPTIONS_DEPTH_FIRST), -1, + -1, ambapp_dev_print, &arg); +} 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..6c7b233b1f --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/analog/gradcdac.c @@ -0,0 +1,582 @@ +/* ADC / DAC (GRADCDAC) interface implementation + * + * 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. + * + * 2009-06-01, Daniel Hellstrom <daniel@gaisler.com> + * Created + * + */ + +#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..2323260335 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/ascs/grascs.c @@ -0,0 +1,629 @@ +/* This file contains the GRASCS RTEMS driver + * + * -------------------------------------------------------------------------- + * -- This file is a part of GAISLER RESEARCH source code. + * -- Copyright (C) 2008, Gaisler Research AB - all rights reserved. + * -- + * -- ANY USE OR REDISTRIBUTION IN PART OR IN WHOLE MUST BE HANDLED IN + * -- ACCORDANCE WITH THE GAISLER LICENSE AGREEMENT AND MUST BE APPROVED + * -- IN ADVANCE IN WRITING. + * -- + * -- BY DEFAULT, DISTRIBUTION OR DISCLOSURE IS NOT PERMITTED. + * -------------------------------------------------------------------------- + * + * Created: + * 2008-02-06: Jonas Ekergarn, ekergarn@gaisler.com + * Modified: + * 2009-01-07: Jonas Ekergarn, ekergarn@gaisler.com + * Replaced volatile variables tcactive and tmactive with + * semaphores tcsem2 and tmsem2. Also fixed a return value, a + * typo, renamed tcsem and tmsem to tcsem1 and tmsem1 + * respectively, and corrected a function description. + * + */ + +#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/bspgetworkarea.c b/c/src/lib/libbsp/sparc/shared/bspgetworkarea.c index a9fe1603a1..5be52549f2 100644 --- a/c/src/lib/libbsp/sparc/shared/bspgetworkarea.c +++ b/c/src/lib/libbsp/sparc/shared/bspgetworkarea.c @@ -21,6 +21,10 @@ /* Tells us where to put the workspace in case remote debugger is present. */ extern uint32_t rdb_start; +extern int _end; + +/* Must be aligned to 8, _end is aligned to 8 */ +unsigned int early_mem = (unsigned int)&_end; /* * This method returns the base address and size of the area which @@ -37,8 +41,11 @@ void bsp_get_work_area( /* must be identical to STACK_SIZE in start.S */ #define STACK_SIZE (16 * 1024) - *work_area_start = &end; - *work_area_size = (void *)rdb_start - (void *)&end - STACK_SIZE; + /* Early dynamic memory allocator is placed just above _end */ + *work_area_start = (void *)early_mem; + *work_area_size = (void *)rdb_start - (void *)early_mem - STACK_SIZE; + early_mem = ~0; /* Signal bsp_early_malloc not to be used anymore */ + *heap_start = BSP_BOOTCARD_HEAP_USES_WORK_AREA; *heap_size = BSP_BOOTCARD_HEAP_SIZE_DEFAULT; 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..2f990fe71e --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/can/canmux.c @@ -0,0 +1,202 @@ +/* + * CAN_MUX driver + * + * -------------------------------------------------------------------------- + * -- This file is a part of GAISLER RESEARCH source code. + * -- Copyright (C) 2008, Gaisler Research AB - all rights reserved. + * -- + * -- ANY USE OR REDISTRIBUTION IN PART OR IN WHOLE MUST BE HANDLED IN + * -- ACCORDANCE WITH THE GAISLER LICENSE AGREEMENT AND MUST BE APPROVED + * -- IN ADVANCE IN WRITING. + * -- + * -- BY DEFAULT, DISTRIBUTION OR DISCLOSURE IS NOT PERMITTED. + * -------------------------------------------------------------------------- + * + */ + +#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/grcan.c b/c/src/lib/libbsp/sparc/shared/can/grcan.c index c176490c68..251519ac86 100644 --- a/c/src/lib/libbsp/sparc/shared/can/grcan.c +++ b/c/src/lib/libbsp/sparc/shared/can/grcan.c @@ -1,13 +1,15 @@ /* * GRCAN driver * - * COPYRIGHT (c) 2007. - * Gaisler Research. + * COPYRIGHT (c) 2008. + * Aeroflex Gaisler. * * 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. * + * 2008-12-10, Daniel Hellstrom <daniel@gaisler.com> + * Converted driver to support driver manager * * 2007-06-13, Daniel Hellstrom <daniel@gaisler.com> * New driver in sparc shared directory. Parts taken @@ -25,6 +27,8 @@ #include <rtems/bspIo.h> #include <grcan.h> +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> #include <ambapp.h> #define WRAP_AROUND_TX_MSGS 1 @@ -32,14 +36,17 @@ #define GRCAN_MSG_SIZE sizeof(struct grcan_msg) #define BLOCK_SIZE (16*4) +/* grcan needs to have it buffers aligned to 1k boundaries */ +#define BUFFER_ALIGNMENT_NEEDS 1024 + /* Default Maximium buffer size for statically allocated buffers */ #ifndef TX_BUF_SIZE -#define TX_BUF_SIZE (BLOCK_SIZE*16) + #define TX_BUF_SIZE (BLOCK_SIZE*16) #endif /* Make receiver buffers bigger than transmitt */ #ifndef RX_BUF_SIZE -#define RX_BUF_SIZE ((3*BLOCK_SIZE)*16) + #define RX_BUF_SIZE ((3*BLOCK_SIZE)*16) #endif #ifndef IRQ_GLOBAL_PREPARE @@ -66,35 +73,13 @@ #define IRQ_MASK(irqno) #endif -#ifndef GRCAN_PREFIX - #define GRCAN_PREFIX(name) grcan##name -#endif - -#ifndef MEMAREA_TO_HW - #define MEMAREA_TO_HW(x) (x) -#endif - -/* default name to /dev/grcan0 */ -#if !defined(GRCAN_DEVNAME) || !defined(GRCAN_DEVNAME_NO) - #undef GRCAN_DEVNAME - #undef GRCAN_DEVNAME_NO - #define GRCAN_DEVNAME "/dev/grcan0" - #define GRCAN_DEVNAME_NO(devstr,no) ((devstr)[10]='0'+(no)) -#endif - -#ifndef GRCAN_REG_INT - #define GRCAN_REG_INT(handler,irqno,arg) set_vector(handler,irqno+0x10,1) - #undef GRCAN_DEFINE_INTHANDLER - #define GRCAN_DEFINE_INTHANDLER -#endif - #ifndef GRCAN_DEFAULT_BAUD - /* default to 500kbits/s */ - #define GRCAN_DEFAULT_BAUD 500000 + /* default to 500kbits/s */ + #define GRCAN_DEFAULT_BAUD 500000 #endif #ifndef GRCAN_SAMPLING_POINT -#define GRCAN_SAMPLING_POINT 80 + #define GRCAN_SAMPLING_POINT 80 #endif /* Uncomment for debug output */ @@ -112,63 +97,9 @@ /*********************************************************/ -/* grcan needs to have it buffers aligned to 1k boundaries */ -#define BUFFER_ALIGNMENT_NEEDS 1024 - -#ifdef STATICALLY_ALLOCATED_TX_BUFFER -static unsigned int tx_circbuf[GRCAN_MAX_CORES][TX_BUF_SIZE] - __attribute__ ((aligned(BUFFER_ALIGNMENT_NEEDS))); -#define STATIC_TX_BUF_SIZE TX_BUF_SIZE -#define STATIC_TX_BUF_ADDR(core) (&tx_circbuf[(core)][0]) -#endif - -#ifdef STATICALLY_ALLOCATED_RX_BUFFER -static unsigned int rx_circbuf[GRCAN_MAX_CORES][RX_BUF_SIZE] - __attribute__ ((aligned(BUFFER_ALIGNMENT_NEEDS))); -#define STATIC_RX_BUF_SIZE RX_BUF_SIZE -#define STATIC_RX_BUF_ADDR(core) (&rx_circbuf[(core)][0]) -#endif - -/* - * If USE_AT697_RAM is defined the RAM on the AT697 board will be used for DMA buffers (but rx message queue is always in AT697 ram). - * USE_AT697_DMA specifies whether the messages will be fetched using DMA or PIO. - * - * RASTA_PCI_BASE is the base address of the GRPCI AHB slave - * - * GRCAN_BUF_SIZE must be set to the size (in bytes) of the GRCAN DMA buffers. - * - * RX_QUEUE_SIZE defines the number of messages that fits in the RX message queue. On RX interrupts the messages in the DMA buffer - * are copied into the message queue (using dma if the rx buf is not in the AT697 ram). - */ - -/*#define USE_AT697_RAM 1 */ -#define USE_AT697_DMA 1 -#define RASTA_PCI_BASE 0xe0000000 -#define GRCAN_BUF_SIZE 4096 -#define RX_QUEUE_SIZE 1024 - -#define INDEX(x) ( x&(RX_QUEUE_SIZE-1) ) - -/* pa(x) - * - * x: address in AT697 address space - * - * returns the address in the RASTA address space that can be used to access x with dma. - * -*/ -#ifdef USE_AT697_RAM -static inline unsigned int pa(unsigned int addr) { - return ((addr & 0x0fffffff) | RASTA_PCI_BASE); -} -#else -static inline unsigned int pa(unsigned int addr) { - return ((addr & 0x0fffffff) | 0x40000000); -} -#endif - struct grcan_msg { - unsigned int head[2]; - unsigned char data[8]; + unsigned int head[2]; + unsigned char data[8]; }; struct grcan_config { @@ -179,43 +110,41 @@ struct grcan_config { }; struct grcan_priv { - unsigned int baseaddr, ram_base; - struct grcan_regs *regs; - int irq; - int minor; - int open; - int started; - unsigned int channel; - int flushing; - unsigned int corefreq_hz; - - /* Circular DMA buffers */ - void *_rx; - void *_tx; - struct grcan_msg *rx; - struct grcan_msg *tx; - unsigned int rxbuf_size; /* requested RX buf size in bytes */ - unsigned int txbuf_size; /* requested TX buf size in bytes */ - - int txblock, rxblock; - int txcomplete, rxcomplete; - int txerror, rxerror; - - struct grcan_filter sfilter; - struct grcan_filter afilter; - int config_changed; /* 0=no changes, 1=changes ==> a Core reset is needed */ - struct grcan_config config; - struct grcan_stats stats; - - rtems_id rx_sem, tx_sem, txempty_sem, dev_sem; + struct drvmgr_dev *dev; /* Driver manager device */ + char devName[32]; /* Device Name */ + unsigned int baseaddr, ram_base; + struct grcan_regs *regs; + int irq; + int minor; + int open; + int started; + unsigned int channel; + int flushing; + unsigned int corefreq_hz; + + /* Circular DMA buffers */ + void *_rx; + void *_tx; + void *txbuf_adr; + void *rxbuf_adr; + struct grcan_msg *rx; + struct grcan_msg *tx; + unsigned int rxbuf_size; /* requested RX buf size in bytes */ + unsigned int txbuf_size; /* requested TX buf size in bytes */ + + int txblock, rxblock; + int txcomplete, rxcomplete; + int txerror, rxerror; + + struct grcan_filter sfilter; + struct grcan_filter afilter; + int config_changed; /* 0=no changes, 1=changes ==> a Core reset is needed */ + struct grcan_config config; + struct grcan_stats stats; + + rtems_id rx_sem, tx_sem, txempty_sem, dev_sem; }; -static int grcan_core_cnt; -struct grcan_priv *grcans; -static amba_confarea_type *amba_bus; -struct grcan_device_info *grcan_cores; -static int grcan_core_cnt; - static rtems_device_driver grcan_initialize(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); static rtems_device_driver grcan_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); static rtems_device_driver grcan_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); @@ -225,6 +154,8 @@ static rtems_device_driver grcan_ioctl(rtems_device_major_number major, rtems_de #define GRCAN_DRIVER_TABLE_ENTRY { grcan_initialize, grcan_open, grcan_close, grcan_read, grcan_write, grcan_ioctl } +static void __inline__ grcan_hw_reset(struct grcan_regs *regs); + static unsigned int grcan_hw_read_try( struct grcan_priv *pDev, struct grcan_regs *regs, @@ -249,11 +180,7 @@ static void grcan_hw_sync( struct grcan_regs *regs, struct grcan_filter *sfilter); -#ifndef GRCAN_DONT_DECLARE_IRQ_HANDLER -static rtems_isr grcan_interrupt_handler(rtems_vector_number v); -#endif - -static void grcan_interrupt(struct grcan_priv *pDev); +static void grcan_interrupt(void *arg); #ifdef GRCAN_REG_BYPASS_CACHE #define READ_REG(address) _grcan_read_nocache((unsigned int)(address)) @@ -266,12 +193,12 @@ static void grcan_interrupt(struct grcan_priv *pDev); #define READ_DMA_BYTE(address) _grcan_read_nocache_byte((unsigned int)(address)) static unsigned char __inline__ _grcan_read_nocache_byte(unsigned int address) { - unsigned char tmp; - asm(" lduba [%1]1, %0 " - : "=r"(tmp) - : "r"(address) - ); - return tmp; + unsigned char tmp; + __asm__(" lduba [%1]1, %0 " + : "=r"(tmp) + : "r"(address) + ); + return tmp; } #else #define READ_DMA_WORD(address) (*(volatile unsigned int *)(address)) @@ -281,17 +208,228 @@ static unsigned char __inline__ _grcan_read_nocache_byte(unsigned int address) #if defined(GRCAN_REG_BYPASS_CACHE) || defined(GRCAN_DMA_BYPASS_CACHE) static unsigned int __inline__ _grcan_read_nocache(unsigned int address) { - unsigned int tmp; - asm(" lda [%1]1, %0 " - : "=r"(tmp) - : "r"(address) - ); - return tmp; + unsigned int tmp; + __asm__(" lda [%1]1, %0 " + : "=r"(tmp) + : "r"(address) + ); + return tmp; } #endif static rtems_driver_address_table grcan_driver = GRCAN_DRIVER_TABLE_ENTRY; +static int grcan_driver_io_registered = 0; +static rtems_device_major_number grcan_driver_io_major = 0; + +/******************* Driver manager interface ***********************/ + +/* Driver prototypes */ +int grcan_register_io(rtems_device_major_number *m); +int grcan_device_init(struct grcan_priv *pDev); + +int grcan_init2(struct drvmgr_dev *dev); +int grcan_init3(struct drvmgr_dev *dev); + +struct drvmgr_drv_ops grcan_ops = +{ + .init = {NULL, grcan_init2, grcan_init3, NULL}, + .remove = NULL, + .info = NULL +}; + +struct amba_dev_id grcan_ids[] = +{ + {VENDOR_GAISLER, GAISLER_GRCAN}, + {VENDOR_GAISLER, GAISLER_GRHCAN}, + {0, 0} /* Mark end of table */ +}; + +struct amba_drv_info grcan_drv_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_GAISLER_GRCAN_ID, /* Driver ID */ + "GRCAN_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &grcan_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + 0, + }, + &grcan_ids[0] +}; + +void grcan_register_drv (void) +{ + DBG("Registering GRCAN driver\n"); + drvmgr_drv_register(&grcan_drv_info.general); +} + +int grcan_init2(struct drvmgr_dev *dev) +{ + struct grcan_priv *priv; + + DBG("GRCAN[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name); + priv = dev->priv = malloc(sizeof(struct grcan_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 grcan_init3(struct drvmgr_dev *dev) +{ + struct grcan_priv *priv; + char prefix[32]; + rtems_status_code status; + + priv = dev->priv; + + /* Do initialization */ + + if ( grcan_driver_io_registered == 0) { + /* Register the I/O driver only once for all cores */ + if ( grcan_register_io(&grcan_driver_io_major) ) { + /* Failed to register I/O driver */ + dev->priv = NULL; + return DRVMGR_FAIL; + } + + grcan_driver_io_registered = 1; + } + + /* I/O system registered and initialized + * Now we take care of device initialization. + */ + + if ( grcan_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/grcan%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/%sgrcan%d", prefix, dev->minor_bus); + } + + /* Register Device */ + status = rtems_io_register_name(priv->devName, grcan_driver_io_major, dev->minor_drv); + if (status != RTEMS_SUCCESSFUL) { + return DRVMGR_FAIL; + } + + return DRVMGR_OK; +} + +/******************* Driver Implementation ***********************/ + +int grcan_register_io(rtems_device_major_number *m) +{ + rtems_status_code r; + + if ((r = rtems_io_register_driver(0, &grcan_driver, m)) == RTEMS_SUCCESSFUL) { + DBG("GRCAN driver successfully registered, major: %d\n", *m); + } else { + switch(r) { + case RTEMS_TOO_MANY: + printk("GRCAN rtems_io_register_driver failed: RTEMS_TOO_MANY\n"); + return -1; + case RTEMS_INVALID_NUMBER: + printk("GRCAN rtems_io_register_driver failed: RTEMS_INVALID_NUMBER\n"); + return -1; + case RTEMS_RESOURCE_IN_USE: + printk("GRCAN rtems_io_register_driver failed: RTEMS_RESOURCE_IN_USE\n"); + return -1; + default: + printk("GRCAN rtems_io_register_driver failed\n"); + return -1; + } + } + return 0; +} + +int grcan_device_init(struct grcan_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 grcan_regs *)pnpinfo->apb_slv->start; + pDev->minor = pDev->dev->minor_drv; + + /* Get frequency in Hz */ + if ( drvmgr_freq_get(pDev->dev, DEV_APB_SLV, &pDev->corefreq_hz) ) { + return -1; + } + + DBG("GRCAN frequency: %d Hz\n", pDev->corefreq_hz); + + /* Reset Hardware before attaching IRQ handler */ + grcan_hw_reset(pDev->regs); + + /* RX Semaphore created with count = 0 */ + if ( rtems_semaphore_create(rtems_build_name('G', 'C', 'R', '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 ) { + return RTEMS_INTERNAL_ERROR; + } + + /* TX Semaphore created with count = 0 */ + if ( rtems_semaphore_create(rtems_build_name('G', 'C', 'T', '0' + pDev->minor), + 0, + RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE|RTEMS_NO_INHERIT_PRIORITY|\ + RTEMS_LOCAL|RTEMS_NO_PRIORITY_CEILING, + 0, + &pDev->tx_sem) != RTEMS_SUCCESSFUL ) { + return RTEMS_INTERNAL_ERROR; + } + + /* TX Empty Semaphore created with count = 0 */ + if ( rtems_semaphore_create(rtems_build_name('G', 'C', 'E', '0' + pDev->minor), + 0, + RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE|RTEMS_NO_INHERIT_PRIORITY|\ + RTEMS_LOCAL|RTEMS_NO_PRIORITY_CEILING, + 0, + &pDev->txempty_sem) != RTEMS_SUCCESSFUL ) { + return RTEMS_INTERNAL_ERROR; + } + + /* Device Semaphore created with count = 1 */ + if ( rtems_semaphore_create(rtems_build_name('G', 'C', 'A', '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 ) { + return RTEMS_INTERNAL_ERROR; + } + + return 0; +} static void __inline__ grcan_hw_reset(struct grcan_regs *regs) { @@ -318,11 +456,13 @@ static rtems_device_driver grcan_start(struct grcan_priv *pDev) } /* Setup receiver */ - pDev->regs->rx0addr = MEMAREA_TO_HW((unsigned int)pDev->rx); + drvmgr_translate(pDev->dev, 0, 0, (void *)pDev->rx, (void **)&pDev->regs->rx0addr); + /*pDev->regs->rx0addr = MEMAREA_TO_HW((unsigned int)pDev->rx);*/ pDev->regs->rx0size = pDev->rxbuf_size; /* Setup Transmitter */ - pDev->regs->tx0addr = MEMAREA_TO_HW((unsigned int)pDev->tx); + drvmgr_translate(pDev->dev, 0, 0, (void *)pDev->tx, (void **)&pDev->regs->tx0addr); + /*pDev->regs->tx0addr = MEMAREA_TO_HW((unsigned int)pDev->tx);*/ pDev->regs->tx0size = pDev->txbuf_size; /* Setup acceptance filters */ @@ -869,7 +1009,7 @@ static int grcan_wait_txspace( IRQ_GLOBAL_DISABLE(oldLevel); - /*pDev->regs->tx0ctrl = GRCAN_TXCTRL_ENABLE;*/ + pDev->regs->tx0ctrl = GRCAN_TXCTRL_ENABLE; size = READ_REG(&pDev->regs->tx0size); wp = READ_REG(&pDev->regs->tx0wr); @@ -966,847 +1106,663 @@ static int grcan_tx_flush(struct grcan_priv *pDev) static int grcan_alloc_buffers(struct grcan_priv *pDev, int rx, int tx) { - FUNCDBG(); - - if ( tx ) { -#ifdef STATIC_TX_BUF_ADDR - pDev->_tx = STATIC_TX_BUF_ADDR(pDev->minor); - if ( pDev->txbuf_size > STATIC_TX_BUF_SIZE ){ - pDev->txbuf_size = STATIC_TX_BUF_SIZE; - return -1; - } - /* Assume aligned buffer */ - pDev->tx = (struct grcan_msg *)pDev->_tx; -#else - pDev->_tx = malloc(pDev->txbuf_size + BUFFER_ALIGNMENT_NEEDS); - if ( !pDev->_tx ) - return -1; + FUNCDBG(); + + if ( tx ) { + if ( pDev->txbuf_adr ) { + /* User defined address */ + pDev->_tx = pDev->txbuf_adr; + } else { + pDev->_tx = malloc(pDev->txbuf_size + BUFFER_ALIGNMENT_NEEDS); + if ( !pDev->_tx ) + return -1; + } - /* Align TX buffer */ - pDev->tx = (struct grcan_msg *) - (((unsigned int)pDev->_tx + (BUFFER_ALIGNMENT_NEEDS-1)) & - ~(BUFFER_ALIGNMENT_NEEDS-1)); -#endif - } + /* Align TX buffer */ + pDev->tx = (struct grcan_msg *) + (((unsigned int)pDev->_tx + (BUFFER_ALIGNMENT_NEEDS-1)) & + ~(BUFFER_ALIGNMENT_NEEDS-1)); + } - if ( rx ) { -#ifdef STATIC_RX_BUF_ADDR - pDev->_rx = STATIC_RX_BUF_ADDR(pDev->minor); - if ( pDev->rxbuf_size > STATIC_RX_BUF_SIZE ){ - pDev->rxbuf_size = STATIC_RX_BUF_SIZE; - return -1; - } - /* Assume aligned buffer */ - pDev->rx = (struct grcan_msg *)pDev->_rx; -#else - pDev->_rx = malloc(pDev->rxbuf_size + BUFFER_ALIGNMENT_NEEDS); - if ( !pDev->_rx ) - return -1; + if ( rx ) { + if ( pDev->rxbuf_adr ) { + /* User defined address */ + pDev->_rx = pDev->rxbuf_adr; + } else { + pDev->_rx = malloc(pDev->rxbuf_size + BUFFER_ALIGNMENT_NEEDS); + if ( !pDev->_rx ) + return -1; + } - /* Align TX buffer */ - pDev->rx = (struct grcan_msg *) - (((unsigned int)pDev->_rx + (BUFFER_ALIGNMENT_NEEDS-1)) & - ~(BUFFER_ALIGNMENT_NEEDS-1)); -#endif - } - return 0; + /* Align RX buffer */ + pDev->rx = (struct grcan_msg *) + (((unsigned int)pDev->_rx + (BUFFER_ALIGNMENT_NEEDS-1)) & + ~(BUFFER_ALIGNMENT_NEEDS-1)); + } + return 0; } static void grcan_free_buffers(struct grcan_priv *pDev, int rx, int tx) { FUNCDBG(); -#ifndef STATIC_TX_BUF_ADDR if ( tx && pDev->_tx ){ free(pDev->_tx); pDev->_tx = NULL; pDev->tx = NULL; } -#endif -#ifndef STATIC_RX_BUF_ADDR + if ( rx && pDev->_rx ){ free(pDev->_rx); pDev->_rx = NULL; pDev->rx = NULL; } -#endif } -#if 0 -static char *almalloc(int sz) -{ - char *tmp; - tmp = calloc(1,2*sz); - tmp = (char *) (((int)tmp+sz) & ~(sz -1)); - return(tmp); -} -#endif - static rtems_device_driver grcan_initialize( rtems_device_major_number major, rtems_device_minor_number unused, void *arg ) { - int minor; - struct grcan_priv *pDev; - amba_apb_device dev; - rtems_status_code status; - char fs_name[20]; - unsigned int sys_freq_hz; - unsigned int deviceid = GAISLER_GRHCAN; - - printk("grcan_initialize()\n\r"); - - FUNCDBG(); - - /* find GRCAN cores */ - if ( !grcan_cores ) { - grcan_core_cnt = amba_get_number_apbslv_devices(amba_bus,VENDOR_GAISLER,deviceid); - if ( grcan_core_cnt < 1 ){ - deviceid = GAISLER_GRCAN; - grcan_core_cnt = amba_get_number_apbslv_devices(amba_bus,VENDOR_GAISLER,deviceid); - if ( grcan_core_cnt < 1 ) { - DBG("GRCAN: Using AMBA Plug&Play, found %d cores\n",grcan_core_cnt); - return RTEMS_UNSATISFIED; - } - } - DBG("GRCAN: Using AMBA Plug&Play, found %d cores\n",grcan_core_cnt); - } - -#ifdef GRCAN_MAX_CORENR - /* limit number of cores */ - if ( grcan_core_cnt > GRCAN_MAX_CORENR ) - grcan_core_cnt = GRCAN_MAX_CORENR; -#endif - - /* Allocate memory for cores */ - grcans = malloc(grcan_core_cnt * sizeof(struct grcan_priv)); - if ( !grcans ) - return RTEMS_NO_MEMORY; - memset(grcans,0,grcan_core_cnt * sizeof(struct grcan_priv)); - - /* make a local copy of device name */ - strcpy(fs_name,GRCAN_DEVNAME); - - /* Detect System Frequency from initialized timer */ -#ifndef SYS_FREQ_HZ -#if defined(LEON3) - /* LEON3: find timer address via AMBA Plug&Play info */ - { - amba_apb_device gptimer; - LEON3_Timer_Regs_Map *tregs; - - if (amba_find_apbslv (&amba_conf, VENDOR_GAISLER, GAISLER_GPTIMER, &gptimer) - == 1) { - tregs = (LEON3_Timer_Regs_Map *) gptimer.start; - sys_freq_hz = (tregs->scaler_reload + 1) * 1000 * 1000; - DBG("GRCAN: detected %dHZ system frequency\n\r", sys_freq_hz); - } else { - sys_freq_hz = 40000000; /* Default to 40MHz */ - printk("GRCAN: Failed to detect system frequency\n\r"); - } - } -#elif defined(LEON2) - /* LEON2: use hardcoded address to get to timer */ - { - LEON_Register_Map *regs = (LEON_Register_Map *) 0x80000000; - - sys_freq_hz = (regs->Scaler_Reload + 1) * 1000 * 1000; - } -#else -#error CPU not supported by driver -#endif -#else - /* Use hardcoded frequency */ - sys_freq_hz = SYS_FREQ_HZ; -#endif - - for(minor=0; minor<grcan_core_cnt; minor++){ - - pDev = &grcans[minor]; - pDev->minor = minor; - pDev->open = 0; - pDev->corefreq_hz = sys_freq_hz; - GRCAN_DEVNAME_NO(fs_name,minor); - - /* Find core address & IRQ */ - if ( !grcan_cores ) { - amba_find_next_apbslv(amba_bus,VENDOR_GAISLER,deviceid,&dev,minor); - pDev->irq = dev.irq; - pDev->regs = (struct grcan_regs *)dev.start; - }else{ - pDev->irq = grcan_cores[minor].irq; - pDev->regs = (struct grcan_regs *)grcan_cores[minor].base_address; - } - - printk("Registering GRCAN core at [0x%x] irq %d, minor %d as %s\n\r",pDev->regs,pDev->irq,minor,fs_name); - - status = rtems_io_register_name(fs_name, major, 0); - if (status != RTEMS_SUCCESSFUL) - rtems_fatal_error_occurred(status); - - /* Reset Hardware before attaching IRQ handler */ - grcan_hw_reset(pDev->regs); - - /* Register interrupt handler */ - GRCAN_REG_INT(GRCAN_PREFIX(_interrupt_handler), pDev->irq+GRCAN_IRQ_IRQ, pDev); - /* - GRCAN_REG_INT(grcan_interrupt_handler, pDev->irq+GRCAN_IRQ_TXSYNC, pDev); - GRCAN_REG_INT(grcan_interrupt_handler, pDev->irq+GRCAN_IRQ_RXSYNC, pDev); - */ - - /* RX Semaphore created with count = 0 */ - if ( rtems_semaphore_create(rtems_build_name('G', 'C', 'R', '0'+minor), - 0, - RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE|RTEMS_NO_INHERIT_PRIORITY|\ - RTEMS_LOCAL|RTEMS_NO_PRIORITY_CEILING, - 0, - &pDev->rx_sem) != RTEMS_SUCCESSFUL ) - return RTEMS_INTERNAL_ERROR; - - /* TX Semaphore created with count = 0 */ - if ( rtems_semaphore_create(rtems_build_name('G', 'C', 'T', '0'+minor), - 0, - RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE|RTEMS_NO_INHERIT_PRIORITY|\ - RTEMS_LOCAL|RTEMS_NO_PRIORITY_CEILING, - 0, - &pDev->tx_sem) != RTEMS_SUCCESSFUL ) - return RTEMS_INTERNAL_ERROR; - - /* TX Empty Semaphore created with count = 0 */ - if ( rtems_semaphore_create(rtems_build_name('G', 'C', 'E', '0'+minor), - 0, - RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE|RTEMS_NO_INHERIT_PRIORITY|\ - RTEMS_LOCAL|RTEMS_NO_PRIORITY_CEILING, - 0, - &pDev->txempty_sem) != RTEMS_SUCCESSFUL ) - return RTEMS_INTERNAL_ERROR; - - /* Device Semaphore created with count = 1 */ - if ( rtems_semaphore_create(rtems_build_name('G', 'C', 'A', '0'+minor), - 1, - RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE|RTEMS_NO_INHERIT_PRIORITY|\ - RTEMS_LOCAL|RTEMS_NO_PRIORITY_CEILING, - 0, - &pDev->dev_sem) != RTEMS_SUCCESSFUL ) - return RTEMS_INTERNAL_ERROR; - } - - return RTEMS_SUCCESSFUL; + return RTEMS_SUCCESSFUL; } -static rtems_device_driver grcan_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) { - struct grcan_priv *pDev; - rtems_device_driver ret; - - FUNCDBG(); +static rtems_device_driver grcan_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + struct grcan_priv *pDev; + rtems_device_driver ret; + struct drvmgr_dev *dev; + union drvmgr_key_value *value; - if ( (minor < 0) || (minor>=grcan_core_cnt) ) { - DBG("Wrong minor %d\n", minor); - return RTEMS_INVALID_NUMBER; - } + FUNCDBG(); - pDev = &grcans[minor]; + if ( drvmgr_get_dev(&grcan_drv_info.general, minor, &dev) ) { + DBG("Wrong minor %d\n", minor); + return RTEMS_INVALID_NAME; + } + pDev = (struct grcan_priv *)dev->priv; - /* Wait until we get semaphore */ - if ( rtems_semaphore_obtain(pDev->dev_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT) != - RTEMS_SUCCESSFUL ){ - return RTEMS_INTERNAL_ERROR; - } + /* Wait until we get semaphore */ + if (rtems_semaphore_obtain(pDev->dev_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT) + != RTEMS_SUCCESSFUL) { + return RTEMS_INTERNAL_ERROR; + } - /* is device busy/taken? */ - if ( pDev->open ) { - ret=RTEMS_RESOURCE_IN_USE; - goto out; - } + /* is device busy/taken? */ + if ( pDev->open ) { + ret=RTEMS_RESOURCE_IN_USE; + goto out; + } - /* Mark device taken */ - pDev->open = 1; - - pDev->txblock = pDev->rxblock = 1; - pDev->txcomplete = pDev->rxcomplete = 0; - pDev->started = 0; - pDev->config_changed = 1; - pDev->config.silent = 0; - pDev->config.abort = 0; - pDev->config.selection.selection = 0; - pDev->config.selection.enable0 = 0; - pDev->config.selection.enable1 = 1; - pDev->flushing = 0; - pDev->rx = pDev->_rx = NULL; - pDev->tx = pDev->_tx = NULL; - pDev->txbuf_size = TX_BUF_SIZE; - pDev->rxbuf_size = RX_BUF_SIZE; - printk("Defaulting to rxbufsize: %d, txbufsize: %d\n",RX_BUF_SIZE,TX_BUF_SIZE); - - /* Default to accept all messages */ - pDev->afilter.mask = 0x00000000; - pDev->afilter.code = 0x00000000; - - /* Default to disable sync messages (only trigger when id is set to all ones) */ - pDev->sfilter.mask = 0xffffffff; - pDev->sfilter.code = 0x00000000; - - /* Calculate default timing register values */ - grcan_calc_timing(GRCAN_DEFAULT_BAUD,pDev->corefreq_hz,GRCAN_SAMPLING_POINT,&pDev->config.timing); - - if ( grcan_alloc_buffers(pDev,1,1) ) { - ret=RTEMS_NO_MEMORY; - goto out; - } + /* Mark device taken */ + pDev->open = 1; + + pDev->txblock = pDev->rxblock = 1; + pDev->txcomplete = pDev->rxcomplete = 0; + pDev->started = 0; + pDev->config_changed = 1; + pDev->config.silent = 0; + pDev->config.abort = 0; + pDev->config.selection.selection = 0; + pDev->config.selection.enable0 = 0; + pDev->config.selection.enable1 = 1; + pDev->flushing = 0; + pDev->rx = pDev->_rx = NULL; + pDev->tx = pDev->_tx = NULL; + pDev->txbuf_adr = 0; + pDev->rxbuf_adr = 0; + pDev->txbuf_size = TX_BUF_SIZE; + pDev->rxbuf_size = RX_BUF_SIZE; + + /* Override default buffer sizes if available from bus resource */ + value = drvmgr_dev_key_get(pDev->dev, "txBufSize", KEY_TYPE_INT); + if ( value ) + pDev->txbuf_size = value->i; + + value = drvmgr_dev_key_get(pDev->dev, "rxBufSize", KEY_TYPE_INT); + if ( value ) + pDev->rxbuf_size = value->i; + + value = drvmgr_dev_key_get(pDev->dev, "txBufAdr", KEY_TYPE_POINTER); + if ( value ) + pDev->txbuf_adr = value->ptr; + + value = drvmgr_dev_key_get(pDev->dev, "rxBufAdr", KEY_TYPE_POINTER); + if ( value ) + pDev->rxbuf_adr = value->ptr; + + DBG("Defaulting to rxbufsize: %d, txbufsize: %d\n",RX_BUF_SIZE,TX_BUF_SIZE); + + /* Default to accept all messages */ + pDev->afilter.mask = 0x00000000; + pDev->afilter.code = 0x00000000; + + /* Default to disable sync messages (only trigger when id is set to all ones) */ + pDev->sfilter.mask = 0xffffffff; + pDev->sfilter.code = 0x00000000; + + /* Calculate default timing register values */ + grcan_calc_timing(GRCAN_DEFAULT_BAUD,pDev->corefreq_hz,GRCAN_SAMPLING_POINT,&pDev->config.timing); + + if ( grcan_alloc_buffers(pDev,1,1) ) { + ret=RTEMS_NO_MEMORY; + goto out; + } - /* Clear statistics */ - memset(&pDev->stats,0,sizeof(struct grcan_stats)); + /* Clear statistics */ + memset(&pDev->stats,0,sizeof(struct grcan_stats)); - ret = RTEMS_SUCCESSFUL; + ret = RTEMS_SUCCESSFUL; out: - rtems_semaphore_release(pDev->dev_sem); - return ret; + rtems_semaphore_release(pDev->dev_sem); + return ret; } static rtems_device_driver grcan_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) { - struct grcan_priv *pDev = &grcans[minor]; + struct grcan_priv *pDev; + struct drvmgr_dev *dev; - FUNCDBG(); + FUNCDBG(); - if ( pDev->started ) - grcan_stop(pDev); + if ( drvmgr_get_dev(&grcan_drv_info.general, minor, &dev) ) { + return RTEMS_INVALID_NAME; + } + pDev = (struct grcan_priv *)dev->priv; - grcan_hw_reset(pDev->regs); + if ( pDev->started ) + grcan_stop(pDev); - grcan_free_buffers(pDev,1,1); + grcan_hw_reset(pDev->regs); - /* Mark Device as closed */ - pDev->open = 0; + grcan_free_buffers(pDev,1,1); - return RTEMS_SUCCESSFUL; + /* Mark Device as closed */ + pDev->open = 0; + + return RTEMS_SUCCESSFUL; } static rtems_device_driver grcan_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) { - struct grcan_priv *pDev = &grcans[minor]; - rtems_libio_rw_args_t *rw_args; - CANMsg *dest; - unsigned int count, left; - int req_cnt; + struct grcan_priv *pDev; + struct drvmgr_dev *dev; + rtems_libio_rw_args_t *rw_args; + CANMsg *dest; + unsigned int count, left; + int req_cnt; - rw_args = (rtems_libio_rw_args_t *) arg; - dest = (CANMsg *) rw_args->buffer; - req_cnt = rw_args->count / sizeof(CANMsg); + FUNCDBG(); - FUNCDBG(); + if ( drvmgr_get_dev(&grcan_drv_info.general, minor, &dev) ) { + return RTEMS_INVALID_NAME; + } + pDev = (struct grcan_priv *)dev->priv; - if ( (!dest) || (req_cnt<1) ) - return RTEMS_INVALID_NAME; + rw_args = (rtems_libio_rw_args_t *) arg; + dest = (CANMsg *) rw_args->buffer; + req_cnt = rw_args->count / sizeof(CANMsg); - if ( !pDev->started ) - return RTEMS_RESOURCE_IN_USE; + if ( (!dest) || (req_cnt<1) ) + return RTEMS_INVALID_NAME; -/* FUNCDBG("grcan_read [%i,%i]: buf: 0x%x len: %i\n",major, minor, (unsigned int)rw_args->buffer,rw_args->count);*/ + if ( !pDev->started ) + return RTEMS_RESOURCE_IN_USE; - count = grcan_hw_read_try(pDev,pDev->regs,dest,req_cnt); - if ( !( pDev->rxblock && pDev->rxcomplete && (count!=req_cnt) ) ){ - if ( count > 0 ) { - /* Successfully received messages (at least one) */ - rw_args->bytes_moved = count * sizeof(CANMsg); - return RTEMS_SUCCESSFUL; - } + /*FUNCDBG("grcan_read [%i,%i]: buf: 0x%x len: %i\n",major, minor, (unsigned int)rw_args->buffer,rw_args->count);*/ - /* nothing read, shall we block? */ - if ( !pDev->rxblock ) { - /* non-blocking mode */ - rw_args->bytes_moved = 0; - return RTEMS_TIMEOUT; - } - } + count = grcan_hw_read_try(pDev,pDev->regs,dest,req_cnt); + if ( !( pDev->rxblock && pDev->rxcomplete && (count!=req_cnt) ) ){ + if ( count > 0 ) { + /* Successfully received messages (at least one) */ + rw_args->bytes_moved = count * sizeof(CANMsg); + return RTEMS_SUCCESSFUL; + } - while(count == 0 || (pDev->rxcomplete && (count!=req_cnt)) ){ + /* nothing read, shall we block? */ + if ( !pDev->rxblock ) { + /* non-blocking mode */ + rw_args->bytes_moved = 0; + return RTEMS_TIMEOUT; + } + } - if ( !pDev->rxcomplete ){ - left = 1; /* return as soon as there is one message available */ - }else{ - left = req_cnt - count; /* return as soon as all data are available */ - - /* never wait for more than the half the maximum size of the receive buffer - * Why? We need some time to copy buffer before to catch up with hw, otherwise - * we would have to copy everything when the data has been received. - */ - if ( left > ((pDev->rxbuf_size/GRCAN_MSG_SIZE)/2) ){ - left = (pDev->rxbuf_size/GRCAN_MSG_SIZE)/2; - } - } + while(count == 0 || (pDev->rxcomplete && (count!=req_cnt)) ){ - if ( grcan_wait_rxdata(pDev,left) ) { - /* The wait has been aborted, probably due to - * the device driver has been closed by another - * thread. - */ - rw_args->bytes_moved = count * sizeof(CANMsg); - return RTEMS_UNSATISFIED; - } + if ( !pDev->rxcomplete ){ + left = 1; /* return as soon as there is one message available */ + }else{ + left = req_cnt - count; /* return as soon as all data are available */ - /* Try read bytes from circular buffer */ - count += grcan_hw_read_try( - pDev, - pDev->regs, - dest+count, - req_cnt-count); - } - /* no need to unmask IRQ as IRQ Handler do that for us. */ - rw_args->bytes_moved = count * sizeof(CANMsg); - return RTEMS_SUCCESSFUL; + /* never wait for more than the half the maximum size of the receive buffer + * Why? We need some time to copy buffer before to catch up with hw, + * otherwise we would have to copy everything when the data has been + * received. + */ + if ( left > ((pDev->rxbuf_size/GRCAN_MSG_SIZE)/2) ){ + left = (pDev->rxbuf_size/GRCAN_MSG_SIZE)/2; + } + } + + if ( grcan_wait_rxdata(pDev,left) ) { + /* The wait has been aborted, probably due to + * the device driver has been closed by another + * thread. + */ + rw_args->bytes_moved = count * sizeof(CANMsg); + return RTEMS_UNSATISFIED; + } + + /* Try read bytes from circular buffer */ + count += grcan_hw_read_try( + pDev, + pDev->regs, + dest+count, + req_cnt-count); + } + /* no need to unmask IRQ as IRQ Handler do that for us. */ + rw_args->bytes_moved = count * sizeof(CANMsg); + return RTEMS_SUCCESSFUL; } static rtems_device_driver grcan_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) { - struct grcan_priv *pDev = &grcans[minor]; - rtems_libio_rw_args_t *rw_args; - CANMsg *source; - unsigned int count, left; - int req_cnt; + struct grcan_priv *pDev; + struct drvmgr_dev *dev; + rtems_libio_rw_args_t *rw_args; + CANMsg *source; + unsigned int count, left; + int req_cnt; - DBGC(DBG_TX,"\n"); - /*FUNCDBG();*/ + DBGC(DBG_TX,"\n"); - if ( !pDev->started || pDev->config.silent || pDev->flushing ) - return RTEMS_RESOURCE_IN_USE; + if ( drvmgr_get_dev(&grcan_drv_info.general, minor, &dev) ) { + return RTEMS_INVALID_NAME; + } + pDev = (struct grcan_priv *)dev->priv; - rw_args = (rtems_libio_rw_args_t *) arg; - req_cnt = rw_args->count / sizeof(CANMsg); - source = (CANMsg *) rw_args->buffer; + if ( !pDev->started || pDev->config.silent || pDev->flushing ) + return RTEMS_RESOURCE_IN_USE; - /* check proper length and buffer pointer */ - if (( req_cnt < 1) || (source == NULL) ){ - return RTEMS_INVALID_NAME; - } + rw_args = (rtems_libio_rw_args_t *) arg; + req_cnt = rw_args->count / sizeof(CANMsg); + source = (CANMsg *) rw_args->buffer; - count = grcan_hw_write_try(pDev,pDev->regs,source,req_cnt); - if ( !(pDev->txblock && pDev->txcomplete && (count!=req_cnt)) ) { - if ( count > 0 ) { - /* Successfully transmitted chars (at least one char) */ - rw_args->bytes_moved = count * sizeof(CANMsg); - return RTEMS_SUCCESSFUL; - } + /* check proper length and buffer pointer */ + if (( req_cnt < 1) || (source == NULL) ){ + return RTEMS_INVALID_NAME; + } - /* nothing written, shall we block? */ - if ( !pDev->txblock ) { - /* non-blocking mode */ - rw_args->bytes_moved = 0; - return RTEMS_TIMEOUT; - } - } + count = grcan_hw_write_try(pDev,pDev->regs,source,req_cnt); + if ( !(pDev->txblock && pDev->txcomplete && (count!=req_cnt)) ) { + if ( count > 0 ) { + /* Successfully transmitted chars (at least one char) */ + rw_args->bytes_moved = count * sizeof(CANMsg); + return RTEMS_SUCCESSFUL; + } - /* if in txcomplete mode we need to transmit all chars */ - while((count == 0) || (pDev->txcomplete && (count!=req_cnt)) ){ - /*** block until room to fit all or as much of transmit buffer as possible IRQ comes - * Set up a valid IRQ point so that an IRQ is received - * when we can put a chunk of data into transmit fifo - */ - if ( !pDev->txcomplete ){ - left = 1; /* wait for anything to fit buffer */ - }else{ - left = req_cnt - count; /* wait for all data to fit in buffer */ + /* nothing written, shall we block? */ + if ( !pDev->txblock ) { + /* non-blocking mode */ + rw_args->bytes_moved = 0; + return RTEMS_TIMEOUT; + } + } - /* never wait for more than the half the maximum size of the transmitt buffer - * Why? We need some time to fill buffer before hw catches up. - */ - if ( left > ((pDev->txbuf_size/GRCAN_MSG_SIZE)/2) ){ - left = (pDev->txbuf_size/GRCAN_MSG_SIZE)/2; - } - } + /* if in txcomplete mode we need to transmit all chars */ + while((count == 0) || (pDev->txcomplete && (count!=req_cnt)) ){ + /*** block until room to fit all or as much of transmit buffer as possible + * IRQ comes. Set up a valid IRQ point so that an IRQ is received + * when we can put a chunk of data into transmit fifo + */ + if ( !pDev->txcomplete ){ + left = 1; /* wait for anything to fit buffer */ + }else{ + left = req_cnt - count; /* wait for all data to fit in buffer */ + + /* never wait for more than the half the maximum size of the transmit + * buffer + * Why? We need some time to fill buffer before hw catches up. + */ + if ( left > ((pDev->txbuf_size/GRCAN_MSG_SIZE)/2) ){ + left = (pDev->txbuf_size/GRCAN_MSG_SIZE)/2; + } + } - /* Wait until more room in transmit buffer */ - if ( grcan_wait_txspace(pDev,left) ){ - /* The wait has been aborted, probably due to - * the device driver has been closed by another - * thread. To avoid deadlock we return directly - * with error status. - */ - rw_args->bytes_moved = count * sizeof(CANMsg); - return RTEMS_UNSATISFIED; - } + /* Wait until more room in transmit buffer */ + if ( grcan_wait_txspace(pDev,left) ){ + /* The wait has been aborted, probably due to + * the device driver has been closed by another + * thread. To avoid deadlock we return directly + * with error status. + */ + rw_args->bytes_moved = count * sizeof(CANMsg); + return RTEMS_UNSATISFIED; + } - if ( pDev->txerror ){ - /* Return number of bytes sent, compare write pointers */ - pDev->txerror = 0; -#if 0 + if ( pDev->txerror ){ + /* Return number of bytes sent, compare write pointers */ + pDev->txerror = 0; +#if 0 #error HANDLE AMBA error #endif - } + } - /* Try read bytes from circular buffer */ - count += grcan_hw_write_try( - pDev, - pDev->regs, - source+count, - req_cnt-count); - } - /* no need to unmask IRQ as IRQ Handler do that for us. */ + /* Try read bytes from circular buffer */ + count += grcan_hw_write_try( + pDev, + pDev->regs, + source+count, + req_cnt-count); + } + /* no need to unmask IRQ as IRQ Handler do that for us. */ - rw_args->bytes_moved = count * sizeof(CANMsg); - return RTEMS_SUCCESSFUL; + rw_args->bytes_moved = count * sizeof(CANMsg); + return RTEMS_SUCCESSFUL; } static rtems_device_driver grcan_ioctl(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) { - struct grcan_priv *pDev = &grcans[minor]; - rtems_libio_ioctl_args_t *ioarg = (rtems_libio_ioctl_args_t *)arg; - unsigned int *data = ioarg->buffer; - struct grcan_timing timing; - unsigned int speed; - struct grcan_selection *selection; - int tmp,ret; - rtems_device_driver status; - struct grcan_stats *stats; - struct grcan_filter *filter; - IRQ_GLOBAL_PREPARE(oldLevel); + struct grcan_priv *pDev; + struct drvmgr_dev *dev; + rtems_libio_ioctl_args_t *ioarg = (rtems_libio_ioctl_args_t *)arg; + unsigned int *data = ioarg->buffer; + struct grcan_timing timing; + unsigned int speed; + struct grcan_selection *selection; + int tmp,ret; + rtems_device_driver status; + struct grcan_stats *stats; + struct grcan_filter *filter; + IRQ_GLOBAL_PREPARE(oldLevel); + + FUNCDBG(); + + if ( drvmgr_get_dev(&grcan_drv_info.general, minor, &dev) ) { + return RTEMS_INVALID_NAME; + } + pDev = (struct grcan_priv *)dev->priv; - FUNCDBG(); + if (!ioarg) + return RTEMS_INVALID_NAME; - if (!ioarg) - return RTEMS_INVALID_NAME; + ioarg->ioctl_return = 0; + switch(ioarg->command) { + case GRCAN_IOC_START: + if ( pDev->started ) + return RTEMS_RESOURCE_IN_USE; /* EBUSY */ - ioarg->ioctl_return = 0; - switch(ioarg->command) { - case GRCAN_IOC_START: - if ( pDev->started ) - return RTEMS_RESOURCE_IN_USE; /* EBUSY */ + if ( (status=grcan_start(pDev)) != RTEMS_SUCCESSFUL ){ + return status; + } + /* Read and write are now open... */ + pDev->started = 1; - if ( (status=grcan_start(pDev)) != RTEMS_SUCCESSFUL ){ - return status; - } - /* Read and write are now open... */ - pDev->started = 1; - break; + /* Register interrupt routine and enable IRQ at IRQ ctrl */ + drvmgr_interrupt_register(dev, 0, "grcan", grcan_interrupt, pDev); - case GRCAN_IOC_STOP: - if ( !pDev->started ) - return RTEMS_RESOURCE_IN_USE; + break; - grcan_stop(pDev); - pDev->started = 0; - break; + case GRCAN_IOC_STOP: + if ( !pDev->started ) + return RTEMS_RESOURCE_IN_USE; - case GRCAN_IOC_ISSTARTED: - if ( !pDev->started ) - return RTEMS_RESOURCE_IN_USE; - break; + /* Disable interrupts */ + drvmgr_interrupt_unregister(dev, 0, grcan_interrupt, pDev); - case GRCAN_IOC_FLUSH: - if ( !pDev->started || pDev->flushing || pDev->config.silent ) - return RTEMS_RESOURCE_IN_USE; - - pDev->flushing = 1; - tmp = grcan_tx_flush(pDev); - pDev->flushing = 0; - if ( tmp ) { - /* The wait has been aborted, probably due to - * the device driver has been closed by another - * thread. - */ - return RTEMS_UNSATISFIED; - } - break; + grcan_stop(pDev); + pDev->started = 0; + break; + + case GRCAN_IOC_ISSTARTED: + if ( !pDev->started ) + return RTEMS_RESOURCE_IN_USE; + break; + + case GRCAN_IOC_FLUSH: + if ( !pDev->started || pDev->flushing || pDev->config.silent ) + return RTEMS_RESOURCE_IN_USE; + + pDev->flushing = 1; + tmp = grcan_tx_flush(pDev); + pDev->flushing = 0; + if ( tmp ) { + /* The wait has been aborted, probably due to + * the device driver has been closed by another + * thread. + */ + return RTEMS_UNSATISFIED; + } + break; #if 0 - /* Set physical link */ + /* Set physical link */ case GRCAN_IOC_SET_LINK: #ifdef REDUNDANT_CHANNELS - if ( pDev->started ) - return RTEMS_RESOURCE_IN_USE; /* EBUSY */ + if ( pDev->started ) + return RTEMS_RESOURCE_IN_USE; /* EBUSY */ - /* switch HW channel */ - pDev->channel = (unsigned int)ioargs->buffer; + /* switch HW channel */ + pDev->channel = (unsigned int)ioargs->buffer; #else - return RTEMS_NOT_IMPLEMENTED; + return RTEMS_NOT_IMPLEMENTED; #endif - break; + break; #endif - case GRCAN_IOC_SET_SILENT: - if ( pDev->started ) - return RTEMS_RESOURCE_IN_USE; - pDev->config.silent = (int)ioarg->buffer; - pDev->config_changed = 1; - break; - - case GRCAN_IOC_SET_ABORT: - if ( pDev->started ) - return RTEMS_RESOURCE_IN_USE; - pDev->config.abort = (int)ioarg->buffer; - /* This Configuration parameter doesn't need HurriCANe reset - * ==> no pDev->config_changed = 1; - */ - break; - - case GRCAN_IOC_SET_SELECTION: - if ( pDev->started ) - return RTEMS_RESOURCE_IN_USE; - - selection = (struct grcan_selection *)ioarg->buffer; - if ( !selection ) - return RTEMS_INVALID_NAME; - - pDev->config.selection = *selection; - pDev->config_changed = 1; - break; - - case GRCAN_IOC_SET_RXBLOCK: - pDev->rxblock = (int)ioarg->buffer; - break; - - case GRCAN_IOC_SET_TXBLOCK: - pDev->txblock = (int)ioarg->buffer; - break; - - case GRCAN_IOC_SET_TXCOMPLETE: - pDev->txcomplete = (int)ioarg->buffer; - break; - - case GRCAN_IOC_SET_RXCOMPLETE: - pDev->rxcomplete = (int)ioarg->buffer; - break; - - case GRCAN_IOC_GET_STATS: - stats = (struct grcan_stats *)ioarg->buffer; - if ( !stats ) - return RTEMS_INVALID_NAME; - *stats = pDev->stats; - break; - - case GRCAN_IOC_CLR_STATS: - IRQ_GLOBAL_DISABLE(oldLevel); - memset(&pDev->stats,0,sizeof(struct grcan_stats)); - IRQ_GLOBAL_ENABLE(oldLevel); - break; + case GRCAN_IOC_SET_SILENT: + if ( pDev->started ) + return RTEMS_RESOURCE_IN_USE; + pDev->config.silent = (int)ioarg->buffer; + pDev->config_changed = 1; + break; + + case GRCAN_IOC_SET_ABORT: + if ( pDev->started ) + return RTEMS_RESOURCE_IN_USE; + pDev->config.abort = (int)ioarg->buffer; + /* This Configuration parameter doesn't need HurriCANe reset + * ==> no pDev->config_changed = 1; + */ + break; + + case GRCAN_IOC_SET_SELECTION: + if ( pDev->started ) + return RTEMS_RESOURCE_IN_USE; + + selection = (struct grcan_selection *)ioarg->buffer; + if ( !selection ) + return RTEMS_INVALID_NAME; + + pDev->config.selection = *selection; + pDev->config_changed = 1; + break; + + case GRCAN_IOC_SET_RXBLOCK: + pDev->rxblock = (int)ioarg->buffer; + break; + + case GRCAN_IOC_SET_TXBLOCK: + pDev->txblock = (int)ioarg->buffer; + break; + + case GRCAN_IOC_SET_TXCOMPLETE: + pDev->txcomplete = (int)ioarg->buffer; + break; + + case GRCAN_IOC_SET_RXCOMPLETE: + pDev->rxcomplete = (int)ioarg->buffer; + break; + + case GRCAN_IOC_GET_STATS: + stats = (struct grcan_stats *)ioarg->buffer; + if ( !stats ) + return RTEMS_INVALID_NAME; + *stats = pDev->stats; + break; + + case GRCAN_IOC_CLR_STATS: + IRQ_GLOBAL_DISABLE(oldLevel); + memset(&pDev->stats,0,sizeof(struct grcan_stats)); + IRQ_GLOBAL_ENABLE(oldLevel); + break; case GRCAN_IOC_SET_SPEED: - - /* cannot change speed during run mode */ - if ( pDev->started ) - return RTEMS_RESOURCE_IN_USE; /* EBUSY */ - - /* get speed rate from argument */ - speed = (unsigned int)ioarg->buffer; - ret = grcan_calc_timing(speed,pDev->corefreq_hz,GRCAN_SAMPLING_POINT,&timing); - if ( ret ) - return RTEMS_INVALID_NAME; /* EINVAL */ - - /* save timing/speed */ - pDev->config.timing = timing; - pDev->config_changed = 1; - break; - - case GRCAN_IOC_SET_BTRS: - /* Set BTR registers manually - * Read GRCAN/HurriCANe Manual. - */ - if ( pDev->started ) - return RTEMS_RESOURCE_IN_USE; /* EBUSY */ - - if ( !ioarg->buffer ) - return RTEMS_INVALID_NAME; - - pDev->config.timing = *(struct grcan_timing *)ioarg->buffer; - pDev->config_changed = 1; - break; - - case GRCAN_IOC_SET_AFILTER: - filter = (struct grcan_filter *)ioarg->buffer; - if ( !filter ){ - /* Disable filtering - let all messages pass */ - pDev->afilter.mask = 0x0; - pDev->afilter.code = 0x0; - }else{ - /* Save filter */ - pDev->afilter = *filter; - } - /* Set hardware acceptance filter */ - grcan_hw_accept(pDev->regs,&pDev->afilter); - break; - - case GRCAN_IOC_SET_SFILTER: - filter = (struct grcan_filter *)ioarg->buffer; - if ( !filter ){ - /* disable TX/RX SYNC filtering */ - pDev->sfilter.mask = 0xffffffff; - pDev->sfilter.mask = 0; - - /* disable Sync interrupt */ - pDev->regs->imr = READ_REG(&pDev->regs->imr) & ~(GRCAN_RXSYNC_IRQ|GRCAN_TXSYNC_IRQ); - }else{ - /* Save filter */ - pDev->sfilter = *filter; - - /* Enable Sync interrupt */ - pDev->regs->imr = READ_REG(&pDev->regs->imr) | (GRCAN_RXSYNC_IRQ|GRCAN_TXSYNC_IRQ); - } - /* Set Sync RX/TX filter */ - grcan_hw_sync(pDev->regs,&pDev->sfilter); - break; - - case GRCAN_IOC_GET_STATUS: - if ( !data ) - return RTEMS_INVALID_NAME; - /* Read out the statsu register from the GRCAN core */ - data[0] = READ_REG(&pDev->regs->stat); - break; - - default: - return RTEMS_NOT_DEFINED; - } - return RTEMS_SUCCESSFUL; + /* cannot change speed during run mode */ + if ( pDev->started ) + return RTEMS_RESOURCE_IN_USE; /* EBUSY */ + + /* get speed rate from argument */ + speed = (unsigned int)ioarg->buffer; + ret = grcan_calc_timing(speed, pDev->corefreq_hz, GRCAN_SAMPLING_POINT, &timing); + if ( ret ) + return RTEMS_INVALID_NAME; /* EINVAL */ + + /* save timing/speed */ + pDev->config.timing = timing; + pDev->config_changed = 1; + break; + + case GRCAN_IOC_SET_BTRS: + /* Set BTR registers manually + * Read GRCAN/HurriCANe Manual. + */ + if ( pDev->started ) + return RTEMS_RESOURCE_IN_USE; /* EBUSY */ + if ( !ioarg->buffer ) + return RTEMS_INVALID_NAME; + + pDev->config.timing = *(struct grcan_timing *)ioarg->buffer; + pDev->config_changed = 1; + break; + + case GRCAN_IOC_SET_AFILTER: + filter = (struct grcan_filter *)ioarg->buffer; + if ( !filter ){ + /* Disable filtering - let all messages pass */ + pDev->afilter.mask = 0x0; + pDev->afilter.code = 0x0; + }else{ + /* Save filter */ + pDev->afilter = *filter; + } + /* Set hardware acceptance filter */ + grcan_hw_accept(pDev->regs,&pDev->afilter); + break; + + case GRCAN_IOC_SET_SFILTER: + filter = (struct grcan_filter *)ioarg->buffer; + if ( !filter ){ + /* disable TX/RX SYNC filtering */ + pDev->sfilter.mask = 0xffffffff; + pDev->sfilter.mask = 0; + + /* disable Sync interrupt */ + pDev->regs->imr = READ_REG(&pDev->regs->imr) & ~(GRCAN_RXSYNC_IRQ|GRCAN_TXSYNC_IRQ); + }else{ + /* Save filter */ + pDev->sfilter = *filter; + + /* Enable Sync interrupt */ + pDev->regs->imr = READ_REG(&pDev->regs->imr) | (GRCAN_RXSYNC_IRQ|GRCAN_TXSYNC_IRQ); + } + /* Set Sync RX/TX filter */ + grcan_hw_sync(pDev->regs,&pDev->sfilter); + break; + + case GRCAN_IOC_GET_STATUS: + if ( !data ) + return RTEMS_INVALID_NAME; + /* Read out the statsu register from the GRCAN core */ + data[0] = READ_REG(&pDev->regs->stat); + break; + + default: + return RTEMS_NOT_DEFINED; + } + return RTEMS_SUCCESSFUL; } -#ifndef GRCAN_DONT_DECLARE_IRQ_HANDLER -/* Find what device caused the IRQ */ -static rtems_isr grcan_interrupt_handler(rtems_vector_number v) -{ - int minor=0; - while ( minor < grcan_core_cnt ){ - if ( (grcans[minor].irq+0x10) == v ){ - grcan_interrupt(&grcans[minor]); - break; - } - } -} -#endif - /* Handle the IRQ */ -static void grcan_interrupt(struct grcan_priv *pDev) +static void grcan_interrupt(void *arg) { - unsigned int status = READ_REG(&pDev->regs->pimsr); - unsigned int canstat = READ_REG(&pDev->regs->stat); + struct grcan_priv *pDev = arg; + unsigned int status = READ_REG(&pDev->regs->pimsr); + unsigned int canstat = READ_REG(&pDev->regs->stat); - /* Spurious IRQ call? */ - if ( !status && !canstat ) - return; + /* Spurious IRQ call? */ + if ( !status && !canstat ) + return; - FUNCDBG(); + FUNCDBG(); - /* Increment number of interrupts counter */ - pDev->stats.ints++; + /* Increment number of interrupts counter */ + pDev->stats.ints++; - if ( (status & GRCAN_ERR_IRQ) || (canstat & GRCAN_STAT_PASS) ){ - /* Error-Passive interrupt */ - pDev->stats.passive_cnt++; - } + if ( (status & GRCAN_ERR_IRQ) || (canstat & GRCAN_STAT_PASS) ){ + /* Error-Passive interrupt */ + pDev->stats.passive_cnt++; + } - if ( (status & GRCAN_OFF_IRQ) || (canstat & GRCAN_STAT_OFF) ){ - /* Bus-off condition interrupt - * The link is brought down by hardware, we wake all threads - * that is blocked in read/write calls and stop futher calls - * to read/write until user has called ioctl(fd,START,0). - */ - pDev->started = 0; - grcan_stop(pDev); /* this mask all IRQ sources */ - status=0x1ffff; /* clear all interrupts */ - goto out; - } + if ( (status & GRCAN_OFF_IRQ) || (canstat & GRCAN_STAT_OFF) ){ + /* Bus-off condition interrupt + * The link is brought down by hardware, we wake all threads + * that is blocked in read/write calls and stop futher calls + * to read/write until user has called ioctl(fd,START,0). + */ + pDev->started = 0; + grcan_stop(pDev); /* this mask all IRQ sources */ + status=0x1ffff; /* clear all interrupts */ + goto out; + } - if ( (status & GRCAN_OR_IRQ) || (canstat & GRCAN_STAT_OR) ){ - /* Over-run during reception interrupt */ - pDev->stats.overrun_cnt++; - } + if ( (status & GRCAN_OR_IRQ) || (canstat & GRCAN_STAT_OR) ){ + /* Over-run during reception interrupt */ + pDev->stats.overrun_cnt++; + } - if ( (status & GRCAN_RXAHBERR_IRQ) || - (status & GRCAN_TXAHBERR_IRQ) || - (canstat & GRCAN_STAT_AHBERR) ){ - /* RX or Tx AHB Error interrupt */ - printk("AHBERROR: status: 0x%x, canstat: 0x%x\n",status,canstat); - pDev->stats.ahberr_cnt++; - } + if ( (status & GRCAN_RXAHBERR_IRQ) || + (status & GRCAN_TXAHBERR_IRQ) || + (canstat & GRCAN_STAT_AHBERR) ){ + /* RX or Tx AHB Error interrupt */ + printk("AHBERROR: status: 0x%x, canstat: 0x%x\n",status,canstat); + pDev->stats.ahberr_cnt++; + } - if ( status & GRCAN_TXLOSS_IRQ ) { - pDev->stats.txloss_cnt++; - } + if ( status & GRCAN_TXLOSS_IRQ ) { + pDev->stats.txloss_cnt++; + } - if ( status & GRCAN_RXIRQ_IRQ ){ - /* RX IRQ pointer interrupt */ - /*printk("RxIrq 0x%x\n",status);*/ - pDev->regs->imr = READ_REG(&pDev->regs->imr) & ~GRCAN_RXIRQ_IRQ; - rtems_semaphore_release(pDev->rx_sem); - } + if ( status & GRCAN_RXIRQ_IRQ ){ + /* RX IRQ pointer interrupt */ + /*printk("RxIrq 0x%x\n",status);*/ + pDev->regs->imr = READ_REG(&pDev->regs->imr) & ~GRCAN_RXIRQ_IRQ; + rtems_semaphore_release(pDev->rx_sem); + } - if ( status & GRCAN_TXIRQ_IRQ ){ - /* TX IRQ pointer interrupt */ - pDev->regs->imr = READ_REG(&pDev->regs->imr) & ~GRCAN_TXIRQ_IRQ; - rtems_semaphore_release(pDev->tx_sem); - } + if ( status & GRCAN_TXIRQ_IRQ ){ + /* TX IRQ pointer interrupt */ + pDev->regs->imr = READ_REG(&pDev->regs->imr) & ~GRCAN_TXIRQ_IRQ; + rtems_semaphore_release(pDev->tx_sem); + } - if ( status & GRCAN_TXSYNC_IRQ ){ - /* TxSync message transmitted interrupt */ - pDev->stats.txsync_cnt++; - } + if ( status & GRCAN_TXSYNC_IRQ ){ + /* TxSync message transmitted interrupt */ + pDev->stats.txsync_cnt++; + } - if ( status & GRCAN_RXSYNC_IRQ ){ - /* RxSync message received interrupt */ - pDev->stats.rxsync_cnt++; - } + if ( status & GRCAN_RXSYNC_IRQ ){ + /* RxSync message received interrupt */ + pDev->stats.rxsync_cnt++; + } - if ( status & GRCAN_TXEMPTY_IRQ ){ - pDev->regs->imr = READ_REG(&pDev->regs->imr) & ~GRCAN_TXEMPTY_IRQ; - rtems_semaphore_release(pDev->txempty_sem); - } + if ( status & GRCAN_TXEMPTY_IRQ ){ + pDev->regs->imr = READ_REG(&pDev->regs->imr) & ~GRCAN_TXEMPTY_IRQ; + rtems_semaphore_release(pDev->txempty_sem); + } out: - /* Clear IRQs */ - pDev->regs->picr = status; -} - -static int grcan_register_internal(void) -{ - rtems_status_code r; - rtems_device_major_number m; - - if ((r = rtems_io_register_driver(0, &grcan_driver, &m)) != - RTEMS_SUCCESSFUL) { - switch(r) { - case RTEMS_TOO_MANY: - DBG2("failed RTEMS_TOO_MANY\n"); - break; - case RTEMS_INVALID_NUMBER: - DBG2("failed RTEMS_INVALID_NUMBER\n"); - break; - case RTEMS_RESOURCE_IN_USE: - DBG2("failed RTEMS_RESOURCE_IN_USE\n"); - break; - default: - DBG("failed %i\n",r); - break; - } - return 1; - } - DBG("Registered GRCAN on major %d\n",m); - return 0; -} - - -/* Use custom addresses and IRQs to find hardware */ -int GRCAN_PREFIX(_register_abs)(struct grcan_device_info *devices, int dev_cnt) -{ - FUNCDBG(); - - if ( !devices || (dev_cnt<0) ) - return 1; - grcan_cores = devices; - grcan_core_cnt = dev_cnt; - - amba_bus = NULL; - return grcan_register_internal(); -} - -/* Use prescanned AMBA Plug&Play information to find all GRCAN cores */ -int GRCAN_PREFIX(_register)(amba_confarea_type *abus) -{ - FUNCDBG(); - - if ( !abus ) - return 1; - amba_bus = abus; - grcan_cores = NULL; - grcan_core_cnt = 0; - return grcan_register_internal(); + /* Clear IRQs */ + pDev->regs->picr = status; } diff --git a/c/src/lib/libbsp/sparc/shared/can/grcan_rasta.c b/c/src/lib/libbsp/sparc/shared/can/grcan_rasta.c deleted file mode 100644 index f9b126f552..0000000000 --- a/c/src/lib/libbsp/sparc/shared/can/grcan_rasta.c +++ /dev/null @@ -1,100 +0,0 @@ - -#include <rasta.h> - -/* PCI frequency */ -#define SYS_FREQ_HZ 30000000 - -/*#define USE_AT697_RAM 1 */ - -/* memarea_to_hw(x) - * - * x: address in AT697 address space - * - * returns the address in the RASTA address space that can be used to access x with dma. - * -*/ -#ifdef USE_AT697_RAM -static inline unsigned int memarea_to_hw(unsigned int addr) { - return ((addr & 0x0fffffff) | RASTA_PCI_BASE); -} -#else -static inline unsigned int memarea_to_hw(unsigned int addr) { - return ((addr & 0x0fffffff) | RASTA_LOCAL_SRAM); -} -#endif - -#define MEMAREA_TO_HW(x) memarea_to_hw(x) - -#define IRQ_CLEAR_PENDING(irqno) -#define IRQ_UNMASK(irqno) -#define IRQ_MASK(irqno) - -#define IRQ_GLOBAL_PREPARE(level) rtems_interrupt_level level -#define IRQ_GLOBAL_DISABLE(level) rtems_interrupt_disable(level) -#define IRQ_GLOBAL_ENABLE(level) rtems_interrupt_enable(level) - -#define GRCAN_REG_INT(handler,irqno,arg) \ - if ( grcan_rasta_int_reg ) \ - grcan_rasta_int_reg(handler,irqno,arg); - -void (*grcan_rasta_int_reg)(void *handler, int irq, void *arg) = 0; - -#define GRCAN_PREFIX(name) grcan_rasta##name - -/* We provide our own handler */ -#define GRCAN_DONT_DECLARE_IRQ_HANDLER - -#define GRCAN_REG_BYPASS_CACHE -#define GRCAN_DMA_BYPASS_CACHE - -#define GRCAN_MAX_CORES 1 - -/* Custom Statically allocated memory */ -#undef STATICALLY_ALLOCATED_TX_BUFFER -#undef STATICALLY_ALLOCATED_RX_BUFFER - -#define STATIC_TX_BUF_SIZE 4096 -#define STATIC_RX_BUF_SIZE 4096 -#define TX_BUF_SIZE 4096 -#define RX_BUF_SIZE 4096 - -#define STATIC_TX_BUF_ADDR(core) \ - ((unsigned int *)\ - (grcan_rasta_rambase+(core)*(STATIC_TX_BUF_SIZE+STATIC_RX_BUF_SIZE))) - -#define STATIC_RX_BUF_ADDR(core) \ - ((unsigned int *) \ - (grcan_rasta_rambase+(core)*(STATIC_TX_BUF_SIZE+STATIC_RX_BUF_SIZE)+STATIC_RX_BUF_SIZE)) - - -#define GRCAN_DEVNAME "/dev/grcan0" -#define GRCAN_DEVNAME_NO(devstr,no) ((devstr)[10]='0'+(no)) - -void grcan_rasta_interrupt_handler(int irq, void *pDev); - -unsigned int grcan_rasta_rambase; - -#include "grcan.c" - - -int grcan_rasta_ram_register(amba_confarea_type *abus, int rambase) -{ - grcan_rasta_rambase = rambase; - - return GRCAN_PREFIX(_register)(abus); -} -#if 0 -static void grcan_rasta_interrupt_handler(int v) -{ - /* We know there is always only one GRCAN core in a RASTA chip... */ - grcan_interrupt(&grcans[0]); - /* - struct grcan_priv *pDev = arg; - grcan_interrupt(pDev); - */ -} -#endif -void GRCAN_PREFIX(_interrupt_handler)(int irq, void *pDev) -{ - grcan_interrupt(pDev); -} diff --git a/c/src/lib/libbsp/sparc/shared/can/occan.c b/c/src/lib/libbsp/sparc/shared/can/occan.c index 8b6dbfd32a..9ff89ea7df 100644 --- a/c/src/lib/libbsp/sparc/shared/can/occan.c +++ b/c/src/lib/libbsp/sparc/shared/can/occan.c @@ -1,13 +1,15 @@ /* OC_CAN driver * - * COPYRIGHT (c) 2007. - * Gaisler Research. + * COPYRIGHT (c) 2008. + * Aeroflex Gaisler. * * 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. * - * Author: Daniel Hellström, Gaisler Research AB, www.gaisler.com + * 2008-12-17, Daniel Hellstrom <daniel@gaisler.com> + * Converted driver to support driver manager + * */ #include <rtems/libio.h> @@ -17,8 +19,8 @@ #include <bsp.h> #include <rtems/bspIo.h> /* printk */ -#include <leon.h> -#include <ambapp.h> +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> #include <occan.h> /* RTEMS -> ERRNO decoding table @@ -52,27 +54,8 @@ rtems_assoc_t errno_assoc[] = { #undef OCCAN_BYTE_REGS #endif -#ifndef OCCAN_PREFIX - #define OCCAN_PREFIX(name) occan##name -#endif - -#if !defined(OCCAN_DEVNAME) || !defined(OCCAN_DEVNAME_NO) - #undef OCCAN_DEVNAME - #undef OCCAN_DEVNAME_NO - #define OCCAN_DEVNAME "/dev/occan0" - #define OCCAN_DEVNAME_NO(devstr,no) ((devstr)[10]='0'+(no)) -#endif - -#ifndef OCCAN_REG_INT - #define OCCAN_REG_INT(handler,irq,arg) set_vector(handler,irq+0x10,1) - #undef OCCAN_DEFINE_INTHANDLER - #define OCCAN_DEFINE_INTHANDLER -#endif - -/* Default to 40MHz system clock */ -/*#ifndef SYS_FREQ_HZ - #define SYS_FREQ_HZ 40000000 -#endif*/ +/* Enable Fixup code older OCCAN with a TX IRQ-FLAG bug */ +#define OCCAN_TX_IRQ_FLAG_FIXUP 1 #define OCCAN_WORD_REG_OFS 0x80 #define OCCAN_NCORE_OFS 0x100 @@ -102,7 +85,7 @@ typedef struct { } occan_fifo; /* PELICAN */ -#ifdef OCCAN_BYTE_REGS + typedef struct { unsigned char mode, @@ -148,8 +131,8 @@ typedef struct { unsigned char rx_msg_cnt; unsigned char unused1; unsigned char clkdiv; -} pelican_regs; -#else +} pelican8_regs; + typedef struct { unsigned char mode, unused0[3], @@ -195,9 +178,15 @@ typedef struct { unsigned char rx_msg_cnt,unused16[3]; unsigned char unused17[4]; unsigned char clkdiv,unused18[3]; -} pelican_regs; +} pelican32_regs; + +#ifdef OCCAN_BYTE_REGS +#define pelican_regs pelican8_regs +#else +#define pelican_regs pelican32_regs #endif + #define MAX_TSEG2 7 #define MAX_TSEG1 15 @@ -216,12 +205,17 @@ typedef struct { } occan_speed_regs; typedef struct { + struct drvmgr_dev *dev; + char devName[32]; + /* hardware shortcuts */ pelican_regs *regs; + int byte_regs; int irq; occan_speed_regs timing; int channel; /* 0=default, 1=second bus */ int single_mode; + unsigned int sys_freq_hz; /* driver state */ rtems_id devsem; @@ -231,6 +225,7 @@ typedef struct { int started; int rxblk; int txblk; + int sending; unsigned int status; occan_stats stats; @@ -265,7 +260,7 @@ static int pelican_start(occan_priv *priv); static void pelican_stop(occan_priv *priv); static int pelican_send(occan_priv *can, CANMsg *msg); static void pelican_set_accept(occan_priv *priv, unsigned char *acode, unsigned char *amask); -static void occan_interrupt(occan_priv *can); +void occan_interrupt(void *arg); #ifdef DEBUG_PRINT_REGMAP static void pelican_regadr_print(pelican_regs *regs); #endif @@ -277,33 +272,55 @@ static rtems_device_driver occan_read(rtems_device_major_number major, rtems_dev static rtems_device_driver occan_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); static rtems_device_driver occan_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); static rtems_device_driver occan_initialize(rtems_device_major_number major, rtems_device_minor_number unused, void *arg); -#ifdef OCCAN_DEFINE_INTHANDLER -static void occan_interrupt_handler(rtems_vector_number v); -#endif -static int can_cores; -static occan_priv *cans; -static amba_confarea_type *amba_bus; -static unsigned int sys_freq_hz; + +#define OCCAN_DRIVER_TABLE_ENTRY { occan_initialize, occan_open, occan_close, occan_read, occan_write, occan_ioctl } +static rtems_driver_address_table occan_driver = OCCAN_DRIVER_TABLE_ENTRY; /* Read byte bypassing */ -#ifdef OCCAN_DONT_BYPASS_CACHE - #define READ_REG(address) (*(volatile unsigned char *)(address)) -#else + /* Bypass cache */ - #define READ_REG(address) _OCCAN_REG_READ((unsigned int)(address)) - static __inline__ unsigned char _OCCAN_REG_READ(unsigned int addr) { - unsigned char tmp; - asm(" lduba [%1]1, %0 " - : "=r"(tmp) - : "r"(addr) - ); - return tmp; +#if 0 +#define READ_REG(address) _OCCAN_REG_READ((unsigned int)(address)) +static __inline__ unsigned char _OCCAN_REG_READ(unsigned int addr) { + unsigned char tmp; + __asm__(" lduba [%1]1, %0 " + : "=r"(tmp) + : "r"(addr) + ); + return tmp; } #endif -#define WRITE_REG(address,data) (*(volatile unsigned char *)(address) = (data)) +/*#define WRITE_REG(address,data) (*(volatile unsigned char *)(address) = (data))*/ + +#define READ_REG(priv, address) occan_reg_read(priv, (unsigned int)address) +#define WRITE_REG(priv, address, data) occan_reg_write(priv, (unsigned int)address, data) + +unsigned int occan_reg_read(occan_priv *priv, unsigned int address) +{ + unsigned int adr; + if ( priv->byte_regs ) { + adr = address; + } else { + /* Word accessed registers */ + adr = (address & (~0x7f)) | ((address & 0x7f)<<2); + } + return *(volatile unsigned char *)adr; +} + +void occan_reg_write(occan_priv *priv, unsigned int address, unsigned char value) +{ + unsigned int adr; + if ( priv->byte_regs ) { + adr = address; + } else { + /* Word accessed registers */ + adr = (address & (~0x7f)) | ((address & 0x7f)<<2); + } + *(volatile unsigned char *)adr = value;; +} /* Mode register bit definitions */ #define PELICAN_MOD_RESET 0x1 @@ -390,9 +407,370 @@ static unsigned int sys_freq_hz; #define PELICAN_S_ 0x80 */ +static int occan_driver_io_registered = 0; +static rtems_device_major_number occan_driver_io_major = 0; + +/******************* Driver manager interface ***********************/ + +/* Driver prototypes */ +int occan_register_io(rtems_device_major_number *m); +int occan_device_init(occan_priv *pDev); + +int occan_init2(struct drvmgr_dev *dev); +int occan_init3(struct drvmgr_dev *dev); + +struct drvmgr_drv_ops occan_ops = +{ + .init = {NULL, occan_init2, occan_init3, NULL}, + .remove = NULL, + .info = NULL +}; + +struct amba_dev_id occan_ids[] = +{ + {VENDOR_GAISLER, GAISLER_CANAHB}, + {0, 0} /* Mark end of table */ +}; + +struct amba_drv_info occan_drv_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_GAISLER_OCCAN_ID, /* Driver ID */ + "OCCAN_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &occan_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + 0, + }, + &occan_ids[0] +}; + +void occan_register_drv (void) +{ + DBG("Registering OCCAN driver\n"); + drvmgr_drv_register(&occan_drv_info.general); +} + +int occan_init2(struct drvmgr_dev *dev) +{ + occan_priv *priv; + + DBG("OCCAN[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name); + priv = dev->priv = malloc(sizeof(occan_priv)); + if ( !priv ) + return DRVMGR_NOMEM; + memset(priv, 0, sizeof(*priv)); + priv->dev = dev; + + return DRVMGR_OK; +} + +int occan_init3(struct drvmgr_dev *dev) +{ + occan_priv *priv; + char prefix[32]; + rtems_status_code status; + + priv = dev->priv; + + /* Do initialization */ + + if ( occan_driver_io_registered == 0) { + /* Register the I/O driver only once for all cores */ + if ( occan_register_io(&occan_driver_io_major) ) { + /* Failed to register I/O driver */ + dev->priv = NULL; + return DRVMGR_FAIL; + } + + occan_driver_io_registered = 1; + } + + /* I/O system registered and initialized + * Now we take care of device initialization. + */ + + if ( occan_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/occan%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/%soccan%d", prefix, dev->minor_bus); + } + + /* Register Device */ + DBG("OCCAN[%d]: Registering %s\n", dev->minor_drv, priv->devName); + status = rtems_io_register_name(priv->devName, occan_driver_io_major, dev->minor_drv); + if (status != RTEMS_SUCCESSFUL) { + return DRVMGR_FAIL; + } + + return DRVMGR_OK; +} + +/******************* Driver Implementation ***********************/ + +int occan_register_io(rtems_device_major_number *m) +{ + rtems_status_code r; + + if ((r = rtems_io_register_driver(0, &occan_driver, m)) == RTEMS_SUCCESSFUL) { + DBG("OCCAN driver successfully registered, major: %d\n", *m); + } else { + switch(r) { + case RTEMS_TOO_MANY: + printk("OCCAN rtems_io_register_driver failed: RTEMS_TOO_MANY\n"); + return -1; + case RTEMS_INVALID_NUMBER: + printk("OCCAN rtems_io_register_driver failed: RTEMS_INVALID_NUMBER\n"); + return -1; + case RTEMS_RESOURCE_IN_USE: + printk("OCCAN rtems_io_register_driver failed: RTEMS_RESOURCE_IN_USE\n"); + return -1; + default: + printk("OCCAN rtems_io_register_driver failed\n"); + return -1; + } + } + return 0; +} + +int occan_device_init(occan_priv *pDev) +{ + struct amba_dev_info *ambadev; + struct ambapp_core *pnpinfo; + rtems_status_code status; + int minor; + + /* 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 = (pelican_regs *)(pnpinfo->ahb_slv->start[0] + OCCAN_NCORE_OFS*pnpinfo->index); + pDev->byte_regs = 1; + minor = pDev->dev->minor_drv; + + /* Get frequency in Hz */ + if ( drvmgr_freq_get(pDev->dev, DEV_AHB_SLV, &pDev->sys_freq_hz) ) { + return -1; + } + + DBG("OCCAN frequency: %d Hz\n", pDev->sys_freq_hz); + + /* initialize software */ + pDev->open = 0; + pDev->started = 0; /* Needed for spurious interrupts */ + pDev->rxfifo = NULL; + pDev->txfifo = NULL; + status = rtems_semaphore_create( + rtems_build_name('C', 'd', 'v', '0'+minor), + 1, + RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | \ + RTEMS_NO_PRIORITY_CEILING, + 0, + &pDev->devsem); + if ( status != RTEMS_SUCCESSFUL ){ + printk("OCCAN[%d]: Failed to create dev semaphore, (%d)\n\r",minor, status); + return RTEMS_UNSATISFIED; + } + status = rtems_semaphore_create( + rtems_build_name('C', 't', 'x', '0'+minor), + 0, + RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | \ + RTEMS_NO_PRIORITY_CEILING, + 0, + &pDev->txsem); + if ( status != RTEMS_SUCCESSFUL ){ + printk("OCCAN[%d]: Failed to create tx semaphore, (%d)\n\r",minor, status); + return RTEMS_UNSATISFIED; + } + status = rtems_semaphore_create( + rtems_build_name('C', 'r', 'x', '0'+minor), + 0, + RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | \ + RTEMS_NO_PRIORITY_CEILING, + 0, + &pDev->rxsem); + if ( status != RTEMS_SUCCESSFUL ){ + printk("OCCAN[%d]: Failed to create rx semaphore, (%d)\n\r",minor, status); + return RTEMS_UNSATISFIED; + } + + /* hardware init/reset */ + pelican_init(pDev); + +#ifdef DEBUG_PRINT_REGMAP + pelican_regadr_print(pDev->regs); +#endif + + return 0; +} + + +#ifdef DEBUG +static void pelican_regs_print(occan_priv *pDev){ + pelican_regs *regs = pDev->regs; + printk("--- PELICAN 0x%lx ---\n\r",(unsigned int)regs); + printk(" MODE: 0x%02x\n\r",READ_REG(pDev, ®s->mode)); + printk(" CMD: 0x%02x\n\r",READ_REG(pDev, ®s->cmd)); + printk(" STATUS: 0x%02x\n\r",READ_REG(pDev, ®s->status)); + /*printk(" INTFLG: 0x%02x\n\r",READ_REG(pDev, ®s->intflags));*/ + printk(" INTEN: 0x%02x\n\r",READ_REG(pDev, ®s->inten)); + printk(" BTR0: 0x%02x\n\r",READ_REG(pDev, ®s->bustim0)); + printk(" BTR1: 0x%02x\n\r",READ_REG(pDev, ®s->bustim1)); + printk(" ARBCODE: 0x%02x\n\r",READ_REG(pDev, ®s->arbcode)); + printk(" ERRCODE: 0x%02x\n\r",READ_REG(pDev, ®s->errcode)); + printk(" ERRWARN: 0x%02x\n\r",READ_REG(pDev, ®s->errwarn)); + printk(" RX_ERR_CNT: 0x%02x\n\r",READ_REG(pDev, ®s->rx_err_cnt)); + printk(" TX_ERR_CNT: 0x%02x\n\r",READ_REG(pDev, ®s->tx_err_cnt)); + if ( READ_REG(pDev, ®s->mode) & PELICAN_MOD_RESET ){ + /* in reset mode it is possible to read acceptance filters */ + printk(" ACR0: 0x%02x (0x%lx)\n\r",READ_REG(pDev, ®s->rx_fi_xff),®s->rx_fi_xff); + printk(" ACR1: 0x%02x (0x%lx)\n\r",READ_REG(pDev, ®s->msg.rst_accept.code[0]),(unsigned int)®s->msg.rst_accept.code[0]); + printk(" ACR1: 0x%02x (0x%lx)\n\r",READ_REG(pDev, ®s->msg.rst_accept.code[1]),(unsigned int)®s->msg.rst_accept.code[1]); + printk(" ACR1: 0x%02x (0x%lx)\n\r",READ_REG(pDev, ®s->msg.rst_accept.code[2]),(unsigned int)®s->msg.rst_accept.code[2]); + printk(" AMR0: 0x%02x (0x%lx)\n\r",READ_REG(pDev, ®s->msg.rst_accept.mask[0]),(unsigned int)®s->msg.rst_accept.mask[0]); + printk(" AMR1: 0x%02x (0x%lx)\n\r",READ_REG(pDev, ®s->msg.rst_accept.mask[1]),(unsigned int)®s->msg.rst_accept.mask[1]); + printk(" AMR2: 0x%02x (0x%lx)\n\r",READ_REG(pDev, ®s->msg.rst_accept.mask[2]),(unsigned int)®s->msg.rst_accept.mask[2]); + printk(" AMR3: 0x%02x (0x%lx)\n\r",READ_REG(pDev, ®s->msg.rst_accept.mask[3]),(unsigned int)®s->msg.rst_accept.mask[3]); + + }else{ + printk(" RXFI_XFF: 0x%02x\n\r",READ_REG(pDev, ®s->rx_fi_xff)); + } + printk(" RX_MSG_CNT: 0x%02x\n\r",READ_REG(pDev, ®s->rx_msg_cnt)); + printk(" CLKDIV: 0x%02x\n\r",READ_REG(pDev, ®s->clkdiv)); + printk("-------------------\n\r"); +} +#endif + +#ifdef DEBUG_PRINT_REGMAP +static void pelican_regadr_print(pelican_regs *regs){ + printk("--- PELICAN 0x%lx ---\n\r",(unsigned int)regs); + printk(" MODE: 0x%lx\n\r",(unsigned int)®s->mode); + printk(" CMD: 0x%lx\n\r",(unsigned int)®s->cmd); + printk(" STATUS: 0x%lx\n\r",(unsigned int)®s->status); + /*printk(" INTFLG: 0x%lx\n\r",®s->intflags);*/ + printk(" INTEN: 0x%lx\n\r",(unsigned int)®s->inten); + printk(" BTR0: 0x%lx\n\r",(unsigned int)®s->bustim0); + printk(" BTR1: 0x%lx\n\r",(unsigned int)®s->bustim1); + printk(" ARBCODE: 0x%lx\n\r",(unsigned int)®s->arbcode); + printk(" ERRCODE: 0x%lx\n\r",(unsigned int)®s->errcode); + printk(" ERRWARN: 0x%lx\n\r",(unsigned int)®s->errwarn); + printk(" RX_ERR_CNT: 0x%lx\n\r",(unsigned int)®s->rx_err_cnt); + printk(" TX_ERR_CNT: 0x%lx\n\r",(unsigned int)®s->tx_err_cnt); + + /* in reset mode it is possible to read acceptance filters */ + printk(" RXFI_XFF: 0x%lx\n\r",(unsigned int)®s->rx_fi_xff); + + /* reset registers */ + printk(" ACR0: 0x%lx\n\r",(unsigned int)®s->rx_fi_xff); + printk(" ACR1: 0x%lx\n\r",(unsigned int)®s->msg.rst_accept.code[0]); + printk(" ACR2: 0x%lx\n\r",(unsigned int)®s->msg.rst_accept.code[1]); + printk(" ACR3: 0x%lx\n\r",(unsigned int)®s->msg.rst_accept.code[2]); + printk(" AMR0: 0x%lx\n\r",(unsigned int)®s->msg.rst_accept.mask[0]); + printk(" AMR1: 0x%lx\n\r",(unsigned int)®s->msg.rst_accept.mask[1]); + printk(" AMR2: 0x%lx\n\r",(unsigned int)®s->msg.rst_accept.mask[2]); + printk(" AMR3: 0x%lx\n\r",(unsigned int)®s->msg.rst_accept.mask[3]); + + /* TX Extended */ + printk(" EFFTX_ID[0]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.id[0]); + printk(" EFFTX_ID[1]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.id[1]); + printk(" EFFTX_ID[2]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.id[2]); + printk(" EFFTX_ID[3]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.id[3]); + + printk(" EFFTX_DATA[0]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.data[0]); + printk(" EFFTX_DATA[1]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.data[1]); + printk(" EFFTX_DATA[2]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.data[2]); + printk(" EFFTX_DATA[3]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.data[3]); + printk(" EFFTX_DATA[4]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.data[4]); + printk(" EFFTX_DATA[5]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.data[5]); + printk(" EFFTX_DATA[6]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.data[6]); + printk(" EFFTX_DATA[7]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.data[7]); + + /* RX Extended */ + printk(" EFFRX_ID[0]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.id[0]); + printk(" EFFRX_ID[1]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.id[1]); + printk(" EFFRX_ID[2]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.id[2]); + printk(" EFFRX_ID[3]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.id[3]); + + printk(" EFFRX_DATA[0]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.data[0]); + printk(" EFFRX_DATA[1]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.data[1]); + printk(" EFFRX_DATA[2]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.data[2]); + printk(" EFFRX_DATA[3]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.data[3]); + printk(" EFFRX_DATA[4]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.data[4]); + printk(" EFFRX_DATA[5]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.data[5]); + printk(" EFFRX_DATA[6]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.data[6]); + printk(" EFFRX_DATA[7]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.data[7]); + + + /* RX Extended */ + printk(" SFFRX_ID[0]: 0x%lx\n\r",(unsigned int)®s->msg.rx_sff.id[0]); + printk(" SFFRX_ID[1]: 0x%lx\n\r",(unsigned int)®s->msg.rx_sff.id[1]); + + printk(" SFFRX_DATA[0]: 0x%lx\n\r",(unsigned int)®s->msg.rx_sff.data[0]); + printk(" SFFRX_DATA[1]: 0x%lx\n\r",(unsigned int)®s->msg.rx_sff.data[1]); + printk(" SFFRX_DATA[2]: 0x%lx\n\r",(unsigned int)®s->msg.rx_sff.data[2]); + printk(" SFFRX_DATA[3]: 0x%lx\n\r",(unsigned int)®s->msg.rx_sff.data[3]); + printk(" SFFRX_DATA[4]: 0x%lx\n\r",(unsigned int)®s->msg.rx_sff.data[4]); + printk(" SFFRX_DATA[5]: 0x%lx\n\r",(unsigned int)®s->msg.rx_sff.data[5]); + printk(" SFFRX_DATA[6]: 0x%lx\n\r",(unsigned int)®s->msg.rx_sff.data[6]); + printk(" SFFRX_DATA[7]: 0x%lx\n\r",(unsigned int)®s->msg.rx_sff.data[7]); + + /* TX Extended */ + printk(" SFFTX_ID[0]: 0x%lx\n\r",(unsigned int)®s->msg.tx_sff.id[0]); + printk(" SFFTX_ID[1]: 0x%lx\n\r",(unsigned int)®s->msg.tx_sff.id[1]); + + printk(" SFFTX_DATA[0]: 0x%lx\n\r",(unsigned int)®s->msg.tx_sff.data[0]); + printk(" SFFTX_DATA[1]: 0x%lx\n\r",(unsigned int)®s->msg.tx_sff.data[1]); + printk(" SFFTX_DATA[2]: 0x%lx\n\r",(unsigned int)®s->msg.tx_sff.data[2]); + printk(" SFFTX_DATA[3]: 0x%lx\n\r",(unsigned int)®s->msg.tx_sff.data[3]); + printk(" SFFTX_DATA[4]: 0x%lx\n\r",(unsigned int)®s->msg.tx_sff.data[4]); + printk(" SFFTX_DATA[5]: 0x%lx\n\r",(unsigned int)®s->msg.tx_sff.data[5]); + printk(" SFFTX_DATA[6]: 0x%lx\n\r",(unsigned int)®s->msg.tx_sff.data[6]); + printk(" SFFTX_DATA[7]: 0x%lx\n\r",(unsigned int)®s->msg.tx_sff.data[7]); + + printk(" RX_MSG_CNT: 0x%lx\n\r",(unsigned int)®s->rx_msg_cnt); + printk(" CLKDIV: 0x%lx\n\r",(unsigned int)®s->clkdiv); + printk("-------------------\n\r"); +} +#endif + +#ifdef DEBUG +static void occan_stat_print(occan_stats *stats){ + printk("----Stats----\n\r"); + printk("rx_msgs: %d\n\r",stats->rx_msgs); + printk("tx_msgs: %d\n\r",stats->tx_msgs); + printk("err_warn: %d\n\r",stats->err_warn); + printk("err_dovr: %d\n\r",stats->err_dovr); + printk("err_errp: %d\n\r",stats->err_errp); + printk("err_arb: %d\n\r",stats->err_arb); + printk("err_bus: %d\n\r",stats->err_bus); + printk("Int cnt: %d\n\r",stats->ints); + printk("tx_buf_err: %d\n\r",stats->tx_buf_error); + printk("-------------\n\r"); +} +#endif + static void pelican_init(occan_priv *priv){ /* Reset core */ - priv->regs->mode = PELICAN_MOD_RESET; + WRITE_REG(priv, &priv->regs->mode, PELICAN_MOD_RESET); /* wait for core to reset complete */ /*usleep(1);*/ @@ -417,20 +795,21 @@ static void pelican_open(occan_priv *priv){ /* Set clock divider to extended mode, clkdiv not connected */ - priv->regs->clkdiv = (1<<PELICAN_CDR_MODE_BITS) | (DEFAULT_CLKDIV & PELICAN_CDR_DIV); + WRITE_REG(priv, &priv->regs->clkdiv, (1<<PELICAN_CDR_MODE_BITS) | (DEFAULT_CLKDIV & PELICAN_CDR_DIV)); - ret = occan_calc_speedregs(sys_freq_hz,priv->speed,&priv->timing); + ret = occan_calc_speedregs(priv->sys_freq_hz,priv->speed,&priv->timing); if ( ret ){ /* failed to set speed for this system freq, try with 50K instead */ priv->speed = OCCAN_SPEED_50K; - occan_calc_speedregs(sys_freq_hz,priv->speed,&priv->timing); + occan_calc_speedregs(priv->sys_freq_hz, priv->speed, + &priv->timing); } /* disable all interrupts */ - priv->regs->inten = 0; + WRITE_REG(priv, &priv->regs->inten, 0); /* clear pending interrupts by reading */ - tmp = READ_REG(&priv->regs->intflags); + tmp = READ_REG(priv, &priv->regs->intflags); } static int pelican_start(occan_priv *priv){ @@ -440,21 +819,22 @@ static int pelican_start(occan_priv *priv){ if ( !priv->rxfifo || !priv->txfifo ) return -1; - /* In case we were started before and stopped we - * should empty the TX fifo or try to resend those - * messages. We make it simple... - */ - occan_fifo_clr(priv->txfifo); + /* In case we were started before and stopped we + * should empty the TX fifo or try to resend those + * messages. We make it simple... + */ + occan_fifo_clr(priv->txfifo); /* Clear status bits */ priv->status = 0; + priv->sending = 0; /* clear pending interrupts */ - tmp = READ_REG(&priv->regs->intflags); + tmp = READ_REG(priv, &priv->regs->intflags); /* clear error counters */ - priv->regs->rx_err_cnt = 0; - priv->regs->tx_err_cnt = 0; + WRITE_REG(priv, &priv->regs->rx_err_cnt, 0); + WRITE_REG(priv, &priv->regs->tx_err_cnt, 0); #ifdef REDUNDANT_CHANNELS if ( (priv->channel == 0) || (priv->channel >= REDUNDANT_CHANNELS) ){ @@ -468,15 +848,21 @@ static int pelican_start(occan_priv *priv){ /* set the speed regs of the CAN core */ occan_set_speedregs(priv,&priv->timing); - DBG("OCCAN: start: set timing regs btr0: 0x%x, btr1: 0x%x\n\r",READ_REG(&priv->regs->bustim0),READ_REG(&priv->regs->bustim1)); + DBG("OCCAN: start: set timing regs btr0: 0x%x, btr1: 0x%x\n\r", + READ_REG(priv, &priv->regs->bustim0), + READ_REG(priv, &priv->regs->bustim1)); /* Set default acceptance filter */ pelican_set_accept(priv,priv->acode,priv->amask); - /* turn on interrupts */ - priv->regs->inten = PELICAN_IE_RX | PELICAN_IE_TX | PELICAN_IE_ERRW | - PELICAN_IE_ERRP | PELICAN_IE_BUS; + /* Nothing can fail from here, this must be set before interrupts are + * enabled */ + priv->started = 1; + /* turn on interrupts */ + WRITE_REG(priv, &priv->regs->inten, + PELICAN_IE_RX | PELICAN_IE_TX | PELICAN_IE_ERRW | + PELICAN_IE_ERRP | PELICAN_IE_BUS); #ifdef DEBUG /* print setup before starting */ pelican_regs_print(priv->regs); @@ -484,17 +870,23 @@ static int pelican_start(occan_priv *priv){ #endif /* core already in reset mode, - * ¤ Exit reset mode + * ¤ Exit reset mode * ¤ Enter Single/Dual mode filtering. */ - priv->regs->mode = (priv->single_mode << 3); + WRITE_REG(priv, &priv->regs->mode, (priv->single_mode << 3)); + + /* Register interrupt routine and unmask IRQ at IRQ controller */ + drvmgr_interrupt_register(priv->dev, 0, "occan", occan_interrupt, priv); return 0; } -static void pelican_stop(occan_priv *priv){ +static void pelican_stop(occan_priv *priv) +{ /* stop HW */ + drvmgr_interrupt_unregister(priv->dev, 0, occan_interrupt, priv); + #ifdef DEBUG /* print setup before stopping */ pelican_regs_print(priv->regs); @@ -502,14 +894,28 @@ static void pelican_stop(occan_priv *priv){ #endif /* put core in reset mode */ - priv->regs->mode = PELICAN_MOD_RESET; + WRITE_REG(priv, &priv->regs->mode, PELICAN_MOD_RESET); /* turn off interrupts */ - priv->regs->inten = 0; + WRITE_REG(priv, &priv->regs->inten, 0); priv->status |= OCCAN_STATUS_RESET; } +static inline int pelican_tx_ready(occan_priv *can) +{ + unsigned char status; + pelican_regs *regs = can->regs; + + /* is there room in send buffer? */ + status = READ_REG(can, ®s->status); + if ( !(status & PELICAN_STAT_TXBUF) ) { + /* tx fifo taken, we have to wait */ + return 0; + } + + return 1; +} /* Try to send message "msg", if hardware txfifo is * full, then -1 is returned. @@ -518,12 +924,11 @@ static void pelican_stop(occan_priv *priv){ * entering this function. */ static int pelican_send(occan_priv *can, CANMsg *msg){ - unsigned char tmp,status; + unsigned char tmp; pelican_regs *regs = can->regs; /* is there room in send buffer? */ - status = READ_REG(®s->status); - if ( !(status & PELICAN_STAT_TXBUF) ){ + if ( !pelican_tx_ready(can) ) { /* tx fifo taken, we have to wait */ return -1; } @@ -534,39 +939,40 @@ static int pelican_send(occan_priv *can, CANMsg *msg){ if ( msg->extended ){ /* Extended Frame */ - regs->rx_fi_xff = 0x80 | tmp; - WRITE_REG(®s->msg.tx_eff.id[0],(msg->id >> (5+8+8)) & 0xff); - WRITE_REG(®s->msg.tx_eff.id[1],(msg->id >> (5+8)) & 0xff); - WRITE_REG(®s->msg.tx_eff.id[2],(msg->id >> (5)) & 0xff); - WRITE_REG(®s->msg.tx_eff.id[3],(msg->id << 3) & 0xf8); + WRITE_REG(can, ®s->rx_fi_xff, 0x80 | tmp); + WRITE_REG(can, ®s->msg.tx_eff.id[0],(msg->id >> (5+8+8)) & 0xff); + WRITE_REG(can, ®s->msg.tx_eff.id[1],(msg->id >> (5+8)) & 0xff); + WRITE_REG(can, ®s->msg.tx_eff.id[2],(msg->id >> (5)) & 0xff); + WRITE_REG(can, ®s->msg.tx_eff.id[3],(msg->id << 3) & 0xf8); tmp = msg->len; while(tmp--){ - WRITE_REG(®s->msg.tx_eff.data[tmp],msg->data[tmp]); + WRITE_REG(can, ®s->msg.tx_eff.data[tmp], msg->data[tmp]); } }else{ /* Standard Frame */ - regs->rx_fi_xff = tmp; - WRITE_REG(®s->msg.tx_sff.id[0],(msg->id >> 3) & 0xff); - WRITE_REG(®s->msg.tx_sff.id[1],(msg->id << 5) & 0xe0); + WRITE_REG(can, ®s->rx_fi_xff, tmp); + WRITE_REG(can, ®s->msg.tx_sff.id[0],(msg->id >> 3) & 0xff); + WRITE_REG(can, ®s->msg.tx_sff.id[1],(msg->id << 5) & 0xe0); tmp = msg->len; while(tmp--){ - WRITE_REG(®s->msg.tx_sff.data[tmp],msg->data[tmp]); + WRITE_REG(can, ®s->msg.tx_sff.data[tmp],msg->data[tmp]); } } /* let HW know of new message */ if ( msg->sshot ){ - regs->cmd = PELICAN_CMD_TXREQ | PELICAN_CMD_ABORT; + WRITE_REG(can, ®s->cmd, PELICAN_CMD_TXREQ | PELICAN_CMD_ABORT); }else{ /* normal case -- try resend until sent */ - regs->cmd = PELICAN_CMD_TXREQ; + WRITE_REG(can, ®s->cmd, PELICAN_CMD_TXREQ); } return 0; } -static void pelican_set_accept(occan_priv *priv, unsigned char *acode, unsigned char *amask){ +static void pelican_set_accept(occan_priv *priv, unsigned char *acode, unsigned char *amask) +{ unsigned char *acode0, *acode1, *acode2, *acode3; unsigned char *amask0, *amask1, *amask2, *amask3; @@ -581,159 +987,17 @@ static void pelican_set_accept(occan_priv *priv, unsigned char *acode, unsigned amask3 = (unsigned char *)&priv->regs->msg.rst_accept.mask[3]; /* Set new mask & code */ - *acode0 = acode[0]; - *acode1 = acode[1]; - *acode2 = acode[2]; - *acode3 = acode[3]; - - *amask0 = amask[0]; - *amask1 = amask[1]; - *amask2 = amask[2]; - *amask3 = amask[3]; -} - -#ifdef DEBUG -static void pelican_regs_print(pelican_regs *regs){ - printk("--- PELICAN 0x%lx ---\n\r",(unsigned int)regs); - printk(" MODE: 0x%02x\n\r",READ_REG(®s->mode)); - printk(" CMD: 0x%02x\n\r",READ_REG(®s->cmd)); - printk(" STATUS: 0x%02x\n\r",READ_REG(®s->status)); - /*printk(" INTFLG: 0x%02x\n\r",READ_REG(®s->intflags));*/ - printk(" INTEN: 0x%02x\n\r",READ_REG(®s->inten)); - printk(" BTR0: 0x%02x\n\r",READ_REG(®s->bustim0)); - printk(" BTR1: 0x%02x\n\r",READ_REG(®s->bustim1)); - printk(" ARBCODE: 0x%02x\n\r",READ_REG(®s->arbcode)); - printk(" ERRCODE: 0x%02x\n\r",READ_REG(®s->errcode)); - printk(" ERRWARN: 0x%02x\n\r",READ_REG(®s->errwarn)); - printk(" RX_ERR_CNT: 0x%02x\n\r",READ_REG(®s->rx_err_cnt)); - printk(" TX_ERR_CNT: 0x%02x\n\r",READ_REG(®s->tx_err_cnt)); - if ( READ_REG(®s->mode) & PELICAN_MOD_RESET ){ - /* in reset mode it is possible to read acceptance filters */ - printk(" ACR0: 0x%02x (0x%lx)\n\r",READ_REG(®s->rx_fi_xff),®s->rx_fi_xff); - printk(" ACR1: 0x%02x (0x%lx)\n\r",READ_REG(®s->msg.rst_accept.code[0]),(unsigned int)®s->msg.rst_accept.code[0]); - printk(" ACR1: 0x%02x (0x%lx)\n\r",READ_REG(®s->msg.rst_accept.code[1]),(unsigned int)®s->msg.rst_accept.code[1]); - printk(" ACR1: 0x%02x (0x%lx)\n\r",READ_REG(®s->msg.rst_accept.code[2]),(unsigned int)®s->msg.rst_accept.code[2]); - printk(" AMR0: 0x%02x (0x%lx)\n\r",READ_REG(®s->msg.rst_accept.mask[0]),(unsigned int)®s->msg.rst_accept.mask[0]); - printk(" AMR1: 0x%02x (0x%lx)\n\r",READ_REG(®s->msg.rst_accept.mask[1]),(unsigned int)®s->msg.rst_accept.mask[1]); - printk(" AMR2: 0x%02x (0x%lx)\n\r",READ_REG(®s->msg.rst_accept.mask[2]),(unsigned int)®s->msg.rst_accept.mask[2]); - printk(" AMR3: 0x%02x (0x%lx)\n\r",READ_REG(®s->msg.rst_accept.mask[3]),(unsigned int)®s->msg.rst_accept.mask[3]); - - }else{ - printk(" RXFI_XFF: 0x%02x\n\r",READ_REG(®s->rx_fi_xff)); - } - printk(" RX_MSG_CNT: 0x%02x\n\r",READ_REG(®s->rx_msg_cnt)); - printk(" CLKDIV: 0x%02x\n\r",READ_REG(®s->clkdiv)); - printk("-------------------\n\r"); + WRITE_REG(priv, acode0, acode[0]); + WRITE_REG(priv, acode1, acode[1]); + WRITE_REG(priv, acode2, acode[2]); + WRITE_REG(priv, acode3, acode[3]); + + WRITE_REG(priv, amask0, amask[0]); + WRITE_REG(priv, amask1, amask[1]); + WRITE_REG(priv, amask2, amask[2]); + WRITE_REG(priv, amask3, amask[3]); } -#endif -#ifdef DEBUG_PRINT_REGMAP -static void pelican_regadr_print(pelican_regs *regs){ - printk("--- PELICAN 0x%lx ---\n\r",(unsigned int)regs); - printk(" MODE: 0x%lx\n\r",(unsigned int)®s->mode); - printk(" CMD: 0x%lx\n\r",(unsigned int)®s->cmd); - printk(" STATUS: 0x%lx\n\r",(unsigned int)®s->status); - /*printk(" INTFLG: 0x%lx\n\r",®s->intflags);*/ - printk(" INTEN: 0x%lx\n\r",(unsigned int)®s->inten); - printk(" BTR0: 0x%lx\n\r",(unsigned int)®s->bustim0); - printk(" BTR1: 0x%lx\n\r",(unsigned int)®s->bustim1); - printk(" ARBCODE: 0x%lx\n\r",(unsigned int)®s->arbcode); - printk(" ERRCODE: 0x%lx\n\r",(unsigned int)®s->errcode); - printk(" ERRWARN: 0x%lx\n\r",(unsigned int)®s->errwarn); - printk(" RX_ERR_CNT: 0x%lx\n\r",(unsigned int)®s->rx_err_cnt); - printk(" TX_ERR_CNT: 0x%lx\n\r",(unsigned int)®s->tx_err_cnt); - - /* in reset mode it is possible to read acceptance filters */ - printk(" RXFI_XFF: 0x%lx\n\r",(unsigned int)®s->rx_fi_xff); - - /* reset registers */ - printk(" ACR0: 0x%lx\n\r",(unsigned int)®s->rx_fi_xff); - printk(" ACR1: 0x%lx\n\r",(unsigned int)®s->msg.rst_accept.code[0]); - printk(" ACR2: 0x%lx\n\r",(unsigned int)®s->msg.rst_accept.code[1]); - printk(" ACR3: 0x%lx\n\r",(unsigned int)®s->msg.rst_accept.code[2]); - printk(" AMR0: 0x%lx\n\r",(unsigned int)®s->msg.rst_accept.mask[0]); - printk(" AMR1: 0x%lx\n\r",(unsigned int)®s->msg.rst_accept.mask[1]); - printk(" AMR2: 0x%lx\n\r",(unsigned int)®s->msg.rst_accept.mask[2]); - printk(" AMR3: 0x%lx\n\r",(unsigned int)®s->msg.rst_accept.mask[3]); - - /* TX Extended */ - printk(" EFFTX_ID[0]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.id[0]); - printk(" EFFTX_ID[1]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.id[1]); - printk(" EFFTX_ID[2]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.id[2]); - printk(" EFFTX_ID[3]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.id[3]); - - printk(" EFFTX_DATA[0]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.data[0]); - printk(" EFFTX_DATA[1]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.data[1]); - printk(" EFFTX_DATA[2]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.data[2]); - printk(" EFFTX_DATA[3]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.data[3]); - printk(" EFFTX_DATA[4]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.data[4]); - printk(" EFFTX_DATA[5]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.data[5]); - printk(" EFFTX_DATA[6]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.data[6]); - printk(" EFFTX_DATA[7]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.data[7]); - - /* RX Extended */ - printk(" EFFRX_ID[0]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.id[0]); - printk(" EFFRX_ID[1]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.id[1]); - printk(" EFFRX_ID[2]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.id[2]); - printk(" EFFRX_ID[3]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.id[3]); - - printk(" EFFRX_DATA[0]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.data[0]); - printk(" EFFRX_DATA[1]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.data[1]); - printk(" EFFRX_DATA[2]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.data[2]); - printk(" EFFRX_DATA[3]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.data[3]); - printk(" EFFRX_DATA[4]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.data[4]); - printk(" EFFRX_DATA[5]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.data[5]); - printk(" EFFRX_DATA[6]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.data[6]); - printk(" EFFRX_DATA[7]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.data[7]); - - - /* RX Extended */ - printk(" SFFRX_ID[0]: 0x%lx\n\r",(unsigned int)®s->msg.rx_sff.id[0]); - printk(" SFFRX_ID[1]: 0x%lx\n\r",(unsigned int)®s->msg.rx_sff.id[1]); - - printk(" SFFRX_DATA[0]: 0x%lx\n\r",(unsigned int)®s->msg.rx_sff.data[0]); - printk(" SFFRX_DATA[1]: 0x%lx\n\r",(unsigned int)®s->msg.rx_sff.data[1]); - printk(" SFFRX_DATA[2]: 0x%lx\n\r",(unsigned int)®s->msg.rx_sff.data[2]); - printk(" SFFRX_DATA[3]: 0x%lx\n\r",(unsigned int)®s->msg.rx_sff.data[3]); - printk(" SFFRX_DATA[4]: 0x%lx\n\r",(unsigned int)®s->msg.rx_sff.data[4]); - printk(" SFFRX_DATA[5]: 0x%lx\n\r",(unsigned int)®s->msg.rx_sff.data[5]); - printk(" SFFRX_DATA[6]: 0x%lx\n\r",(unsigned int)®s->msg.rx_sff.data[6]); - printk(" SFFRX_DATA[7]: 0x%lx\n\r",(unsigned int)®s->msg.rx_sff.data[7]); - - /* TX Extended */ - printk(" SFFTX_ID[0]: 0x%lx\n\r",(unsigned int)®s->msg.tx_sff.id[0]); - printk(" SFFTX_ID[1]: 0x%lx\n\r",(unsigned int)®s->msg.tx_sff.id[1]); - - printk(" SFFTX_DATA[0]: 0x%lx\n\r",(unsigned int)®s->msg.tx_sff.data[0]); - printk(" SFFTX_DATA[1]: 0x%lx\n\r",(unsigned int)®s->msg.tx_sff.data[1]); - printk(" SFFTX_DATA[2]: 0x%lx\n\r",(unsigned int)®s->msg.tx_sff.data[2]); - printk(" SFFTX_DATA[3]: 0x%lx\n\r",(unsigned int)®s->msg.tx_sff.data[3]); - printk(" SFFTX_DATA[4]: 0x%lx\n\r",(unsigned int)®s->msg.tx_sff.data[4]); - printk(" SFFTX_DATA[5]: 0x%lx\n\r",(unsigned int)®s->msg.tx_sff.data[5]); - printk(" SFFTX_DATA[6]: 0x%lx\n\r",(unsigned int)®s->msg.tx_sff.data[6]); - printk(" SFFTX_DATA[7]: 0x%lx\n\r",(unsigned int)®s->msg.tx_sff.data[7]); - - printk(" RX_MSG_CNT: 0x%lx\n\r",(unsigned int)®s->rx_msg_cnt); - printk(" CLKDIV: 0x%lx\n\r",(unsigned int)®s->clkdiv); - printk("-------------------\n\r"); -} -#endif - -#ifdef DEBUG -static void occan_stat_print(occan_stats *stats){ - printk("----Stats----\n\r"); - printk("rx_msgs: %d\n\r",stats->rx_msgs); - printk("tx_msgs: %d\n\r",stats->tx_msgs); - printk("err_warn: %d\n\r",stats->err_warn); - printk("err_dovr: %d\n\r",stats->err_dovr); - printk("err_errp: %d\n\r",stats->err_errp); - printk("err_arb: %d\n\r",stats->err_arb); - printk("err_bus: %d\n\r",stats->err_bus); - printk("Int cnt: %d\n\r",stats->ints); - printk("tx_buf_err: %d\n\r",stats->tx_buf_error); - printk("-------------\n\r"); -} -#endif /* This function calculates BTR0 and BTR1 values for a given bitrate. * @@ -743,7 +1007,8 @@ static void occan_stat_print(occan_stats *stats){ * \param result Pointer to where resulting BTRs will be stored. * \return zero if successful to calculate a baud rate. */ -static int occan_calc_speedregs(unsigned int clock_hz, unsigned int rate, occan_speed_regs *result){ +static int occan_calc_speedregs(unsigned int clock_hz, unsigned int rate, occan_speed_regs *result) +{ int best_error = 1000000000; int error; int best_tseg=0, best_brp=0, best_rate=0, brp=0; @@ -827,12 +1092,14 @@ static int occan_calc_speedregs(unsigned int clock_hz, unsigned int rate, occan_ return 0; } -static int occan_set_speedregs(occan_priv *priv, occan_speed_regs *timing){ +static int occan_set_speedregs(occan_priv *priv, occan_speed_regs *timing) +{ if ( !timing || !priv || !priv->regs) return -1; - priv->regs->bustim0 = timing->btr0; - priv->regs->bustim1 = timing->btr1; + WRITE_REG(priv, &priv->regs->bustim0, timing->btr0); + WRITE_REG(priv, &priv->regs->bustim1, timing->btr1); + return 0; } @@ -861,7 +1128,7 @@ static int pelican_speed_auto(occan_priv *priv){ while ( (speed=pelican_speed_auto_steplist[i]) > 0){ /* Reset core */ - priv->regs->mode = PELICAN_MOD_RESET; + WRITE_REG(priv, &priv->regs->mode, PELICAN_MOD_RESET); /* tell int handler about the auto speed detection test */ @@ -873,7 +1140,7 @@ static int pelican_speed_auto(occan_priv *priv){ pelican_set_accept(priv); /* calc timing params for this */ - if ( occan_calc_speedregs(sys_freq_hz,speed,&timing) ){ + if ( occan_calc_speedregs(priv->sys_freq_hz,speed,&timing) ){ /* failed to get good timings for this frequency * test with next */ @@ -887,13 +1154,13 @@ static int pelican_speed_auto(occan_priv *priv){ /* Empty previous messages in hardware RX fifo */ /* - while( READ_REG(&priv->regs->) ){ + while( READ_REG(priv, &priv->regs->) ){ } */ /* Clear pending interrupts */ - tmp = READ_REG(&priv->regs->intflags); + tmp = READ_REG(priv, &priv->regs->intflags); /* enable RX & ERR interrupt */ priv->regs->inten = @@ -911,172 +1178,23 @@ static int pelican_speed_auto(occan_priv *priv){ #endif } - -static rtems_device_driver occan_initialize(rtems_device_major_number major, rtems_device_minor_number unused, void *arg){ - int dev_cnt,minor,subcore_cnt,devi,subi,subcores; - amba_ahb_device ambadev; - occan_priv *can; - char fs_name[20]; - rtems_status_code status; - - strcpy(fs_name,OCCAN_DEVNAME); - - /* find device on amba bus */ - dev_cnt = amba_get_number_ahbslv_devices(amba_bus,VENDOR_GAISLER,GAISLER_OCCAN); - if ( dev_cnt < 1 ){ - /* Failed to find any CAN cores! */ - printk("OCCAN: Failed to find any CAN cores\n\r"); - return -1; - } - - /* Detect System Frequency from initialized timer */ -#ifndef SYS_FREQ_HZ -#if defined(LEON3) - /* LEON3: find timer address via AMBA Plug&Play info */ - { - amba_apb_device gptimer; - LEON3_Timer_Regs_Map *tregs; - - if ( amba_find_apbslv(&amba_conf,VENDOR_GAISLER,GAISLER_GPTIMER,&gptimer) == 1 ){ - tregs = (LEON3_Timer_Regs_Map *)gptimer.start; - sys_freq_hz = (tregs->scaler_reload+1)*1000*1000; - DBG("OCCAN: detected %dHZ system frequency\n\r",sys_freq_hz); - }else{ - sys_freq_hz = 40000000; /* Default to 40MHz */ - printk("OCCAN: Failed to detect system frequency\n\r"); - } - - } -#elif defined(LEON2) - /* LEON2: use hardcoded address to get to timer */ - { - LEON_Register_Map *regs = (LEON_Register_Map *)0x80000000; - sys_freq_hz = (regs->Scaler_Reload+1)*1000*1000; - } -#else - #error CPU not supported for OC_CAN driver -#endif -#else - /* Use hardcoded frequency */ - sys_freq_hz = SYS_FREQ_HZ; -#endif - - DBG("OCCAN: Detected %dHz system frequency\n\r",sys_freq_hz); - - /* OCCAN speciality: - * Mulitple cores are supported through the same amba AHB interface. - * The number of "sub cores" can be detected by decoding the AMBA - * Plug&Play version information. verion = ncores. A maximum of 8 - * sub cores are supported, each separeated with 0x100 inbetween. - * - * Now, lets detect sub cores. - */ - - for(subcore_cnt=devi=0; devi<dev_cnt; devi++){ - amba_find_next_ahbslv(amba_bus,VENDOR_GAISLER,GAISLER_OCCAN,&ambadev,devi); - subcore_cnt += (ambadev.ver & 0x7)+1; - } - - printk("OCCAN: Found %d devs, totally %d sub cores\n\r",dev_cnt,subcore_cnt); - - /* allocate memory for cores */ - can_cores = subcore_cnt; - cans = calloc(subcore_cnt*sizeof(occan_priv),1); - - minor=0; - for(devi=0; devi<dev_cnt; devi++){ - - /* Get AHB device info */ - amba_find_next_ahbslv(amba_bus,VENDOR_GAISLER,GAISLER_OCCAN,&ambadev,devi); - subcores = (ambadev.ver & 0x7)+1; - DBG("OCCAN: on dev %d found %d sub cores\n\r",devi,subcores); - - /* loop all subcores, at least 1 */ - for(subi=0; subi<subcores; subi++){ - can = &cans[minor]; - -#ifdef OCCAN_BYTE_REGS - /* regs is byte regs */ - can->regs = (void *)(ambadev.start[0] + OCCAN_NCORE_OFS*subi); -#else - /* regs is word regs, accessed 0x100 from base address */ - can->regs = (void *)(ambadev.start[0] + OCCAN_NCORE_OFS*subi+ OCCAN_WORD_REG_OFS); -#endif - - /* remember IRQ number */ - can->irq = ambadev.irq+subi; - - /* bind filesystem name to device */ - OCCAN_DEVNAME_NO(fs_name,minor); - printk("OCCAN: Registering %s to [%d %d] @ 0x%lx irq %d\n\r",fs_name,major,minor,(unsigned int)can->regs,can->irq); - status = rtems_io_register_name(fs_name, major, minor); - if (RTEMS_SUCCESSFUL != status ) - rtems_fatal_error_occurred(status); - - /* initialize software */ - can->open = 0; - can->rxfifo = NULL; - can->txfifo = NULL; - status = rtems_semaphore_create( - rtems_build_name('C', 'd', 'v', '0'+minor), - 1, - RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | \ - RTEMS_NO_PRIORITY_CEILING, - 0, - &can->devsem); - if ( status != RTEMS_SUCCESSFUL ){ - printk("OCCAN: Failed to create dev semaphore for minor %d, (%d)\n\r",minor,status); - return RTEMS_UNSATISFIED; - } - status = rtems_semaphore_create( - rtems_build_name('C', 't', 'x', '0'+minor), - 0, - RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | \ - RTEMS_NO_PRIORITY_CEILING, - 0, - &can->txsem); - if ( status != RTEMS_SUCCESSFUL ){ - printk("OCCAN: Failed to create tx semaphore for minor %d, (%d)\n\r",minor,status); - return RTEMS_UNSATISFIED; - } - status = rtems_semaphore_create( - rtems_build_name('C', 'r', 'x', '0'+minor), - 0, - RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | \ - RTEMS_NO_PRIORITY_CEILING, - 0, - &can->rxsem); - if ( status != RTEMS_SUCCESSFUL ){ - printk("OCCAN: Failed to create rx semaphore for minor %d, (%d)\n\r",minor,status); - return RTEMS_UNSATISFIED; - } - - /* hardware init/reset */ - pelican_init(can); - - /* Setup interrupt handler for each channel */ - OCCAN_REG_INT(OCCAN_PREFIX(_interrupt_handler), can->irq, can); - - minor++; -#ifdef DEBUG_PRINT_REGMAP - pelican_regadr_print(can->regs); -#endif - } - } - +static rtems_device_driver occan_initialize(rtems_device_major_number major, rtems_device_minor_number unused, void *arg) +{ return RTEMS_SUCCESSFUL; } static rtems_device_driver occan_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg){ occan_priv *can; + struct drvmgr_dev *dev; DBG("OCCAN: Opening %d\n\r",minor); - if ( minor >= can_cores ) + /* get can device */ + if ( drvmgr_get_dev(&occan_drv_info.general, minor, &dev) ) { + DBG("Wrong minor %d\n", minor); return RTEMS_UNSATISFIED; /* NODEV */ - - /* get can device */ - can = &cans[minor]; + } + can = (occan_priv *)dev->priv; /* already opened? */ rtems_semaphore_obtain(can->devsem,RTEMS_WAIT, RTEMS_NO_TIMEOUT); @@ -1122,17 +1240,24 @@ static rtems_device_driver occan_open(rtems_device_major_number major, rtems_dev return RTEMS_SUCCESSFUL; } -static rtems_device_driver occan_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg){ - occan_priv *can = &cans[minor]; +static rtems_device_driver occan_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + occan_priv *can; + struct drvmgr_dev *dev; DBG("OCCAN: Closing %d\n\r",minor); + if ( drvmgr_get_dev(&occan_drv_info.general, minor, &dev) ) { + return RTEMS_INVALID_NAME; + } + can = (occan_priv *)dev->priv; + /* stop if running */ if ( can->started ) pelican_stop(can); /* Enter Reset Mode */ - can->regs->mode = PELICAN_MOD_RESET; + WRITE_REG(can, &can->regs->mode, PELICAN_MOD_RESET); /* free fifo memory */ occan_fifo_free(can->rxfifo); @@ -1141,16 +1266,24 @@ static rtems_device_driver occan_close(rtems_device_major_number major, rtems_de can->rxfifo = NULL; can->txfifo = NULL; + can->open = 0; + return RTEMS_SUCCESSFUL; } static rtems_device_driver occan_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg){ - occan_priv *can = &cans[minor]; + occan_priv *can; + struct drvmgr_dev *dev; rtems_libio_rw_args_t *rw_args=(rtems_libio_rw_args_t *) arg; CANMsg *dstmsg, *srcmsg; rtems_interrupt_level oldLevel; int left; + if ( drvmgr_get_dev(&occan_drv_info.general, minor, &dev) ) { + return RTEMS_INVALID_NAME; + } + can = (occan_priv *)dev->priv; + if ( !can->started ){ DBG("OCCAN: cannot read from minor %d when not started\n\r",minor); return RTEMS_RESOURCE_IN_USE; /* -EBUSY*/ @@ -1201,8 +1334,9 @@ static rtems_device_driver occan_read(rtems_device_major_number major, rtems_dev DBG("OCCAN: Waiting for RX int\n\r"); - /* wait for incomming messages */ - rtems_semaphore_obtain(can->rxsem,RTEMS_WAIT,RTEMS_NO_TIMEOUT); + /* wait for incoming messages */ + rtems_semaphore_obtain(can->rxsem, RTEMS_WAIT, + RTEMS_NO_TIMEOUT); /* did we get woken up by a BUS OFF error? */ if ( can->status & (OCCAN_STATUS_ERR_BUSOFF|OCCAN_STATUS_RESET) ){ @@ -1240,7 +1374,8 @@ static rtems_device_driver occan_read(rtems_device_major_number major, rtems_dev } static rtems_device_driver occan_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg){ - occan_priv *can = &cans[minor]; + occan_priv *can; + struct drvmgr_dev *dev; rtems_libio_rw_args_t *rw_args=(rtems_libio_rw_args_t *) arg; CANMsg *msg,*fifo_msg; rtems_interrupt_level oldLevel; @@ -1248,6 +1383,11 @@ static rtems_device_driver occan_write(rtems_device_major_number major, rtems_de DBG("OCCAN: Writing %d bytes from 0x%lx (%d)\n\r",rw_args->count,rw_args->buffer,sizeof(CANMsg)); + if ( drvmgr_get_dev(&occan_drv_info.general, minor, &dev) ) { + return RTEMS_INVALID_NAME; + } + can = (occan_priv *)dev->priv; + if ( !can->started ) return RTEMS_RESOURCE_IN_USE; /* EBUSY */ @@ -1289,6 +1429,11 @@ static rtems_device_driver occan_write(rtems_device_major_number major, rtems_de left -= sizeof(CANMsg); msg++; +#ifdef OCCAN_TX_IRQ_FLAG_FIXUP + /* Mark that we have put at least one msg in TX FIFO */ + can->sending = 1; +#endif + /* bump stat counters */ can->stats.tx_msgs++; @@ -1337,11 +1482,16 @@ static rtems_device_driver occan_write(rtems_device_major_number major, rtems_de if ( occan_fifo_empty(can->txfifo) ){ if ( !pelican_send(can,msg) ) { /* First message put directly into HW TX fifo - * This will turn TX interrupt on. - */ + * This will turn TX interrupt on. + */ left -= sizeof(CANMsg); msg++; +#ifdef OCCAN_TX_IRQ_FLAG_FIXUP + /* Mark that we have put at least one msg in TX FIFO */ + can->sending = 1; +#endif + /* bump stat counters */ can->stats.tx_msgs++; @@ -1377,15 +1527,21 @@ static rtems_device_driver occan_write(rtems_device_major_number major, rtems_de static rtems_device_driver occan_ioctl(rtems_device_major_number major, rtems_device_minor_number minor, void *arg){ int ret; occan_speed_regs timing; - occan_priv *can = &cans[minor]; + occan_priv *can; + struct drvmgr_dev *dev; unsigned int speed; - rtems_libio_ioctl_args_t *ioarg = (rtems_libio_ioctl_args_t *) arg; + rtems_libio_ioctl_args_t *ioarg = (rtems_libio_ioctl_args_t *) arg; struct occan_afilter *afilter; occan_stats *dststats; unsigned int rxcnt,txcnt; DBG("OCCAN: IOCTL %d\n\r",ioarg->command); + if ( drvmgr_get_dev(&occan_drv_info.general, minor, &dev) ) { + return RTEMS_INVALID_NAME; + } + can = (occan_priv *)dev->priv; + ioarg->ioctl_return = 0; switch(ioarg->command){ case OCCAN_IOC_SET_SPEED: @@ -1396,7 +1552,7 @@ static rtems_device_driver occan_ioctl(rtems_device_major_number major, rtems_de /* get speed rate from argument */ speed = (unsigned int)ioarg->buffer; - ret = occan_calc_speedregs(sys_freq_hz,speed,&timing); + ret = occan_calc_speedregs(can->sys_freq_hz,speed,&timing); if ( ret ) return RTEMS_INVALID_NAME; /* EINVAL */ @@ -1475,8 +1631,8 @@ static rtems_device_driver occan_ioctl(rtems_device_major_number major, rtems_de return RTEMS_INVALID_NAME; /* EINVAL */ /* copy data stats into userspace buffer */ - if ( can->rxfifo ) - can->stats.rx_sw_dovr = can->rxfifo->ovcnt; + if ( can->rxfifo ) + can->stats.rx_sw_dovr = can->rxfifo->ovcnt; *dststats = can->stats; break; @@ -1539,7 +1695,9 @@ static rtems_device_driver occan_ioctl(rtems_device_major_number major, rtems_de return RTEMS_RESOURCE_IN_USE; /* EBUSY */ if ( pelican_start(can) ) return RTEMS_NO_MEMORY; /* failed because of no memory, can happen if SET_BUFLEN failed */ - can->started = 1; + /* can->started = 1; -- Is set in pelican_start due to interrupt may occur before we + * get here. + */ break; case OCCAN_IOC_STOP: @@ -1555,7 +1713,9 @@ static rtems_device_driver occan_ioctl(rtems_device_major_number major, rtems_de return RTEMS_SUCCESSFUL; } -static void occan_interrupt(occan_priv *can){ +void occan_interrupt(void *arg) +{ + occan_priv *can = arg; unsigned char iflags; pelican_regs *regs = can->regs; CANMsg *msg; @@ -1563,11 +1723,33 @@ static void occan_interrupt(occan_priv *can){ unsigned char tmp, errcode, arbcode; int tx_error_cnt,rx_error_cnt; - can->stats.ints++; + if ( !can->started ) + return; /* Spurious Interrupt, do nothing */ + + while (1) { + + iflags = READ_REG(can, &can->regs->intflags); + +#ifdef OCCAN_TX_IRQ_FLAG_FIXUP + /* TX IRQ may be cleared when reading regs->intflags due + * to a bug in some chips. Instead of looking at the TX_IRQ_FLAG + * the TX-fifo emoty register is looked at when something has + * been scheduled for transmission. + */ + if ((iflags & PELICAN_IF_TX) == 0) { + if (can->sending && pelican_tx_ready(can)) { + can->sending = 0; + iflags |= PELICAN_IF_TX; + } + } +#endif - while ( (iflags = READ_REG(&can->regs->intflags)) != 0 ){ + if (iflags == 0) + break; /* still interrupts to handle */ + can->stats.ints++; + if ( iflags & PELICAN_IF_RX ){ /* the rx fifo is not empty * put 1 message into rxfifo for later use @@ -1575,52 +1757,53 @@ static void occan_interrupt(occan_priv *can){ /* get empty (or make room) message */ msg = occan_fifo_put_claim(can->rxfifo,1); - tmp = READ_REG(®s->rx_fi_xff); + tmp = READ_REG(can, ®s->rx_fi_xff); msg->extended = tmp >> 7; msg->rtr = (tmp >> 6) & 1; msg->len = tmp = tmp & 0x0f; if ( msg->extended ){ /* extended message */ - msg->id = READ_REG(®s->msg.rx_eff.id[0])<<(5+8+8) | - READ_REG(®s->msg.rx_eff.id[1])<<(5+8) | - READ_REG(®s->msg.rx_eff.id[2])<<5 | - READ_REG(®s->msg.rx_eff.id[3])>>3; + msg->id = READ_REG(can, ®s->msg.rx_eff.id[0])<<(5+8+8) | + READ_REG(can, ®s->msg.rx_eff.id[1])<<(5+8) | + READ_REG(can, ®s->msg.rx_eff.id[2])<<5 | + READ_REG(can, ®s->msg.rx_eff.id[3])>>3; + while(tmp--){ - msg->data[tmp] = READ_REG(®s->msg.rx_eff.data[tmp]); + msg->data[tmp] = READ_REG(can, ®s->msg.rx_eff.data[tmp]); } /* - msg->data[0] = READ_REG(®s->msg.rx_eff.data[0]); - msg->data[1] = READ_REG(®s->msg.rx_eff.data[1]); - msg->data[2] = READ_REG(®s->msg.rx_eff.data[2]); - msg->data[3] = READ_REG(®s->msg.rx_eff.data[3]); - msg->data[4] = READ_REG(®s->msg.rx_eff.data[4]); - msg->data[5] = READ_REG(®s->msg.rx_eff.data[5]); - msg->data[6] = READ_REG(®s->msg.rx_eff.data[6]); - msg->data[7] = READ_REG(®s->msg.rx_eff.data[7]); + msg->data[0] = READ_REG(can, ®s->msg.rx_eff.data[0]); + msg->data[1] = READ_REG(can, ®s->msg.rx_eff.data[1]); + msg->data[2] = READ_REG(can, ®s->msg.rx_eff.data[2]); + msg->data[3] = READ_REG(can, ®s->msg.rx_eff.data[3]); + msg->data[4] = READ_REG(can, ®s->msg.rx_eff.data[4]); + msg->data[5] = READ_REG(can, ®s->msg.rx_eff.data[5]); + msg->data[6] = READ_REG(can, ®s->msg.rx_eff.data[6]); + msg->data[7] = READ_REG(can, ®s->msg.rx_eff.data[7]); */ }else{ /* standard message */ - msg->id = READ_REG(®s->msg.rx_sff.id[0])<<3 | - READ_REG(®s->msg.rx_sff.id[1])>>5; + msg->id = READ_REG(can, ®s->msg.rx_sff.id[0])<<3 | + READ_REG(can, ®s->msg.rx_sff.id[1])>>5; while(tmp--){ - msg->data[tmp] = READ_REG(®s->msg.rx_sff.data[tmp]); + msg->data[tmp] = READ_REG(can, ®s->msg.rx_sff.data[tmp]); } /* - msg->data[0] = READ_REG(®s->msg.rx_sff.data[0]); - msg->data[1] = READ_REG(®s->msg.rx_sff.data[1]); - msg->data[2] = READ_REG(®s->msg.rx_sff.data[2]); - msg->data[3] = READ_REG(®s->msg.rx_sff.data[3]); - msg->data[4] = READ_REG(®s->msg.rx_sff.data[4]); - msg->data[5] = READ_REG(®s->msg.rx_sff.data[5]); - msg->data[6] = READ_REG(®s->msg.rx_sff.data[6]); - msg->data[7] = READ_REG(®s->msg.rx_sff.data[7]); + msg->data[0] = READ_REG(can, ®s->msg.rx_sff.data[0]); + msg->data[1] = READ_REG(can, ®s->msg.rx_sff.data[1]); + msg->data[2] = READ_REG(can, ®s->msg.rx_sff.data[2]); + msg->data[3] = READ_REG(can, ®s->msg.rx_sff.data[3]); + msg->data[4] = READ_REG(can, ®s->msg.rx_sff.data[4]); + msg->data[5] = READ_REG(can, ®s->msg.rx_sff.data[5]); + msg->data[6] = READ_REG(can, ®s->msg.rx_sff.data[6]); + msg->data[7] = READ_REG(can, ®s->msg.rx_sff.data[7]); */ } /* Re-Enable RX buffer for a new message */ - regs->cmd = PELICAN_CMD_RELRXBUF; + WRITE_REG(can, ®s->cmd, PELICAN_CMD_RELRXBUF); /* make message available to the user */ occan_fifo_put(can->rxfifo); @@ -1632,7 +1815,8 @@ static void occan_interrupt(occan_priv *can){ signal_rx = 1; } - if ( iflags & PELICAN_IF_TX ){ + if ( iflags & PELICAN_IF_TX ) { + /* there is room in tx fifo of HW */ if ( !occan_fifo_empty(can->txfifo) ){ @@ -1651,6 +1835,9 @@ static void occan_interrupt(occan_priv *can){ can->status |= OCCAN_STATUS_QUEUE_ERROR; can->stats.tx_buf_error++; } +#ifdef OCCAN_TX_IRQ_FLAG_FIXUP + can->sending = 1; +#endif /* free software-fifo space taken by sent message */ occan_fifo_get(can->txfifo); @@ -1664,8 +1851,8 @@ static void occan_interrupt(occan_priv *can){ } if ( iflags & PELICAN_IF_ERRW ){ - tx_error_cnt = READ_REG(®s->tx_err_cnt); - rx_error_cnt = READ_REG(®s->rx_err_cnt); + tx_error_cnt = READ_REG(can, ®s->tx_err_cnt); + rx_error_cnt = READ_REG(can, ®s->rx_err_cnt); /* 1. if bus off tx error counter = 127 */ if ( (tx_error_cnt > 96) || (rx_error_cnt > 96) ){ @@ -1673,7 +1860,7 @@ static void occan_interrupt(occan_priv *can){ can->status |= OCCAN_STATUS_WARN; /* check reset bit for reset mode */ - if ( READ_REG(®s->mode) & PELICAN_MOD_RESET ){ + if ( READ_REG(can, ®s->mode) & PELICAN_MOD_RESET ){ /* in reset mode ==> bus off */ can->status |= OCCAN_STATUS_ERR_BUSOFF | OCCAN_STATUS_RESET; @@ -1681,7 +1868,7 @@ static void occan_interrupt(occan_priv *can){ * turn off interrupts * enter reset mode (HW already done that for us) */ - regs->inten = 0; + WRITE_REG(can, ®s->inten,0); /* Indicate that we are not started any more. * This will make write/read return with EBUSY @@ -1718,8 +1905,8 @@ static void occan_interrupt(occan_priv *can){ /* Let the error counters decide what kind of * interrupt it was. In/Out of EPassive area. */ - tx_error_cnt = READ_REG(®s->tx_err_cnt); - rx_error_cnt = READ_REG(®s->rx_err_cnt); + tx_error_cnt = READ_REG(can, ®s->tx_err_cnt); + rx_error_cnt = READ_REG(can, ®s->rx_err_cnt); if ( (tx_error_cnt > 127) || (rx_error_cnt > 127) ){ can->status |= OCCAN_STATUS_ERR_PASSIVE; @@ -1732,7 +1919,7 @@ static void occan_interrupt(occan_priv *can){ } if ( iflags & PELICAN_IF_ARB){ - arbcode = READ_REG(®s->arbcode); + arbcode = READ_REG(can, ®s->arbcode); can->stats.err_arb_bitnum[arbcode & PELICAN_ARB_BITS]++; can->stats.err_arb++; DBG("OCCAN_INT: ARB (0x%x)\n\r",arbcode & PELICAN_ARB_BITS); @@ -1743,7 +1930,7 @@ static void occan_interrupt(occan_priv *can){ * statistics. Error Register is decoded * and put into can->stats. */ - errcode = READ_REG(®s->errcode); + errcode = READ_REG(can, ®s->errcode); switch( errcode & PELICAN_ECC_CODE ){ case PELICAN_ECC_CODE_BIT: can->stats.err_bus_bit++; @@ -1784,56 +1971,12 @@ static void occan_interrupt(occan_priv *can){ } } -#ifdef OCCAN_DEFINE_INTHANDLER -static void occan_interrupt_handler(rtems_vector_number v){ - int minor; - - /* convert to */ - for(minor = 0; minor < can_cores; minor++) { - if ( v == (cans[minor].irq+0x10) ) { - occan_interrupt(&cans[minor]); - return; - } - } -} -#endif - -#define OCCAN_DRIVER_TABLE_ENTRY { occan_initialize, occan_open, occan_close, occan_read, occan_write, occan_ioctl } - -static rtems_driver_address_table occan_driver = OCCAN_DRIVER_TABLE_ENTRY; - -int OCCAN_PREFIX(_register)(amba_confarea_type *bus){ - rtems_status_code r; - rtems_device_major_number m; - - amba_bus = bus; - if ( !bus ) - return 1; - - if ((r = rtems_io_register_driver(0, &occan_driver, &m)) == RTEMS_SUCCESSFUL) { - DBG("OCCAN driver successfully registered, major: %d\n\r", m); - }else{ - switch(r) { - case RTEMS_TOO_MANY: - printk("OCCAN rtems_io_register_driver failed: RTEMS_TOO_MANY\n\r"); break; - case RTEMS_INVALID_NUMBER: - printk("OCCAN rtems_io_register_driver failed: RTEMS_INVALID_NUMBER\n\r"); break; - case RTEMS_RESOURCE_IN_USE: - printk("OCCAN rtems_io_register_driver failed: RTEMS_RESOURCE_IN_USE\n\r"); break; - default: - printk("OCCAN rtems_io_register_driver failed\n\r"); - } - return 1; - } - return 0; -} - - /******************************************************************************* * FIFO IMPLEMENTATION */ -static occan_fifo *occan_fifo_create(int cnt){ +static occan_fifo *occan_fifo_create(int cnt) +{ occan_fifo *fifo; fifo = malloc(sizeof(occan_fifo)+cnt*sizeof(CANMsg)); if ( fifo ){ @@ -1848,21 +1991,25 @@ static occan_fifo *occan_fifo_create(int cnt){ return fifo; } -static void occan_fifo_free(occan_fifo *fifo){ +static void occan_fifo_free(occan_fifo *fifo) +{ if ( fifo ) free(fifo); } -static int occan_fifo_full(occan_fifo *fifo){ +static int occan_fifo_full(occan_fifo *fifo) +{ return fifo->full; } -static int occan_fifo_empty(occan_fifo *fifo){ +static int occan_fifo_empty(occan_fifo *fifo) +{ return (!fifo->full) && (fifo->head == fifo->tail); } /* Stage 1 - get buffer to fill (never fails if force!=0) */ -static CANMsg *occan_fifo_put_claim(occan_fifo *fifo, int force){ +static CANMsg *occan_fifo_put_claim(occan_fifo *fifo, int force) +{ if ( !fifo ) return NULL; @@ -1880,7 +2027,8 @@ static CANMsg *occan_fifo_put_claim(occan_fifo *fifo, int force){ } /* Stage 2 - increment indexes */ -static void occan_fifo_put(occan_fifo *fifo){ +static void occan_fifo_put(occan_fifo *fifo) +{ if ( occan_fifo_full(fifo) ) return; @@ -1891,7 +2039,8 @@ static void occan_fifo_put(occan_fifo *fifo){ fifo->full = 1; } -static CANMsg *occan_fifo_claim_get(occan_fifo *fifo){ +static CANMsg *occan_fifo_claim_get(occan_fifo *fifo) +{ if ( occan_fifo_empty(fifo) ) return NULL; @@ -1900,7 +2049,8 @@ static CANMsg *occan_fifo_claim_get(occan_fifo *fifo){ } -static void occan_fifo_get(occan_fifo *fifo){ +static void occan_fifo_get(occan_fifo *fifo) +{ if ( !fifo ) return; @@ -1908,14 +2058,16 @@ static void occan_fifo_get(occan_fifo *fifo){ return; /* increment indexes */ - fifo->tail = (fifo->tail >= &fifo->base[fifo->cnt-1])? fifo->base : fifo->tail+1; + fifo->tail = (fifo->tail >= &fifo->base[fifo->cnt-1]) ? + fifo->base : fifo->tail+1; fifo->full = 0; } -static void occan_fifo_clr(occan_fifo *fifo){ - fifo->full = 0; - fifo->ovcnt = 0; - fifo->head = fifo->tail = fifo->base; +static void occan_fifo_clr(occan_fifo *fifo) +{ + fifo->full = 0; + fifo->ovcnt = 0; + fifo->head = fifo->tail = fifo->base; } -/*******************************************************************************/ +/******************************************************************************/ diff --git a/c/src/lib/libbsp/sparc/shared/can/occan_pci.c b/c/src/lib/libbsp/sparc/shared/can/occan_pci.c deleted file mode 100644 index 2dd2e5be21..0000000000 --- a/c/src/lib/libbsp/sparc/shared/can/occan_pci.c +++ /dev/null @@ -1,64 +0,0 @@ - -/* PCI cannot do byte accesses to addresses aligned byte wise - * Use alternative reg map. - */ -#define OCCAN_WORD_REGS - -/* Set registered device name */ -#define OCCAN_DEVNAME "/dev/occanpci0" -#define OCCAN_DEVNAME_NO(devstr,no) ((devstr)[13]='0'+(no)) - -/* Any non-static function will begin with */ -#define OCCAN_PREFIX(name) occanpci##name - -/* do nothing, assume that the interrupt handler is called - * setup externally calling b1553_interrupt_handler. - */ -#define OCCAN_REG_INT(handler,irq,arg) \ - if ( occan_pci_int_reg ) \ - occan_pci_int_reg(handler,irq,arg); - -void (*occan_pci_int_reg)(void *handler, int irq, void *arg) = 0; - -void occanpci_interrupt_handler(int irq, void *arg); - -/* AMBA Bus is clocked using the PCI clock (33.3MHz) */ -#define SYS_FREQ_HZ 33333333 - -/* Enable two redundant channels */ -#define REDUNDANT_CHANNELS 2 - -#define OCCAN_SET_CHANNEL(priv,channel) occanpci_set_channel(priv,channel) - -#include "occan.c" - -/* Define method that sets redundant channel - * The channel select register: - * 0x00 = byte regs - * 0x40 = channel select - * 0x80 = word regs - */ -static void inline occanpci_set_channel(occan_priv *priv, int channel){ - unsigned int *chan_sel = (unsigned int *)(((unsigned int)priv->regs & ~0xff)+0x40); - if ( channel == 0 ) - *chan_sel = 0; - else - *chan_sel = 0xffffffff; -} - -int occan_pci_register(amba_confarea_type *bus) -{ - /* Setup configuration */ - - /* Register the driver */ - return OCCAN_PREFIX(_register)(bus); -} - - -/* Call this from PCI interrupt handler - * irq = the irq number of the HW device local to that IRQMP controller - * - */ -void occanpci_interrupt_handler(int irq, void *arg){ - occan_interrupt(arg); -} 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..1921c6bd12 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/can/satcan.c @@ -0,0 +1,721 @@ +/* + * RTEMS SatCAN FPGA driver + * + * -------------------------------------------------------------------------- + * -- This file is a part of GAISLER RESEARCH source code. + * -- Copyright (C) 2008, Gaisler Research AB - all rights reserved. + * -- + * -- ANY USE OR REDISTRIBUTION IN PART OR IN WHOLE MUST BE HANDLED IN + * -- ACCORDANCE WITH THE GAISLER LICENSE AGREEMENT AND MUST BE APPROVED + * -- IN ADVANCE IN WRITING. + * -- + * -- BY DEFAULT, DISTRIBUTION OR DISCLOSURE IS NOT PERMITTED. + * -------------------------------------------------------------------------- + * + * Based on the OC_CAN driver. + * + */ + +#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/drvmgr/ambapp_bus.c b/c/src/lib/libbsp/sparc/shared/drvmgr/ambapp_bus.c new file mode 100644 index 0000000000..007cefb9e2 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/drvmgr/ambapp_bus.c @@ -0,0 +1,762 @@ +/* General part of a AMBA Plug & Play bus driver. + * + * COPYRIGHT (c) 2008. + * Aeroflex Gaisler. + * + * 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. + * + * This is the general part of the different AMBA Plug & Play + * drivers. The drivers are wrappers around this driver, making + * the code size smaller for systems with multiple AMBA Plug & + * Play buses. + * + * The BSP define APBUART_INFO_AVAIL in order to add the info routine + * used for debugging. + * + * + * 2009-11-20, Daniel Hellstrom <daniel@gaisler.com> + * Added support for AMBA-RMAP + * + * 2008-12-03, Daniel Hellstrom <daniel@gaisler.com> + * Created + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> + +#include <bsp.h> +#include <ambapp.h> + +/*#define DEBUG 1*/ +#define DBG(args...) +/*#define DBG(args...) printk(args)*/ + +struct grlib_gptimer_regs { + volatile unsigned int scaler_value; /* common timer registers */ + volatile unsigned int scaler_reload; + volatile unsigned int status; + volatile unsigned int notused; +}; + +/* AMBA IMPLEMENTATION */ + +int ambapp_bus_init1(struct drvmgr_bus *bus); +int ambapp_bus_remove(struct drvmgr_bus *bus); +int ambapp_unite(struct drvmgr_drv *drv, struct drvmgr_dev *dev); +int ambapp_int_register(struct drvmgr_dev *dev, int index, const char *info, drvmgr_isr isr, void *arg); +int ambapp_int_unregister(struct drvmgr_dev *dev, int index, drvmgr_isr isr, void *arg); +int ambapp_int_clear(struct drvmgr_dev *dev, int index); +int ambapp_int_mask(struct drvmgr_dev *dev, int index); +int ambapp_int_unmask(struct drvmgr_dev *dev, int index); +int ambapp_get_params(struct drvmgr_dev *dev, struct drvmgr_bus_params *params); +int ambapp_bus_freq_get( + struct drvmgr_dev *dev, + int options, + unsigned int *freq_hz); +void ambapp_dev_info(struct drvmgr_dev *, void (*print)(void *p, char *str), void *p); + +struct drvmgr_bus_ops ambapp_bus_ops = +{ + .init = + { + /* init1 */ ambapp_bus_init1, + /* init2 */ NULL, + /* init3 */ NULL, + /* init4 */ NULL + }, + .remove = ambapp_bus_remove, + .unite = ambapp_unite, + .int_register = ambapp_int_register, + .int_unregister = ambapp_int_unregister, + .int_clear = ambapp_int_clear, + .int_mask = ambapp_int_mask, + .int_unmask = ambapp_int_unmask, + .get_params = ambapp_get_params, + .freq_get = ambapp_bus_freq_get, +#ifdef AMBAPPBUS_INFO_AVAIL + .info_dev = ambapp_dev_info, +#endif +}; + +struct ambapp_priv { + struct ambapp_config *config; +}; + +int ambapp_unite(struct drvmgr_drv *drv, struct drvmgr_dev *dev) +{ + struct amba_drv_info *adrv; + struct amba_dev_id *id; + struct amba_dev_info *amba; + + if ( !drv || !dev || !dev->parent ) + return 0; + + if ( ! (((drv->bus_type == DRVMGR_BUS_TYPE_AMBAPP) && (dev->parent->bus_type == DRVMGR_BUS_TYPE_AMBAPP)) || + ((drv->bus_type == DRVMGR_BUS_TYPE_AMBAPP_RMAP) && (dev->parent->bus_type == DRVMGR_BUS_TYPE_AMBAPP_RMAP)) || + ((drv->bus_type == DRVMGR_BUS_TYPE_AMBAPP_DIST) && (dev->parent->bus_type == DRVMGR_BUS_TYPE_AMBAPP_DIST))) + ) { + return 0; + } + + amba = (struct amba_dev_info *)dev->businfo; + if ( !amba ) + return 0; + + adrv = (struct amba_drv_info *)drv; + id = adrv->ids; + if ( !id ) + return 0; + while( id->vendor != 0 ) { + if ( (id->vendor == amba->id.vendor) && + (id->device == amba->id.device) ) { + /* Unite device and driver */ + DBG("DRV 0x%x and DEV 0x%x united\n", (unsigned int)drv, (unsigned int)dev); + return 1; + } + id++; + } + + return 0; +} + +static int ambapp_int_get(struct drvmgr_dev *dev, int index) +{ + int irq; + + /* Relative (positive) or absolute (negative) IRQ number */ + if ( index >= 0 ) { + /* IRQ Index relative to Cores base IRQ */ + + /* Get Base IRQ */ + irq = ((struct amba_dev_info *)dev->businfo)->info.irq; + if ( irq < 0 ) + return -1; + irq += index; + } else { + /* Absolute IRQ number */ + irq = -index; + } + return irq; +} + +int ambapp_int_register( + struct drvmgr_dev *dev, + int index, + const char *info, + drvmgr_isr isr, + void *arg) +{ + struct drvmgr_dev *busdev; + struct ambapp_priv *priv; + int irq; + + busdev = dev->parent->dev; + priv = dev->parent->priv; + + /* Get IRQ number from index and device information */ + irq = ambapp_int_get(dev, index); + if ( irq < 0 ) + return DRVMGR_EINVAL; + + DBG("Register interrupt on 0x%x for dev 0x%x (IRQ: %d)\n", (unsigned int)busdev, (unsigned int)dev, irq); + + if ( priv->config->ops->int_register ) { + /* Let device override driver default */ + return priv->config->ops->int_register(dev, irq, info, isr, arg); + } else { + return DRVMGR_ENOSYS; + } +} + +int ambapp_int_unregister( + struct drvmgr_dev *dev, + int index, + drvmgr_isr isr, + void *arg) +{ + struct drvmgr_dev *busdev; + struct ambapp_priv *priv; + int irq; + + busdev = dev->parent->dev; + priv = dev->parent->priv; + + /* Get IRQ number from index and device information */ + irq = ambapp_int_get(dev, index); + if ( irq < 0 ) + return DRVMGR_EINVAL; + + DBG("Unregister interrupt on 0x%x for dev 0x%x (IRQ: %d)\n", (unsigned int)busdev, (unsigned int)dev, irq); + + if ( priv->config->ops->int_unregister ) { + /* Let device override driver default */ + return priv->config->ops->int_unregister(dev, irq, isr, arg); + } else { + return DRVMGR_ENOSYS; + } +} + +int ambapp_int_clear( + struct drvmgr_dev *dev, + int index) +{ + struct drvmgr_dev *busdev; + struct ambapp_priv *priv; + int irq; + + busdev = dev->parent->dev; + priv = dev->parent->priv; + + /* Get IRQ number from index and device information */ + irq = ambapp_int_get(dev, index); + if ( irq < 0 ) + return -1; + + DBG("Clear interrupt on 0x%x for dev 0x%x (IRQ: %d)\n", (unsigned int)busdev, (unsigned int)dev, irq); + + if ( priv->config->ops->int_clear ) { + /* Let device override driver default */ + return priv->config->ops->int_clear(dev, irq); + } else { + return DRVMGR_ENOSYS; + } +} + +int ambapp_int_mask( + struct drvmgr_dev *dev, + int index) +{ + struct drvmgr_dev *busdev; + struct ambapp_priv *priv; + int irq; + + busdev = dev->parent->dev; + priv = dev->parent->priv; + + /* Get IRQ number from index and device information */ + irq = ambapp_int_get(dev, index); + if ( irq < 0 ) + return -1; + + DBG("MASK interrupt on 0x%x for dev 0x%x (IRQ: %d)\n", (unsigned int)busdev, (unsigned int)dev, irq); + + if ( priv->config->ops->int_mask ) { + /* Let device override driver default */ + return priv->config->ops->int_mask(dev, irq); + } else { + return DRVMGR_ENOSYS; + } +} + +int ambapp_int_unmask( + struct drvmgr_dev *dev, + int index) +{ + struct drvmgr_dev *busdev; + struct ambapp_priv *priv; + int irq; + + busdev = dev->parent->dev; + priv = dev->parent->priv; + + /* Get IRQ number from index and device information */ + irq = ambapp_int_get(dev, index); + if ( irq < 0 ) + return DRVMGR_EINVAL; + + DBG("UNMASK interrupt on 0x%x for dev 0x%x (IRQ: %d)\n", (unsigned int)busdev, (unsigned int)dev, irq); + + if ( priv->config->ops->int_unmask ) { + /* Let device override driver default */ + return priv->config->ops->int_unmask(dev, irq); + } else { + return DRVMGR_ENOSYS; + } +} + +/* Assign frequency to an AMBA Bus */ +void ambapp_bus_freq_register( + struct drvmgr_dev *dev, + int amba_interface, + unsigned int freq_hz + ) +{ + struct ambapp_priv *priv = (struct ambapp_priv *)dev->parent->priv; + struct ambapp_dev *adev; + struct amba_dev_info *pnp = dev->businfo; + + if ( freq_hz == 0 ) + return; + + if ( amba_interface == DEV_AHB_MST ) { + adev = (struct ambapp_dev *) + ((unsigned int)pnp->info.ahb_mst - + sizeof(struct ambapp_dev)); + } else if ( amba_interface == DEV_AHB_SLV ) { + adev = (struct ambapp_dev *) + ((unsigned int)pnp->info.ahb_slv - + sizeof(struct ambapp_dev)); + } else if ( amba_interface == DEV_APB_SLV ) { + adev = (struct ambapp_dev *) + ((unsigned int)pnp->info.apb_slv - + sizeof(struct ambapp_dev)); + } else { + return; + } + + /* Calculate Top bus frequency from lower part. The frequency comes + * from some kind of hardware able to report local bus frequency. + */ + ambapp_freq_init(priv->config->abus, adev, freq_hz); +} + +int ambapp_bus_freq_get( + struct drvmgr_dev *dev, + int options, + unsigned int *freq_hz) +{ + struct ambapp_priv *priv = (struct ambapp_priv *)dev->parent->priv; + struct ambapp_dev *adev; + struct amba_dev_info *pnp = dev->businfo; + + if ( options == DEV_AHB_MST ) { + adev = (struct ambapp_dev *) + ((unsigned int)pnp->info.ahb_mst - + sizeof(struct ambapp_dev)); + } else if ( options == DEV_AHB_SLV ) { + adev = (struct ambapp_dev *) + ((unsigned int)pnp->info.ahb_slv - + sizeof(struct ambapp_dev)); + } else if ( options == DEV_APB_SLV ) { + adev = (struct ambapp_dev *) + ((unsigned int)pnp->info.apb_slv - + sizeof(struct ambapp_dev)); + } else { + *freq_hz = 0; + return -1; + } + + /* Calculate core/bus frequency from top most bus frequency. */ + *freq_hz = ambapp_freq_get(priv->config->abus, adev); + if ( *freq_hz == 0 ) + return -1; + return 0; +} + +int ambapp_get_params(struct drvmgr_dev *dev, struct drvmgr_bus_params *params) +{ + struct ambapp_priv *priv = dev->parent->priv; + + if ( priv->config->ops->get_params ) { + /* Let device override driver default */ + return priv->config->ops->get_params(dev, params); + } else { + return -1; + } +} + +#ifdef AMBAPPBUS_INFO_AVAIL +void ambapp_dev_info( + struct drvmgr_dev *dev, + void (*print_line)(void *p, char *str), + void *p) +{ + struct amba_dev_info *devinfo; + struct ambapp_core *core; + char buf[64]; + int ver, i; + char *str1, *str2, *str3; + unsigned int ahbmst_freq, ahbslv_freq, apbslv_freq; + + if (!dev) + return; + + devinfo = (struct amba_dev_info *)dev->businfo; + if (!devinfo) + return; + core = &devinfo->info; + + print_line(p, "AMBA PnP DEVICE"); + + str1 = ambapp_vendor_id2str(devinfo->id.vendor); + if (str1 == NULL) + str1 = "unknown"; + sprintf(buf, "VENDOR ID: 0x%04x (%s)", devinfo->id.vendor, str1); + print_line(p, buf); + + str1 = ambapp_device_id2str(devinfo->id.vendor, devinfo->id.device); + if (str1 == NULL) + str1 = "unknown"; + sprintf(buf, "DEVICE ID: 0x%04x (%s)", devinfo->id.device, str1); + print_line(p, buf); + + ahbmst_freq = ahbslv_freq = apbslv_freq = 0; + ver = 0; + str1 = str2 = str3 = ""; + if (core->ahb_mst) { + str1 = "AHBMST "; + ver = core->ahb_mst->ver; + ambapp_bus_freq_get(dev, DEV_AHB_MST, &ahbmst_freq); + } + if (core->ahb_slv) { + str2 = "AHBSLV "; + ver = core->ahb_slv->ver; + ambapp_bus_freq_get(dev, DEV_AHB_SLV, &ahbslv_freq); + } + if (core->apb_slv) { + str3 = "APBSLV"; + ver = core->apb_slv->ver; + ambapp_bus_freq_get(dev, DEV_APB_SLV, &apbslv_freq); + } + + sprintf(buf, "IRQ: %d", ambapp_int_get(dev, 0)); + print_line(p, buf); + + sprintf(buf, "VERSION: 0x%x", ver); + print_line(p, buf); + + sprintf(buf, "ambapp_core: %p", core); + print_line(p, buf); + + sprintf(buf, "interfaces: %s%s%s", str1, str2, str3); + print_line(p, buf); + + if (ahbmst_freq != 0) { + sprintf(buf, "AHBMST FREQ: %dkHz", ahbmst_freq/1000); + print_line(p, buf); + } + + if (ahbslv_freq != 0) { + sprintf(buf, "AHBSLV FREQ: %dkHz", ahbslv_freq/1000); + print_line(p, buf); + } + + if (apbslv_freq != 0) { + sprintf(buf, "APBSLV FREQ: %dkHz", apbslv_freq/1000); + print_line(p, buf); + } + + if (core->ahb_slv) { + for(i=0; i<4; i++) { + if (core->ahb_slv->type[i] == AMBA_TYPE_AHBIO) + str1 = " ahbio"; + else if (core->ahb_slv->type[i] == AMBA_TYPE_MEM) + str1 = "ahbmem"; + else + continue; + sprintf(buf, " %s[%d]: 0x%08x-0x%08x", str1, i, + core->ahb_slv->start[i], + core->ahb_slv->start[i]+core->ahb_slv->mask[i]-1); + print_line(p, buf); + } + } + if (core->apb_slv) { + sprintf(buf, " apb: 0x%08x-0x%08x", + core->apb_slv->start, + core->apb_slv->start + core->apb_slv->mask - 1); + print_line(p, buf); + } +} +#endif + +/* Fix device in last stage */ +int ambapp_dev_fixup(struct drvmgr_dev *dev, struct amba_dev_info *pnp) +{ + /* OCCAN speciality: + * Mulitple cores are supported through the same amba AHB interface. + * The number of "sub cores" can be detected by decoding the AMBA + * Plug&Play version information. verion = ncores. A maximum of 8 + * sub cores are supported, each separeated with 0x100 inbetween. + * + * Now, lets detect sub cores. + */ + if ( (pnp->info.device == GAISLER_CANAHB) && (pnp->info.vendor == VENDOR_GAISLER) ) { + struct drvmgr_dev *newdev; + struct amba_dev_info *pnpinfo; + int subcores; + int core; + + subcores = (pnp->info.ahb_slv->ver & 0x7) + 1; + for(core = 1; core < subcores; core++) { + drvmgr_alloc_dev(&newdev, sizeof(*pnpinfo)); + memcpy(newdev, dev, sizeof(*newdev)); + pnpinfo = (struct amba_dev_info *)(newdev+1); + memcpy(pnpinfo, pnp, sizeof(*pnp)); + pnpinfo->info.index = core; + pnpinfo->info.irq += core; + newdev->businfo = (void *)pnpinfo; + + /* Register device */ + drvmgr_dev_register(newdev); + } + } else if ( (pnp->info.device == GAISLER_GPIO) && (pnp->info.vendor == VENDOR_GAISLER) ) { + /* PIO[N] is connected to IRQ[N]. */ + pnp->info.irq = 0; + } + return 0; +} + +struct ambapp_dev_reg_struct { + struct ambapp_bus *abus; + struct drvmgr_bus *bus; + struct ambapp_dev *ahb_mst; + struct ambapp_dev *ahb_slv; + struct ambapp_dev *apb_slv; +}; + +void ambapp_core_register( + struct ambapp_dev *ahb_mst, + struct ambapp_dev *ahb_slv, + struct ambapp_dev *apb_slv, + struct ambapp_dev_reg_struct *arg + ) +{ + struct drvmgr_dev *newdev; + struct amba_dev_info *pnpinfo; + unsigned short device; + unsigned char vendor; + int namelen; + char buf[64]; + + if ( ahb_mst ) { + device = ahb_mst->device; + vendor = ahb_mst->vendor; + }else if ( ahb_slv ) { + device = ahb_slv->device; + vendor = ahb_slv->vendor; + }else if( apb_slv ) { + device = apb_slv->device; + vendor = apb_slv->vendor; + } else { + DBG("NO DEV!\n"); + return; + } + + DBG("CORE REGISTER DEV [%x:%x] MST: 0x%x, SLV: 0x%x, APB: 0x%x\n", vendor, device, (unsigned int)ahb_mst, (unsigned int)ahb_slv, (unsigned int)apb_slv); + + /* Get unique device name from AMBA data base by combining VENDOR and + * DEVICE short names + */ + namelen = ambapp_vendev_id2str(vendor, device, buf); + + /* Allocate a device */ + drvmgr_alloc_dev(&newdev, sizeof(struct amba_dev_info) + namelen); + pnpinfo = (struct amba_dev_info *)(newdev + 1); + newdev->parent = arg->bus; /* Ourselfs */ + newdev->minor_drv = 0; + newdev->minor_bus = 0; + newdev->priv = NULL; + newdev->drv = NULL; + if (namelen > 0) { + newdev->name = (char *)(pnpinfo + 1); + strcpy(newdev->name, buf); + } else { + newdev->name = NULL; + } + newdev->next_in_drv = NULL; + newdev->bus = NULL; + + /* Init PnP information, Assign Core interfaces with this device */ + pnpinfo->id.vendor = vendor; + pnpinfo->id.device = device; + pnpinfo->info.vendor = vendor; + pnpinfo->info.device = device; + pnpinfo->info.index = 0; + if ( ahb_mst ) { + pnpinfo->info.ahb_mst = (struct ambapp_ahb_info *) + ahb_mst->devinfo; + ambapp_alloc_dev(ahb_mst, (void *)newdev); + if ( pnpinfo->info.ahb_mst->irq ) + pnpinfo->info.irq = pnpinfo->info.ahb_mst->irq; + } + if ( ahb_slv ) { + pnpinfo->info.ahb_slv = (struct ambapp_ahb_info *) + ahb_slv->devinfo; + ambapp_alloc_dev(ahb_slv, (void *)newdev); + if ( pnpinfo->info.ahb_slv->irq ) + pnpinfo->info.irq = pnpinfo->info.ahb_slv->irq; + } + if ( apb_slv ) { + pnpinfo->info.apb_slv = (struct ambapp_apb_info *) + apb_slv->devinfo; + ambapp_alloc_dev(apb_slv, (void *)newdev); + if ( pnpinfo->info.apb_slv->irq ) + pnpinfo->info.irq = pnpinfo->info.apb_slv->irq; + } + if ( pnpinfo->info.irq == 0 ) + pnpinfo->info.irq = -1; /* indicate no IRQ */ + + /* Connect device with PnP information */ + newdev->businfo = (void *)pnpinfo; + + ambapp_dev_fixup(newdev, pnpinfo); + + /* Register New Device */ + drvmgr_dev_register(newdev); +} + +/* Register one AMBA device */ +int ambapp_dev_register(struct ambapp_dev *dev, int index, void *arg) +{ + struct ambapp_dev_reg_struct *p = arg; + +#ifdef DEBUG + char *type; + + if ( dev->dev_type == DEV_AHB_MST ) + type = "AHB MST"; + else if ( dev->dev_type == DEV_AHB_SLV ) + type = "AHB SLV"; + else if ( dev->dev_type == DEV_APB_SLV ) + type = "APB SLV"; + + DBG("Found [%d:%x:%x], %s\n", index, dev->vendor, dev->device, type); +#endif + + if ( dev->dev_type == DEV_AHB_MST ) { + if ( p->ahb_mst ) { + /* This should not happen */ + printk("ambapp_dev_register: ahb_mst not NULL!\n"); + exit(1); + } + + /* Remember AHB Master */ + p->ahb_mst = dev; + + /* Find AHB Slave and APB slave for this Core */ + ambapp_for_each(p->abus, (OPTIONS_AHB_SLVS|OPTIONS_APB_SLVS|OPTIONS_FREE), dev->vendor, dev->device, ambapp_dev_register, p); + + ambapp_core_register(p->ahb_mst, p->ahb_slv, p->apb_slv, p); + p->ahb_mst = p->ahb_slv = p->apb_slv = NULL; + return 0; + + } else if ( dev->dev_type == DEV_AHB_SLV ) { + if ( p->ahb_slv ) { + /* Already got our AHB Slave interface */ + return 0; + } + + /* Remember AHB Slave */ + p->ahb_slv = dev; + + if ( p->ahb_mst ) { + /* Continue searching for APB Slave */ + return 0; + } else { + /* Find APB Slave interface for this Core */ + ambapp_for_each(p->abus, (OPTIONS_APB_SLVS|OPTIONS_FREE), dev->vendor, dev->device, ambapp_dev_register, p); + + ambapp_core_register(p->ahb_mst, p->ahb_slv, p->apb_slv, p); + p->ahb_mst = p->ahb_slv = p->apb_slv = NULL; + return 0; + } + } else if ( dev->dev_type == DEV_APB_SLV ) { + if ( p->apb_slv ) { + /* This should not happen */ + printk("ambapp_dev_register: apb_slv not NULL!\n"); + exit(1); + } + /* Remember APB Slave */ + p->apb_slv = dev; + + if ( p->ahb_mst || p->ahb_slv ) { + /* Stop scanning */ + return 1; + } else { + ambapp_core_register(p->ahb_mst, p->ahb_slv, p->apb_slv, p); + p->ahb_mst = p->ahb_slv = p->apb_slv = NULL; + return 0; + } + } + + return 0; +} + +/* Register all AMBA devices available on the AMBAPP bus */ +int ambapp_ids_register(struct drvmgr_bus *bus) +{ + struct ambapp_priv *priv = bus->priv; + struct ambapp_bus *abus; + struct ambapp_dev_reg_struct arg; + + DBG("ambapp_ids_register:\n"); + + memset(&arg, 0, sizeof(arg)); + + abus = priv->config->abus; + arg.abus = abus; + arg.bus = bus; + + /* Combine the AHB MST, AHB SLV and APB SLV interfaces of a core. A core has often more than + * one interface. A core can not have more than one interface of the same type. + */ + ambapp_for_each(abus, (OPTIONS_ALL_DEVS|OPTIONS_FREE), -1, -1, ambapp_dev_register, &arg); + +#ifdef DEBUG + ambapp_print(abus->root, 1); +#endif + + return DRVMGR_OK; +} + +/*** DEVICE FUNCTIONS ***/ + +int ambapp_bus_register(struct drvmgr_dev *dev, struct ambapp_config *config) +{ + struct ambapp_priv *priv; + + if ( !config || !config->ops ) + return DRVMGR_OK; + + DBG("AMBAPP BUS: initializing\n"); + + /* Register BUS */ + drvmgr_alloc_bus(&dev->bus, sizeof(struct ambapp_priv)); + priv = (struct ambapp_priv *)(dev->bus + 1); + priv->config = config; + if ( priv->config->bus_type == DRVMGR_BUS_TYPE_AMBAPP_DIST ) + dev->bus->bus_type = DRVMGR_BUS_TYPE_AMBAPP_DIST; + else if ( priv->config->bus_type == DRVMGR_BUS_TYPE_AMBAPP_RMAP ) + dev->bus->bus_type = DRVMGR_BUS_TYPE_AMBAPP_RMAP; + else + dev->bus->bus_type = DRVMGR_BUS_TYPE_AMBAPP; + dev->bus->next = NULL; + dev->bus->dev = dev; + dev->bus->priv = priv; + dev->bus->children = NULL; + dev->bus->ops = &ambapp_bus_ops; + dev->bus->funcs = config->funcs; + dev->bus->dev_cnt = 0; + dev->bus->reslist = NULL; + dev->bus->maps_up = config->maps_up; + dev->bus->maps_down = config->maps_down; + + /* Add resource configuration */ + if ( priv->config->resources ) + drvmgr_bus_res_add(dev->bus, priv->config->resources); + + drvmgr_bus_register(dev->bus); + + return DRVMGR_OK; +} + +/*** BUS INITIALIZE FUNCTIONS ***/ + +/* Initialize the bus, register devices on this bus */ +int ambapp_bus_init1(struct drvmgr_bus *bus) +{ + /* Initialize the bus, register devices on this bus */ + return ambapp_ids_register(bus); +} + +int ambapp_bus_remove(struct drvmgr_bus *bus) +{ + return DRVMGR_OK; +} diff --git a/c/src/lib/libbsp/sparc/shared/drvmgr/ambapp_bus_grlib.c b/c/src/lib/libbsp/sparc/shared/drvmgr/ambapp_bus_grlib.c new file mode 100644 index 0000000000..eb4e5deb77 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/drvmgr/ambapp_bus_grlib.c @@ -0,0 +1,230 @@ +/* LEON3 GRLIB AMBA Plug & Play bus driver. + * + * COPYRIGHT (c) 2008. + * Aeroflex Gaisler. + * + * This is driver is a wrapper for the general AMBA Plug & Play bus + * driver. This is the root bus driver for GRLIB systems. + * + * 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. + * + * 2008-12-03, Daniel Hellstrom <daniel@gaisler.com> + * Created + * + */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <libcpu/access.h> + +#include <drvmgr/ambapp_bus.h> +#include <drvmgr/ambapp_bus_grlib.h> +#include <genirq.h> + +#include <bsp.h> + +#define DBG(args...) +/*#define DBG(args...) printk(args)*/ + +int ambapp_grlib_int_register( + struct drvmgr_dev *dev, + int irq, + const char *info, + drvmgr_isr isr, + void *arg); +int ambapp_grlib_int_unregister( + struct drvmgr_dev *dev, + int irq, + drvmgr_isr isr, + void *arg); +int ambapp_grlib_int_clear( + struct drvmgr_dev *dev, + int irq); +int ambapp_grlib_int_mask( + struct drvmgr_dev *dev, + int irq); +int ambapp_grlib_int_unmask( + struct drvmgr_dev *dev, + int irq); +int ambapp_grlib_get_params( + struct drvmgr_dev *dev, + struct drvmgr_bus_params *params); + +int ambapp_grlib_init1(struct drvmgr_dev *dev); +int ambapp_grlib_init2(struct drvmgr_dev *dev); +int ambapp_grlib_remove(struct drvmgr_dev *dev); + +/* READ/WRITE access to SpaceWire target over RMAP */ +void *ambapp_grlib_rw_arg(struct drvmgr_dev *dev); + +struct ambapp_ops ambapp_grlib_ops = { + .int_register = ambapp_grlib_int_register, + .int_unregister = ambapp_grlib_int_unregister, + .int_clear = ambapp_grlib_int_clear, + .int_mask = ambapp_grlib_int_mask, + .int_unmask = ambapp_grlib_int_unmask, + .get_params = ambapp_grlib_get_params +}; + +void *ambapp_grlib_rw_arg(struct drvmgr_dev *dev) +{ + return dev; /* No argument really needed, but for debug? */ +} + +struct drvmgr_func ambapp_grlib_funcs[] = +{ + DRVMGR_FUNC(AMBAPP_RW_ARG, ambapp_grlib_rw_arg), + + DRVMGR_FUNC(AMBAPP_R8, _ld8), + DRVMGR_FUNC(AMBAPP_R16, _ld16), + DRVMGR_FUNC(AMBAPP_R32, _ld32), + DRVMGR_FUNC(AMBAPP_R64, _ld64), + + DRVMGR_FUNC(AMBAPP_W8, _st8), + DRVMGR_FUNC(AMBAPP_W16, _st16), + DRVMGR_FUNC(AMBAPP_W32, _st32), + DRVMGR_FUNC(AMBAPP_W64, _st64), + + DRVMGR_FUNC(AMBAPP_RMEM, memcpy), + DRVMGR_FUNC(AMBAPP_WMEM, memcpy), + + DRVMGR_FUNC_END, +}; + +struct drvmgr_drv_ops ambapp_grlib_drv_ops = +{ + .init = {ambapp_grlib_init1, ambapp_grlib_init2, NULL, NULL}, + .remove = ambapp_grlib_remove, + .info = NULL, +}; + +struct drvmgr_drv ambapp_bus_drv_grlib = +{ + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_GRLIB_AMBAPP_ID, /* Driver ID */ + "AMBAPP_GRLIB_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_ROOT, /* Bus Type */ + &ambapp_grlib_drv_ops, + NULL, /* Funcs */ + 0, + 0, +}; + +static struct grlib_config *drv_mgr_grlib_config = NULL; + +void ambapp_grlib_register(void) +{ + drvmgr_drv_register(&ambapp_bus_drv_grlib); +} + +int ambapp_grlib_root_register(struct grlib_config *config) +{ + + /* Save the configuration for later */ + drv_mgr_grlib_config = config; + + /* Register root device driver */ + drvmgr_root_drv_register(&ambapp_bus_drv_grlib); + + return 0; +} + +/* Function called from Driver Manager Initialization Stage 1 */ +int ambapp_grlib_init1(struct drvmgr_dev *dev) +{ + struct ambapp_config *config; + + dev->priv = NULL; + dev->name = "GRLIB AMBA PnP"; + + DBG("AMBAPP GRLIB: intializing\n"); + + config = malloc(sizeof(struct ambapp_config)); + if ( !config ) + return RTEMS_NO_MEMORY; + + config->ops = &ambapp_grlib_ops; + config->maps_up = DRVMGR_TRANSLATE_ONE2ONE; + config->maps_down = DRVMGR_TRANSLATE_ONE2ONE; + config->abus = drv_mgr_grlib_config->abus; + config->resources = drv_mgr_grlib_config->resources; + config->funcs = ambapp_grlib_funcs; + config->bus_type = DRVMGR_BUS_TYPE_AMBAPP; + + /* Initialize the generic part of the AMBA Bus */ + return ambapp_bus_register(dev, config); +} + +int ambapp_grlib_init2(struct drvmgr_dev *dev) +{ + return 0; +} + +int ambapp_grlib_remove(struct drvmgr_dev *dev) +{ + return 0; +} + +int ambapp_grlib_int_register + ( + struct drvmgr_dev *dev, + int irq, + const char *info, + drvmgr_isr isr, + void *arg + ) +{ + return BSP_shared_interrupt_register(irq, info, isr, arg); +} + +int ambapp_grlib_int_unregister + ( + struct drvmgr_dev *dev, + int irq, + drvmgr_isr isr, + void *arg + ) +{ + return BSP_shared_interrupt_unregister(irq, isr, arg); +} + +int ambapp_grlib_int_clear + ( + struct drvmgr_dev *dev, + int irq) +{ + BSP_shared_interrupt_clear(irq); + return DRVMGR_OK; +} + +int ambapp_grlib_int_mask + ( + struct drvmgr_dev *dev, + int irq + ) +{ + BSP_shared_interrupt_mask(irq); + return DRVMGR_OK; +} + +int ambapp_grlib_int_unmask + ( + struct drvmgr_dev *dev, + int irq + ) +{ + BSP_shared_interrupt_unmask(irq); + return DRVMGR_OK; +} + +int ambapp_grlib_get_params(struct drvmgr_dev *dev, struct drvmgr_bus_params *params) +{ + /* Leave params->freq_hz untouched for default */ + params->dev_prefix = ""; + return 0; +} diff --git a/c/src/lib/libbsp/sparc/shared/drvmgr/ambapp_bus_leon2.c b/c/src/lib/libbsp/sparc/shared/drvmgr/ambapp_bus_leon2.c new file mode 100644 index 0000000000..d68a362470 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/drvmgr/ambapp_bus_leon2.c @@ -0,0 +1,268 @@ +/* LEON2 GRLIB AMBA Plug & Play bus driver. + * + * COPYRIGHT (c) 2008. + * Aeroflex Gaisler. + * + * This is driver is a wrapper for the general AMBA Plug & Play bus + * driver. This is a bus driver for LEON2-GRLIB systems providing a + * AMBA Plug & Play bus, the parent bus must be a LEON2 hardcoded + * Bus. All IRQs must be routed to this bus driver in order for IRQs + * to work. The PnP information is used to extract IRQs and base + * register addresses. + * + * 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. + * + * 2008-12-03, Daniel Hellstrom <daniel@gaisler.com> + * Created + * + */ + +#include <bsp.h> + +#ifdef LEON2 +#include <stdlib.h> +#include <stdio.h> +#include <libcpu/access.h> +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> +#include <drvmgr/leon2_amba_bus.h> + +#define DBG(args...) + +int ambapp_leon2_int_register( + struct drvmgr_dev *dev, + int index, + const char *info, + drvmgr_isr isr, + void *arg); +int ambapp_leon2_int_unregister( + struct drvmgr_dev *dev, + int index, + drvmgr_isr isr, + void *arg); +int ambapp_leon2_int_clear( + struct drvmgr_dev *dev, + int index); +int ambapp_leon2_int_mask( + struct drvmgr_dev *dev, + int irq); +int ambapp_leon2_int_unmask( + struct drvmgr_dev *dev, + int irq); +int ambapp_leon2_get_params( + struct drvmgr_dev *dev, + struct drvmgr_bus_params *params); + +int ambapp_leon2_init1(struct drvmgr_dev *dev); +int ambapp_leon2_init2(struct drvmgr_dev *dev); +int ambapp_leon2_remove(struct drvmgr_dev *dev); + +/* READ/WRITE access to SpaceWire target over RMAP */ +void *ambapp_leon2_rw_arg(struct drvmgr_dev *dev); + +struct ambappl2_priv { + struct ambapp_bus abus; + struct ambapp_config config; +}; + +struct ambapp_ops ambapp_leon2_ops = { + .int_register = ambapp_leon2_int_register, + .int_unregister = ambapp_leon2_int_unregister, + .int_clear = ambapp_leon2_int_clear, + .int_mask = ambapp_leon2_int_mask, + .int_unmask = ambapp_leon2_int_unmask, + .get_params = ambapp_leon2_get_params +}; + +struct drvmgr_func ambapp_leon2_funcs[] = { + DRVMGR_FUNC(AMBAPP_RW_ARG, ambapp_leon2_rw_arg), + + DRVMGR_FUNC(AMBAPP_R8, _ld8), + DRVMGR_FUNC(AMBAPP_R16, _ld16), + DRVMGR_FUNC(AMBAPP_R32, _ld32), + DRVMGR_FUNC(AMBAPP_R64, _ld64), + + DRVMGR_FUNC(AMBAPP_W8, _st8), + DRVMGR_FUNC(AMBAPP_W16, _st16), + DRVMGR_FUNC(AMBAPP_W32, _st32), + DRVMGR_FUNC(AMBAPP_W64, _st64), + + DRVMGR_FUNC(AMBAPP_RMEM, memcpy), + DRVMGR_FUNC(AMBAPP_WMEM, memcpy), + + DRVMGR_FUNC_END +}; + +struct drvmgr_drv_ops ambapp_ops = { + .init = {ambapp_leon2_init1, ambapp_leon2_init2, NULL, NULL}, + .remove = ambapp_leon2_remove, + .info = NULL, +}; + +struct leon2_amba_dev_id ambapp_leon2_ids[] = { + {LEON2_AMBA_AMBAPP_ID}, + {0} +}; + +struct leon2_amba_drv_info ambapp_bus_drv_leon2 = { + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_LEON2_AMBA_AMBAPP, /* Driver ID */ + "AMBAPP_LEON2_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_LEON2_AMBA, /* Bus Type */ + &ambapp_ops, + NULL, /* Funcs */ + 0, + sizeof(struct ambappl2_priv), /* Let DrvMgr allocate priv */ + }, + &ambapp_leon2_ids[0] +}; + +void ambapp_leon2_register(void) +{ + drvmgr_drv_register(&ambapp_bus_drv_leon2.general); +} + +/* Function called from a hard configuration */ +int ambapp_leon2_init1(struct drvmgr_dev *dev) +{ + union drvmgr_key_value *value; + struct ambappl2_priv *priv = dev->priv; + struct leon2_amba_dev_info *devinfo; + struct ambapp_config *config; + unsigned int ioarea; + unsigned int freq_hz; + LEON_Register_Map *regs; + + dev->name = "LEON2 AMBA PnP"; + + if (!priv) + return DRVMGR_NOMEM; + + config = &priv->config; + config->abus = &priv->abus; + config->ops = &ambapp_leon2_ops; + config->maps_up = DRVMGR_TRANSLATE_ONE2ONE; + config->maps_down = DRVMGR_TRANSLATE_ONE2ONE; + config->funcs = ambapp_leon2_funcs; + config->bus_type = DRVMGR_BUS_TYPE_LEON2_AMBA; + + /* Get AMBA PnP Area from REG0 */ + devinfo = (struct leon2_amba_dev_info *)dev->businfo; + ioarea = devinfo->reg_base; + + /* Scan AMBA PnP Bus. ABUS has already been cleared with memset() */ + ambapp_scan(&priv->abus, ioarea, NULL, NULL); + + /* Try to get Configuration from resource configuration */ + + value = drvmgr_dev_key_get(dev, "busFreq", KEY_TYPE_INT); + if (value) { + /* Set frequency of AMBA bus if specified by user. The frequency + * must be for AHB bus which IOAREA matches (AHB bus 0). + */ + freq_hz = value->i; + } else { + /* Get Bus/LEON2 Frequency from timer prescaler, + * the hardcoded address is used to get to timer + */ + regs = (LEON_Register_Map *) 0x80000000; + freq_hz = (regs->Scaler_Reload + 1) * 1000 * 1000; + } + /* Note that this can be overrided by a driver on the AMBA PnP bus.*/ + ambapp_freq_init(&priv->abus, NULL, freq_hz); + + value = drvmgr_dev_key_get(dev, "drvRes", KEY_TYPE_POINTER); + if (!value) { + DBG("ambapp_leon2_init1: Failed getting resource drvRes\n"); + config->resources = NULL; + } else { + DBG("ambapp_leon2_init1: drvRes: 0x%08x\n", (unsigned int)value->ptr); + config->resources = (struct drvmgr_bus_res *)value->ptr; + } + + /* Initialize the AMBA Bus */ + return ambapp_bus_register(dev, config); +} + +int ambapp_leon2_init2(struct drvmgr_dev *dev) +{ + return 0; +} + +int ambapp_leon2_remove(struct drvmgr_dev *dev) +{ + return 0; +} + +void *ambapp_leon2_rw_arg(struct drvmgr_dev *dev) +{ + return dev; /* No argument really needed, by for debug */ +} + +int ambapp_leon2_int_register + ( + struct drvmgr_dev *dev, + int index, + const char *info, + drvmgr_isr isr, + void *arg + ) +{ + /* Let LEON2 bus handle interrupt requests */ + return drvmgr_interrupt_register(dev->parent->dev, index, info, isr, arg); +} + +int ambapp_leon2_int_unregister + ( + struct drvmgr_dev *dev, + int index, + drvmgr_isr isr, + void *arg + ) +{ + /* Let LEON2 bus handle interrupt requests */ + return drvmgr_interrupt_unregister(dev->parent->dev, index, isr, arg); +} + +int ambapp_leon2_int_clear + ( + struct drvmgr_dev *dev, + int index + ) +{ + /* Let LEON2 bus handle interrupt requests */ + return drvmgr_interrupt_clear(dev->parent->dev, index); +} + +int ambapp_leon2_int_mask + ( + struct drvmgr_dev *dev, + int index + ) +{ + /* Let LEON2 bus handle interrupt requests */ + return drvmgr_interrupt_mask(dev->parent->dev, index); +} + +int ambapp_leon2_int_unmask + ( + struct drvmgr_dev *dev, + int index + ) +{ + /* Let LEON2 bus handle interrupt requests */ + return drvmgr_interrupt_unmask(dev->parent->dev, index); +} + +int ambapp_leon2_get_params(struct drvmgr_dev *dev, struct drvmgr_bus_params *params) +{ + params->dev_prefix = ""; + return 0; +} + +#endif diff --git a/c/src/lib/libbsp/sparc/shared/drvmgr/ambapp_bus_rmap.c b/c/src/lib/libbsp/sparc/shared/drvmgr/ambapp_bus_rmap.c new file mode 100644 index 0000000000..a5c6afb834 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/drvmgr/ambapp_bus_rmap.c @@ -0,0 +1,776 @@ +/* RMAP AMBA Plug & Play bus driver. The AMBA bus is accessed over a SpaceWire network. + * + * COPYRIGHT (c) 2008. + * Aeroflex Gaisler. + * + * 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. + * + * 2009-11-20, Daniel Hellstrom <daniel@gaisler.com> + * Created + * 2010-02-03, Daniel Hellstrom <daniel@gaisler.com> + * Added support for prefix in device name. + * + * MEMORY PARTITIONS + * ----------------- + * A partition represents a memory area, for example the SRAM of a system, the drivers + * may allocate from different partitions to make sure that the descriptors of buffers + * are located at the correct place. + * + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <drvmgr/spw_bus.h> +#include <drvmgr/ambapp_bus.h> +#include <drvmgr/ambapp_bus_rmap.h> +#include <genirq.h> +#include <grlib.h> + +#include <bsp.h> + +/* Remote RMAP Access macros */ +#define WRITE_REG(pDev, adr, value) priv->rw_w32((uint32_t *)adr, (uint32_t)value, &pDev->rw_arg) +#define READ_REG(pDev, adr) priv->rw_r32((uint32_t *)adr, &pDev->rw_arg) + +#define PARTITION_MAX 4 +#define INHERIT_LENGTH 11 + +#define DBG(args...) +/*#define DBG(args...) printk(args)*/ + +int ambapp_bus_rmap_debug = 0; + +struct mem_block; + +struct mem_block { + struct mem_block *next; + struct mem_block *prev; + unsigned int start; + unsigned int length; +}; + +struct mem_partition { + /* Free memory */ + struct mem_block *head; + struct mem_block *tail; +}; + +struct ambapp_rmap_priv { + struct drvmgr_dev *dev; + char prefix[32]; + int minor; + struct ambapp_config config; + struct ambapp_bus abus; + + /* MEMORY ALLOCATION */ + unsigned int partition_valid; + struct mem_partition partitions[PARTITION_MAX]; + + /* IRQ HANDLING */ + int irq_support; + genirq_t genirq; + struct irqmp_regs *irq; + unsigned int mask; + + /* Access routines */ + struct drvmgr_func funcs[INHERIT_LENGTH+3]; + struct drvmgr_rw_arg rw_arg; + spwbus_w32 rw_w32; + spwbus_r32 rw_r32; + spwbus_rmem rw_rmem; +}; + +int ambapp_rmap_int_register( + struct drvmgr_dev *dev, + int irq, + const char *info, + drvmgr_isr isr, + void *arg); +int ambapp_rmap_int_unregister( + struct drvmgr_dev *dev, + int irq, + drvmgr_isr isr, + void *arg); +int ambapp_rmap_int_unmask(struct drvmgr_dev *dev, int irq); +int ambapp_rmap_int_mask(struct drvmgr_dev *dev, int irq); +int ambapp_rmap_int_clear(struct drvmgr_dev *dev, int irq); +int ambapp_rmap_get_params( + struct drvmgr_dev *dev, + struct drvmgr_bus_params *params); +void ambapp_rmap_isr(void *arg); + +int ambapp_rmap_init1(struct drvmgr_dev *dev); +int ambapp_rmap_init2(struct drvmgr_dev *dev); + +void *ambapp_rmap_rw_arg(struct drvmgr_dev *dev); +void ambapp_rmap_rw_err(struct drvmgr_rw_arg *a, struct drvmgr_bus *bus, + int funcid, void *adr); + +struct ambapp_ops ambapp_rmap_ops = { + .int_register = ambapp_rmap_int_register, + .int_unregister = ambapp_rmap_int_unregister, + .int_clear = ambapp_rmap_int_clear, + .int_unmask = ambapp_rmap_int_unmask, + .int_mask = ambapp_rmap_int_mask, + .get_params = ambapp_rmap_get_params +}; + +struct spw_id spw_rmap_ids[] = +{ + {SPW_NODE_ID_GRLIB}, + {SPW_NODE_ID_NONE} +}; + +struct drvmgr_drv_ops ambapp_rmap_drv_ops = +{ + .init = {ambapp_rmap_init1, ambapp_rmap_init2, NULL, NULL}, + .remove = NULL, + .info = NULL, +}; + +struct spw_bus_drv_info ambapp_bus_drv_rmap = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_SPW_RMAP_AMBAPP_ID, /* Driver ID */ + "AMBAPP_RMAP_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_SPW_RMAP, /* Bus Type */ + &ambapp_rmap_drv_ops, + NULL, /* Funcs */ + 0, + sizeof(struct ambapp_rmap_priv), + }, + &spw_rmap_ids[0], + +}; + +int ambapp_rmap_inherit_list[INHERIT_LENGTH] = +{ + SPWBUS_R8, + SPWBUS_R16, + SPWBUS_R32, + SPWBUS_R64, + SPWBUS_W8, + SPWBUS_W16, + SPWBUS_W32, + SPWBUS_W64, + SPWBUS_RMEM, + SPWBUS_WMEM, + SPWBUS_MEMSET, +}; + +struct drvmgr_bus_res **ambapp_rmap_resources = NULL; +int ambapp_rmap_resources_cnt = 0; + +void ambapp_rmap_register(void) +{ + drvmgr_drv_register(&ambapp_bus_drv_rmap.general); +} + +void ambapp_rmap_set_resources(struct drvmgr_bus_res **resources, int cnt) +{ + ambapp_rmap_resources = resources; + ambapp_rmap_resources_cnt = cnt; +} + +void *ambapp_rmap_memcpy( + void *dest, + const void *src, + int n, + struct ambapp_bus *abus) +{ + struct ambapp_rmap_priv *priv = (struct ambapp_rmap_priv *) + ((unsigned int)abus - + offsetof(struct ambapp_rmap_priv, abus)); + + priv->rw_rmem(dest, src, n, &priv->rw_arg); + + return dest; +} + +/* Function called from Driver Manager Initialization Stage 1 */ +int ambapp_rmap_init1(struct drvmgr_dev *dev) +{ + struct ambapp_config *config; + union drvmgr_key_value *value; + int status, i, funcid; + unsigned int ioarea, freq; + struct ambapp_rmap_priv *priv; + struct spw_bus_dev_info *businfo; + struct ambapp_dev *tmp; + char prefix[32]; + + dev->name = "RMAP AMBA PnP"; + businfo = (struct spw_bus_dev_info *)dev->businfo; + + DBG("AMBAPP RMAP: intializing\n"); + + priv = dev->priv; + if ( !priv ) + return DRVMGR_NOMEM; + + /* Get Configuration */ + ioarea = 0xfff00000; /* Defualt IO Area */ + value = drvmgr_dev_key_get(dev, "IOArea", KEY_TYPE_INT); + if ( value ) { + ioarea = value->i; + } + freq = 0; + value = drvmgr_dev_key_get(dev, "BusFreq", KEY_TYPE_INT); + if ( value ) + freq = value->i; + value = drvmgr_dev_key_get(dev, "noIRQ", KEY_TYPE_INT); + if ( value ) + priv->irq_support = 0; + else + priv->irq_support = 1; + + priv->dev = dev; + if ( priv->irq_support ) + priv->genirq = genirq_init(16); + + /* Get Read/Write operations for bus */ + priv->rw_arg.dev = dev; + priv->rw_arg.arg = (void *)drvmgr_func_call(dev->parent, SPWBUS_RW_ARG, dev, NULL, NULL, NULL); + drvmgr_func_get(dev->parent, SPWBUS_R32, (void **)&priv->rw_r32); + drvmgr_func_get(dev->parent, SPWBUS_W32, (void **)&priv->rw_w32); + drvmgr_func_get(dev->parent, SPWBUS_RMEM, (void **)&priv->rw_rmem); + + /* Scan Amba Bus */ + status = ambapp_scan(&priv->abus, ioarea, ambapp_rmap_memcpy, NULL); + if ( status ) { + return DRVMGR_FAIL; + } + + if ( ambapp_bus_rmap_debug ) + ambapp_print(&priv->abus, 10); + + /* Find IRQ controller */ + tmp = (void *)ambapp_for_each(&priv->abus, (OPTIONS_ALL|OPTIONS_APB_SLVS), + VENDOR_GAISLER, GAISLER_IRQMP, + ambapp_find_by_idx, NULL); + if ( !tmp ) { + /* Silent error if not IRQ controller is found and IRQ support + * is disabled + */ + if ( priv->irq_support ) + return -4; + } else { + priv->irq = (struct irqmp_regs *)DEV_TO_APB(tmp)->start; + /* Set up IRQ controller */ + WRITE_REG(priv, &priv->irq->iclear, 0xffff); + WRITE_REG(priv, &priv->irq->ilevel, 0); + WRITE_REG(priv, &priv->irq->mask[0], 0); + + /* Clear any old interrupt requests (IRQ IS LEVEL) */ + drvmgr_interrupt_clear(priv->dev, 0); + } + + /* 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->prefix, "/dev/rmap_%02x", + (unsigned char)businfo->dstadr); + } else { + /* Got special prefix, this means we have a bus prefix + * And we should use our "bus minor" + */ + sprintf(priv->prefix, "/dev/%srmap_%02x", + prefix, (unsigned char)businfo->dstadr); + } + + mkdir(priv->prefix, S_IRWXU | S_IRWXG | S_IRWXO); + strcat(priv->prefix, "/"); + + printf("\n\n--- RMAP AMBAPnP[%d] ---\n", dev->minor_drv); + printf(" SpW ADDRESS: %d\n", businfo->dstadr); + printf(" DEV BASE: %s\n", priv->prefix); + printf(" FREQ: %u [Hz]\n", freq); + printf(" I/O AREA: 0x%08x\n", ioarea); + printf(" IRQ REGS: 0x%08x\n", (unsigned int)priv->irq); + + /* Initialize the Frequency of the AMBA bus */ + ambapp_freq_init(&priv->abus, NULL, freq); + + /* Inherit R/W functions from parent */ + for (i = 0; i < INHERIT_LENGTH; i++) { + funcid = ambapp_rmap_inherit_list[i]; + if (drvmgr_func_get(dev->parent, funcid, &priv->funcs[i].func) != DRVMGR_OK) + return -5; + priv->funcs[i].funcid = funcid; + } + priv->funcs[i].funcid = AMBAPP_RMAP_RW_ARG; + priv->funcs[i].func = ambapp_rmap_rw_arg; + priv->funcs[i+1].funcid = AMBAPP_RMAP_RW_ERR; + priv->funcs[i+1].func = ambapp_rmap_rw_err; + priv->funcs[i+2].funcid = DRVMGR_FUNCID_NONE; + + config = &priv->config; + config->ops = &ambapp_rmap_ops; + config->maps_up = DRVMGR_TRANSLATE_NO_BRIDGE; + config->maps_down = DRVMGR_TRANSLATE_NO_BRIDGE; + config->abus = &priv->abus; + config->bus_type = DRVMGR_BUS_TYPE_AMBAPP_RMAP; + config->funcs = priv->funcs; + /* Set this AMBA Bus driver resources */ + if ( ambapp_rmap_resources && (priv->dev->minor_drv < ambapp_rmap_resources_cnt) ) { + config->resources = ambapp_rmap_resources[priv->dev->minor_drv]; + } else { + value = drvmgr_dev_key_get(dev, "BusRes", KEY_TYPE_POINTER); + if ( value ) + config->resources = value->ptr; + else + config->resources = NULL; + } + + /* Target is assumed to be equipped with 256Mb RAM */ + /*ambapp_rmap_partition_create(dev, 0, 0x40000000, 0x10000000);*/ + /*ambapp_rmap_partition_create_internal(priv, 0, 0x40000000, 0x10000000);*/ + + /* Initialize the AMBA Bus */ + return ambapp_bus_register(dev, config); +} + +int ambapp_rmap_init2(struct drvmgr_dev *dev) +{ + struct ambapp_rmap_priv *priv = dev->priv; + + /* Enable System IRQ so that the SpW Node Core's interrupt goes through. + * + * It is important to enable it in stage init2. If interrupts were + * enabled in init1 this might hang the system when more than one SpW + * Node is connected to the same IRQ line, this is because interrupts + * might be shared and Node 2 have not initialized and might therefore + * drive interrupt already when entering init1(). + */ + if ( priv->irq_support ) + drvmgr_interrupt_register(priv->dev, 0, "ambapp_rmap", + ambapp_rmap_isr, (void *)priv); + + return 0; +} + +/* The ISR is executed on the SpW-BUS ISR Task, ie. not in interrupt context */ +void ambapp_rmap_isr (void *arg) +{ + struct ambapp_rmap_priv *priv = arg; + unsigned int status, tmp; + int irq; + tmp = status = READ_REG(priv, &priv->irq->ipend) & priv->mask; + /* DBG("AMBAPP-RMAP-ISR: IRQ 0x%x\n",status); */ + + /* Clear handled IRQs at remote IRQ controller, These IRQs are not + * level sensitive which makes it ok to clear the before they are + * handled. + */ + if ( status ) + WRITE_REG(priv, &priv->irq->iclear, status); + + for (irq=0; irq<16; irq++) { + if ( status & (1<<irq) ) { + genirq_doirq(priv->genirq, irq); + status &= ~(1<<irq); + if ( status == 0 ) + break; + } + } + + /* ACK interrupt, this is because Interrupt is Level, so the IRQ + * Controller still drives the IRQ. + */ + if ( tmp ) + drvmgr_interrupt_clear(priv->dev, 0); + + DBG("AMBAPP-RMAP-ISR: 0x%x\n", tmp); +} + +void *ambapp_rmap_rw_arg(struct drvmgr_dev *dev) +{ + struct ambapp_rmap_priv *priv; + + if (!dev || !dev->parent || !dev->parent->dev) + return (void *)DRVMGR_FAIL; + + priv = dev->parent->dev->priv; + + /* Use same argument as for ourselves */ + return priv->rw_arg.arg; +} + +/* Called by SpaceWire Lay if a device directly on this bus has been witness + * to an error. + */ +void ambapp_rmap_rw_err(struct drvmgr_rw_arg *a, struct drvmgr_bus *bus, + int funcid, void *adr) +{ + printk("AMBAPP_RMAP: erraccess %p with 0x%08x (amba: %p, dev: %p)\n", + adr, funcid, a->dev->parent, a->dev); + /* Take some error action here? Remove bus or just faulting device? */ +} + +int ambapp_rmap_int_register( + struct drvmgr_dev *dev, + int irq, + const char *info, + drvmgr_isr isr, + void *arg) +{ + struct ambapp_rmap_priv *priv = dev->parent->dev->priv; + int status; + unsigned int tmp; + + DBG("AMBAPP-RMAP-INT_REG: %d\n", irq); + + if (priv->irq_support == 0) + return -1; + + status = genirq_register(priv->genirq, irq, isr, arg); + if ( status == 0 ) { + /* Disable and clear IRQ for first registered handler */ + WRITE_REG(priv, &priv->irq->iclear, (1<<irq)); + tmp = READ_REG(priv, &priv->irq->mask[0]); + WRITE_REG(priv, &priv->irq->mask[0], (tmp & ~(1<<irq))); /* mask interrupt source */ + } else if ( status == 1 ) + status = 0; + + if (status != 0) + return DRVMGR_FAIL; + + status = genirq_enable(priv->genirq, irq, isr, arg); + if ( status == 0 ) { + /* Enable IRQ for first enabled handler only */ + + /* unmask interrupt source */ + priv->mask = READ_REG(priv, &priv->irq->mask[0]); + priv->mask |= (1<<irq); + WRITE_REG(priv, &priv->irq->mask[0], priv->mask); + } else if ( status == 1 ) + status = DRVMGR_OK; + + return status; +} + +int ambapp_rmap_int_unregister( + struct drvmgr_dev *dev, + int irq, + drvmgr_isr isr, + void *arg) +{ + struct ambapp_rmap_priv *priv = dev->parent->dev->priv; + int status; + unsigned int tmp; + + DBG("AMBAPP-RMAP-INT_UNREG: %d\n", irq); + + if (priv->irq_support == 0) + return -1; + + status = genirq_disable(priv->genirq, irq, isr, arg); + if ( status == 0 ) { + /* Disable IRQ only when no enabled handler exists */ + + /* mask interrupt source */ + priv->mask = READ_REG(priv, &priv->irq->mask[0]); + priv->mask &= ~(1<<irq); + WRITE_REG(priv, &priv->irq->mask[0], priv->mask); + } + + status = genirq_unregister(priv->genirq, irq, isr, arg); + if ( status != 0 ) + status = DRVMGR_FAIL; + + return status; +} + +int ambapp_rmap_int_unmask(struct drvmgr_dev *dev, int irq) +{ + struct ambapp_rmap_priv *priv = dev->parent->dev->priv; + + DBG("AMBAPP-RMAP-INT_UNMASK: %d\n", irq); + + if (priv->irq_support == 0) + return -1; + + if ( genirq_check(priv->genirq, irq) ) + return DRVMGR_FAIL; + + /* unmask interrupt source */ + priv->mask = READ_REG(priv, &priv->irq->mask[0]); + priv->mask |= (1<<irq); + WRITE_REG(priv, &priv->irq->mask[0], priv->mask); + + return DRVMGR_OK; +} + +int ambapp_rmap_int_mask(struct drvmgr_dev *dev, int irq) +{ + struct ambapp_rmap_priv *priv = dev->parent->dev->priv; + + DBG("AMBAPP-RMAP-INT_MASK: %d\n", irq); + + if (priv->irq_support == 0) + return -1; + + if ( genirq_check(priv->genirq, irq) ) + return DRVMGR_FAIL; + + /* mask interrupt source */ + priv->mask = READ_REG(priv, &priv->irq->mask[0]); + priv->mask &= (1<<irq); + WRITE_REG(priv, &priv->irq->mask[0], priv->mask); + + return DRVMGR_OK; +} + +int ambapp_rmap_int_clear(struct drvmgr_dev *dev, int irq) +{ + struct ambapp_rmap_priv *priv = dev->parent->dev->priv; + + if (priv->irq_support == 0) + return -1; + + if ( genirq_check(priv->genirq, irq) ) + return DRVMGR_FAIL; + + WRITE_REG(priv, &priv->irq->iclear, (1<<irq)); + + return 0; +} + +int ambapp_rmap_get_params(struct drvmgr_dev *dev, struct drvmgr_bus_params *params) +{ + struct ambapp_rmap_priv *priv = dev->parent->dev->priv; + + params->dev_prefix = &priv->prefix[5]; + return 0; +} + +/************* MEMORY SERVICES FOR REMOTE TARGETS ************* + * + * This was written with resource usage in mind, and alignment needs + * that has to be fullfilled. It is typically used by drivers to allocate + * large data areas that need a certain alignment. + */ + +/* Caller know that there is enough memory within block, this function allocates it */ +void *alloc_block(struct mem_partition *part, struct mem_block *block, unsigned int start, size_t size) +{ + struct mem_block *newblock; + unsigned int block_end; + + DBG("ALLOC_BLOCK: 0x%x - 0x%x\n", start, start + size); + + /* At start of block? */ + if ( block->start == start ) { + block->start += size; + block->length -= size; + return (void *)start; + } + + /* At End of block? */ + block_end = block->start + block->length; + if ( block_end == (start + size) ) { + block->length -= size; + return (void *)start; + } + + /* Not at start or end means that we must insert a new block describing + * the area in between the two blocks. + */ + newblock = (struct mem_block *)malloc(sizeof(struct mem_block)); + if ( !newblock ) { + return NULL; + } + + /* 1. assign unused area after the allocated area to newblock + * 2. shink the first block that the area is taken from + * 3. insert newblock into free memory list. + * + * 1. + */ + newblock->start = start + size; + newblock->length = block_end - (start + size); + + /* 2. */ + block->length = start - block->start; + + /* 3. */ + newblock->prev = block; + newblock->next = block->next; + if ( block->next ) { + block->next->prev = newblock; + } else { + part->tail = newblock; + } + block->next = newblock; + + return (void *)start; +} + +/* Try to allocate from a block */ +void *alloc_try(struct mem_partition *part, struct mem_block *block, unsigned int start, size_t size) +{ + unsigned int end = start + size; + unsigned int block_end = block->start + block->length; + + /* Is the section available in this block? */ + if ( (start >= block->start) && (start <= block_end) && /* Check start and end */ + (end <= block_end) && (end > block->start) ) { + /* Found a block that is available, allocate it */ + return alloc_block(part, block, start, size); + } + + return NULL; +} + +/* Iterate over all free memory blocks from the tail to head*/ +void *alloc_back(struct mem_partition *part, size_t boundary, size_t size) +{ + struct mem_block *block; + unsigned int start, block_end; + void *mem; + + /* Find a section of memory matching the alignment and size */ + block = part->tail; + while ( block ) { + if ( block->length >= size ) { + /* Calculate the last address that is needed within this block */ + block_end = block->start + block->length; + start = (block_end - size) & ~(boundary - 1); + + if ( (mem=alloc_try(part, block, start, size)) != NULL ) { + return mem; + } + } + + /* memory not available within this block */ + block = block->prev; + } + + DBG("AMBAPP_RMAP_MEMALIGN_BACK: FAILED TO ALLOCATE %dBytes at a 0x%x boundary\n", size, boundary); + + return NULL; +} + +void *alloc_front(struct mem_partition *part, size_t boundary, size_t size) +{ + struct mem_block *block; + unsigned int start; + void *mem; + + /* Find a section of memory matching the alignment and size */ + block = part->head; + while ( block ) { + + if ( block->length >= size ) { + /* Calculate the first address that is needed + * within this block + */ + start = (block->start + (boundary - 1)) & + ~(boundary - 1); + + if ((mem=alloc_try(part, block, start, size)) != NULL) { + return mem; + } + } + /* memory not available within this block */ + block = block->next; + } + + DBG("AMBAPP_RMAP_MEMALIGN_FRONT: FAILED TO ALLOCATE %dBytes at a 0x%x boundary\n", size, boundary); + + return NULL; +} + +void *ambapp_rmap_partition_memalign( + struct drvmgr_dev *dev, + int partition, + size_t boundary, + size_t size) +{ + struct ambapp_rmap_priv *priv; + struct mem_partition *part = NULL; + + if ( size < 1 ) { + DBG("AMBAPP_RMAP_MEMALIGN: size < 1\n"); + return NULL; + } + + /* Never deal with smaller units than 16 bytes */ + if ( boundary < 0x10 ) { + boundary = 0x10; + } + + DBG("RMAP MEMALIGN: partition %d, boundary 0x%x, size 0x%x\n", partition, boundary, size); + + /* Get Partition */ + priv = dev->parent->dev->priv; + if ( (partition >= PARTITION_MAX) || (priv->partition_valid & (1<<partition)) == 0 ) { + DBG("RMAP MEMALIGN: partition invalid\n"); + return NULL; + } + part = &priv->partitions[partition]; + + if ( (boundary >= 0x10000) || (size >= 0x10000) ) { + return alloc_front(part, boundary, size); + } else { + return alloc_back(part, boundary, size); + } +} + +int ambapp_rmap_partition_create_internal( + struct ambapp_rmap_priv *priv, + int partition, + unsigned int start, + size_t size) +{ + struct mem_partition *part; + struct mem_block *block; + + /* Get Partition */ + if ( (partition >= PARTITION_MAX) || (priv->partition_valid & (1<<partition)) ) { + DBG("RMAP PART CREATE: invalid part num %d\n", partition); + return -1; + } + part = &priv->partitions[partition]; + if ( part->head || part->tail ) { + DBG("RMAP PART CREATE: part %d already initied\n", partition); + return -1; + } + + /* Init on free memory block */ + block = (struct mem_block *)malloc(sizeof(struct mem_block)); + part->head = part->tail = block; + block->next = block->prev = NULL; + block->start = start; + block->length = size; + priv->partition_valid |= 1 << partition; + + DBG("RMAP PART CREATE: DONE initiing part %d\n", partition); + + return 0; +} + +int ambapp_rmap_partition_create( + struct drvmgr_dev *dev, + int partition, + unsigned int start, + size_t size) +{ + struct ambapp_rmap_priv *priv; + + priv = dev->parent->dev->priv; + + return ambapp_rmap_partition_create_internal(priv, partition, start, size); +} diff --git a/c/src/lib/libbsp/sparc/shared/drvmgr/leon2_amba_bus.c b/c/src/lib/libbsp/sparc/shared/drvmgr/leon2_amba_bus.c new file mode 100644 index 0000000000..9d194ec0a6 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/drvmgr/leon2_amba_bus.c @@ -0,0 +1,463 @@ +/* LEON2 Hardcoded bus driver. + * + * COPYRIGHT (c) 2008. + * Aeroflex Gaisler. + * + * Bus driver for a hardcoded setup. LEON2 systems have some + * cores always present, here called "Standard Cores". In + * addtion to the standard cores there are often extra cores + * that can be defined using the "Custom Cores" mechanism. + * + * A Core is described by assigning a base register and + * IRQ0..IRQ15 using the leon2_core structure. + * + * 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. + * + * 2008-12-03, Daniel Hellstrom <daniel@gaisler.com> + * Created + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <drvmgr/drvmgr.h> +#include <drvmgr/leon2_amba_bus.h> + +#include <bsp.h> + +#define DBG(args...) +/*#define DBG(args...) printk(args)*/ + +struct drvmgr_drv leon2_bus_drv; + +int leon2_amba_bus_init1(struct drvmgr_bus *bus); +int leon2_amba_unite(struct drvmgr_drv *drv, struct drvmgr_dev *dev); +int leon2_amba_int_register( + struct drvmgr_dev *dev, + int index, + const char *info, + drvmgr_isr isr, + void *arg); +int leon2_amba_int_unregister( + struct drvmgr_dev *dev, + int index, + drvmgr_isr isr, + void *arg); +int leon2_amba_int_clear( + struct drvmgr_dev *dev, + int index); +int leon2_amba_int_mask( + struct drvmgr_dev *dev, + int index); +int leon2_amba_int_unmask( + struct drvmgr_dev *dev, + int index); + +/* LEON2 bus operations */ +struct drvmgr_bus_ops leon2_amba_bus_ops = +{ + .init = { + leon2_amba_bus_init1, + NULL, + NULL, + NULL + }, + .remove = NULL, + .unite = leon2_amba_unite, + .int_register = leon2_amba_int_register, + .int_unregister = leon2_amba_int_unregister, + .int_clear = leon2_amba_int_clear, + .int_mask = leon2_amba_int_mask, + .int_unmask = leon2_amba_int_unmask, + .get_params = NULL, +}; + +struct leon2_isr_handler { + void (*handler)(int irq, void *arg); + void *arg; +}; + +/* Interrupt handlers */ +struct leon2_isr_handler leon2_isrs[16]; + +/* Standard LEON2 configuration */ + +struct drvmgr_key leon2_timers[] = +{ + {"REG0", KEY_TYPE_INT, {0x80000040}}, + {"IRQ0", KEY_TYPE_INT, {8}}, + {"IRQ1", KEY_TYPE_INT, {9}}, + KEY_EMPTY +}; + +struct drvmgr_key leon2_uart1[] = +{ + {"REG0", KEY_TYPE_INT, {0x80000070}}, + {"IRQ0", KEY_TYPE_INT, {3}}, + KEY_EMPTY +}; + +struct drvmgr_key leon2_uart2[] = +{ + {"REG0", KEY_TYPE_INT, {0x80000080}}, + {"IRQ0", KEY_TYPE_INT, {2}}, + KEY_EMPTY +}; + +struct drvmgr_key leon2_irqctrl[] = +{ + {"REG0", KEY_TYPE_INT, {0x80000090}}, + KEY_EMPTY +}; + +struct drvmgr_key leon2_gpio0[] = +{ + {"REG0", KEY_TYPE_INT, {0x800000A0}}, + {"IRQ0", KEY_TYPE_INT, {4}}, + {"IRQ1", KEY_TYPE_INT, {5}}, + {"IRQ2", KEY_TYPE_INT, {6}}, + {"IRQ3", KEY_TYPE_INT, {7}}, + KEY_EMPTY +}; + +struct leon2_core leon2_std_cores[] = +{ + {{LEON2_AMBA_TIMER_ID}, "Timers", &leon2_timers[0]}, + {{LEON2_AMBA_UART_ID}, "Uart1", &leon2_uart1[0]}, + {{LEON2_AMBA_UART_ID}, "Uart2", &leon2_uart2[0]}, + {{LEON2_AMBA_IRQCTRL_ID}, "IRQCtrl", &leon2_irqctrl[0]}, + {{LEON2_AMBA_GPIO_ID}, "GPIO", &leon2_gpio0[0]}, + EMPTY_LEON2_CORE +}; + +static struct leon2_bus *leon2_bus_config = NULL; +static struct drvmgr_bus_res *leon2_bus_res = NULL; + +int leon2_root_register(struct leon2_bus *bus_config, struct drvmgr_bus_res *resources) +{ + /* Save the configuration for later */ + leon2_bus_config = bus_config; + leon2_bus_res = resources; + + /* Register root device driver */ + drvmgr_root_drv_register(&leon2_bus_drv); + + return 0; +} + +int leon2_amba_dev_register(struct drvmgr_bus *bus, struct leon2_core *core, int index) +{ + struct drvmgr_dev *newdev; + struct leon2_amba_dev_info *info; + union drvmgr_key_value *value; + char irq_name[8]; + int i; + + /* Allocate new device and businfo */ + drvmgr_alloc_dev(&newdev, sizeof(struct leon2_amba_dev_info)); + info = (struct leon2_amba_dev_info *)(newdev + 1); + + /* Set Core ID */ + info->core_id = core->id.core_id; + + /* Get information from bus configuration */ + value = drvmgr_key_val_get(core->keys, "REG0", KEY_TYPE_INT); + if ( !value ) { + printk("leon2_amba_dev_register: Failed getting resource REG0\n"); + info->reg_base = 0x00000000; + } else { + DBG("leon2_amba_dev_register: REG0: 0x%08x\n", value->i); + info->reg_base = value->i; + } + + strcpy(irq_name, "IRQ"); + for(i=0; i<16; i++){ + if ( i < 10 ){ + irq_name[3] = '0' + i; + irq_name[4] = '\0'; + } else { + irq_name[3] = '1'; + irq_name[4] = '0' + (i-10); + irq_name[5] = '\0'; + } + + value = drvmgr_key_val_get(core->keys, irq_name, KEY_TYPE_INT); + if ( !value ) { + DBG("leon2_amba_dev_register: Failed getting resource IRQ%d for REG 0x%x\n", i, info->reg_base); + info->irqs[i] = 0; + } else { + DBG("leon2_amba_dev_register: IRQ%d: %d\n", i, value->i); + info->irqs[i] = value->i; + } + } + + /* Init new device */ + newdev->next = NULL; + newdev->parent = bus; /* Ourselfs */ + newdev->minor_drv = 0; + newdev->minor_bus = 0; + newdev->businfo = (void *)info; + newdev->priv = NULL; + newdev->drv = NULL; + newdev->name = core->name; + newdev->next_in_drv = NULL; + newdev->bus = NULL; + + /* Register new device */ + drvmgr_dev_register(newdev); + + return 0; +} + +int leon2_amba_init1(struct drvmgr_dev *dev) +{ + /* Init our own device */ + dev->priv = NULL; + dev->name = "LEON2 AMBA"; + + memset(leon2_isrs, 0, sizeof(leon2_isrs)); + + /* Init the bus */ + drvmgr_alloc_bus(&dev->bus, 0); + dev->bus->bus_type = DRVMGR_BUS_TYPE_LEON2_AMBA; + dev->bus->next = NULL; + dev->bus->dev = dev; + dev->bus->priv = NULL; + dev->bus->children = NULL; + dev->bus->ops = &leon2_amba_bus_ops; + dev->bus->dev_cnt = 0; + dev->bus->reslist = NULL; + dev->bus->maps_up = leon2_bus_config->maps_up; + dev->bus->maps_down = leon2_bus_config->maps_down; + drvmgr_bus_register(dev->bus); + + return DRVMGR_OK; +} + +int leon2_amba_init2(struct drvmgr_dev *dev) +{ + return DRVMGR_OK; +} + +int leon2_amba_remove(struct drvmgr_dev *dev) +{ + return DRVMGR_OK; +} + +int leon2_amba_bus_init1(struct drvmgr_bus *bus) +{ + struct leon2_core *core; + int i; + + if ( leon2_bus_res ) + drvmgr_bus_res_add(bus, leon2_bus_res); + + /**** REGISTER NEW DEVICES ****/ + i=0; + core = leon2_bus_config->std_cores; + if ( core ) { + while ( core->id.core_id ) { + if ( leon2_amba_dev_register(bus, core, i) ) { + return RTEMS_UNSATISFIED; + } + i++; + core++; + } + } + core = leon2_bus_config->custom_cores; + if ( core ) { + while ( core->id.core_id ) { + if ( leon2_amba_dev_register(bus, core, i) ) { + return RTEMS_UNSATISFIED; + } + i++; + core++; + } + } + + return 0; +} + +int leon2_amba_unite(struct drvmgr_drv *drv, struct drvmgr_dev *dev) +{ + struct leon2_amba_dev_info *info; + struct leon2_amba_drv_info *adrv; + struct leon2_amba_dev_id *id; + + if ( !drv || !dev || !dev->parent ) + return 0; + + if ( (drv->bus_type!=DRVMGR_BUS_TYPE_LEON2_AMBA) || (dev->parent->bus_type != DRVMGR_BUS_TYPE_LEON2_AMBA) ) { + return 0; + } + + info = (struct leon2_amba_dev_info *)dev->businfo; + if ( !info ) + return 0; + + /* Get LEON2 AMBA driver info */ + adrv = (struct leon2_amba_drv_info *)drv; + id = adrv->ids; + if ( !id ) + return 0; + + while ( id->core_id ) { + if ( id->core_id == info->core_id ) { + /* Driver is suitable for device, Unite them */ + return 1; + } + id++; + } + + return 0; +} + +rtems_isr leon2_amba_isr(rtems_vector_number v) +{ + int irq = v - 0x10; /* Convert Vector number to Interrupt number */ + struct leon2_isr_handler *isr; + + isr = &leon2_isrs[irq]; + if ( isr->handler ) { + isr->handler(irq, isr->arg); + } +} + +int leon2_amba_get_irq(struct drvmgr_dev *dev, int index) +{ + int irq; + struct leon2_amba_dev_info *info; + + if ( !dev || (index > 15) ) + return -1; + + /* Relative (positive) or absolute (negative) IRQ number */ + if ( index >= 0 ) { + /* IRQ Index relative to Cores base IRQ */ + + /* Get IRQ array configured by user */ + info = (struct leon2_amba_dev_info *)dev->businfo; + irq = info->irqs[index]; + if ( irq == 0 ) + return -1; + } else { + /* Absolute IRQ number */ + irq = -index; + } + return irq; +} + +int leon2_amba_int_register + ( + struct drvmgr_dev *dev, + int index, + const char *info, + drvmgr_isr isr, + void *arg + ) +{ + int irq; + + irq = leon2_amba_get_irq(dev, index); + if ( irq < 0 ) + return -1; + + DBG("Registering IRQ %d to func 0x%x arg 0x%x\n", irq, (unsigned int)isr, (unsigned int)arg); + + return BSP_shared_interrupt_register(irq, info, isr, arg); +} + +int leon2_amba_int_unregister + ( + struct drvmgr_dev *dev, + int index, + drvmgr_isr isr, + void *arg + ) +{ + int irq; + + irq = leon2_amba_get_irq(dev, index); + if ( irq < 0 ) + return -1; + + DBG("Unregistering IRQ %d to func 0x%x arg 0x%x\n", irq, (unsigned int)handler, (unsigned int)arg); + + return BSP_shared_interrupt_unregister(irq, isr, arg); +} + +int leon2_amba_int_clear + ( + struct drvmgr_dev *dev, + int index + ) +{ + int irq; + + irq = leon2_amba_get_irq(dev, index); + if ( irq < 0 ) + return -1; + + BSP_shared_interrupt_clear(irq); + + return DRVMGR_OK; +} + +int leon2_amba_int_mask + ( + struct drvmgr_dev *dev, + int index + ) +{ + int irq; + + irq = leon2_amba_get_irq(dev, index); + if ( irq < 0 ) + return -1; + + BSP_shared_interrupt_mask(irq); + + return DRVMGR_OK; +} + +int leon2_amba_int_unmask + ( + struct drvmgr_dev *dev, + int index + ) +{ + int irq; + + irq = leon2_amba_get_irq(dev, index); + if ( irq < 0 ) + return -1; + + BSP_shared_interrupt_unmask(irq); + + return DRVMGR_OK; +} + +struct drvmgr_drv_ops leon2_amba_ops = +{ + .init = {leon2_amba_init1, leon2_amba_init2, NULL, NULL}, + .remove = leon2_amba_remove, + .info = NULL +}; + +struct drvmgr_drv leon2_bus_drv = +{ + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_LEON2_AMBA_ID, /* Driver ID */ + "LEON2_AMBA_DRV", /* Must be placed at top bus */ + DRVMGR_BUS_TYPE_ROOT, /* Bus Type */ + &leon2_amba_ops, /* Bus Operations */ + NULL, /* Funcs */ + 0, /* Device Count */ + 0, /* Private structure size */ +}; diff --git a/c/src/lib/libbsp/sparc/shared/drvmgr/spw_bus.c b/c/src/lib/libbsp/sparc/shared/drvmgr/spw_bus.c new file mode 100644 index 0000000000..4556e62b9a --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/drvmgr/spw_bus.c @@ -0,0 +1,960 @@ +/* SpaceWire bus driver interface. + * + * COPYRIGHT (c) 2008. + * Aeroflex Gaisler. + * + * 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. + * + * 2009-11-20, Daniel Hellstrom <daniel@gaisler.com> + * Created + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <drvmgr/drvmgr.h> +#include <drvmgr/spw_bus.h> +#include <genirq.h> +#include <gpiolib.h> +#include <rmap.h> + +#include <bsp.h> +#include <stdint.h> + +#undef DEBUG + +#ifdef DEBUG + #define DBG(args...) printk(args) +#else + #define DBG(args...) +#endif + +struct virq_entry { + char *gpio_fsname; + int fd; +}; + +struct spw_bus_priv { + /* SpW-Bus driver handle */ + struct drvmgr_bus *bus; + + /* User configuration */ + struct spw_bus_config *config; /* User configuration */ + + /* Device prefix */ + int spwbus_id; + char prefix[16]; /* Device name prefix */ + + /* IRQ Handling */ + volatile unsigned int irq_mask; /* Bit mask of which IRQs has been received, and to handle */ + genirq_t genirq; /* Shared IRQ, ISR handling */ + rtems_id irqlock; /* Protect IRQ handling (register,enable,disable etc.) */ + rtems_id isr_task; /* TASK used to execute registered ISRs on SpaceWire Bus */ + rtems_id isr_execute_sem; /* Used to signal to ISR TASK that one or more IRQs has been received */ + volatile int task_terminate; /* Used to signal to ISR TASK to stop it's execution, and delete itself */ + char virqs[4]; /* Virtual IRQ table, used to separate IRQ from different GPIO pins */ +}; + +struct drvmgr_drv spw_bus_drv; +static int spw_bus_cnt = 0; + +int spw_bus_init1(struct drvmgr_bus *bus); +int spw_bus_unite(struct drvmgr_drv *drv, struct drvmgr_dev *dev); +int spw_bus_int_register(struct drvmgr_dev *dev, int index, const char *info, drvmgr_isr handler, void *arg); +int spw_bus_int_unregister(struct drvmgr_dev *dev, int index, drvmgr_isr isr, void *arg); +int spw_bus_int_mask(struct drvmgr_dev *dev, int index); +int spw_bus_int_unmask(struct drvmgr_dev *dev, int index); +int spw_bus_int_clear(struct drvmgr_dev *dev, int index); + +int spw_bus_freq_get( + struct drvmgr_dev *dev, + int options, + unsigned int *freq_hz); +/* READ/WRITE access to SpaceWire target over RMAP */ +int spw_bus_memcpy(void *dest, const void *src, int n, struct drvmgr_rw_arg *a); +int spw_bus_write_mem(void *dest, const void *src, int n, struct drvmgr_rw_arg *a); +int spw_bus_memset(void *dest, int c, int n, struct drvmgr_rw_arg *a); +uint8_t spw_bus_r8(uint8_t *srcadr, struct drvmgr_rw_arg *a); +uint16_t spw_bus_r16(uint16_t *srcadr, struct drvmgr_rw_arg *a); +uint32_t spw_bus_r32(uint32_t *srcadr, struct drvmgr_rw_arg *a); +uint64_t spw_bus_r64(uint64_t *srcadr, struct drvmgr_rw_arg *a); +void spw_bus_w8(uint8_t *dstadr, uint8_t data, struct drvmgr_rw_arg *a); +void spw_bus_w16(uint16_t *dstadr, uint16_t data, struct drvmgr_rw_arg *a); +void spw_bus_w32(uint32_t *dstadr, uint32_t data, struct drvmgr_rw_arg *a); +void spw_bus_w64(uint64_t *dstadr, uint64_t data, struct drvmgr_rw_arg *a); +int spw_bus_get_params(struct drvmgr_dev *dev, struct drvmgr_bus_params *params); +void *spw_bus_rw_arg(struct drvmgr_dev *dev); + +/* SPW RMAP bus operations */ +struct drvmgr_bus_ops spw_bus_ops = +{ + .init = + { + spw_bus_init1, + NULL, + NULL, + NULL + }, + .remove = NULL, + .unite = spw_bus_unite, + .int_register = spw_bus_int_register, + .int_unregister = spw_bus_int_unregister, + .int_mask = spw_bus_int_mask, + .int_unmask = spw_bus_int_unmask, + .int_clear = spw_bus_int_clear, + .get_params = spw_bus_get_params, + .freq_get = spw_bus_freq_get, +}; + +struct drvmgr_func spw_bus_funcs[] = +{ + DRVMGR_FUNC(SPWBUS_RW_ARG, spw_bus_rw_arg), + + DRVMGR_FUNC(SPWBUS_R8, spw_bus_r8), + DRVMGR_FUNC(SPWBUS_R16, spw_bus_r16), + DRVMGR_FUNC(SPWBUS_R32, spw_bus_r32), + DRVMGR_FUNC(SPWBUS_R64, spw_bus_r64), + + DRVMGR_FUNC(SPWBUS_W8, spw_bus_w8), + DRVMGR_FUNC(SPWBUS_W16, spw_bus_w16), + DRVMGR_FUNC(SPWBUS_W32, spw_bus_w32), + DRVMGR_FUNC(SPWBUS_W64, spw_bus_w64), + + DRVMGR_FUNC(SPWBUS_RMEM, spw_bus_memcpy), + DRVMGR_FUNC(SPWBUS_WMEM, spw_bus_write_mem), + DRVMGR_FUNC(SPWBUS_MEMSET, spw_bus_memset), + + DRVMGR_FUNC_END, +}; + +int spw_bus_dev_register(struct drvmgr_bus *bus, struct spw_node *node, int index) +{ + struct drvmgr_dev *newdev; + struct spw_bus_dev_info *info; + union drvmgr_key_value *value; + + int virq; + char virq_name[6]; + + /* Allocate new device and bus information */ + drvmgr_alloc_dev(&newdev, sizeof(struct spw_bus_dev_info)); + info = (struct spw_bus_dev_info *)(newdev + 1); + + /* Set Node ID */ + info->spwid = node->id.spwid; + + /* Get information from bus configuration */ + value = drvmgr_key_val_get(node->keys, "DST_ADR", KEY_TYPE_INT); + if ( !value ) { + printk("spw_bus_dev_register: Failed getting resource DST_ADR\n"); + info->dstadr = 0xfe; + } else { + DBG("spw_bus_dev_register: DST_ADR: 0x%02x\n", value->i); + info->dstadr = value->i; + } + value = drvmgr_key_val_get(node->keys, "DST_KEY", KEY_TYPE_INT); + if ( !value ) { + printk("spw_bus_dev_register: Failed getting resource DST_KEY\n"); + info->dstkey = 0; + } else { + DBG("spw_bus_dev_register: DST_KEY: 0x%02x\n", value->i); + info->dstkey = value->i; + } + /* Get the Virtual IRQ numbers, that will be looked up in VIRQ->GPIO table */ + strcpy(virq_name, "VIRQX"); + for (virq=1; virq<5; virq++) { + virq_name[4] = '0' + virq; + value = drvmgr_key_val_get(node->keys, virq_name, KEY_TYPE_INT); + if ( !value ) { + /* IRQ is optional, this device does not support VIRQ[X] */ + info->virqs[virq-1] = -1; + } else { + DBG("spw_bus_dev_register: %s: %d\n", virq_name, value->i); + info->virqs[virq-1] = value->i; + } + } + + /* Init new device */ + newdev->next = NULL; + newdev->parent = bus; /* Ourselfs */ + newdev->minor_drv = 0; + newdev->minor_bus = 0; + newdev->businfo = (void *)info; + newdev->priv = NULL; + newdev->drv = NULL; + newdev->name = node->name; + newdev->next_in_drv = NULL; + newdev->bus = NULL; + + /* Register new device */ + drvmgr_dev_register(newdev); + + return 0; +} + +/* Interrupt Service Routine, executes in interrupt context. This ISR: + * 1. Disable/Mask IRQ on IRQ controller, this disables further interrupts on + this IRQ number + * 2. Mark in the private struct that the IRQ has happened + * 3. Wake ISR TASK that will handle each marked IRQ + * + * The TASK will for every IRQ that is marked: + * 1. Call the ISRs that the SpW Node drivers have registered for this specific IRQ + * 2. unmask IRQ again. + * + * wakes */ +void spw_bus_isr(void *arg) +{ + struct spw_bus_priv *priv; + unsigned int old_irq_mask; + char *pvirq = (char *)arg; + int virq = *pvirq; + + priv = (struct spw_bus_priv *)(pvirq - offsetof(struct spw_bus_priv, virqs) - (virq-1)); + + gpiolib_irq_mask(priv->config->virq_table[virq-1].handle); + + /* Mark IRQ was received */ + old_irq_mask = priv->irq_mask; + priv->irq_mask = old_irq_mask | (1 << virq); + + /* Wake ISR execution TASK only if not woken before */ + if ( old_irq_mask == 0 ) { + rtems_semaphore_release(priv->isr_execute_sem); + } +} + +void spwbus_task(rtems_task_argument argument) +{ + int virq; + rtems_interrupt_level level; + unsigned int mask; + struct spw_bus_priv *priv = (struct spw_bus_priv *)argument; + + DBG("SpW-Bus: ISR Task is started\n"); + + while ( priv->task_terminate == 0 ) { + while ( (mask = priv->irq_mask) != 0 ) { + + /* Mark the IRQs handled */ + rtems_interrupt_disable(level); + priv->irq_mask &= ~mask; + rtems_interrupt_enable(level); + + mask = mask >> 1; + virq = 1; + while ( mask ) { + if ( mask & 1 ) { + /* execute all ISRs on this IRQ */ + DBG("SpW-Bus: ISR Task is executing VIRQ %d\n", virq); + genirq_doirq(priv->genirq, virq); + } + + /* Reenable the handled IRQ */ + gpiolib_irq_unmask(priv->config->virq_table[virq-1].handle); + + virq++; + mask = mask >> 1; + } + } + + DBG("SpW-Bus: ISR Task going to sleep\n"); + + rtems_task_wake_after(1); + + /* Wait for new IRQs to handle */ + rtems_semaphore_obtain(priv->isr_execute_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + + DBG("SpW-Bus: ISR Task woke up\n"); + } + + DBG("SpW-Bus: ISR Task is deleted\n"); + + rtems_task_delete(RTEMS_SELF); +} + +int spw_bus_init1(struct drvmgr_bus *bus) +{ + struct spw_node *node; + int i; + struct spw_bus_priv *priv = (struct spw_bus_priv *)bus->priv; + int status; + struct spwbus_virq_config *vcfg; + int virq; + struct gpiolib_config gpiocfg; + + DBG("SpW-BUS: init\n"); + + priv->spwbus_id = spw_bus_cnt++; + priv->irq_mask = 0; + priv->task_terminate = 0; + + priv->genirq = genirq_init(32); + if ( priv->genirq == NULL ) { + return RTEMS_UNSATISFIED; + } + + if ( priv->config->resources ) + drvmgr_bus_res_add(bus, priv->config->resources); + + /* Create Semaphore used when doing IRQ/ISR registering/enabling etc. */ + status = rtems_semaphore_create( + rtems_build_name('S', 'P', 'C', '0' + bus->dev->minor_drv), + 1, + RTEMS_FIFO | RTEMS_COUNTING_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | \ + RTEMS_LOCAL | RTEMS_NO_PRIORITY_CEILING, + 0, + &priv->irqlock); + if ( status != RTEMS_SUCCESSFUL ) { + DBG("SpW-BUS: Failed to create irqlock semaphore: %d\n", status); + return -1; + } + + /* Create Semaphore used to Signal to ISR TASK that a Interrupt has happened */ + status = rtems_semaphore_create( + rtems_build_name('S', 'P', 'D', '0' + bus->dev->minor_drv), + 0, + RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | \ + RTEMS_LOCAL | RTEMS_NO_PRIORITY_CEILING, + 0, + &priv->isr_execute_sem); + if ( status != RTEMS_SUCCESSFUL ) { + DBG("SpW-BUS: Failed to create irqlock semaphore: %d\n", status); + return -1; + } + + /* Create ISR task */ + status = rtems_task_create( + rtems_build_name( 'I', 'S', 'T', '0' + bus->dev->minor_drv), + 1, + RTEMS_MINIMUM_STACK_SIZE, + RTEMS_NO_PREEMPT, + RTEMS_LOCAL | RTEMS_NO_FLOATING_POINT, + &priv->isr_task); + if (status != RTEMS_SUCCESSFUL) { + DBG ("SpW-BUS: Can't create task: %d\n", status); + return -1; + } + + /* Initialize VIRQs and open the GPIO drivers if not already done */ + vcfg = &priv->config->virq_table[0]; + for (virq=1; virq<5; virq++) { + priv->virqs[virq-1] = -1; + if ( vcfg->handle == NULL ) { + /* Open GPIO PIN and diable IRQ for now */ + if ( vcfg->gpio_fsname ) { + vcfg->handle = gpiolib_open_by_name(vcfg->gpio_fsname); + if ( vcfg->handle != NULL ) { + priv->virqs[virq-1] = virq; + gpiocfg.mask = 0; + gpiocfg.irq_level = GPIOLIB_IRQ_LEVEL; + gpiocfg.irq_polarity = GPIOLIB_IRQ_POL_HIGH; + gpiolib_set_config(vcfg->handle, &gpiocfg); + if ( gpiolib_set(vcfg->handle, 0, 0) ) { + DBG("SpW-BUS: Failed to configure GPIO as input for VIRQ%d\n", virq); + } + if ( gpiolib_irq_register(vcfg->handle, spw_bus_isr, &priv->virqs[virq-1]) ) { + DBG("SpW-BUS: Failed to register GPIO ISR for VIRQ%d\n", virq); + } + } else { + DBG("SpW-BUS: Failed to open GPIO (%s) for VIRQ%d\n",vcfg->gpio_fsname, virq); + } + } + } else { + /* Already opened for us */ + priv->virqs[virq-1] = virq; + } + vcfg++; + } + + /* Start ISR Task, there is no work to do, so the task will wait for isr_execute_sem semaphore released by real ISR */ + status = rtems_task_start(priv->isr_task, spwbus_task, (int)priv); + if ( status != RTEMS_SUCCESSFUL ) { + DBG("SpW-BUS: Failed to start ISR task: %d\n", status); + return -1; + } + + /* Create Device name */ + strcpy(priv->prefix, "/dev/spwbus0"); + priv->prefix[11] = '0' + priv->spwbus_id; + mkdir(priv->prefix, S_IRWXU | S_IRWXG | S_IRWXO); + priv->prefix[12] = '/'; + priv->prefix[13] = '\0'; + + /**** REGISTER NEW DEVICES ****/ + i=0; + node = priv->config->nodes; + if ( node ) { + while ( node->id.spwid ) { + DBG("SpW-BUS: register node %d (%p)\n", i, node); + if ( spw_bus_dev_register(bus, node, i) ) { + return RTEMS_UNSATISFIED; + } + i++; + node++; + } + } + + return DRVMGR_OK; +} + +int spw_bus_unite(struct drvmgr_drv *drv, struct drvmgr_dev *dev) +{ + struct spw_bus_dev_info *info; + struct spw_bus_drv_info *spwdrv; + struct spw_id *id; + + if ( !drv || !dev || !dev->parent ) + return 0; + + if ( (drv->bus_type!=DRVMGR_BUS_TYPE_SPW_RMAP) || (dev->parent->bus_type != DRVMGR_BUS_TYPE_SPW_RMAP) ) { + return 0; + } + + info = (struct spw_bus_dev_info *)dev->businfo; + if ( !info ) + return 0; + + /* Get SPW RMAP driver info */ + spwdrv = (struct spw_bus_drv_info *)drv; + id = spwdrv->ids; + if ( !id ) + return 0; + + while ( id->spwid ) { + if ( id->spwid == info->spwid ) { + /* Driver is suitable for device, Unite them */ + return 1; + } + id++; + } + + return 0; +} + +int spw_bus_int_get(struct drvmgr_dev *dev, int index) +{ + int virq; + + /* Relative (positive) or absolute (negative) IRQ number */ + if ( index >= 0 ) { + /* IRQ Index relative to Cores base IRQ */ + + /* Get Base IRQ */ + virq = ((struct spw_bus_dev_info *)dev->businfo)->virqs[index]; + if ( virq <= 0 ) + return -1; + } else { + /* Absolute IRQ number */ + virq = -index; + } + return virq; +} + +int spw_bus_int_register( + struct drvmgr_dev *dev, + int index, + const char *info, + drvmgr_isr handler, + void *arg) +{ + struct drvmgr_bus *bus; + struct spw_bus_priv *priv; + int status, virq; + void *handle; + + /* Get IRQ number from index and device information */ + virq = spw_bus_int_get(dev, index); + if ( virq <= 0 ) + return DRVMGR_FAIL; + + bus = dev->parent; + priv = bus->priv; + + DBG("SpW-BUS: Register ISR for VIRQ%d\n", virq); + + handle = priv->config->virq_table[virq-1].handle; + if ( handle == NULL ) + return DRVMGR_FAIL; + + rtems_semaphore_obtain(priv->irqlock, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + + status = genirq_register(priv->genirq, virq, handler, arg); + if ( status >= 0 ) { + status = genirq_enable(priv->genirq, virq, handler, arg); + if ( status == 0 ) { + /* Enable IRQ for first enabled handler only */ + + /* Unmask the GPIO IRQ at the source (at the GPIO core), + * and at the IRQ controller + */ + struct gpiolib_config gpiocfg; + gpiocfg.mask = 1; + gpiocfg.irq_level = GPIOLIB_IRQ_LEVEL; + gpiocfg.irq_polarity = GPIOLIB_IRQ_POL_HIGH; + gpiolib_set_config(handle, &gpiocfg); + + gpiolib_irq_enable(handle); + } + } + + rtems_semaphore_release(priv->irqlock); + + if (status < 0) + return DRVMGR_FAIL; + else + return DRVMGR_OK; +} + +int spw_bus_int_unregister(struct drvmgr_dev *dev, int index, drvmgr_isr isr, void *arg) +{ + struct drvmgr_bus *bus; + struct spw_bus_priv *priv; + int virq, status; + void *handle; + + /* Get IRQ number from index and device information */ + virq = spw_bus_int_get(dev, index); + if ( virq <= 0 ) + return DRVMGR_FAIL; + + DBG("SpW-BUS: unregister ISR for VIRQ%d\n", virq); + + bus = dev->parent; + priv = bus->priv; + + handle = priv->config->virq_table[virq-1].handle; + if ( handle == NULL ) + return DRVMGR_FAIL; + + rtems_semaphore_obtain(priv->irqlock, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + + status = genirq_disable(priv->genirq, virq, isr, arg); + if ( status >= 0 ) { + if ( status == 0 ) { + /* Disable IRQ only when no other handler is enabled */ + + /* Mask the GPIO IRQ at the source (at the GPIO core), + * disable the IRQ at the interrupt controller. + */ + struct gpiolib_config gpiocfg; + gpiocfg.mask = 0; + gpiocfg.irq_level = GPIOLIB_IRQ_LEVEL; + gpiocfg.irq_polarity = GPIOLIB_IRQ_POL_HIGH; + gpiolib_set_config(handle, &gpiocfg); + + gpiolib_irq_disable(handle); + } + status = genirq_unregister(priv->genirq, virq, isr, arg); + } + + rtems_semaphore_release(priv->irqlock); + + if (status < 0) + return DRVMGR_FAIL; + else + return DRVMGR_OK; +} + +/* Unmask interrupt */ +int spw_bus_int_unmask(struct drvmgr_dev *dev, int index) +{ + struct drvmgr_bus *bus; + struct spw_bus_priv *priv; + int virq; + void *handle; + + /* Get IRQ number from index and device information */ + virq = spw_bus_int_get(dev, index); + if ( virq <= 0 ) + return DRVMGR_FAIL; + + bus = dev->parent; + priv = bus->priv; + + DBG("SpW-BUS: unmask IRQ for VIRQ%d\n", virq); + + handle = priv->config->virq_table[virq-1].handle; + if ( handle == NULL ) + return DRVMGR_FAIL; + + if ( genirq_check(priv->genirq, virq) ) + return DRVMGR_FAIL; + + rtems_semaphore_obtain(priv->irqlock, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + + gpiolib_irq_unmask(handle); + + rtems_semaphore_release(priv->irqlock); + + return DRVMGR_OK; +} + +/* mask interrupt */ +int spw_bus_int_mask(struct drvmgr_dev *dev, int index) +{ + struct drvmgr_bus *bus; + struct spw_bus_priv *priv; + int virq; + void *handle; + + /* Get IRQ number from index and device information */ + virq = spw_bus_int_get(dev, index); + if ( virq <= 0 ) + return DRVMGR_FAIL; + + bus = dev->parent; + priv = bus->priv; + + handle = priv->config->virq_table[virq-1].handle; + if ( handle == NULL ) + return DRVMGR_FAIL; + + if ( genirq_check(priv->genirq, virq) ) + return DRVMGR_FAIL; + + rtems_semaphore_obtain(priv->irqlock, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + + /* Register a ISR for the first registered handler */ + gpiolib_irq_mask(handle); + + rtems_semaphore_release(priv->irqlock); + + return DRVMGR_OK; +} + +int spw_bus_int_clear(struct drvmgr_dev *dev, int index) +{ + struct drvmgr_bus *bus; + struct spw_bus_priv *priv; + int virq; + void *handle; + + /* Get IRQ number from index and device information */ + virq = spw_bus_int_get(dev, index); + if ( virq < 0 ) + return DRVMGR_FAIL; + + bus = dev->parent; + priv = bus->priv; + + handle = priv->config->virq_table[virq-1].handle; + if ( handle == NULL ) + return DRVMGR_FAIL; + + /* Register a ISR for the first registered handler */ + /*drvmgr_interrupt_clear(bus->dev, -irq, spw_bus_isr, priv);*/ + if ( gpiolib_irq_clear(handle)) { + DBG("SpW-BUS: Failed to Clear IRQ for VIRQ%d\n", virq); + } + + return DRVMGR_OK; +} + +void *spw_bus_rw_arg(struct drvmgr_dev *dev) +{ + if (dev == NULL) + return (void *)DRVMGR_FAIL; + return dev; +} + +/* Copy */ +int spw_bus_memcpy(void *dest, const void *src, int n, struct drvmgr_rw_arg *a) +{ + struct rmap_command_read readcmd; + int status; + struct drvmgr_dev *dev = (struct drvmgr_dev *)a->arg; + struct spw_bus_dev_info *info = (struct spw_bus_dev_info *)dev->businfo; + struct spw_bus_priv *priv = (struct spw_bus_priv *)dev->parent->priv; + int max_pkt_size = 128; /* We assume that RMAP can do at least 128 bytes data per packet */ + + unsigned int source, destination, left; + + if ( n > max_pkt_size ) { + struct rmap_config stack_cfg; + rmap_ioctl(priv->config->rmap, RMAP_IOCTL_GET_CONFIG, &stack_cfg); + max_pkt_size = stack_cfg.max_rx_len; + } + + source = (unsigned int)src; + destination = (unsigned int )dest; + left = n; + while ( left > 0 ) { + readcmd.type = RMAP_CMD_RI; + readcmd.dstadr = info->dstadr; + readcmd.dstkey = info->dstkey; + readcmd.address = source; + readcmd.length = (left > max_pkt_size) ? max_pkt_size : left; + readcmd.datalength = 0; + readcmd.data = (void *)destination; + + DBG("RMAP READ1: 0x%08x - 0x%08x\n", source, (source + (readcmd.length - 1))); + + /* Send Command */ + status = rmap_send(priv->config->rmap, (struct rmap_command *)&readcmd); + + if ( status ) { + printf("RMAP_MEMCPY READ: Failed to send/receive command %d\n", status); + memset(dest, 0, n); + /* Should we remove device? */ + return -1; + } + + /* Read Data */ + if ( readcmd.status != 0 ) { + printf("RMAP_MEMCPY READ: Status non-zero 0x%x, dlen: 0x%x\n", readcmd.status, readcmd.datalength); + memset(dest, 0, n); + /* Should we remove device? */ + return -1; + } + + source += readcmd.length; + destination += readcmd.length; + left -= readcmd.length; + } + +#ifdef DEBUG + if ( n == 4 ) { + printf("RMAP READ2: 0x%08x(4): 0x%08x\n", src, *(unsigned int *)dest); + } else { + printf("RMAP READ2: 0x%08x - 0x%08x\n", src, ((unsigned int)src)+(n-1)); + } +#endif + + /* Return sucessful */ + return 0; +} + +/* Note that ((unsigned char *)src)[n] will be overwitten with the RMAP DATA CRC */ +int spw_bus_write_mem(void *dest, const void *src, int n, struct drvmgr_rw_arg *a) +{ + struct rmap_command_write writecmd; + int status; + struct drvmgr_dev *dev = (struct drvmgr_dev *)a->arg; + struct spw_bus_dev_info *info = (struct spw_bus_dev_info *)dev->businfo; + struct spw_bus_priv *priv = (struct spw_bus_priv *)dev->parent->priv; + + /* Use Verify Write when accessing registers (length 1,2,4). */ + if ( n <= 4 && n != 3) { + writecmd.type = RMAP_CMD_WIV; + } else { + writecmd.type = RMAP_CMD_WI; + } + writecmd.dstadr = info->dstadr; + writecmd.dstkey = info->dstkey; + writecmd.address = (unsigned int)dest; + writecmd.length = n; + writecmd.data = (unsigned char *)src; +#ifdef DEBUG + if ( n == 4 ) { + printf("RMAP WRITE: 0x%08x(4): 0x%08x\n", + dest, *(unsigned int *)src); + } else { + printf("RMAP WRITE: 0x%08x - 0x%08x\n", + dest, ((unsigned int)dest)+(n-1)); + } +#endif + /* Send Command */ + status = rmap_send(priv->config->rmap, + (struct rmap_command *)&writecmd); + + if ( status ) { + printf("RMAP_MEMCPY WRITE: Failed to send/receive command %d\n", + status); + return -1; + } + + /* Read Data */ + if ( writecmd.status != 0 ) { + printf("RMAP_MEMCPY WRITE: Status non-zero 0x%x, adr: 0x%x\n", + (unsigned char)writecmd.status, + (unsigned char)writecmd.address); + return -1; + } + + /* Return sucessful */ + return 0; +} + +/* Use standard Driver manager skeleton for this implementation */ +int spw_bus_memset(void *dest, int c, int n, struct drvmgr_rw_arg *a) +{ + drvmgr_rw_memset(dest, c, n, a, (drvmgr_wmem_arg)spw_bus_write_mem); + return 0; +} + +int spw_bus_freq_get( + struct drvmgr_dev *dev, + int options, + unsigned int *freq_hz) +{ + /* Link Frequency does not translate so good here, since it is + * a SpaceWire network, different parts may have different + * transfer rates. + */ + *freq_hz = 0; + + return -1; +} + +uint8_t spw_bus_r8(uint8_t *srcadr, struct drvmgr_rw_arg *a) +{ + uint8_t result; + if ( spw_bus_memcpy((void *)&result, (const void *)srcadr, 1, a) ) { + return 0xff; + } + return result; +} + +uint16_t spw_bus_r16(uint16_t *srcadr, struct drvmgr_rw_arg *a) +{ + uint16_t result; + if ( spw_bus_memcpy((void *)&result, (const void *)srcadr, 2, a) ) { + return 0xff; + } + return result; +} + +uint32_t spw_bus_r32(uint32_t *srcadr, struct drvmgr_rw_arg *a) +{ + uint32_t result; + if ( spw_bus_memcpy((void *)&result, (const void *)srcadr, 4, a) ) { + return 0xff; + } + return result; +} + +uint64_t spw_bus_r64(uint64_t *srcadr, struct drvmgr_rw_arg *a) +{ + uint64_t result; + if ( spw_bus_memcpy((void *)&result, (const void *)srcadr, 8, a) ) { + return 0xff; + } + return result; +} + +void spw_bus_w8(uint8_t *dstadr, uint8_t data, struct drvmgr_rw_arg *a) +{ + uint8_t buf[2]; /* One byte extra room for RMAP DATA CRC */ + + buf[0] = data; + if ( spw_bus_write_mem((void *)dstadr, (const void *)&buf[0], 1, a) ) { + /* Handle Error */ + } +} + +void spw_bus_w16(uint16_t *dstadr, uint16_t data, struct drvmgr_rw_arg *a) +{ + uint16_t buf[2]; /* One byte extra room for RMAP DATA CRC */ + + buf[0] = data; + if ( spw_bus_write_mem((void *)dstadr, (const void *)&buf[0], 2, a) ) { + /* Handle Error */ + } +} + +void spw_bus_w32(uint32_t *dstadr, uint32_t data, struct drvmgr_rw_arg *a) +{ + uint32_t buf[2]; /* One byte extra room for RMAP DATA CRC */ + + buf[0] = data; + if ( spw_bus_write_mem((void *)dstadr, (const void *)&buf[0], 4, a) ) { + /* Handle Error */ + } +} + +void spw_bus_w64(uint64_t *dstadr, uint64_t data, struct drvmgr_rw_arg *a) +{ + uint64_t buf[2]; /* One byte extra room for RMAP DATA CRC */ + + buf[0] = data; + if ( spw_bus_write_mem((void *)dstadr, (const void *)&buf[0], 8, a) ) { + /* Handle Error */ + } +} + +int spw_bus_get_params(struct drvmgr_dev *dev, struct drvmgr_bus_params *params) +{ + struct spw_bus_priv *priv = dev->parent->priv; + + params->dev_prefix = &priv->prefix[5]; + + return 0; +} + +/************* USER INTERFACE *************/ + +/*** START: THIS SHOULD BE MOVED TO GRSPW DRIVER ***/ +struct grspw_priv { + /* configuration parameters */ + struct drvmgr_dev *dev; /* Driver manager device */ + char devName[32]; /* Device Name */ + void *regs; +}; +extern struct drvmgr_drv grspw_drv_info; + +/* Find GRSPW device from device name */ +static struct drvmgr_dev *grspw_find_dev(char *devName) +{ + struct drvmgr_drv *drv = &grspw_drv_info; + struct drvmgr_dev *dev; + struct grspw_priv *grspw_priv; + + dev = drv->dev; + while(dev) { + grspw_priv = dev->priv; + if ( strcmp(devName, grspw_priv->devName) == 0 ) + return dev; + dev = dev->next_in_drv; + } + return NULL; +} +/*** STOP: THIS SHOULD BE MOVED TO GRSPW DRIVER ***/ + + +/* Called from USER to attach bus */ +int spw_bus_register(struct spw_bus_config *config) +{ + struct drvmgr_dev *grspw_dev; + struct drvmgr_bus *bus; + struct spw_bus_priv *priv; + + DBG("SpW-BUS: finding GRSPW device\n"); + + /* Find GRSPW Driver to attach bus to */ + grspw_dev = grspw_find_dev(config->devName); + if ( !grspw_dev ) { + DBG("SpW-BUS: Failed to find GRSPW device\n"); + return -1; + } + if ( grspw_dev->bus ) { + DBG("SpW-BUS: GRSPW already has a bus attached to it, aborting\n"); + return -1; + } + + /* Allocate Bus and private structures */ + drvmgr_alloc_bus(&bus, sizeof(*priv)); + priv = (struct spw_bus_priv *)(bus + 1); + + /* Save the configuration for later */ + priv->config = config; + + /* Init the bus */ + grspw_dev->bus = bus; + bus->bus_type = DRVMGR_BUS_TYPE_SPW_RMAP; + bus->dev = grspw_dev; + bus->priv = priv; + bus->ops = (struct drvmgr_bus_ops *)&spw_bus_ops; + bus->funcs = spw_bus_funcs; + priv->bus = bus; + + DBG("SpW-BUS: registering bus\n"); + drvmgr_bus_register(bus); /* this will call spw_bus_init to register the devices */ + + 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..98ebd75002 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/gpio/gpiolib.c @@ -0,0 +1,280 @@ +/* GPIOLIB interface implementation + * + * 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. + * + * 2009-05-20, Daniel Hellstrom <daniel@gaisler.com> + * Created + * + */ + +#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); +} + +int gpiolib_irq_mask(void *handle) +{ + return gpiolib_irq_opts(handle, GPIOLIB_IRQ_MASK); +} + +int gpiolib_irq_unmask(void *handle) +{ + return gpiolib_irq_opts(handle, GPIOLIB_IRQ_UNMASK); +} + + +/*** 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..ab08e1bfbb --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/gpio/grgpio.c @@ -0,0 +1,454 @@ +/* + * GRGPIO GPIO Driver interface. + * + * COPYRIGHT (c) 2009. + * Gaisler Research + * + * 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. + * + * 2009-05-20, Daniel Hellstrom <daniel@gaisler.com> + * Created + */ + + +#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; + } + } + if ( options & GPIOLIB_IRQ_MASK ) { + /* Mask (disable) interrupt at interrupt controller */ + if ( drvmgr_interrupt_mask(priv->dev, portnr) ) { + return -1; + } + } + if ( options & GPIOLIB_IRQ_UNMASK ) { + /* Unmask (enable) interrupt at interrupt controller */ + if ( drvmgr_interrupt_unmask(priv->dev, portnr) ) { + 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/graes/graes.c b/c/src/lib/libbsp/sparc/shared/graes/graes.c new file mode 100644 index 0000000000..1e729837b7 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/graes/graes.c @@ -0,0 +1,1290 @@ +/* GRAES AES dma driver + * + * -------------------------------------------------------------------------- + * -- This file is a part of GAISLER RESEARCH source code. + * -- Copyright (C) 2009, Aeroflex Gaisler AB - all rights reserved. + * -- + * -- ANY USE OR REDISTRIBUTION IN PART OR IN WHOLE MUST BE HANDLED IN + * -- ACCORDANCE WITH THE GAISLER LICENSE AGREEMENT AND MUST BE APPROVED + * -- IN ADVANCE IN WRITING. + * -- + * -- BY DEFAULT, DISTRIBUTION OR DISCLOSURE IS NOT PERMITTED. + * -------------------------------------------------------------------------- + * + * 2010-06-29, Konrad Eisele <konrad@gaisler.com> + * GRAES DMA driver based on GRTM driver + * + * 2008-12-11, Daniel Hellstrom <daniel@gaisler.com> + * Converted to support driver manager + * + * 2007-04-17, Daniel Hellstrom <daniel@gaisler.com> + * New driver in sparc shared directory. + * + */ + + +#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 <graes.h> + +#define REMOTE_DESCRIPTORS + +#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> + +/* GRAES register map */ +struct graes_regs { + volatile unsigned int dma_ctrl; /* DMA Control Register (0x00) */ + volatile unsigned int dma_status; /* DMA Status Register (0x04) */ + volatile unsigned int dma_bd; /* DMA Descriptor Pointer Register (0x08) */ + volatile unsigned int d0; /* */ + + volatile unsigned int d1; /* */ + volatile unsigned int d2; /* */ + + int unused0[(0x80-0x18)/4]; + +}; + +#define GRAES_BDAR_ENTRIES 32 +#define GRAES_BDAR_SIZE (GRAES_BDAR_ENTRIES * sizeof(struct graes_bd)) + +/* DMA Control Register (0x00) */ +#define GRAES_DMA_CTRL_EN_BIT 0 +#define GRAES_DMA_CTRL_IE_BIT 1 + +#define GRAES_DMA_CTRL_EN (1<<GRAES_DMA_CTRL_EN_BIT) +#define GRAES_DMA_CTRL_IE (1<<GRAES_DMA_CTRL_IE_BIT) + +/* GRAES transmit descriptor (GRAES_BDAR_SIZE Alignment need) */ +struct graes_bd { + volatile unsigned int ctrl; + union { + unsigned int d[5]; + struct { + unsigned int in; + unsigned int out; + unsigned int iv; + unsigned int key; + unsigned int next; + }; + } u; +}; + +#define GRAES_BD_EN_BIT 0 +#define GRAES_BD_IE_BIT 1 +#define GRAES_BD_WR_BIT 2 +#define GRAES_BD_KEY_BIT 7 +#define GRAES_BD_IV_BIT 6 +#define GRAES_BD_OUT_BIT 5 + +#define GRAES_BD_EN (1<<GRAES_BD_EN_BIT) +#define GRAES_BD_WR (1<<GRAES_BD_WR_BIT) +#define GRAES_BD_IE (1<<GRAES_BD_IE_BIT) +#define GRAES_BD_KEY (1<<GRAES_BD_KEY_BIT) +#define GRAES_BD_IV (1<<GRAES_BD_IV_BIT) +#define GRAES_BD_OUT (1<<GRAES_BD_OUT_BIT) + +/* Load register */ + +#define READ_REG(address) (*(volatile unsigned int *)address) + +/* Driver functions */ +static rtems_device_driver graes_initialize(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver graes_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver graes_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver graes_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver graes_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver graes_ioctl(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); + +#define GRAES_DRIVER_TABLE_ENTRY { graes_initialize, graes_open, graes_close, graes_read, graes_write, graes_ioctl } + +static rtems_driver_address_table graes_driver = GRAES_DRIVER_TABLE_ENTRY; + +/* Structure that connects BD with SoftWare Frame */ +struct graes_ring { + struct graes_ring *next; + struct graes_bd *bd; + struct graes_block *frm; +}; + +struct graes_priv { + struct drvmgr_dev *dev; /* Driver manager device */ + char devName[32]; /* Device Name */ + struct graes_regs *regs; + int irq; + int minor; + + int open; + int running; + + struct graes_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 graes_ring *_ring; /* Root of ring */ + struct graes_ring *ring; /* Next ring to use for new frames to be transmitted */ + struct graes_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 graes_list ready; /* Frames Waiting for free BDs */ + struct graes_list scheduled; /* Frames in BDs beeing transmitted */ + struct graes_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 graes_ioc_hw hw_avail; /* Hardware support available */ + struct graes_ioc_config config; + struct graes_ioc_stats stats; + + rtems_id sem_rx; +}; + +/* Prototypes */ +static void *graes_memalign(unsigned int boundary, unsigned int length, void *realbuf); +static void graes_hw_reset(struct graes_priv *pDev); +static void graes_interrupt(int irq, void *arg); + +/* Common Global Variables */ +static rtems_id graes_dev_sem; +static int graes_driver_io_registered = 0; +static rtems_device_major_number graes_driver_io_major = 0; + +/******************* Driver manager interface ***********************/ + +/* Driver prototypes */ +static int graes_register_io(rtems_device_major_number *m); +static int graes_device_init(struct graes_priv *pDev); + +static int graes_init2(struct drvmgr_dev *dev); +static int graes_init3(struct drvmgr_dev *dev); + +static struct drvmgr_drv_ops graes_ops = +{ + {NULL, graes_init2, graes_init3, NULL}, + NULL, + NULL, +}; + +static struct amba_dev_id graes_ids[] = +{ + {VENDOR_GAISLER, GAISLER_GRAESDMA}, + {0, 0} /* Mark end of table */ +}; + +static struct amba_drv_info graes_drv_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_GAISLER_GRAES_ID, /* Driver ID */ + "GRAES_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &graes_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + 0, + }, + &graes_ids[0] +}; + +void graes_register_drv (void) +{ + DBG("Registering GRAES driver\n"); + drvmgr_drv_register(&graes_drv_info.general); +} + +static int graes_init2(struct drvmgr_dev *dev) +{ + struct graes_priv *priv; + + DBG("GRAES[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name); + priv = dev->priv = malloc(sizeof(struct graes_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 graes_init3(struct drvmgr_dev *dev) +{ + struct graes_priv *priv; + char prefix[32]; + rtems_status_code status; + + priv = dev->priv; + + /* Do initialization */ + + if ( graes_driver_io_registered == 0) { + /* Register the I/O driver only once for all cores */ + if ( graes_register_io(&graes_driver_io_major) ) { + /* Failed to register I/O driver */ + DBG("GRAES[%d] Failed to register I/O driver\n", dev->minor_drv); + dev->priv = NULL; + return DRVMGR_FAIL; + } + + graes_driver_io_registered = 1; + } + + /* I/O system registered and initialized + * Now we take care of device initialization. + */ + if ( graes_device_init(priv) ) { + DBG("GRAES[%d] Failed to call graes_device_init\n", dev->minor_drv); + 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/graes%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/%sgraes%d", prefix, dev->minor_bus); + } + + DBG("GRAES: add dev %s\n",priv->devName); + + /* Register Device */ + status = rtems_io_register_name(priv->devName, graes_driver_io_major, dev->minor_drv); + if (status != RTEMS_SUCCESSFUL) { + return status; + } + + return DRVMGR_OK; +} + +/******************* Driver Implementation ***********************/ + +static int graes_register_io(rtems_device_major_number *m) +{ + rtems_status_code r; + + if ((r = rtems_io_register_driver(0, &graes_driver, m)) == RTEMS_SUCCESSFUL) { + DBG("GRAES driver successfully registered, major: %d\n", *m); + } else { + switch(r) { + case RTEMS_TOO_MANY: + printk("GRAES rtems_io_register_driver failed: RTEMS_TOO_MANY\n"); + return -1; + case RTEMS_INVALID_NUMBER: + printk("GRAES rtems_io_register_driver failed: RTEMS_INVALID_NUMBER\n"); + return -1; + case RTEMS_RESOURCE_IN_USE: + printk("GRAES rtems_io_register_driver failed: RTEMS_RESOURCE_IN_USE\n"); + return -1; + default: + printk("GRAES rtems_io_register_driver failed\n"); + return -1; + } + } + return 0; +} + +static int graes_device_init(struct graes_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 graes_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', 'P', 'R', '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; + } + + /* Allocate Memory for Descriptors */ +#ifdef REMOTE_DESCRIPTORS + pDev->bds = 0xc0800000; + pDev->_bds = 0xc0800000; +#else + pDev->bds = (struct graes_bd *)graes_memalign(GRAES_BDAR_SIZE, GRAES_BDAR_SIZE, &pDev->_bds); +#endif + if ( !pDev->bds ) { + DBG("GRAES: Failed to allocate descriptor table\n"); + return -1; + } + memset(pDev->bds, 0, GRAES_BDAR_SIZE); + + pDev->_ring = malloc(sizeof(struct graes_ring) * GRAES_BDAR_ENTRIES); + if ( !pDev->_ring ) { + DBG("GRAES: Failed to allocate ring\n"); + return -1; + } + + /* Reset Hardware before attaching IRQ handler */ + graes_hw_reset(pDev); + + return 0; +} + + +static inline void graes_list_clr(struct graes_list *list) +{ + list->head = NULL; + list->tail = NULL; +} + +static void graes_hw_reset(struct graes_priv *pDev) +{ + /* Reset Core */ +} + +static void graes_hw_get_implementation(struct graes_priv *pDev, struct graes_ioc_hw *hwcfg) +{ + hwcfg->key_size= 256; +} + +#warning Extra: Implement proper default calculation from hardware configuration +static void graes_hw_get_default_modes(struct graes_ioc_config *cfg, struct graes_ioc_hw *hwcfg) +{ + cfg->key_size = 256; + + /* Interrupt options */ + cfg->blocking = 0; /* non-blocking mode is default */ + cfg->enable_cnt = 1; /* generate interrupt every 16 descriptor */ + cfg->isr_desc_proc = 1; /* Let interrupt handler do descriptor processing */ + cfg->timeout = RTEMS_NO_TIMEOUT; + +} + +static void *graes_memalign(unsigned int boundary, unsigned int length, void *realbuf) +{ + *(int *)realbuf = (int)malloc(length+boundary); + DBG("GRAES: Alloced %d (0x%x) bytes, requested: %d\n",length+boundary,length+boundary,length); + return (void *)(((*(unsigned int *)realbuf)+boundary) & ~(boundary-1)); +} + +static int graes_hw_set_config(struct graes_priv *pDev, struct graes_ioc_config *cfg, struct graes_ioc_hw *hwcfg) +{ + struct graes_regs *regs = pDev->regs; + unsigned int tmp; + + return 0; +} + +static int graes_start(struct graes_priv *pDev) +{ + struct graes_regs *regs = pDev->regs; + int i; + struct graes_ioc_config *cfg = &pDev->config; + volatile unsigned int *txrdy_reg; + unsigned int txrdy_mask, transaddr; + + /* Clear Descriptors */ + memset(pDev->bds,0,GRAES_BDAR_SIZE); + + /* Clear stats */ + memset(&pDev->stats,0,sizeof(struct graes_ioc_stats)); + + /* Init Descriptor Ring */ + memset(pDev->_ring,0,sizeof(struct graes_ring)*GRAES_BDAR_ENTRIES); + for(i=0;i<(GRAES_BDAR_ENTRIES-1);i++){ + pDev->_ring[i].next = &pDev->_ring[i+1]; + pDev->_ring[i].bd = &pDev->bds[i]; + pDev->_ring[i].frm = NULL; + } + pDev->_ring[(GRAES_BDAR_ENTRIES-1)].next = &pDev->_ring[0]; + pDev->_ring[(GRAES_BDAR_ENTRIES-1)].bd = &pDev->bds[(GRAES_BDAR_ENTRIES-1)]; + pDev->_ring[(GRAES_BDAR_ENTRIES-1)].frm = NULL; + + pDev->ring = &pDev->_ring[0]; + pDev->ring_end = &pDev->_ring[0]; + + /* Clear Scheduled, Ready and Sent list */ + graes_list_clr(&pDev->ready); + graes_list_clr(&pDev->scheduled); + graes_list_clr(&pDev->sent); + + /* Software init */ + pDev->handling_transmission = 0; + + /* Reset the transmitter */ + regs->dma_ctrl = 0; /* Leave Reset */ + + + /* Set Descriptor Pointer Base register to point to first descriptor */ + drvmgr_translate(pDev->dev, 0, 0, (void *)pDev->bds, (void **)&transaddr); + regs->dma_bd = transaddr; + + DBG("GRAES: set bd to 0x%08x\n",transaddr); + + /*regs->dma_bd = (unsigned int)pDev->bds;*/ + + /* Set hardware options as defined by config */ + if ( graes_hw_set_config(pDev, cfg, &pDev->hw_avail) ) { + return RTEMS_IO_ERROR; + } + + + DBG("GRAES: reset time %d\n",i); + + + /* Mark running before enabling the DMA transmitter */ + pDev->running = 1; + + /* Enable interrupts (Error and DMA TX) */ + regs->dma_ctrl = GRAES_DMA_CTRL_IE; + + DBG("GRAES: STARTED\n"); + + return RTEMS_SUCCESSFUL; +} + +static void graes_stop(struct graes_priv *pDev) +{ + struct graes_regs *regs = pDev->regs; + + /* Disable the transmitter & Interrupts */ + regs->dma_ctrl = 0; + + + DBG("GRAES: STOPPED\n"); + + /* Flush semaphore in case a thread is stuck waiting for TX Interrupts */ + rtems_semaphore_flush(pDev->sem_rx); +} + +static rtems_device_driver graes_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg) +{ + struct graes_priv *pDev; + struct drvmgr_dev *dev; + + FUNCDBG(); + + if ( drvmgr_get_dev(&graes_drv_info.general, minor, &dev) ) { + DBG("Wrong minor %d\n", minor); + return RTEMS_INVALID_NUMBER; + } + pDev = (struct graes_priv *)dev->priv; + + /* Wait until we get semaphore */ + if ( rtems_semaphore_obtain(graes_dev_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT) != RTEMS_SUCCESSFUL ){ + return RTEMS_INTERNAL_ERROR; + } + + /* Is device in use? */ + if ( pDev->open ){ + rtems_semaphore_release(graes_dev_sem); + return RTEMS_RESOURCE_IN_USE; + } + + /* Mark device taken */ + pDev->open = 1; + + rtems_semaphore_release(graes_dev_sem); + + DBG("graes_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. + */ + graes_hw_get_implementation(pDev, &pDev->hw_avail); + + /* Get default modes */ + graes_hw_get_default_modes(&pDev->config,&pDev->hw_avail); + + return RTEMS_SUCCESSFUL; +} + +static rtems_device_driver graes_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + struct graes_priv *pDev; + struct drvmgr_dev *dev; + + FUNCDBG(); + + if ( drvmgr_get_dev(&graes_drv_info.general, minor, &dev) ) { + return RTEMS_INVALID_NUMBER; + } + pDev = (struct graes_priv *)dev->priv; + + if ( pDev->running ){ + graes_stop(pDev); + pDev->running = 0; + } + + /* Reset core */ + graes_hw_reset(pDev); + + /* Clear descriptor area just for sure */ + memset(pDev->bds, 0, GRAES_BDAR_SIZE); + + /* Mark not open */ + pDev->open = 0; + + return RTEMS_SUCCESSFUL; +} + +static rtems_device_driver graes_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + FUNCDBG(); + return RTEMS_NOT_IMPLEMENTED; +} + +static rtems_device_driver graes_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 graes_free_encrypted(struct graes_priv *pDev) +{ + struct graes_ring *curr; + struct graes_block *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)) & GRAES_BD_EN) ){ + /* Handle one sent Frame */ +#ifdef DEBUG + printk(" fini bd: 0x%08x @ 0x%08x\n",ctrl,curr->bd); +#endif + + /* 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 |= GRAES_FLAGS_PROCESSED; /* Mark sent */ + + /* Update Stats */ + pDev->stats.blocks_processed++; + + 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; +} + +static unsigned int graes_trans(struct graes_priv *pDev, struct graes_block *curr_frm, unsigned int addr_in ) { + unsigned int addr = addr_in; + /* Prepare descriptor address. Three cases: + * - GRAES core on same bus as CPU ==> no translation (Address used by CPU = address used by GRAES) + * - GRAES core on remote bus, and payload address given as used by CPU ==> Translation needed + * - GRAES core on remote bus, and payload address given as used by GRAES ==> no translation [ USER does custom translation] + */ + if ( curr_frm->flags & (GRAES_FLAGS_TRANSLATE|GRAES_FLAGS_TRANSLATE_AND_REMEMBER) ) { + /* Do translation */ + drvmgr_translate(pDev->dev, 0, 0, (void *)addr_in, (void **)&addr); + if ( curr_frm->flags & GRAES_FLAGS_TRANSLATE_AND_REMEMBER ) { + if ( addr_in != addr ) { + /* Translation needed */ + curr_frm->flags &= ~GRAES_FLAGS_TRANSLATE_AND_REMEMBER; + curr_frm->flags |= GRAES_FLAGS_TRANSLATE; + } else { + /* No Trnaslation needed */ + curr_frm->flags &= ~(GRAES_FLAGS_TRANSLATE|GRAES_FLAGS_TRANSLATE_AND_REMEMBER); + } + } + } else { + /* Custom translation or no translation needed */ + addr = (unsigned int)addr_in; + } + return addr; +} + +/* 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 graes_schedule_ready(struct graes_priv *pDev, int ints_off) +{ + int cnt; + unsigned int ctrl, dmactrl, oldLevel, addr; + struct graes_ring *curr_bd; + struct graes_block *curr_frm, *last_frm; + + if ( !pDev->ready.head ){ + return 0; + } + + cnt=0; + curr_frm = pDev->ready.head; + curr_bd = pDev->ring; + while( !curr_bd->frm ){ + int i = 0, j; unsigned int kaddr = -1, iaddr = -1, oaddr = -1, daddr, naddr; + /* Assign frame to descriptor */ + curr_bd->frm = curr_frm; + + /* Prepare descriptor address. Three cases: + * - GRAES core on same bus as CPU ==> no translation (Address used by CPU = address used by GRAES) + * - GRAES core on remote bus, and payload address given as used by CPU ==> Translation needed + * - GRAES core on remote bus, and payload address given as used by GRAES ==> no translation [ USER does custom translation] + */ + + ctrl = GRAES_BD_EN; + + daddr = addr = graes_trans(pDev, curr_frm, curr_frm->payload); + curr_bd->bd->u.d[i++] = addr; + + if (curr_frm->out) { + ctrl |= GRAES_BD_OUT; + oaddr = addr = graes_trans(pDev, curr_frm, curr_frm->out); + curr_bd->bd->u.d[i++] = addr; + } + if (curr_frm->iv) { + ctrl |= GRAES_BD_IV; + iaddr = addr = graes_trans(pDev, curr_frm, curr_frm->iv); + curr_bd->bd->u.d[i++] = addr; + } + if (curr_frm->key) { + ctrl |= GRAES_BD_KEY; + kaddr = addr = graes_trans(pDev, curr_frm, curr_frm->key); + curr_bd->bd->u.d[i++] = addr; + } + + drvmgr_translate(pDev->dev, 0, 0, (void *)curr_bd->next->bd, (void **)&naddr); + curr_bd->bd->u.d[i++] = naddr; + + if ( curr_bd->next == pDev->_ring ){ + + /* Wrap around */ + } + + ctrl |= ((curr_frm->length & 0x7ff) << 21); + + /* Apply user options/flags */ + ctrl |= (curr_frm->flags & GRAES_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 |= GRAES_BD_IE; + } + } + + /* Enable descriptor */ + curr_bd->bd->ctrl = ctrl; + +#ifdef DEBUG + printk(" add bd: 0x%08x @ 0x%08x [0x%08x",ctrl,curr_bd->bd, daddr); + if(oaddr != -1) + printk(",o:0x%08x",oaddr); + if(iaddr != -1) + printk(",i:0x%08x",iaddr); + if(kaddr != -1) + printk(",k:0x%08x",kaddr); + printk("] ["); + for (j = 0; j < 6; j++) { + printk(" 0x%08x",((unsigned int *)curr_bd->bd)[j]); + } + printk("]\n"); + +#endif + + 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 |= GRAES_DMA_CTRL_EN; + pDev->regs->dma_ctrl = dmactrl; + + if ( !ints_off ) { + IRQ_GLOBAL_ENABLE(oldLevel); + } + } + return cnt; +} + +static void graes_printchain(struct graes_priv *pDev, struct graes_print_status *ps, struct graes_list *chain) +{ + struct graes_block *curr; + curr = chain->head; + while(curr){ + printk(" 0x%08x: [@0x%08x]\n",curr,curr->payload); + if (curr == chain->tail) + break; + curr = curr->next; + } +} + +static void graes_printstatus(struct graes_priv *pDev, struct graes_print_status *ps) +{ + int oldLevel; + + IRQ_GLOBAL_DISABLE(oldLevel); + + printk("processed : %d\n",(int)pDev->stats.blocks_processed); + + printk("ready_cnt : %d\n",pDev->ready_cnt); + printk("scheduled_cnt: %d\n",pDev->scheduled_cnt); + printk("sent_cnt : %d\n",pDev->sent_cnt); + + printk(" ready:\n"); + graes_printchain(pDev, ps, &pDev->ready); + printk(" scheduled:\n"); + graes_printchain(pDev, ps, &pDev->scheduled); + printk(" sent:\n"); + graes_printchain(pDev, ps, &pDev->sent); + IRQ_GLOBAL_ENABLE(oldLevel); +} + + +static rtems_device_driver graes_ioctl(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + struct graes_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 graes_ioc_config *cfg; + struct graes_ioc_hw_status *hwregs; + IRQ_GLOBAL_PREPARE(oldLevel); + struct graes_list *chain; + struct graes_block *curr; + struct graes_ioc_hw *hwimpl; + struct graes_ioc_stats *stats; + struct graes_print_status *ps; + int num,ret; + + FUNCDBG(); + + if ( drvmgr_get_dev(&graes_drv_info.general, minor, &dev) ) { + return RTEMS_INVALID_NUMBER; + } + pDev = (struct graes_priv *)dev->priv; + + if (!ioarg) + return RTEMS_INVALID_NAME; + + ioarg->ioctl_return = 0; + switch(ioarg->command) { + case GRAES_IOC_START: + if ( pDev->running ) { + return RTEMS_RESOURCE_IN_USE; /* EBUSY */ + } + if ( (status=graes_start(pDev)) != RTEMS_SUCCESSFUL ){ + return status; + } + /* Register interrupt handler and unmask IRQ at IRQ ctrl */ + drvmgr_interrupt_register(dev, 0, "graes", graes_interrupt, pDev); + + /* Read and write are now open... */ + break; + + case GRAES_IOC_STOP: + if ( !pDev->running ) { + return RTEMS_RESOURCE_IN_USE; + } + + /* Disable interrupts */ + drvmgr_interrupt_unregister(dev, 0, graes_interrupt, pDev); + graes_stop(pDev); + pDev->running = 0; + break; + + case GRAES_IOC_ISSTARTED: + if ( !pDev->running ) { + return RTEMS_RESOURCE_IN_USE; + } + break; + + case GRAES_IOC_SET_BLOCKING_MODE: + if ( (unsigned int)data > GRAES_BLKMODE_BLK ) { + return RTEMS_INVALID_NAME; + } + DBG("GRAES: Set blocking mode: %d\n",(unsigned int)data); + pDev->config.blocking = (unsigned int)data; + break; + + case GRAES_IOC_SET_TIMEOUT: + DBG("GRAES: Timeout: %d\n",(unsigned int)data); + pDev->config.timeout = (rtems_interval)data; + break; + + case GRAES_IOC_SET_CONFIG: + cfg = (struct graes_ioc_config *)data; + if ( !cfg ) { + return RTEMS_INVALID_NAME; + } + + if ( pDev->running ) { + return RTEMS_RESOURCE_IN_USE; + } + + pDev->config = *cfg; + break; + + case GRAES_IOC_GET_STATS: + stats = (struct graes_ioc_stats *)data; + if ( !stats ) { + return RTEMS_INVALID_NAME; + } + memcpy(stats,&pDev->stats,sizeof(struct graes_ioc_stats)); + break; + + case GRAES_IOC_CLR_STATS: + memset(&pDev->stats,0,sizeof(struct graes_ioc_stats)); + break; + + case GRAES_IOC_GET_CONFIG: + cfg = (struct graes_ioc_config *)data; + if ( !cfg ) { + return RTEMS_INVALID_NAME; + } + + *cfg = pDev->config; + break; + + case GRAES_IOC_GET_HW_IMPL: + hwimpl = (struct graes_ioc_hw *)data; + if ( !hwimpl ) { + return RTEMS_INVALID_NAME; + } + *hwimpl = pDev->hw_avail; + break; + + case GRAES_IOC_GET_HW_STATUS: + hwregs = (struct graes_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); + IRQ_GLOBAL_ENABLE(oldLevel); + break; + + case GRAES_IOC_PRINT_STATUS: + ps = (struct graes_print_status *)data; + graes_printstatus(pDev, ps); + 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 GRAES_IOC_ENCRYPT: + if ( !pDev->running ){ + return RTEMS_RESOURCE_IN_USE; + } + num=0; + + /* Get pointer to frame chain wished be sent */ + chain = (struct graes_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; + } + + + /* Mark ready frames unsent by clearing GRAES_FLAGS_PROCESSED of all frames */ + + curr = chain->head; + while(curr != chain->tail){ + curr->flags = curr->flags & ~(GRAES_FLAGS_PROCESSED|GRRM_FLAGS_ERR); + curr = curr->next; + num++; + } + curr->flags = curr->flags & ~(GRAES_FLAGS_PROCESSED|GRRM_FLAGS_ERR); + num++; + + DBG("GRAES_ENCRYPT: head: 0x%x, tail: 0x%x num: %d\n",chain->head,chain->tail, 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 = graes_free_encrypted(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 = graes_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 GRAES_IOC_RECLAIM: + /* Get pointer to were to place reaped chain */ + chain = (struct graes_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 = graes_free_encrypted(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 = graes_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_rx,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 */ + graes_list_clr(&pDev->sent); + + 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 graes_interrupt(int irq, void *arg) +{ + struct graes_priv *pDev = arg; + struct graes_regs *regs = pDev->regs; + unsigned int status; + int num; + + /* Clear interrupt by reading it */ + status = READ_REG(®s->dma_status); + + + DBG("GRAES: irq 0x%x\n",(unsigned int)status); + + /* Spurious Interrupt? */ + if ( !pDev->running ) + return; + + if ( status ) + regs->dma_status = status; + + + if ( 1 ){ + + if ( pDev->config.isr_desc_proc && !pDev->handling_transmission ) { + /* Free used descriptors and put the sent frame into the "Sent queue" + * (SCHEDULED->SENT) + */ + num = graes_free_encrypted(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 = graes_schedule_ready(pDev,1); + pDev->ready_cnt -= num; + pDev->scheduled_cnt += num; + +#if 0 + if ( (pDev->config.blocking==GRAES_BLKMODE_COMPLETE) && pDev->timeout ){ + /* Signal to thread only if enough data is available */ + if ( pDev->wait_for_frames > graes_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_rx); + } +#endif + } + + if ( pDev->config.blocking == GRAES_BLKMODE_BLK ) { + /* Blocking mode */ + +#if 0 + /* Disable further Interrupts until handled by waiting task. */ + regs->dma_ctrl = READ_REG(®s->dma_ctrl) & ~GRAES_DMA_CTRL_IE; +#endif + + /* Signal Semaphore to wake waiting thread in ioctl(ENCRYPT|RECLAIM) */ + rtems_semaphore_release(pDev->sem_rx); + } + + } + +procceed_processing_interrupts: + ; +} + +static rtems_device_driver graes_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', 'P', 'R'), + 1, + RTEMS_FIFO|RTEMS_NO_INHERIT_PRIORITY|RTEMS_LOCAL|RTEMS_NO_PRIORITY_CEILING, + 0, + &graes_dev_sem) != RTEMS_SUCCESSFUL ) { + return RTEMS_INTERNAL_ERROR; + } + + return RTEMS_SUCCESSFUL; +} diff --git a/c/src/lib/libbsp/sparc/shared/grpwrx/grpwrx.c b/c/src/lib/libbsp/sparc/shared/grpwrx/grpwrx.c new file mode 100644 index 0000000000..2f29c148a4 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/grpwrx/grpwrx.c @@ -0,0 +1,1317 @@ +/* GRPWRX Packetwire driver + * + * -------------------------------------------------------------------------- + * -- This file is a part of GAISLER RESEARCH source code. + * -- Copyright (C) 2009, Aeroflex Gaisler AB - all rights reserved. + * -- + * -- ANY USE OR REDISTRIBUTION IN PART OR IN WHOLE MUST BE HANDLED IN + * -- ACCORDANCE WITH THE GAISLER LICENSE AGREEMENT AND MUST BE APPROVED + * -- IN ADVANCE IN WRITING. + * -- + * -- BY DEFAULT, DISTRIBUTION OR DISCLOSURE IS NOT PERMITTED. + * -------------------------------------------------------------------------- + * + * 2010-06-29, Konrad Eisele <konrad@gaisler.com> + * Packetwire rx driver based on GRTM driver + * + * 2008-12-11, Daniel Hellstrom <daniel@gaisler.com> + * Converted to support driver manager + * + * 2007-04-17, Daniel Hellstrom <daniel@gaisler.com> + * New driver in sparc shared directory. + * + */ + + +#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 <grpwrx.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> + +/* GRPWRX register map */ +struct grpwrx_regs { + volatile unsigned int dma_ctrl; /* DMA Control Register (0x00) */ + volatile unsigned int dma_status; /* DMA Status Register (0x04) */ + volatile unsigned int dma_bd; /* DMA Descriptor Pointer Register (0x08) */ + volatile unsigned int d0; /* DMA Length Register (0x08) */ + + volatile unsigned int d1; /* DMA Configuration Register (0x10) */ + volatile unsigned int d2; /* GRPWRX Revision Register (0x14) */ + + int unused0[(0x80-0x18)/4]; + + volatile unsigned int ctrl; /* GRPWRX Control Register (0x80) */ + volatile unsigned int status; /* GRPWRX Status Register (0x84) */ + volatile unsigned int cfg; /* GRPWRX Configuration Register (0x88) */ + volatile unsigned int phy; /* GRPWRX Physical Layer Register (0x8c) */ +}; + +#define GRPWRX_BDAR_SIZE 0x4000 +#define GRPWRX_BDAR_ENTRIES (GRPWRX_BDAR_SIZE / sizeof(struct grpwrx_bd)) + +/* DMA Control Register (0x00) */ +#define GRPWRX_DMA_CTRL_EN_BIT 0 +#define GRPWRX_DMA_CTRL_IE_BIT 1 +#define GRPWRX_DMA_CTRL_TXRST_BIT 2 +#define GRPWRX_DMA_CTRL_RST_BIT 3 +#define GRPWRX_DMA_CTRL_TFIE_BIT 4 + +#define GRPWRX_DMA_CTRL_EN (1<<GRPWRX_DMA_CTRL_EN_BIT) +#define GRPWRX_DMA_CTRL_IE (1<<GRPWRX_DMA_CTRL_IE_BIT) +#define GRPWRX_DMA_CTRL_TXRST (1<<GRPWRX_DMA_CTRL_TXRST_BIT) +#define GRPWRX_DMA_CTRL_RST (1<<GRPWRX_DMA_CTRL_RST_BIT) +#define GRPWRX_DMA_CTRL_TFIE (1<<GRPWRX_DMA_CTRL_TFIE_BIT) + +/* DMA Status Register (0x04) */ +#define GRPWRX_DMA_STS_RE_BIT 0 +#define GRPWRX_DMA_STS_RI_BIT 1 +#define GRPWRX_DMA_STS_RA_BIT 2 + +#define GRPWRX_DMA_STS_RE (1<<GRPWRX_DMA_STS_RE_BIT) +#define GRPWRX_DMA_STS_RI (1<<GRPWRX_DMA_STS_RI_BIT) +#define GRPWRX_DMA_STS_RA (1<<GRPWRX_DMA_STS_RA_BIT) +#define GRPWRX_DMA_STS_ALL 0x7 + +/* DMA Descriptor Pointer Register (0x0c) */ +#define GRPWRX_DMA_BD_INDEX_BIT 0 +#define GRPWRX_DMA_BD_BASE_BIT 10 + +#define GRPWRX_DMA_BD_INDEX (0x3ff<<GRPWRX_DMA_BD_INDEX_BIT) +#define GRPWRX_DMA_BD_BASE (0xfffffc<<GRPWRX_DMA_BD_BASE_BIT) + +/* GRPWRX Control Register (0x80) */ +#define GRPWRX_CTRL_EN_BIT 0 +#define GRPWRX_CTRL_RST_BIT 2 + +#define GRPWRX_CTRL_EN (1<<GRPWRX_CTRL_EN_BIT) +#define GRPWRX_CTRL_RST (1<<GRPWRX_CTRL_RST_BIT) + + +/* TM Physical Layer Register (0x90) */ +#define GRPWRX_PHY_CLKRISE_BIT 4 +#define GRPWRX_PHY_VALIDPOS_BIT 5 +#define GRPWRX_PHY_READYPOS_BIT 6 +#define GRPWRX_PHY_BUSYPOS_BIT 7 + +#define GRPWRX_PHY_CLKRISE (1 << GRPWRX_PHY_CLKRISE_BIT) +#define GRPWRX_PHY_VALIDPOS (1 << GRPWRX_PHY_VALIDPOS_BIT) +#define GRPWRX_PHY_READYPOS (1 << GRPWRX_PHY_READYPOS_BIT) +#define GRPWRX_PHY_BUSYPOS (1 << GRPWRX_PHY_BUSYPOS_BIT) + + +/* TM FSH/Insert Zone Registers (0xc0..0xcc) */ +#define GRPWRX_FSH_DATA_BIT 0 + +#define GRPWRX_FSH_DATA 0xffffffff + +/* GRPWRX transmit descriptor (GRPWRX_BDAR_SIZE Alignment need) */ +struct grpwrx_bd { + volatile unsigned int ctrl; + unsigned int address; + unsigned int dummy1; + unsigned int dummy2; +}; + +#define GRPWRX_BD_EN_BIT 0 +#define GRPWRX_BD_IE_BIT 1 +#define GRPWRX_BD_WR_BIT 2 + +#define GRPWRX_BD_EN (1<<GRPWRX_BD_EN_BIT) +#define GRPWRX_BD_WR (1<<GRPWRX_BD_WR_BIT) +#define GRPWRX_BD_IE (1<<GRPWRX_BD_IE_BIT) + + +/* Load register */ + +#define READ_REG(address) (*(volatile unsigned int *)address) + +/* Driver functions */ +static rtems_device_driver grpwrx_initialize(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver grpwrx_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver grpwrx_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver grpwrx_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver grpwrx_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver grpwrx_ioctl(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); + +#define GRPWRX_DRIVER_TABLE_ENTRY { grpwrx_initialize, grpwrx_open, grpwrx_close, grpwrx_read, grpwrx_write, grpwrx_ioctl } + +static rtems_driver_address_table grpwrx_driver = GRPWRX_DRIVER_TABLE_ENTRY; + +/* Structure that connects BD with SoftWare Frame */ +struct grpwrx_ring { + struct grpwrx_ring *next; + struct grpwrx_bd *bd; + struct grpwrx_packet *frm; +}; + +struct grpwrx_priv { + struct drvmgr_dev *dev; /* Driver manager device */ + char devName[32]; /* Device Name */ + struct grpwrx_regs *regs; + int irq; + int minor; + + int open; + int running; + + struct grpwrx_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 grpwrx_ring *_ring; /* Root of ring */ + struct grpwrx_ring *ring; /* Next ring to use for new frames to be transmitted */ + struct grpwrx_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 grpwrx_list ready; /* Frames Waiting for free BDs */ + struct grpwrx_list scheduled; /* Frames in BDs beeing transmitted */ + struct grpwrx_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 grpwrx_ioc_hw hw_avail; /* Hardware support available */ + struct grpwrx_ioc_config config; + struct grpwrx_ioc_stats stats; + + rtems_id sem_rx; +}; + +/* Prototypes */ +static void *grpwrx_memalign(unsigned int boundary, unsigned int length, void *realbuf); +static void grpwrx_hw_reset(struct grpwrx_priv *pDev); +static void grpwrx_interrupt(int irq, void *arg); + +/* Common Global Variables */ +static rtems_id grpwrx_dev_sem; +static int grpwrx_driver_io_registered = 0; +static rtems_device_major_number grpwrx_driver_io_major = 0; + +/******************* Driver manager interface ***********************/ + +/* Driver prototypes */ +static int grpwrx_register_io(rtems_device_major_number *m); +static int grpwrx_device_init(struct grpwrx_priv *pDev); + +static int grpwrx_init2(struct drvmgr_dev *dev); +static int grpwrx_init3(struct drvmgr_dev *dev); + +static struct drvmgr_drv_ops grpwrx_ops = +{ + {NULL, grpwrx_init2, grpwrx_init3, NULL}, + NULL, + NULL +}; + +static struct amba_dev_id grpwrx_ids[] = +{ + {VENDOR_GAISLER, GAISLER_PW2APB}, + {0, 0} /* Mark end of table */ +}; + +static struct amba_drv_info grpwrx_drv_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_GAISLER_GRPWRX_ID,/* Driver ID */ + "GRPWRX_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &grpwrx_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + 0, + }, + &grpwrx_ids[0] +}; + +void grpwrx_register_drv (void) +{ + DBG("Registering GRPWRX driver\n"); + drvmgr_drv_register(&grpwrx_drv_info.general); +} + +static int grpwrx_init2(struct drvmgr_dev *dev) +{ + struct grpwrx_priv *priv; + + DBG("GRPWRX[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name); + priv = dev->priv = malloc(sizeof(struct grpwrx_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 grpwrx_init3(struct drvmgr_dev *dev) +{ + struct grpwrx_priv *priv; + char prefix[32]; + rtems_status_code status; + + priv = dev->priv; + + /* Do initialization */ + + if ( grpwrx_driver_io_registered == 0) { + /* Register the I/O driver only once for all cores */ + if ( grpwrx_register_io(&grpwrx_driver_io_major) ) { + /* Failed to register I/O driver */ + dev->priv = NULL; + return DRVMGR_FAIL; + } + + grpwrx_driver_io_registered = 1; + } + + /* I/O system registered and initialized + * Now we take care of device initialization. + */ + if ( grpwrx_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/grpwrx%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/%sgrpwrx%d", prefix, dev->minor_bus); + } + + DBG("GRPWRX: add dev %s\n",priv->devName); + + /* Register Device */ + status = rtems_io_register_name(priv->devName, grpwrx_driver_io_major, dev->minor_drv); + if (status != RTEMS_SUCCESSFUL) { + return status; + } + + return DRVMGR_OK; +} + +/******************* Driver Implementation ***********************/ + +static int grpwrx_register_io(rtems_device_major_number *m) +{ + rtems_status_code r; + + if ((r = rtems_io_register_driver(0, &grpwrx_driver, m)) == RTEMS_SUCCESSFUL) { + DBG("GRPWRX driver successfully registered, major: %d\n", *m); + } else { + switch(r) { + case RTEMS_TOO_MANY: + printk("GRPWRX rtems_io_register_driver failed: RTEMS_TOO_MANY\n"); + return -1; + case RTEMS_INVALID_NUMBER: + printk("GRPWRX rtems_io_register_driver failed: RTEMS_INVALID_NUMBER\n"); + return -1; + case RTEMS_RESOURCE_IN_USE: + printk("GRPWRX rtems_io_register_driver failed: RTEMS_RESOURCE_IN_USE\n"); + return -1; + default: + printk("GRPWRX rtems_io_register_driver failed\n"); + return -1; + } + } + return 0; +} + +static int grpwrx_device_init(struct grpwrx_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 grpwrx_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', 'P', 'R', '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; + } + + /* 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 grpwrx_bd *)value->ptr; + pDev->_bds = (void *)value->ptr; + } else { + pDev->bds = (struct grpwrx_bd *)grpwrx_memalign(GRPWRX_BDAR_SIZE, GRPWRX_BDAR_SIZE, &pDev->_bds); + } + if ( !pDev->bds ) { + DBG("GRPWRX: Failed to allocate descriptor table\n"); + return -1; + } + memset(pDev->bds, 0, GRPWRX_BDAR_SIZE); + + pDev->_ring = malloc(sizeof(struct grpwrx_ring) * GRPWRX_BDAR_ENTRIES); + if ( !pDev->_ring ) { + return -1; + } + + /* Reset Hardware before attaching IRQ handler */ + grpwrx_hw_reset(pDev); + + return 0; +} + + +static inline void grpwrx_list_clr(struct grpwrx_list *list) +{ + list->head = NULL; + list->tail = NULL; +} + +static void grpwrx_hw_reset(struct grpwrx_priv *pDev) +{ + /* Reset Core */ + pDev->regs->ctrl = GRPWRX_CTRL_RST; + pDev->regs->dma_status = GRPWRX_DMA_STS_ALL; +} + +static void grpwrx_hw_get_implementation(struct grpwrx_priv *pDev, struct grpwrx_ioc_hw *hwcfg) +{ + unsigned int cfg = READ_REG(&pDev->regs->cfg), phy = READ_REG(&pDev->regs->phy); + + hwcfg->fifo_size= (cfg >> 8) & 0xffff; + hwcfg->mode= (cfg >> 0) & 0x1; + + hwcfg->clkdivide= (phy >> 20) & 0xfff; +} + +#warning Extra: Implement proper default calculation from hardware configuration +static void grpwrx_hw_get_default_modes(struct grpwrx_ioc_config *cfg, struct grpwrx_ioc_hw *hwcfg) +{ + cfg->framing = 1; + + /* Physical */ + cfg->phy_clkrise = 1; /* sample at falling for rx */ + cfg->phy_validpos = 1; + cfg->phy_readypos = 1; + cfg->phy_busypos = 0; + + /* Interrupt options */ + cfg->blocking = 0; /* non-blocking mode is default */ + cfg->enable_cnt = 1; /* generate interrupt every 16 descriptor */ + cfg->isr_desc_proc = 1; /* Let interrupt handler do descriptor processing */ + cfg->timeout = RTEMS_NO_TIMEOUT; + +} + +static void *grpwrx_memalign(unsigned int boundary, unsigned int length, void *realbuf) +{ + *(int *)realbuf = (int)malloc(length+boundary); + DBG("GRPWRX: Alloced %d (0x%x) bytes, requested: %d\n",length+boundary,length+boundary,length); + return (void *)(((*(unsigned int *)realbuf)+boundary) & ~(boundary-1)); +} + +static int grpwrx_hw_set_config(struct grpwrx_priv *pDev, struct grpwrx_ioc_config *cfg, struct grpwrx_ioc_hw *hwcfg) +{ + struct grpwrx_regs *regs = pDev->regs; + unsigned int tmp; + + /* Physical layer options */ + tmp = ((cfg->phy_clkrise << GRPWRX_PHY_CLKRISE_BIT) & GRPWRX_PHY_CLKRISE) | + ((cfg->phy_validpos << GRPWRX_PHY_VALIDPOS_BIT) & GRPWRX_PHY_VALIDPOS) | + ((cfg->phy_readypos << GRPWRX_PHY_READYPOS_BIT) & GRPWRX_PHY_READYPOS) | + ((cfg->phy_busypos << GRPWRX_PHY_BUSYPOS_BIT) & GRPWRX_PHY_BUSYPOS); + regs->phy = tmp; + + regs->cfg = cfg->framing ? 1 : 0; + + return 0; +} + +static int grpwrx_start(struct grpwrx_priv *pDev) +{ + struct grpwrx_regs *regs = pDev->regs; + int i; + struct grpwrx_ioc_config *cfg = &pDev->config; + volatile unsigned int *txrdy_reg; + unsigned int txrdy_mask, transaddr; + + /* Clear Descriptors */ + memset(pDev->bds,0,GRPWRX_BDAR_SIZE); + + /* Clear stats */ + memset(&pDev->stats,0,sizeof(struct grpwrx_ioc_stats)); + + /* Init Descriptor Ring */ + memset(pDev->_ring,0,sizeof(struct grpwrx_ring)*GRPWRX_BDAR_ENTRIES); + for(i=0;i<(GRPWRX_BDAR_ENTRIES-1);i++){ + pDev->_ring[i].next = &pDev->_ring[i+1]; + pDev->_ring[i].bd = &pDev->bds[i]; + pDev->_ring[i].frm = NULL; + } + pDev->_ring[(GRPWRX_BDAR_ENTRIES-1)].next = &pDev->_ring[0]; + pDev->_ring[(GRPWRX_BDAR_ENTRIES-1)].bd = &pDev->bds[(GRPWRX_BDAR_ENTRIES-1)]; + pDev->_ring[(GRPWRX_BDAR_ENTRIES-1)].frm = NULL; + + pDev->ring = &pDev->_ring[0]; + pDev->ring_end = &pDev->_ring[0]; + + /* Clear Scheduled, Ready and Sent list */ + grpwrx_list_clr(&pDev->ready); + grpwrx_list_clr(&pDev->scheduled); + grpwrx_list_clr(&pDev->sent); + + /* Software init */ + pDev->handling_transmission = 0; + + /* Reset the transmitter */ + regs->dma_ctrl = GRPWRX_DMA_CTRL_TXRST; + regs->dma_ctrl = 0; /* Leave Reset */ + + /* Clear old interrupts */ + regs->dma_status = GRPWRX_DMA_STS_ALL; + + /* Set Descriptor Pointer Base register to point to first descriptor */ + drvmgr_translate(pDev->dev, 0, 0, (void *)pDev->bds, (void **)&transaddr); + regs->dma_bd = transaddr; + + DBG("GRPWRX: set bd to 0x%08x\n",transaddr); + + /*regs->dma_bd = (unsigned int)pDev->bds;*/ + + /* Set hardware options as defined by config */ + if ( grpwrx_hw_set_config(pDev, cfg, &pDev->hw_avail) ) { + return RTEMS_IO_ERROR; + } + + /* Enable GRPWRX Transmitter */ + regs->ctrl = GRPWRX_CTRL_EN; + + DBG("GRPWRX: reset time %d\n",i); + + /* Mark running before enabling the DMA transmitter */ + pDev->running = 1; + + /* Enable interrupts (Error and DMA TX) */ + regs->dma_ctrl = GRPWRX_DMA_CTRL_IE; + + DBG("GRPWRX: STARTED\n"); + + return RTEMS_SUCCESSFUL; +} + +static void grpwrx_stop(struct grpwrx_priv *pDev) +{ + struct grpwrx_regs *regs = pDev->regs; + + /* Disable the transmitter & Interrupts */ + regs->dma_ctrl = 0; + + /* Clear any pending interrupt */ + regs->dma_status = GRPWRX_DMA_STS_ALL; + + DBG("GRPWRX: STOPPED\n"); + + /* Flush semaphore in case a thread is stuck waiting for TX Interrupts */ + rtems_semaphore_flush(pDev->sem_rx); +} + +static rtems_device_driver grpwrx_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg) +{ + struct grpwrx_priv *pDev; + struct drvmgr_dev *dev; + + FUNCDBG(); + + if ( drvmgr_get_dev(&grpwrx_drv_info.general, minor, &dev) ) { + DBG("Wrong minor %d\n", minor); + return RTEMS_INVALID_NUMBER; + } + pDev = (struct grpwrx_priv *)dev->priv; + + /* Wait until we get semaphore */ + if ( rtems_semaphore_obtain(grpwrx_dev_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT) != RTEMS_SUCCESSFUL ){ + return RTEMS_INTERNAL_ERROR; + } + + /* Is device in use? */ + if ( pDev->open ){ + rtems_semaphore_release(grpwrx_dev_sem); + return RTEMS_RESOURCE_IN_USE; + } + + /* Mark device taken */ + pDev->open = 1; + + rtems_semaphore_release(grpwrx_dev_sem); + + DBG("grpwrx_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. + */ + grpwrx_hw_get_implementation(pDev, &pDev->hw_avail); + + /* Get default modes */ + grpwrx_hw_get_default_modes(&pDev->config,&pDev->hw_avail); + + return RTEMS_SUCCESSFUL; +} + +static rtems_device_driver grpwrx_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + struct grpwrx_priv *pDev; + struct drvmgr_dev *dev; + + FUNCDBG(); + + if ( drvmgr_get_dev(&grpwrx_drv_info.general, minor, &dev) ) { + return RTEMS_INVALID_NUMBER; + } + pDev = (struct grpwrx_priv *)dev->priv; + + if ( pDev->running ){ + grpwrx_stop(pDev); + pDev->running = 0; + } + + /* Reset core */ + grpwrx_hw_reset(pDev); + + /* Clear descriptor area just for sure */ + memset(pDev->bds, 0, GRPWRX_BDAR_SIZE); + + /* Mark not open */ + pDev->open = 0; + + return RTEMS_SUCCESSFUL; +} + +static rtems_device_driver grpwrx_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + FUNCDBG(); + return RTEMS_NOT_IMPLEMENTED; +} + +static rtems_device_driver grpwrx_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 grpwrx_free_received(struct grpwrx_priv *pDev) +{ + struct grpwrx_ring *curr; + struct grpwrx_packet *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)) & GRPWRX_BD_EN) ){ + /* Handle one sent Frame */ + + DBG("fini bd: 0x%x @ 0x%x\n",(int)ctrl, curr->bd); + + /* 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 |= GRPWRX_FLAGS_RECEIVED; /* Mark sent */ + + /* Update Stats */ + pDev->stats.packets_received++; + + 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 grpwrx_schedule_ready(struct grpwrx_priv *pDev, int ints_off) +{ + int cnt; + unsigned int ctrl, dmactrl, oldLevel, transaddr; + struct grpwrx_ring *curr_bd; + struct grpwrx_packet *curr_frm, *last_frm; + + 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: + * - GRPWRX core on same bus as CPU ==> no translation (Address used by CPU = address used by GRPWRX) + * - GRPWRX core on remote bus, and payload address given as used by CPU ==> Translation needed + * - GRPWRX core on remote bus, and payload address given as used by GRPWRX ==> no translation [ USER does custom translation] + */ + if ( curr_frm->flags & (GRPWRX_FLAGS_TRANSLATE|GRPWRX_FLAGS_TRANSLATE_AND_REMEMBER) ) { + /* Do translation */ + drvmgr_translate(pDev->dev, 0, 0, (void *)curr_frm->payload, (void **)&transaddr); + curr_bd->bd->address = transaddr; + if ( curr_frm->flags & GRPWRX_FLAGS_TRANSLATE_AND_REMEMBER ) { + if ( curr_frm->payload != curr_bd->bd->address ) { + /* Translation needed */ + curr_frm->flags &= ~GRPWRX_FLAGS_TRANSLATE_AND_REMEMBER; + curr_frm->flags |= GRPWRX_FLAGS_TRANSLATE; + } else { + /* No Trnaslation needed */ + curr_frm->flags &= ~(GRPWRX_FLAGS_TRANSLATE|GRPWRX_FLAGS_TRANSLATE_AND_REMEMBER); + } + } + } else { + /* Custom translation or no translation needed */ + transaddr = curr_bd->bd->address = (unsigned int)curr_frm->payload; + } + + ctrl = GRPWRX_BD_EN; + if ( curr_bd->next == pDev->_ring ){ + ctrl |= GRPWRX_BD_WR; /* Wrap around */ + } + /* Apply user options/flags */ + ctrl |= (curr_frm->flags & GRPWRX_FLAGS_MASK); + ctrl |= (curr_frm->length & 0xffff) << 16; + + /* 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 |= GRPWRX_BD_IE; + } + } + + /* Enable descriptor */ + curr_bd->bd->ctrl = ctrl; + + DBG("add bd: [0x%08x,0x%08x] @ 0x%08x\n",(int)ctrl, transaddr,curr_bd->bd); + + 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 &= ~(GRPWRX_DMA_CTRL_TXRST | GRPWRX_DMA_CTRL_RST); + dmactrl |= GRPWRX_DMA_CTRL_EN; + pDev->regs->dma_ctrl = dmactrl; + pDev->regs->ctrl = GRPWRX_CTRL_EN; + + if ( !ints_off ) { + IRQ_GLOBAL_ENABLE(oldLevel); + } + } + return cnt; +} + +static void grpwrx_printchain(struct grpwrx_priv *pDev, struct grpwrx_print_status *ps, struct grpwrx_list *chain) +{ + struct grpwrx_packet *curr; + curr = chain->head; + while(curr){ + printk(" 0x%08x: [0x%08x@0x%08x:0x%08x]\n",curr,curr->length,curr->payload,curr->flags); + if (curr == chain->tail) + break; + curr = curr->next; + } + +} + +static void grpwrx_printstatus(struct grpwrx_priv *pDev, struct grpwrx_print_status *ps) +{ + int oldLevel; + + IRQ_GLOBAL_DISABLE(oldLevel); + printk("pack_rec: %d\n",(int)pDev->stats.packets_received); + + printk("ready_cnt : %d\n",pDev->ready_cnt); + printk("scheduled_cnt: %d\n",pDev->scheduled_cnt); + printk("sent_cnt : %d\n",pDev->sent_cnt); + + printk(" ready:\n"); + grpwrx_printchain(pDev, ps, &pDev->ready); + printk(" scheduled:\n"); + grpwrx_printchain(pDev, ps, &pDev->scheduled); + printk(" sent:\n"); + grpwrx_printchain(pDev, ps, &pDev->sent); + IRQ_GLOBAL_ENABLE(oldLevel); +} + + +static rtems_device_driver grpwrx_ioctl(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + struct grpwrx_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 grpwrx_ioc_config *cfg; + struct grpwrx_ioc_hw_status *hwregs; + IRQ_GLOBAL_PREPARE(oldLevel); + struct grpwrx_list *chain; + struct grpwrx_packet *curr; + struct grpwrx_ioc_hw *hwimpl; + struct grpwrx_ioc_stats *stats; + struct grpwrx_print_status *ps; + int num,ret; + + FUNCDBG(); + + if ( drvmgr_get_dev(&grpwrx_drv_info.general, minor, &dev) ) { + return RTEMS_INVALID_NUMBER; + } + pDev = (struct grpwrx_priv *)dev->priv; + + if (!ioarg) + return RTEMS_INVALID_NAME; + + ioarg->ioctl_return = 0; + switch(ioarg->command) { + case GRPWRX_IOC_START: + if ( pDev->running ) { + return RTEMS_RESOURCE_IN_USE; /* EBUSY */ + } + if ( (status=grpwrx_start(pDev)) != RTEMS_SUCCESSFUL ){ + return status; + } + /* Register interrupt handler & Enable interrupt */ + drvmgr_interrupt_register(dev, 0, "grpwrx", grpwrx_interrupt, pDev); + + /* Read and write are now open... */ + break; + + case GRPWRX_IOC_STOP: + if ( !pDev->running ) { + return RTEMS_RESOURCE_IN_USE; + } + + /* Disable interrupts */ + drvmgr_interrupt_unregister(dev, 0, grpwrx_interrupt, pDev); + grpwrx_stop(pDev); + pDev->running = 0; + break; + + case GRPWRX_IOC_ISSTARTED: + if ( !pDev->running ) { + return RTEMS_RESOURCE_IN_USE; + } + break; + + case GRPWRX_IOC_SET_BLOCKING_MODE: + if ( (unsigned int)data > GRPWRX_BLKMODE_BLK ) { + return RTEMS_INVALID_NAME; + } + DBG("GRPWRX: Set blocking mode: %d\n",(unsigned int)data); + pDev->config.blocking = (unsigned int)data; + break; + + case GRPWRX_IOC_SET_TIMEOUT: + DBG("GRPWRX: Timeout: %d\n",(unsigned int)data); + pDev->config.timeout = (rtems_interval)data; + break; + + case GRPWRX_IOC_SET_CONFIG: + cfg = (struct grpwrx_ioc_config *)data; + if ( !cfg ) { + return RTEMS_INVALID_NAME; + } + + if ( pDev->running ) { + return RTEMS_RESOURCE_IN_USE; + } + + pDev->config = *cfg; + break; + + case GRPWRX_IOC_GET_STATS: + stats = (struct grpwrx_ioc_stats *)data; + if ( !stats ) { + return RTEMS_INVALID_NAME; + } + memcpy(stats,&pDev->stats,sizeof(struct grpwrx_ioc_stats)); + break; + + case GRPWRX_IOC_CLR_STATS: + memset(&pDev->stats,0,sizeof(struct grpwrx_ioc_stats)); + break; + + case GRPWRX_IOC_GET_CONFIG: + cfg = (struct grpwrx_ioc_config *)data; + if ( !cfg ) { + return RTEMS_INVALID_NAME; + } + + *cfg = pDev->config; + break; + + case GRPWRX_IOC_GET_HW_IMPL: + hwimpl = (struct grpwrx_ioc_hw *)data; + if ( !hwimpl ) { + return RTEMS_INVALID_NAME; + } + *hwimpl = pDev->hw_avail; + break; + + case GRPWRX_IOC_GET_HW_STATUS: + hwregs = (struct grpwrx_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); + IRQ_GLOBAL_ENABLE(oldLevel); + break; + + case GRPWRX_IOC_PRINT_STATUS: + ps = (struct grpwrx_print_status *)data; + grpwrx_printstatus(pDev, ps); + 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 GRPWRX_IOC_RECV: + if ( !pDev->running ){ + return RTEMS_RESOURCE_IN_USE; + } + num=0; + + /* Get pointer to frame chain wished be sent */ + chain = (struct grpwrx_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; + } + + + /* Mark ready frames unsent by clearing GRPWRX_FLAGS_RECEIVED of all frames */ + + curr = chain->head; + while(curr != chain->tail){ + curr->flags = curr->flags & ~(GRPWRX_FLAGS_RECEIVED|GRRM_FLAGS_ERR); + curr = curr->next; + num++; + } + curr->flags = curr->flags & ~(GRPWRX_FLAGS_RECEIVED|GRRM_FLAGS_ERR); + num++; + + DBG("GRPWRX_RECV: head: 0x%x, tail: 0x%x num: %d\n",chain->head,chain->tail, 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 = grpwrx_free_received(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 = grpwrx_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 GRPWRX_IOC_RECLAIM: + /* Get pointer to were to place reaped chain */ + chain = (struct grpwrx_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 = grpwrx_free_received(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 = grpwrx_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_rx,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 */ + grpwrx_list_clr(&pDev->sent); + + 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 grpwrx_interrupt(int irq, void *arg) +{ + struct grpwrx_priv *pDev = arg; + struct grpwrx_regs *regs = pDev->regs; + unsigned int status; + int num; + + /* Clear interrupt by reading it */ + status = READ_REG(®s->dma_status); + + DBG("irq 0x%x\n",status); + + /* Spurious Interrupt? */ + if ( !pDev->running ) + return; + + if ( status ) + regs->dma_status = status; + + + if ( status & GRPWRX_DMA_STS_RA ){ + pDev->stats.err_ahb++; + } + + if ( status & GRPWRX_DMA_STS_RE ){ + pDev->stats.err_tx++; + } + + if ( status & GRPWRX_DMA_STS_RI ){ + + + if ( pDev->config.isr_desc_proc && !pDev->handling_transmission ) { + /* Free used descriptors and put the sent frame into the "Sent queue" + * (SCHEDULED->SENT) + */ + num = grpwrx_free_received(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 = grpwrx_schedule_ready(pDev,1); + pDev->ready_cnt -= num; + pDev->scheduled_cnt += num; + +#if 0 + if ( (pDev->config.blocking==GRPWRX_BLKMODE_COMPLETE) && pDev->timeout ){ + /* Signal to thread only if enough data is available */ + if ( pDev->wait_for_frames > grpwrx_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_rx); + } +#endif + } + + if ( pDev->config.blocking == GRPWRX_BLKMODE_BLK ) { + /* Blocking mode */ + +#if 0 + /* Disable further Interrupts until handled by waiting task. */ + regs->dma_ctrl = READ_REG(®s->dma_ctrl) & ~GRPWRX_DMA_CTRL_IE; +#endif + + /* Signal Semaphore to wake waiting thread in ioctl(SEND|RECLAIM) */ + rtems_semaphore_release(pDev->sem_rx); + } + + } + +procceed_processing_interrupts: + ; +} + +static rtems_device_driver grpwrx_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', 'P', 'R'), + 1, + RTEMS_FIFO|RTEMS_NO_INHERIT_PRIORITY|RTEMS_LOCAL|RTEMS_NO_PRIORITY_CEILING, + 0, + &grpwrx_dev_sem) != RTEMS_SUCCESSFUL ) { + return RTEMS_INTERNAL_ERROR; + } + + return RTEMS_SUCCESSFUL; +} diff --git a/c/src/lib/libbsp/sparc/shared/i2c/i2cmst.c b/c/src/lib/libbsp/sparc/shared/i2c/i2cmst.c index fb6a573763..b600225737 100644 --- a/c/src/lib/libbsp/sparc/shared/i2c/i2cmst.c +++ b/c/src/lib/libbsp/sparc/shared/i2c/i2cmst.c @@ -10,25 +10,45 @@ * * This file contains the driver and initialization code * + * 2009-03-24: Added support for multiple I2C Cores, converted + * to driver manager. (daniel@gaisler.com) * 2007-09-27: First version of driver (jan@gaisler.com) */ #include <bsp.h> -#include <i2cmst.h> +#include <stdlib.h> +#include <stdio.h> #include <ambapp.h> #include <rtems/libi2c.h> +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> + +#include <i2cmst.h> /* Enable debug printks? */ -/* #define DEBUG */ +/*#define DEBUG*/ -/* Default to 40 MHz system clock? */ -/* - #ifndef SYS_FREQ_kHZ - #define SYS_FREQ_kHZ 40000 - #endif -*/ +#ifdef DEBUG + #define DBG(args...) printk(args) +#else + #define DBG(args...) +#endif +/* The OC I2C core will perform a write after a start unless the RD bit + in the command register has been set. Since the rtems framework has + a send_start function we buffer that command and use it when the first + data is written. The START is buffered in the sendstart member below */ +typedef struct gr_i2cmst_prv { + rtems_libi2c_bus_t i2clib_desc; + struct drvmgr_dev *dev; + gr_i2cmst_regs_t *reg_ptr; + unsigned int sysfreq; /* System clock frequency in kHz */ + int minor; + unsigned char sendstart; /* START events are buffered here */ + /* rtems_irq_number irq_number; */ + /* rtems_id irq_sema_id; */ +} gr_i2cmst_prv_t; /* Calculates the scaler value for 100 kHz operation */ static int gr_i2cmst_calc_scaler(int sysfreq) @@ -41,12 +61,12 @@ static int gr_i2cmst_wait(gr_i2cmst_prv_t *prv_ptr, uint8_t expected_sts) { uint32_t tout = 0; int current_sts; -#if defined(DEBUG) - printk("(gr_i2cmst_wait called..."); -#endif + + DBG("(gr_i2cmst_wait called..."); do { if (tout++ > 1000000) { + DBG("gr_i2cmst_wait: TIMEOUT\n"); return RTEMS_TIMEOUT; } } while (prv_ptr->reg_ptr->cmdsts & GRI2C_STS_TIP); @@ -56,32 +76,31 @@ static int gr_i2cmst_wait(gr_i2cmst_prv_t *prv_ptr, uint8_t expected_sts) if (current_sts != expected_sts) { #if defined(DEBUG) if (prv_ptr->reg_ptr->cmdsts & GRI2C_STS_RXACK) { - printk("Transfer NAKed.."); + DBG("Transfer NAKed.."); } if (prv_ptr->reg_ptr->cmdsts & GRI2C_STS_AL) { - printk("arbitration lost.."); + DBG("arbitration lost.."); } if (prv_ptr->reg_ptr->cmdsts & GRI2C_STS_TIP) { - printk("transfer still in progress, huh?.."); + DBG("transfer still in progress, huh?.."); } - printk("exited with IO error..)"); + DBG("exited with IO error..)"); #endif + DBG("gr_i2cmst_wait: IO-ERROR\n"); return RTEMS_IO_ERROR; } -#if defined(DEBUG) - printk("exited...)"); -#endif + DBG("exited...)"); + return RTEMS_SUCCESSFUL; } /* Initialize hardware core */ static rtems_status_code gr_i2cmst_init(rtems_libi2c_bus_t *bushdl) { - gr_i2cmst_prv_t *prv_ptr = &(((gr_i2cmst_desc_t *)(bushdl))->prv); -#if defined(DEBUG) - printk("gr_i2cmst_init called..."); -#endif + gr_i2cmst_prv_t *prv_ptr = (gr_i2cmst_prv_t *)bushdl; + + DBG("gr_i2cmst_init called..."); /* Disable core before changing prescale register */ prv_ptr->reg_ptr->ctrl = 0; @@ -95,54 +114,48 @@ static rtems_status_code gr_i2cmst_init(rtems_libi2c_bus_t *bushdl) /* Clear possible START condition */ prv_ptr->sendstart = 0; -#if defined(DEBUG) - printk("exited\n"); -#endif + DBG("exited\n"); + return RTEMS_SUCCESSFUL; } static rtems_status_code gr_i2cmst_send_start(rtems_libi2c_bus_t *bushdl) { - gr_i2cmst_prv_t *prv_ptr = &(((gr_i2cmst_desc_t *)(bushdl))->prv); -#if defined(DEBUG) - printk("gr_i2cmst_send_start called..."); -#endif + gr_i2cmst_prv_t *prv_ptr = (gr_i2cmst_prv_t *)bushdl; + + DBG("gr_i2cmst_send_start called..."); /* The OC I2C core does not work with stand alone START events, instead the event is buffered */ prv_ptr->sendstart = GRI2C_CMD_STA; -#if defined(DEBUG) - printk("exited\n"); -#endif + DBG("exited\n"); + return RTEMS_SUCCESSFUL; } static rtems_status_code gr_i2cmst_send_stop(rtems_libi2c_bus_t *bushdl) { - gr_i2cmst_prv_t *prv_ptr = &(((gr_i2cmst_desc_t *)(bushdl))->prv); -#if defined(DEBUG) - printk("gr_i2cmst_send_stop called..."); -#endif + gr_i2cmst_prv_t *prv_ptr = (gr_i2cmst_prv_t *)bushdl; + + DBG("gr_i2cmst_send_stop called..."); prv_ptr->reg_ptr->cmdsts = GRI2C_CMD_STO; -#if defined(DEBUG) - printk("exited\n"); -#endif + DBG("exited\n"); + return RTEMS_SUCCESSFUL; } static rtems_status_code gr_i2cmst_send_addr(rtems_libi2c_bus_t *bushdl, uint32_t addr, int rw) { - gr_i2cmst_prv_t *prv_ptr = &(((gr_i2cmst_desc_t *)(bushdl))->prv); + gr_i2cmst_prv_t *prv_ptr = (gr_i2cmst_prv_t *)bushdl; uint8_t addr_byte; rtems_status_code rc; -#if defined(DEBUG) - printk("gr_i2cmst_send_addr called, addr = 0x%x, rw = %d...", + + DBG("gr_i2cmst_send_addr called, addr = 0x%x, rw = %d...", addr, rw); -#endif /* Check if long address is needed */ if (addr > 0x7f) { @@ -155,9 +168,9 @@ static rtems_status_code gr_i2cmst_send_addr(rtems_libi2c_bus_t *bushdl, /* Wait for transfer to complete */ rc = gr_i2cmst_wait(prv_ptr, GRI2C_STATUS_IDLE); if (rc != RTEMS_SUCCESSFUL) { -#if defined(DEBUG) - printk("exited with error\n"); -#endif + + DBG("exited with error\n"); + return -rc; } } @@ -175,16 +188,12 @@ static rtems_status_code gr_i2cmst_send_addr(rtems_libi2c_bus_t *bushdl, /* Wait for transfer to complete */ rc = gr_i2cmst_wait(prv_ptr, GRI2C_STATUS_IDLE); if (rc != RTEMS_SUCCESSFUL) { -#if defined(DEBUG) - printk("exited with error\n"); -#endif + DBG("exited with error\n"); return -rc; } - } + } -#if defined(DEBUG) - printk("exited\n"); -#endif + DBG("exited\n"); return rc; } @@ -192,13 +201,12 @@ static rtems_status_code gr_i2cmst_send_addr(rtems_libi2c_bus_t *bushdl, static int gr_i2cmst_read_bytes(rtems_libi2c_bus_t *bushdl, unsigned char *bytes, int nbytes) { - gr_i2cmst_prv_t *prv_ptr = &(((gr_i2cmst_desc_t *)(bushdl))->prv); + gr_i2cmst_prv_t *prv_ptr = (gr_i2cmst_prv_t *)bushdl; unsigned char *buf = bytes; rtems_status_code rc; unsigned char expected_sts = GRI2C_STATUS_IDLE; -#if defined(DEBUG) - printk("gr_i2cmst_read_bytes called, nbytes = %d...", nbytes); -#endif + + DBG("gr_i2cmst_read_bytes called, nbytes = %d...", nbytes); while (nbytes-- > 0) { if (nbytes == 0) { @@ -213,34 +221,30 @@ static int gr_i2cmst_read_bytes(rtems_libi2c_bus_t *bushdl, /* Wait until end of transfer */ rc = gr_i2cmst_wait(prv_ptr, expected_sts); if (rc != RTEMS_SUCCESSFUL) { + DBG("exited with error\n"); return -rc; -#if defined(DEBUG) - printk("exited with error\n"); -#endif } *buf++ = prv_ptr->reg_ptr->tdrd; } -#if defined(DEBUG) - printk("exited\n"); -#endif + DBG("exited\n"); + return buf - bytes; } static int gr_i2cmst_write_bytes(rtems_libi2c_bus_t *bushdl, unsigned char *bytes, int nbytes) { - gr_i2cmst_prv_t *prv_ptr = &(((gr_i2cmst_desc_t *)(bushdl))->prv); + gr_i2cmst_prv_t *prv_ptr = (gr_i2cmst_prv_t *)bushdl; unsigned char *buf = bytes; rtems_status_code rc; -#if defined(DEBUG) - printk("gr_i2cmst_write_bytes called, nbytes = %d...", nbytes); -#endif + + DBG("gr_i2cmst_write_bytes called, nbytes = %d...", nbytes); while (nbytes-- > 0) { -#if defined(DEBUG) - printk("writing byte 0x%02X...", *buf); -#endif + + DBG("writing byte 0x%02X...", *buf); + prv_ptr->reg_ptr->tdrd = *buf++; prv_ptr->reg_ptr->cmdsts = GRI2C_CMD_WR | prv_ptr->sendstart; prv_ptr->sendstart = 0; @@ -249,16 +253,13 @@ static int gr_i2cmst_write_bytes(rtems_libi2c_bus_t *bushdl, rc = gr_i2cmst_wait(prv_ptr, GRI2C_STATUS_IDLE); if (rc != RTEMS_SUCCESSFUL) { -#if defined(DEBUG) - printk("exited with error\n"); -#endif + DBG("exited with error\n"); return -rc; } } -#if defined(DEBUG) - printk("exited\n"); -#endif + DBG("exited\n"); + return buf - bytes; } @@ -271,88 +272,148 @@ static rtems_libi2c_bus_ops_t gr_i2cmst_ops = { write_bytes: gr_i2cmst_write_bytes, }; +/* Get Hardware and disable it */ +int i2cmst_device_init(gr_i2cmst_prv_t *priv) +{ + struct amba_dev_info *ambadev; + struct ambapp_core *pnpinfo; -static gr_i2cmst_desc_t gr_i2cmst_desc = { - { /* rtems_libi2c_bus_t */ - ops : &gr_i2cmst_ops, - size : sizeof(gr_i2cmst_ops), - }, - { /* gr_i2cmst_prv_t, private data */ - reg_ptr : NULL, - sysfreq : 40000, - } + /* Get device information from AMBA PnP information */ + ambadev = (struct amba_dev_info *)priv->dev->businfo; + if ( ambadev == NULL ) { + return -1; + } + pnpinfo = &ambadev->info; + priv->reg_ptr = (gr_i2cmst_regs_t *)pnpinfo->apb_slv->start; + + /* Disable core */ + priv->reg_ptr->ctrl = 0; + + priv->i2clib_desc.ops = &gr_i2cmst_ops; + priv->i2clib_desc.size = sizeof(gr_i2cmst_ops); + return 0; +} + +/******************* Driver Manager Part ***********************/ + +int i2cmst_init2(struct drvmgr_dev *dev); +int i2cmst_init3(struct drvmgr_dev *dev); + +struct drvmgr_drv_ops i2cmst_ops = +{ + .init = {NULL, i2cmst_init2, i2cmst_init3, NULL}, + .remove = NULL, + .info = NULL }; -/* Scans for I2CMST core and initalizes i2c library */ -rtems_status_code leon_register_i2c(amba_confarea_type *abus) +struct amba_dev_id i2cmst_ids[] = { -#if defined(DEBUG) - printk("leon_register_i2c called..."); -#endif + {VENDOR_GAISLER, GAISLER_I2CMST}, + {0, 0} /* Mark end of table */ +}; + +struct amba_drv_info i2cmst_drv_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_GAISLER_I2CMST_ID, /* Driver ID */ + "I2CMST_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &i2cmst_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + 0, + }, + &i2cmst_ids[0] +}; + +void i2cmst_register_drv (void) +{ + DBG("Registering I2CMST driver\n"); + drvmgr_drv_register(&i2cmst_drv_info.general); +} - int rc; - int device_found = 0; - amba_apb_device apbi2cmst; +/* The I2CMST Driver is informed about a new hardware device */ +int i2cmst_init2(struct drvmgr_dev *dev) +{ + gr_i2cmst_prv_t *priv; - /* Scan AMBA bus for I2CMST core */ - device_found = amba_find_apbslv(abus, VENDOR_GAISLER, GAISLER_I2CMST, - &apbi2cmst); + DBG("I2CMST[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name); - if (device_found == 1) { + priv = dev->priv = malloc(sizeof(gr_i2cmst_prv_t)); + if ( !priv ) + return DRVMGR_NOMEM; + memset(priv, 0, sizeof(*priv)); + priv->dev = dev; - /* Initialize i2c library */ - rc = rtems_libi2c_initialize(); - if (rc < 0) { -#if defined(DEBUG) - printk("rtems_libi2x_initialize failed, exiting...\n"); -#endif - return rc; - } + /* This core will not find other cores, so we wait for init2() */ - gr_i2cmst_desc.prv.reg_ptr = (gr_i2cmst_regs_t *)apbi2cmst.start; + return DRVMGR_OK; +} - /* Detect system frequency, same as in apbuart_initialize */ -#ifndef SYS_FREQ_kHZ -#if defined(LEON3) - /* LEON3: find timer address via AMBA Plug&Play info */ - { - amba_apb_device gptimer; - LEON3_Timer_Regs_Map *tregs; - - if (amba_find_apbslv(abus,VENDOR_GAISLER, - GAISLER_GPTIMER,&gptimer) == 1 ) { - tregs = (LEON3_Timer_Regs_Map *)gptimer.start; - gr_i2cmst_desc.prv.sysfreq = (tregs->scaler_reload+1)*1000; - } else { - gr_i2cmst_desc.prv.sysfreq = 40000; /* Default to 40MHz */ - } +/* Init stage 2 */ +int i2cmst_init3(struct drvmgr_dev *dev) +{ + gr_i2cmst_prv_t *priv; + char prefix[32]; + char devName[32]; + int rc; + + priv = (gr_i2cmst_prv_t *)dev->priv; + + /* Do initialization */ + + /* Initialize i2c library */ + rc = rtems_libi2c_initialize(); + if (rc != 0) { + DBG("I2CMST: rtems_libi2c_initialize failed, exiting...\n"); + free(dev->priv); + dev->priv = NULL; + return DRVMGR_FAIL; } -#elif defined(LEON2) - /* LEON2: use hardcoded address to get to timer */ - { - LEON_Register_Map *regs = (LEON_Register_Map *)0x80000000; - gr_i2cmst_desc.prv.sysfreq = (regs->Scaler_Reload+1)*1000; + + /* I/O system registered and initialized + * Now we take care of device initialization. + */ + + /* Get frequency */ + if ( drvmgr_freq_get(dev, DEV_APB_SLV, &priv->sysfreq) ) { + return DRVMGR_FAIL; } -#else -#error CPU not supported for I2CMST driver */ -#endif -#else - /* Use hardcoded frequency */ - gr_i2cmst_desc.prv.sysfreq = SYS_FREQ_kHZ; -#endif + priv->sysfreq = priv->sysfreq / 1000; /* Convert to kHz */ - rc = rtems_libi2c_register_bus("/dev/i2c1", &gr_i2cmst_desc.bus_desc); - if (rc < 0) { -#if defined(DEBUG) - printk("rtems_libi2c_register_bus failed, exiting..\n"); -#endif - return -rc; - } - } + if ( i2cmst_device_init(priv) ) { + free(dev->priv); + dev->priv = NULL; + return DRVMGR_FAIL; + } -#if defined(DEBUG) - printk("exited\n"); -#endif - return 0; + /* 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/i2c%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/%si2c%d", prefix, dev->minor_bus+1); + } + + /* Register Bus for this Device */ + rc = rtems_libi2c_register_bus(devName, &priv->i2clib_desc); + if (rc < 0) { + DBG("I2CMST: rtems_libi2c_register_bus(%s) failed, exiting..\n", devName); + free(dev->priv); + dev->priv = NULL; + return DRVMGR_FAIL; + } + priv->minor = rc; + + return DRVMGR_OK; } 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..28244a6588 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/ahbstat.h @@ -0,0 +1,74 @@ +/* + * AHBSTAT driver interface + * + * COPYRIGHT (c) 2011 + * Gaisler Research + * + * 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/ambapp.h b/c/src/lib/libbsp/sparc/shared/include/ambapp.h index e2b557d85e..6d9611a489 100644 --- a/c/src/lib/libbsp/sparc/shared/include/ambapp.h +++ b/c/src/lib/libbsp/sparc/shared/include/ambapp.h @@ -1,10 +1,8 @@ /* - * AMBA Plag & Play Bus Driver Macros for LEON2 + * AMBA Plug & Play routines * - * Macros used for AMBA Plug & Play bus scanning - * - * COPYRIGHT (c) 2007. - * Gaisler Research + * COPYRIGHT (c) 2009. + * Aeroflex Gaisler. * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at @@ -16,265 +14,355 @@ #ifndef __AMBAPP_H__ #define __AMBAPP_H__ +/* Include VENDOR and DEVICE definitions */ +#include <ambapp_ids.h> + #ifdef __cplusplus extern "C" { #endif -#define AMBA_CONF_AREA 0xff000 -#define AMBA_AHB_SLAVE_CONF_AREA (1 << 11) - -#define AMBA_AHB_CONF_WORDS 8 -#define AMBA_APB_CONF_WORDS 2 -#define AMBA_AHB_MASTERS 16 -#define AMBA_AHB_SLAVES 16 -#define AMBA_APB_SLAVES 16 -#define AMBA_APBUARTS 8 - -/* Vendor codes */ -#define VENDOR_GAISLER 1 -#define VENDOR_PENDER 2 -#define VENDOR_ESA 4 -#define VENDOR_OPENCORES 8 - -/* Gaisler Research device id's */ -#define GAISLER_LEON3 0x03 -#define GAISLER_LEON3DSU 0x04 -#define GAISLER_ETHAHB 0x05 -#define GAISLER_APBMST 0x06 -#define GAISLER_AHBUART 0x07 -#define GAISLER_SRCTRL 0x08 -#define GAISLER_SDCTRL 0x09 -#define GAISLER_APBUART 0x0C -#define GAISLER_IRQMP 0x0D -#define GAISLER_AHBRAM 0x0E -#define GAISLER_GPTIMER 0x11 -#define GAISLER_PCITRG 0x12 -#define GAISLER_PCISBRG 0x13 -#define GAISLER_PCIFBRG 0x14 -#define GAISLER_PCITRACE 0x15 -#define GAISLER_DMACTRL 0x16 -#define GAISLER_OCCAN 0x19 -#define GAISLER_PIOPORT 0x1A -#define GAISLER_ETHMAC 0x1D -#define GAISLER_SPACEWIRE 0x1f -#define GAISLER_AHB2AHB 0x20 -#define GAISLER_I2CMST 0x28 -#define GAISLER_GRSPW2 0x29 -#define GAISLER_GRCAN 0x34 -#define GAISLER_GRHCAN 0x3d -#define GAISLER_GRFIFO 0x35 -#define GAISLER_GRADCDAC 0x36 -#define GAISLER_GRPULSE 0x37 -#define GAISLER_GRTIMER 0x38 -#define GAISLER_FTAHBRAM 0x50 -#define GAISLER_FTMCTRL 0x54 -#define GAISLER_BRM 0x72 - - -/* European Space Agency device id's */ -#define ESA_LEON2 0x2 -#define ESA_MCTRL 0xF -#define ESA_SPW2 0x12 - -/* Opencores device id's */ -#define OPENCORES_PCIBR 0x4 -#define OPENCORES_ETHMAC 0x5 +/* Max supported AHB buses */ +#define AHB_BUS_MAX 6 + +struct ambapp_dev; +struct ambapp_core; +struct ambapp_apb_info; +struct ambapp_ahb_info; + +struct ambapp_dev { + struct ambapp_dev *next; /* Next */ + struct ambapp_dev *prev; /* Previous Device. If (this == + * rev->child) prev is bus bridge */ + struct ambapp_dev *children; /* Points to first device on sub-bus */ + void *owner; /* Owner of this AMBA device */ + unsigned char dev_type; /* AHB MST, AHB SLV or APB SLV*/ + unsigned char vendor; /* Vendor ID */ + unsigned short device; /* Device ID */ + int devinfo[0]; /* Device info (APB/AHB dep. on type) */ +}; -/* - * - * Macros for manipulating Configuration registers - * - */ -#define amba_get_confword(tab, index, word) (*((tab).addr[(index)]+(word))) +#define AMBAPP_FLAG_FFACT_DIR 0x100 /* Frequency factor direction, 0=down, 1=up */ +#define AMBAPP_FLAG_FFACT 0x0f0 /* Frequency factor against top bus */ +#define AMBAPP_FLAG_MBUS 0x00c +#define AMBAPP_FLAG_SBUS 0x003 + +/* Get APB or AHB information from a AMBA device */ +#define DEV_TO_APB(adev) ((struct ambapp_apb_info *)((adev)->devinfo)) +#define DEV_TO_AHB(adev) ((struct ambapp_ahb_info *)((adev)->devinfo)) +#define DEV_TO_COMMON(adev) ((struct ambapp_common_info *)((adev)->devinfo)) +/* Convert address of ambapp_apb_info/ambapp_ahb_info into ambapp_dev */ +#define APB_TO_DEV(apb_info) ((struct ambapp_dev *)(unsigned int(apb_info) - \ + offsetof(struct ambapp_dev, devinfo))) +#define AHB_TO_DEV(ahb_info) ((struct ambapp_dev *)(unsigned int(ahb_info) - \ + offsetof(struct ambapp_dev, devinfo))) + +struct ambapp_common_info { + unsigned char irq; + unsigned char ver; + unsigned char ahbidx; /* AHB Bus Index */ +}; -#define amba_vendor(x) (((x) >> 24) & 0xff) +struct ambapp_apb_info { + /* COMMON */ + unsigned char irq; + unsigned char ver; + unsigned char ahbidx; /* AHB Bus Index */ -#define amba_device(x) (((x) >> 12) & 0xfff) + /* APB SPECIFIC */ + unsigned int start; + unsigned int mask; +}; -#define amba_ahb_get_membar(tab, index, nr) (*((tab).addr[(index)]+4+(nr))) +struct ambapp_ahb_info { + /* COMMON */ + unsigned char irq; + unsigned char ver; + unsigned char ahbidx; /* AHB Bus Index */ + + /* AHB SPECIFIC */ + unsigned int start[4]; + unsigned int mask[4]; + char type[4]; /* type[N] Determine type of start[N]-mask[N], + * 2=AHB Memory Space, 3=AHB I/O Space */ + unsigned int custom[3]; +}; -#define amba_ahb_get_custom(tab, index, nr) (*((tab).addr[(index)]+1+(nr))) +/* Describes a complete AMBA Core. Each device may consist of 3 interfaces */ +struct ambapp_core { + char irq; /* irq=-1 indicate no IRQ */ + unsigned char vendor; + unsigned short device; + int index; /* Core index */ + struct ambapp_ahb_info *ahb_mst; + struct ambapp_ahb_info *ahb_slv; + struct ambapp_apb_info *apb_slv; +}; -#define amba_apb_get_membar(tab, index) (*((tab).addr[(index)]+1)) +struct ambapp_ahb_bus { + unsigned int ioarea; /* AHB Bus IOAREA */ + unsigned int freq_hz; /* Frequency of AHB Bus */ + struct ambapp_dev *bridge;/* Bridge Device on Parent AHB Bus */ + struct ambapp_dev *dev; /* First Device on AHB Bus */ +}; -#define amba_membar_start(mbar) (((mbar) & 0xfff00000) & (((mbar) & 0xfff0) << 16)) +struct ambapp_mmap { + unsigned int size; + unsigned int local_adr; + unsigned int remote_adr; +}; -#define amba_iobar_start(base, iobar) ((base) | ((((iobar) & 0xfff00000)>>12) & (((iobar) & 0xfff0)<<4)) ) +/* Complete AMBA PnP information */ +struct ambapp_bus { + struct ambapp_dev *root; /* AHB/APB Device Tree*/ + struct ambapp_mmap *mmaps; /* Memory MAP Array */ + struct ambapp_ahb_bus ahbs[AHB_BUS_MAX]; /* AHB Buses */ +}; -#define amba_irq(conf) ((conf) & 0x1f) +/* + * Return values + * 0 - continue + * 1 - stop scanning + */ +typedef int (*ambapp_func_t)(struct ambapp_dev *dev, int index, void *arg); + +#define DEV_IS_FREE(dev) (dev->owner == NULL) +#define DEV_IS_ALLOCATED(dev) (dev->owner != NULL) + +/* Options to ambapp_for_each */ +#define OPTIONS_AHB_MSTS 0x00000001 +#define OPTIONS_AHB_SLVS 0x00000002 +#define OPTIONS_APB_SLVS 0x00000004 +#define OPTIONS_ALL_DEVS (OPTIONS_AHB_MSTS|OPTIONS_AHB_SLVS|OPTIONS_APB_SLVS) + +#define OPTIONS_FREE 0x00000010 +#define OPTIONS_ALLOCATED 0x00000020 +#define OPTIONS_ALL (OPTIONS_FREE|OPTIONS_ALLOCATED) + +/* Depth first search, Defualt is breath first search. */ +#define OPTIONS_DEPTH_FIRST 0x00000100 + +#define DEV_AHB_NONE 0 +#define DEV_AHB_MST 1 +#define DEV_AHB_SLV 2 +#define DEV_APB_SLV 3 + +/* Structures used to access Plug&Play information directly */ +struct ambapp_pnp_ahb { + const unsigned int id; /* VENDOR, DEVICE, VER, IRQ, */ + const unsigned int custom[3]; + const unsigned int mbar[4]; /* MASK, ADDRESS, TYPE, CACHABLE/PREFETCHABLE */ +}; -#define amba_ver(conf) (((conf)>>5) & 0x1f) +struct ambapp_pnp_apb { + const unsigned int id; /* VENDOR, DEVICE, VER, IRQ, */ + const unsigned int iobar; /* MASK, ADDRESS, TYPE, CACHABLE/PREFETCHABLE */ +}; + +#define ambapp_pnp_vendor(id) (((id) >> 24) & 0xff) +#define ambapp_pnp_device(id) (((id) >> 12) & 0xfff) +#define ambapp_pnp_ver(id) (((id)>>5) & 0x1f) +#define ambapp_pnp_irq(id) ((id) & 0x1f) + +#define ambapp_pnp_start(mbar) (((mbar) & 0xfff00000) & (((mbar) & 0xfff0) << 16)) +#define ambapp_pnp_mbar_mask(mbar) (((mbar)>>4) & 0xfff) +#define ambapp_pnp_mbar_type(mbar) ((mbar) & 0xf) + +#define ambapp_pnp_apb_start(iobar, base) ((base) | ((((iobar) & 0xfff00000)>>12) & (((iobar) & 0xfff0)<<4)) ) +#define ambapp_pnp_apb_mask(iobar) ((~(ambapp_pnp_mbar_mask(iobar)<<8) & 0x000fffff) + 1) -#define amba_membar_type(mbar) ((mbar) & 0xf) +#define AMBA_TYPE_AHBIO_ADDR(addr,base_ioarea) ((unsigned int)(base_ioarea) | ((addr) >> 12)) #define AMBA_TYPE_APBIO 0x1 #define AMBA_TYPE_MEM 0x2 #define AMBA_TYPE_AHBIO 0x3 -#define AMBA_TYPE_AHBIO_ADDR(addr,base_ioarea) ((unsigned int)(base_ioarea) | ((addr) >> 12)) - -/* - * Types and structure used for AMBA Plug & Play bus scanning +/* Copy Data from AMBA PnP I/O Area */ +typedef void *(*ambapp_memcpy_t)( + void *dest, /* Destination RAM copy */ + const void *src, /* Source AMBA PnP Address to copy from */ + int n, /* Number of bytes to be copied */ + struct ambapp_bus *abus /* Optional AMBA Bus pointer */ + ); + +/* Scan a AMBA Plug & Play bus and create all device structures describing the + * the devices. The devices will form a tree, where every node describes one + * interface. The resulting tree is placed in the location pointed to by root. + * + * Since it the tree is located in RAM it is easier to work with AMBA buses + * that is located over PCI and SpaceWire etc. + * + * \param ioarea The IO-AREA where Plug & Play information can be found. + * \param parent Used internally when recursing down a bridge. Set to NULL. + * \param mmaps Is used to perform address translation if needed. + * \param root Resulting device node tree root is stored here. * */ -typedef struct amba_device_table { - int devnr; /* numbrer of devices on AHB or APB bus */ - unsigned int *addr[16]; /* addresses to the devices configuration tables */ -} amba_device_table; - -typedef struct { - int devnr; - unsigned int *addr[AMBA_APB_SLAVES]; /* addresses to the devices configuration tables */ - unsigned int apbmst[AMBA_APB_SLAVES]; /* pointer to AHB slave (which is a APB master) */ -} amba_apb_dev; - -struct amba_mmap { - unsigned int cpu_adr; - unsigned int size; - unsigned int remote_amba_adr; -}; - -typedef struct _amba_confarea_type amba_confarea_type; - -struct _amba_confarea_type { - amba_confarea_type *next; /* next bus in chain */ - int notroot; /* is root of a bus (mother AHB has 64 masters/slaves rest 16) */ - unsigned int ioarea; - struct amba_mmap *mmaps; - amba_device_table ahbmst; - amba_device_table ahbslv; - amba_apb_dev apbslv; -}; - -typedef struct { - unsigned int start, irq, bus_id; -} amba_apb_device; - -typedef struct { - unsigned int start[4], irq, ver; -} amba_ahb_device; - -/* Scans AMBA Plug&Play Information and convers that information - * to a more readable format in RAM. +extern int ambapp_scan( + struct ambapp_bus *abus, + unsigned int ioarea, + ambapp_memcpy_t memfunc, + struct ambapp_mmap *mmaps + ); + +/* Initialize the frequency [Hz] of all AHB Buses from knowing the frequency + * of one particular APB/AHB Device. + */ +extern void ambapp_freq_init( + struct ambapp_bus *abus, + struct ambapp_dev *dev, + unsigned int freq); + +/* Returns the frequency [Hz] of a AHB/APB device */ +extern unsigned int ambapp_freq_get( + struct ambapp_bus *abus, + struct ambapp_dev *dev); + +/* Iterates through all AMBA devices previously found, it calls func + * once for every device that match the search arguments. + * + * SEARCH OPTIONS + * All search options must be fulfilled, type of devices searched (options) + * and AMBA Plug&Play ID [VENDOR,DEVICE], before func() is called. The options + * can be use to search only for AMBA APB or AHB Slaves or AHB Masters for + * example. Note that when VENDOR=-1 or DEVICE=-1 it will match any vendor or + * device ID, this means setting both VENDOR and DEVICE to -1 will result in + * calling all devices matches the options argument. * - * Will scan for - AHB Masters - * - AHB Slaves - * - APB Slaves (if a AHB/APB bridge is found) + * \param abus AMBAPP Bus to search + * \param options Search options, see OPTIONS_* above + * \param vendor AMBAPP VENDOR ID to search for + * \param device AMBAPP DEVICE ID to search for + * \param func Function called for every device matching search options + * \param arg Optional argument passed on to func * - * \param amba_conf AMBA P&P device info is placed here. - * \param ioarea address of AMBA Plug&Play information, - * on LEON3 systems default is 0xfff00000 - * \param mmaps Memory mmap specific to this amba bus, - * if NULL no translation will be made (default). - * A array of maps, ending with a entry with size=0. + * func return value affects the search, returning a non-zero value will + * stop the search and ambapp_for_each will return immediately returning the + * same non-zero value. + * + * Return Values + * 0 - all devices was scanned + * non-zero - stopped by user function returning the non-zero value */ -void amba_scan (amba_confarea_type * amba_conf, unsigned int ioarea, - struct amba_mmap *mmaps); - -/* Print AMBA Plug&Play info on terminal */ -void amba_print_conf (amba_confarea_type * amba_conf); - - - - -/***** APB SLAVES *****/ - -/* Return number of APB Slave devices which has given vendor and device */ -int amba_get_number_apbslv_devices (amba_confarea_type * amba_conf, int vendor, - int device); - -/* Get First APB Slave device of this vendor&device id */ -int amba_find_apbslv (amba_confarea_type * amba_conf, int vendor, int device, - amba_apb_device * dev); - -/* Get APB Slave device of this vendor&device id. (setting nr to 0 is eqivalent to calling amba_find_apbslv() ) */ -int amba_find_next_apbslv (amba_confarea_type * amba_conf, int vendor, - int device, amba_apb_device * dev, int index); - -/* Get first nr APB Slave devices, put them into dev (which is an array of nr length) */ -int amba_find_apbslvs (amba_confarea_type * amba_conf, int vendor, int device, - amba_apb_device * devs, int maxno); - - - -/***** AHB SLAVES *****/ - -/* Return number of AHB Slave devices which has given vendor and device */ -int amba_get_number_ahbslv_devices (amba_confarea_type * amba_conf, int vendor, - int device); - -/* Get First AHB Slave device of this vendor&device id */ -int amba_find_ahbslv (amba_confarea_type * amba_conf, int vendor, int device, - amba_ahb_device * dev); - -/* Get AHB Slave device of this vendor&device id. (setting nr to 0 is eqivalent to calling amba_find_ahbslv() ) */ -int amba_find_next_ahbslv (amba_confarea_type * amba_conf, int vendor, - int device, amba_ahb_device * dev, int index); - -/* Get first nr AHB Slave devices, put them into dev (which is an array of nr length) */ -int amba_find_ahbslvs (amba_confarea_type * amba_conf, int vendor, int device, - amba_ahb_device * devs, int maxno); - - - -/***** AHB MASTERS *****/ - -/* Return number of AHB Master devices which has given vendor and device */ -int amba_get_number_ahbmst_devices (amba_confarea_type * amba_conf, int vendor, - int device); +extern int ambapp_for_each( + struct ambapp_bus *abus, + unsigned int options, + int vendor, + int device, + ambapp_func_t func, + void *arg); + +/* Helper function for ambapp_for_each(), find a device by index. If pcount + * is NULL the first device is returned, else pcount is interpreted as index + * by decrementing the value until zero is reaced: *count=0 first device, + * *count=1 second device etc. + * + * The matching device is returned, which will stop the ambapp_for_each search. + * If zero is returned from ambapp_for_each no device matching the index was + * found + */ +extern int ambapp_find_by_idx(struct ambapp_dev *dev, int index, void *pcount); -/* Get First AHB Master device of this vendor&device id */ -int amba_find_ahbmst (amba_confarea_type * amba_conf, int vendor, int device, - amba_ahb_device * dev); +/* Get number of devices matching the options/vendor/device arguments, the + * arguments are passed onto ambapp_for_each(). + */ +extern int ambapp_dev_count(struct ambapp_bus *abus, unsigned int options, + int vendor, int device); -/* Get AHB Master device of this vendor&device id. (setting nr to 0 is eqivalent to calling amba_find_ahbmst() ) */ -int amba_find_next_ahbmst (amba_confarea_type * amba_conf, int vendor, - int device, amba_ahb_device * dev, int index); +/* Print short information about devices on the AMBA bus onto the console */ +extern void ambapp_print(struct ambapp_bus *abus, int show_depth); -/* Get first nr AHB Master devices, put them into dev (which is an array of nr length) */ -int amba_find_ahbmsts (amba_confarea_type * amba_conf, int vendor, int device, - amba_ahb_device * devs, int maxno); +/* Mark a device taken (allocate), Owner field is set with owner Data. Returns + * -1 if device has already been allocated. + */ +extern int ambapp_alloc_dev(struct ambapp_dev *dev, void *owner); +/* Owner field is cleared, which indicates that device is not allocated */ +extern void ambapp_free_dev(struct ambapp_dev *dev); -/******** AMBA DEVICES *******/ +/* Find AHB/APB Bridge or AHB/AHB Bridge Parent */ +extern struct ambapp_dev *ambapp_find_parent(struct ambapp_dev *dev); -/* ESA MEMORY CONTROLLER */ -typedef struct { - unsigned int mcfg1; - unsigned int mcfg2; - unsigned int mcfg3; -} ambapp_regmap_mctrl; +/* Returns bus depth (number of sub AHB buses) of device from root bus */ +extern int ambapp_depth(struct ambapp_dev *dev); -/* APB UART */ -typedef struct { - volatile unsigned int data; - volatile unsigned int status; - volatile unsigned int ctrl; - volatile unsigned int scaler; -} ambapp_apb_uart; +/* Get Device Name from AMBA PnP name database */ +extern char *ambapp_device_id2str(int vendor, int id); -typedef struct { - volatile unsigned int ilevel; - volatile unsigned int ipend; - volatile unsigned int iforce; - volatile unsigned int iclear; - volatile unsigned int mpstat; - volatile unsigned int notused01; - volatile unsigned int notused02; - volatile unsigned int notused03; - volatile unsigned int notused10; - volatile unsigned int notused11; - volatile unsigned int notused12; - volatile unsigned int notused13; - volatile unsigned int notused20; - volatile unsigned int notused21; - volatile unsigned int notused22; - volatile unsigned int notused23; - volatile unsigned int mask[16]; - volatile unsigned int force[16]; -} LEON3_IrqCtrl_Regs_Map; +/* Get Vendor Name from AMBA PnP name database */ +extern char *ambapp_vendor_id2str(int vendor); -/*****************************/ +/* Set together VENDOR_DEVICE Name from AMBA PnP name database. Return length + * of C-string stored in buf not including string termination '\0'. + */ +extern int ambapp_vendev_id2str(int vendor, int id, char *buf); + +/* Help functions for backwards compability */ + +extern int ambapp_find_apbslv( + struct ambapp_bus *abus, + int vendor, + int device, + struct ambapp_apb_info *dev); + +extern int ambapp_find_apbslv_next( + struct ambapp_bus *abus, + int vendor, + int device, + struct ambapp_apb_info *dev, + int index); + +extern int ambapp_find_apbslvs_next( + struct ambapp_bus *abus, + int vendor, + int device, + struct ambapp_apb_info *dev, + int index, + int maxno); + +extern int ambapp_find_apbslvs( + struct ambapp_bus *abus, + int vendor, + int device, + struct ambapp_apb_info *dev, + int maxno); + +extern int ambapp_get_number_apbslv_devices( + struct ambapp_bus *abus, + int vendor, + int device); + +extern int ambapp_find_ahbslv( + struct ambapp_bus *abus, + int vendor, + int device, + struct ambapp_ahb_info *dev); + +extern int ambapp_find_ahbslv_next( + struct ambapp_bus *abus, + int vendor, + int device, + struct ambapp_ahb_info *dev, + int index); + +extern int ambapp_find_ahbslvs_next( + struct ambapp_bus *abus, + int vendor, + int device, + struct ambapp_ahb_info *dev, + int index, + int maxno); + +extern int ambapp_find_ahbslvs( + struct ambapp_bus *abus, + int vendor, + int device, + struct ambapp_ahb_info *dev, + int maxno); + +extern int ambapp_get_number_ahbslv_devices( + struct ambapp_bus *abus, + int vendor, + int device); #ifdef __cplusplus } diff --git a/c/src/lib/libbsp/sparc/shared/include/ambapp_ids.h b/c/src/lib/libbsp/sparc/shared/include/ambapp_ids.h new file mode 100644 index 0000000000..e6f3601ee2 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/ambapp_ids.h @@ -0,0 +1,245 @@ +/* AMBA Plug & Play Bus Vendor and Device IDs. + * + * COPYRIGHT (c) 2008. + * Gaisler Research + * + * This header file provide all known VENDOR and DEVICE IDs available + * in the AMBA Plug & Play information. Taken from GRLIB 3386. + * + * 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 __AMBAPP_DEVS_H__ +#define __AMBAPP_DEVS_H__ + +/* Vendor codes */ +#define VENDOR_GAISLER 1 +#define VENDOR_PENDER 2 +#define VENDOR_ESA 4 +#define VENDOR_ASTRIUM 6 +#define VENDOR_OPENCHIP 7 +#define VENDOR_OPENCORES 8 +#define VENDOR_CONTRIB 9 +#define VENDOR_EONIC 11 +#define VENDOR_RADIONOR 15 +#define VENDOR_GLEICHMANN 16 +#define VENDOR_MENTA 17 +#define VENDOR_SUN 19 +#define VENDOR_MOVIDIA 20 +#define VENDOR_ORBITA 23 +#define VENDOR_SYNOPSYS 33 +#define VENDOR_NASA 34 +#define VENDOR_ACTEL 172 +#define VENDOR_CAL 202 +#define VENDOR_EMBEDDIT 234 +#define VENDOR_CETON 203 + +/* Gaisler Research device id's */ +#define GAISLER_LEON2DSU 0x002 +#define GAISLER_LEON3 0x003 +#define GAISLER_LEON3DSU 0x004 +#define GAISLER_ETHAHB 0x005 +#define GAISLER_APBMST 0x006 +#define GAISLER_AHBUART 0x007 +#define GAISLER_SRCTRL 0x008 +#define GAISLER_SDCTRL 0x009 +#define GAISLER_SSRCTRL 0x00a +#define GAISLER_APBUART 0x00c +#define GAISLER_IRQMP 0x00d +#define GAISLER_AHBRAM 0x00e +#define GAISLER_AHBDPRAM 0x00f +#define GAISLER_GPTIMER 0x011 +#define GAISLER_PCITRG 0x012 +#define GAISLER_PCISBRG 0x013 +#define GAISLER_PCIFBRG 0x014 +#define GAISLER_PCITRACE 0x015 +#define GAISLER_DMACTRL 0x016 +#define GAISLER_AHBTRACE 0x017 +#define GAISLER_DSUCTRL 0x018 +#define GAISLER_CANAHB 0x019 +#define GAISLER_GPIO 0x01a +#define GAISLER_AHBROM 0x01b +#define GAISLER_AHBJTAG 0x01c +#define GAISLER_ETHMAC 0x01d +#define GAISLER_SWNODE 0x01e +#define GAISLER_SPW 0x01f +#define GAISLER_AHB2AHB 0x020 +#define GAISLER_USBDC 0x021 +#define GAISLER_USB_DCL 0x022 +#define GAISLER_DDRMP 0x023 +#define GAISLER_ATACTRL 0x024 +#define GAISLER_DDRSP 0x025 +#define GAISLER_EHCI 0x026 +#define GAISLER_UHCI 0x027 +#define GAISLER_I2CMST 0x028 +#define GAISLER_SPW2 0x029 +#define GAISLER_AHBDMA 0x02a +#define GAISLER_NUHOSP3 0x02b +#define GAISLER_CLKGATE 0x02c +#define GAISLER_SPICTRL 0x02d +#define GAISLER_DDR2SP 0x02e +#define GAISLER_SLINK 0x02f +#define GAISLER_GRTM 0x030 +#define GAISLER_GRTC 0x031 +#define GAISLER_GRPW 0x032 +#define GAISLER_GRCTM 0x033 +#define GAISLER_GRHCAN 0x034 +#define GAISLER_GRFIFO 0x035 +#define GAISLER_GRADCDAC 0x036 +#define GAISLER_GRPULSE 0x037 +#define GAISLER_GRTIMER 0x038 +#define GAISLER_AHB2PP 0x039 +#define GAISLER_GRVERSION 0x03a +#define GAISLER_APB2PW 0x03b +#define GAISLER_PW2APB 0x03c +#define GAISLER_GRCAN 0x03d +#define GAISLER_I2CSLV 0x03e +#define GAISLER_U16550 0x03f +#define GAISLER_AHBMST_EM 0x040 +#define GAISLER_AHBSLV_EM 0x041 +#define GAISLER_GRTESTMOD 0x042 +#define GAISLER_ASCS 0x043 +#define GAISLER_IPMVBCTRL 0x044 +#define GAISLER_SPIMCTRL 0x045 +#define GAISLER_L4STAT 0x047 +#define GAISLER_LEON4 0x048 +#define GAISLER_LEON4DSU 0x049 +#define GAISLER_GRPWM 0x04a +#define GAISLER_L2CACHE 0x04b +#define GAISLER_GR1553B 0x04d +#define GAISLER_GRIOMMU 0x04f +#define GAISLER_FTAHBRAM 0x050 +#define GAISLER_FTSRCTRL 0x051 +#define GAISLER_AHBSTAT 0x052 +#define GAISLER_LEON3FT 0x053 +#define GAISLER_FTMCTRL 0x054 +#define GAISLER_FTSDCTRL 0x055 +#define GAISLER_FTSRCTRL8 0x056 +#define GAISLER_MEMSCRUB 0x057 +#define GAISLER_APBPS2 0x060 +#define GAISLER_VGACTRL 0x061 +#define GAISLER_LOGAN 0x062 +#define GAISLER_SVGACTRL 0x063 +#define GAISLER_T1AHB 0x064 +#define GAISLER_MP7WRAP 0x065 +#define GAISLER_GRSYSMON 0x066 +#define GAISLER_GRACECTRL 0x067 +#define GAISLER_B1553BC 0x070 +#define GAISLER_B1553RT 0x071 +#define GAISLER_B1553BRM 0x072 +#define GAISLER_GRAES 0x073 +#define GAISLER_SATCAN 0x080 +#define GAISLER_CANMUX 0x081 +#define GAISLER_GRTMRX 0x082 +#define GAISLER_GRTCTX 0x083 +#define GAISLER_GRTMDESC 0x084 +#define GAISLER_GRTMVC 0x085 +#define GAISLER_GEFFE 0x086 +#define GAISLER_AES 0x073 +#define GAISLER_ECC 0x074 +#define GAISLER_PCIF 0x075 +#define GAISLER_CLKMOD 0x076 +#define GAISLER_HAPSTRAK 0x077 +#define GAISLER_TEST_1X2 0x078 +#define GAISLER_WILD2AHB 0x079 +#define GAISLER_BIO1 0x07a +#define GAISLER_GRAESDMA 0x07b +#define GAISLER_GRPCI2 0x07c +#define GAISLER_GRPCI2_DMA 0x07d +#define GAISLER_SPWCUC 0x089 +#define GAISLER_SPW2_DMA 0x08a +#define GAISLER_SPW_ROUTER 0x08b + + +#define GAISLER_PIPEWRAPPER 0xffa +#define GAISLER_L2TIME 0xffd /* internal device: leon2 timer */ +#define GAISLER_L2C 0xffe /* internal device: leon2compat */ +#define GAISLER_PLUGPLAY 0xfff /* internal device: plug & play configarea */ + +/* European Space Agency device id's */ +#define ESA_LEON2 0x002 +#define ESA_LEON2APB 0x003 +#define ESA_IRQ 0x005 +#define ESA_TIMER 0x006 +#define ESA_UART 0x007 +#define ESA_CFG 0x008 +#define ESA_IO 0x009 +#define ESA_MCTRL 0x00f +#define ESA_PCIARB 0x010 +#define ESA_HURRICANE 0x011 +#define ESA_SPW_RMAP 0x012 +#define ESA_SPW2 0x012 +#define ESA_AHBUART 0x013 +#define ESA_SPWA 0x014 +#define ESA_BOSCHCAN 0x015 +#define ESA_IRQ2 0x016 +#define ESA_AHBSTAT 0x017 +#define ESA_WPROT 0x018 +#define ESA_WPROT2 0x019 +#define ESA_PDEC3AMBA 0x020 +#define ESA_PTME3AMBA 0x021 + +#define OPENCHIP_APBGPIO 0x001 +#define OPENCHIP_APBI2C 0x002 +#define OPENCHIP_APBSPI 0x003 +#define OPENCHIP_APBCHARLCD 0x004 +#define OPENCHIP_APBPWM 0x005 +#define OPENCHIP_APBPS2 0x006 +#define OPENCHIP_APBMMCSD 0x007 +#define OPENCHIP_APBNAND 0x008 +#define OPENCHIP_APBLPC 0x009 +#define OPENCHIP_APBCF 0x00a +#define OPENCHIP_APBSYSACE 0x00b +#define OPENCHIP_APB1WIRE 0x00c +#define OPENCHIP_APBJTAG 0x00d +#define OPENCHIP_APBSUI 0x00e + + +#define CONTRIB_CORE1 0x001 +#define CONTRIB_CORE2 0x002 + +#define GLEICHMANN_CUSTOM 0x001 +#define GLEICHMANN_GEOLCD01 0x002 +#define GLEICHMANN_DAC 0x003 +#define GLEICHMANN_HPI 0x004 +#define GLEICHMANN_SPI 0x005 +#define GLEICHMANN_HIFC 0x006 +#define GLEICHMANN_ADCDAC 0x007 +#define GLEICHMANN_SPIOC 0x008 +#define GLEICHMANN_AC97 0x009 + +#define SUN_T1 0x001 +#define SUN_S1 0x011 + +#define ORBITA_1553B 0x001 +#define ORBITA_429 0x002 +#define ORBITA_SPI 0x003 +#define ORBITA_I2C 0x004 +#define ORBITA_SMARTCARD 0x064 +#define ORBITA_SDCARD 0x065 +#define ORBITA_UART16550 0x066 +#define ORBITA_CRYPTO 0x067 +#define ORBITA_SYSIF 0x068 +#define ORBITA_PIO 0x069 +#define ORBITA_RTC 0x0c8 +#define ORBITA_COLORLCD 0x12c +#define ORBITA_PCI 0x190 +#define ORBITA_DSP 0x1f4 +#define ORBITA_USBHOST 0x258 +#define ORBITA_USBDEV 0x2bc + +#define NASA_EP32 0x001 + +#define CAL_DDRCTRL 0x188 + +#define ACTEL_COREMP7 0x001 + +/* Opencores device id's */ +#define OPENCORES_PCIBR 0x4 +#define OPENCORES_ETHMAC 0x5 + +#endif diff --git a/c/src/lib/libbsp/sparc/shared/include/apbuart.h b/c/src/lib/libbsp/sparc/shared/include/apbuart.h index c8a6e35fec..079f349ef2 100644 --- a/c/src/lib/libbsp/sparc/shared/include/apbuart.h +++ b/c/src/lib/libbsp/sparc/shared/include/apbuart.h @@ -1,7 +1,7 @@ /* * Driver interface for APBUART * - * COPYRIGHT (c) 2007. + * COPYRIGHT (c) 2008. * Gaisler Research * * The license and distribution terms for this file may be @@ -13,7 +13,7 @@ #ifndef __APBUART_H__ #define __APBUART_H__ -#include <ambapp.h> +#include <drvmgr/drvmgr.h> #ifdef __cplusplus extern "C" { @@ -57,6 +57,7 @@ typedef struct { #define APBUART_CTRL_EC 0x100 #define APBUART_CTRL_TF 0x200 #define APBUART_CTRL_RF 0x400 +#define APBUART_CTRL_DB 0x800 #define APBUART_STATUS_DR 0x1 #define APBUART_STATUS_TS 0x2 @@ -70,11 +71,8 @@ typedef struct { #define APBUART_STATUS_TF 0x200 #define APBUART_STATUS_RF 0x400 -/* Register APBUART driver - * bus = pointer to AMBA bus description used to search for APBUART(s). - * (&amba_conf for LEON3), (LEON2: see amba_scan) - */ -int apbuart_register (amba_confarea_type * bus); +/* Register APBUART driver to the driver manager system */ +void apbuart_register_drv (void); #ifdef __cplusplus } diff --git a/c/src/lib/libbsp/sparc/shared/include/apbuart_pci.h b/c/src/lib/libbsp/sparc/shared/include/apbuart_pci.h deleted file mode 100644 index e8064297c8..0000000000 --- a/c/src/lib/libbsp/sparc/shared/include/apbuart_pci.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * APBUART via PCI - driver interface - * - * COPYRIGHT (c) 2007. - * Gaisler Research - * - * 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 __APBUART_PCI_H__ -#define __APBUART_PCI_H__ - -#include <apbuart.h> - -#ifdef __cplusplus -extern "C" { -#endif - -/* Register APBUART driver, if APBUART devices are found. - * bus = pointer to AMBA bus description used to search for APBUART(s). - * - */ - -int apbuart_pci_register (amba_confarea_type * bus); - -/* This function must be called on APBUART interrupt. Called from the - * PCI interrupt handler. - * irq = AMBA IRQ assigned to the APBUART device, is found by reading - * pending register on IRQMP connected to the APBUART device. - * - */ -void apbuartpci_interrupt_handler (int irq, void *arg); - -extern void (*apbuart_pci_int_reg) (void *handler, int irq, void *arg); - -#ifdef __cplusplus -} -#endif - -#endif /* __APBUART_PCI_H__ */ diff --git a/c/src/lib/libbsp/sparc/shared/include/apbuart_rasta.h b/c/src/lib/libbsp/sparc/shared/include/apbuart_rasta.h deleted file mode 100644 index 1edf6f98d3..0000000000 --- a/c/src/lib/libbsp/sparc/shared/include/apbuart_rasta.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * APBUART RASTA via PCI - driver interface - * - * COPYRIGHT (c) 2007. - * Gaisler Research - * - * 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 __APBUART_RASTA_H__ -#define __APBUART_RASTA_H__ - -#include <apbuart.h> - -#ifdef __cplusplus -extern "C" { -#endif - -/* Register APBUART driver, if APBUART devices are found. - * bus = pointer to AMBA bus description used to search for APBUART(s). - * - */ - -int apbuart_rasta_register(amba_confarea_type *bus); - -/* This function must be called on APBUART interrupt. Called from the - * RASTA interrupt handler. - * irq = AMBA IRQ assigned to the APBUART device, is found by reading - * pending register on IRQMP connected to the APBUART device. - * - */ -void apbuartrasta_interrupt_handler(int irq, void *arg); - -extern void (*apbuart_rasta_int_reg)(void *handler, int irq, void *arg); - -#ifdef __cplusplus -} -#endif - -#endif /* __APBUART_RASTA_H__ */ diff --git a/c/src/lib/libbsp/sparc/shared/include/b1553brm.h b/c/src/lib/libbsp/sparc/shared/include/b1553brm.h index f5fd343078..c1ceb1578b 100644 --- a/c/src/lib/libbsp/sparc/shared/include/b1553brm.h +++ b/c/src/lib/libbsp/sparc/shared/include/b1553brm.h @@ -1,7 +1,7 @@ - /* - * Macros used for brm controller +/* + * B1553BRM driver interface. * - * COPYRIGHT (c) 2006. + * COPYRIGHT (c) 2008. * Gaisler Research * * The license and distribution terms for this file may be @@ -13,8 +13,6 @@ #ifndef __B1553BRM_H__ #define __B1553BRM_H__ -#include <ambapp.h> - #ifdef __cplusplus extern "C" { #endif @@ -96,6 +94,7 @@ struct bc_msg { #define BC_RTRT 0x0002 #define BC_BUSA 0x0004 #define BC_EOL 0x0020 +#define BC_SKIP 0x0040 #define BC_BAME 0x8000 #define BRM_MBC_IRQ 1 /* Monitor Block Counter irq */ @@ -108,7 +107,7 @@ struct bc_msg { #define BRM_IXEQ0_IRQ 256 /* Index Equal Zero irq */ #define BRM_BDRCV_IRQ 512 /* Broadcast Command Received irq */ #define BRM_SUBAD_IRQ 1024 /* Subaddress Accessed irq */ -#define BRM_MERR_IRQ 4096 /* Message Error irq */ +#define BRM_MERR_IRQ 2048 /* Message Error irq */ #define BRM_TAPF_IRQ 8192 /* Terminal Address Parity Fail irq */ #define BRM_WRAPF_IRQ 16384 /* Wrap Fail irq */ #define BRM_DMAF_IRQ 32768 /* DMA Fail irq */ @@ -138,13 +137,6 @@ struct bc_msg { #define BRM_MODE_BM 0x2 #define BRM_MODE_BM_RT 0x3 /* both RT and BM */ - -/* Register RAMON FPGA BRM driver, calls brm_register */ -int brm_register_leon3_ramon_fpga(void); - -/* Register RAMON ASIC BRM driver, calls brm_register */ -int brm_register_leon3_ramon_asic(void); - #define BRM_FREQ_12MHZ 0 #define BRM_FREQ_16MHZ 1 #define BRM_FREQ_20MHZ 2 @@ -155,15 +147,11 @@ int brm_register_leon3_ramon_asic(void); #define CLKSEL_MASK 0x7 -/* Register BRM driver - * See (struct brm_reg).w_ctrl for clksel and clkdiv. - * See Enhanced register (the least signinficant 2 bits) in BRM Core for brm_freq - * bus = &amba_conf for LEON3. (LEON2 not yet supported for this driver) - */ -int b1553brm_register(amba_confarea_type *bus, unsigned int clksel, unsigned int clkdiv, unsigned int brm_freq); +void b1553brm_register_drv(void); #ifdef __cplusplus } #endif #endif /* __BRM_H__ */ + diff --git a/c/src/lib/libbsp/sparc/shared/include/b1553brm_pci.h b/c/src/lib/libbsp/sparc/shared/include/b1553brm_pci.h deleted file mode 100644 index 74f9d321de..0000000000 --- a/c/src/lib/libbsp/sparc/shared/include/b1553brm_pci.h +++ /dev/null @@ -1,57 +0,0 @@ - /* - * Macros used for brm controller - * - * COPYRIGHT (c) 2006. - * Gaisler Research - * - * 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 __B1553BRM_PCI_H__ -#define __B1553BRM_PCI_H__ - -#include <b1553brm.h> - -#ifdef __cplusplus -extern "C" { -#endif - -/* Register BRM driver - * See (struct brm_reg).w_ctrl for clksel and clkdiv. - * See Enhanced register (the least signinficant 2 bits) in BRM Core for brm_freq - * bus = &amba_conf for LEON3. (LEON2 not yet supported for this driver) - * - * Memory setup: - * memarea = 128k aligned pointer to memory (if zero malloc will be used) (as the CPU sees it) - * hw_address = address that HW must use to access memarea. (used in the translation process) - */ - -int b1553brm_pci_register( - amba_confarea_type *bus, - unsigned int clksel, - unsigned int clkdiv, - unsigned int brm_freq, - unsigned int memarea, - unsigned int hw_address - ); - - -/* This function must be called on BRM interrupt. Called from the - * PCI interrupt handler. irq = AMBA IRQ MASK assigned to the BRM device, - * is found by reading pending register on IRQMP connected to BRM - * device. - * - * Return 0=not handled. nono-zero=handled - */ -int b1553brm_pci_interrupt_handler(int irq, void *arg); - -extern void (*b1553brm_pci_int_reg)(void *handler, int irq, void *arg); - -#ifdef __cplusplus -} -#endif - -#endif /* __B1553BRM_PCI_H__ */ diff --git a/c/src/lib/libbsp/sparc/shared/include/b1553brm_rasta.h b/c/src/lib/libbsp/sparc/shared/include/b1553brm_rasta.h deleted file mode 100644 index cd5165801c..0000000000 --- a/c/src/lib/libbsp/sparc/shared/include/b1553brm_rasta.h +++ /dev/null @@ -1,57 +0,0 @@ - /* - * Macros used for brm controller - * - * COPYRIGHT (c) 2006. - * Gaisler Research - * - * 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 __B1553BRM_RASTA_H__ -#define __B1553BRM_RASTA_H__ - -#include <b1553brm.h> - -#ifdef __cplusplus -extern "C" { -#endif - -/* Register BRM driver - * See (struct brm_reg).w_ctrl for clksel and clkdiv. - * See Enhanced register (the least signinficant 2 bits) in BRM Core for brm_freq - * bus = &amba_conf for LEON3. (LEON2 not yet supported for this driver) - * - * Memory setup: - * memarea = 128k aligned pointer to memory (if zero malloc will be used) (as the CPU sees it) - * hw_address = address that HW must use to access memarea. (used in the translation process) - */ - -int b1553brm_rasta_register( - amba_confarea_type *bus, - unsigned int clksel, - unsigned int clkdiv, - unsigned int brm_freq, - unsigned int memarea, - unsigned int hw_address - ); - - -/* This function must be called on BRM interrupt. Called from the - * PCI interrupt handler. irq = AMBA IRQ MASK assigned to the BRM device, - * is found by reading pending register on IRQMP connected to BRM - * device. - * - * Return 0=not handled. nono-zero=handled - */ -int b1553brm_rasta_interrupt_handler(int irq, void *arg); - -extern void (*b1553brm_rasta_int_reg)(void *handler, int irq, void *arg); - -#ifdef __cplusplus -} -#endif - -#endif /* __B1553BRM_RASTA_H__ */ 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..a92324700b --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/b1553rt.h @@ -0,0 +1,76 @@ +/* + * 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..6ee4c19395 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/canmux.h @@ -0,0 +1,37 @@ +/* + * Header file for RTEMS CAN_MUX driver + * + * -------------------------------------------------------------------------- + * -- This file is a part of GAISLER RESEARCH source code. + * -- Copyright (C) 2008, Gaisler Research AB - all rights reserved. + * -- + * -- ANY USE OR REDISTRIBUTION IN PART OR IN WHOLE MUST BE HANDLED IN + * -- ACCORDANCE WITH THE GAISLER LICENSE AGREEMENT AND MUST BE APPROVED + * -- IN ADVANCE IN WRITING. + * -- + * -- BY DEFAULT, DISTRIBUTION OR DISCLOSURE IS NOT PERMITTED. + * -------------------------------------------------------------------------- + * + */ + +#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/cons.h b/c/src/lib/libbsp/sparc/shared/include/cons.h new file mode 100644 index 0000000000..816e20f899 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/cons.h @@ -0,0 +1,42 @@ +/* Console driver interface to UART drivers + * + * - First console device that has System Console flag set will be + * system console. + * - If none of the registered console devices has system console set, + * the first is registered device is used, unless it has + * + * COPYRIGHT (c) 2010. + * Aeroflex Gaisler. + * + * 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 __CONS_H__ +#define __CONS_H__ + +struct console_dev; + +#define CONSOLE_FLAG_SYSCON 0x01 + +struct console_cons_ops { + void (*get_uart_attrs)(struct console_dev *, struct termios *t); +}; + +struct console_dev { + /* Set to non-zero if this UART should be system console and/or + * debug console. + */ + int flags; + char *fsname; /* File system prefix */ + const struct rtems_termios_callbacks *callbacks; /* TERMIOS Callbacks */ + struct console_cons_ops ops; +}; + +extern void console_dev_register(struct console_dev *dev); +#if 0 +extern void console_dev_unregister(struct console_dev *dev); +#endif + +#endif 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 new file mode 100644 index 0000000000..3fad75d57a --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/drvmgr/ambapp_bus.h @@ -0,0 +1,135 @@ +/* General part of a AMBA Plug & Play bus driver. + * + * COPYRIGHT (c) 2008. + * Aeroflex Gaisler. + * + * This is the general part of the different AMBA Plug & Play + * drivers. The drivers are wrappers around this driver, making + * the code size smaller for systems with multiple AMBA Plug & + * Play buses. + * + * 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 __AMBAPP_BUS_H__ +#define __AMBAPP_BUS_H__ + +#include <drvmgr/drvmgr.h> +#include <ambapp.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* GRLIB AMBA Plug&Play Driver ID generation */ +#define DRIVER_AMBAPP_ID(vendor, device) \ + DRIVER_ID(DRVMGR_BUS_TYPE_AMBAPP, ((((vendor) & 0xff) << 16) | ((device) & 0xfff))) + +/*** Gaisler Hardware Device Driver IDs ***/ +#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_GRSPW2_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_SPW2) +#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; + unsigned short device; + /* Version ? */ +}; + +struct amba_drv_info { + struct drvmgr_drv general; /* General bus info */ + /* AMBA specific bus information */ + struct amba_dev_id *ids; /* Supported hardware */ +}; + +struct amba_dev_info { + struct amba_dev_id id; + struct ambapp_core info; +}; + +struct ambapp_ops { + int (*int_register) + (struct drvmgr_dev *dev, int index, const char *info, drvmgr_isr isr, void *arg); + int (*int_unregister) + (struct drvmgr_dev *dev, int index, drvmgr_isr isr, void *arg); + int (*int_clear)(struct drvmgr_dev *dev, int index); + int (*int_mask)(struct drvmgr_dev *dev, int index); + int (*int_unmask)(struct drvmgr_dev *dev, int index); + int (*get_params) + (struct drvmgr_dev *, struct drvmgr_bus_params *); +}; + +struct ambapp_config { + struct ambapp_bus *abus; /* Prescanned AMBA PnP bus */ + struct ambapp_ops *ops; /* AMBA bus operations */ + struct drvmgr_map_entry *maps_up; /* Bus memory map up-stream towards CPU */ + struct drvmgr_map_entry *maps_down; /* Bus memory map down-stream towards HW */ + struct drvmgr_bus_res *resources; /* Driver Resources */ + int bus_type; /* Set DRVMGR_BUS_TYPE_AMBAPP_DIST if distributed AMBA Bus */ + struct drvmgr_func *funcs; /* Custom functions */ +}; + +/*** Bus operations with READ/WRITE access operations *** + * + * The functions are implemented using the standard drvmgr RW interface + */ +#define AMBAPP_R8 DRVMGR_RWFUNC(RW_SIZE_1|RW_READ|RW_REG) +#define AMBAPP_R16 DRVMGR_RWFUNC(RW_SIZE_2|RW_READ|RW_REG) +#define AMBAPP_R32 DRVMGR_RWFUNC(RW_SIZE_4|RW_READ|RW_REG) +#define AMBAPP_R64 DRVMGR_RWFUNC(RW_SIZE_8|RW_READ|RW_REG) +#define AMBAPP_W8 DRVMGR_RWFUNC(RW_SIZE_1|RW_WRITE|RW_REG) +#define AMBAPP_W16 DRVMGR_RWFUNC(RW_SIZE_2|RW_WRITE|RW_REG) +#define AMBAPP_W32 DRVMGR_RWFUNC(RW_SIZE_4|RW_WRITE|RW_REG) +#define AMBAPP_W64 DRVMGR_RWFUNC(RW_SIZE_8|RW_WRITE|RW_REG) +#define AMBAPP_RMEM DRVMGR_RWFUNC(RW_SIZE_ANY|RW_READ|RW_MEM) +#define AMBAPP_WMEM DRVMGR_RWFUNC(RW_SIZE_ANY|RW_WRITE|RW_MEM) +#define AMBAPP_MEMSET DRVMGR_RWFUNC(RW_SIZE_ANY|RW_SET|RW_MEM) +#define AMBAPP_RW_ARG DRVMGR_RWFUNC(RW_ARG) + +/* Register an ambapp bus on-top of a device */ +extern int ambapp_bus_register( + struct drvmgr_dev *dev, + struct ambapp_config *config + ); + +extern void ambapp_bus_freq_register( + struct drvmgr_dev *dev, + int amba_interface, + unsigned int freq_hz); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/lib/libbsp/sparc/shared/include/drvmgr/ambapp_bus_grlib.h b/c/src/lib/libbsp/sparc/shared/include/drvmgr/ambapp_bus_grlib.h new file mode 100644 index 0000000000..f14c546abd --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/drvmgr/ambapp_bus_grlib.h @@ -0,0 +1,35 @@ +/* LEON3 GRLIB AMBA Plug & Play bus driver interface. + * + * COPYRIGHT (c) 2008. + * Aeroflex Gaisler. + * + * This is driver is a wrapper for the general AMBA Plug & Play bus + * driver. This is the root bus driver for GRLIB systems. + * + * 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 __AMBAPP_BUS_GRLIB_H__ +#define __AMBAPP_BUS_GRLIB_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +struct grlib_config { + struct ambapp_bus *abus; + struct drvmgr_bus_res *resources; +}; + +/* Register GRLIB AMBA PnP Bus as root bus at driver manager */ +extern int ambapp_grlib_root_register(struct grlib_config *config); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/lib/libbsp/sparc/shared/include/drvmgr/ambapp_bus_rmap.h b/c/src/lib/libbsp/sparc/shared/include/drvmgr/ambapp_bus_rmap.h new file mode 100644 index 0000000000..c7ec880ba2 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/drvmgr/ambapp_bus_rmap.h @@ -0,0 +1,79 @@ +/* SpaceWire RMAP AMBA Plug & Play bus driver interface. + * + * COPYRIGHT (c) 2009. + * Aeroflex Gaisler. + * + * 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 __AMBAPP_BUS_RMAP_H__ +#define __AMBAPP_BUS_RMAP_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +extern void ambapp_rmap_register(void); + +/*** Bus operations with READ/WRITE access operations *** + * + * The functions are implemented using the standard drvmgr RW interface + */ +#define AMBAPP_RMAP_R8 DRVMGR_RWFUNC(RW_SIZE_1|RW_READ|RW_REG) +#define AMBAPP_RMAP_R16 DRVMGR_RWFUNC(RW_SIZE_2|RW_READ|RW_REG) +#define AMBAPP_RMAP_R32 DRVMGR_RWFUNC(RW_SIZE_4|RW_READ|RW_REG) +#define AMBAPP_RMAP_R64 DRVMGR_RWFUNC(RW_SIZE_8|RW_READ|RW_REG) +#define AMBAPP_RMAP_W8 DRVMGR_RWFUNC(RW_SIZE_1|RW_WRITE|RW_REG) +#define AMBAPP_RMAP_W16 DRVMGR_RWFUNC(RW_SIZE_2|RW_WRITE|RW_REG) +#define AMBAPP_RMAP_W32 DRVMGR_RWFUNC(RW_SIZE_4|RW_WRITE|RW_REG) +#define AMBAPP_RMAP_W64 DRVMGR_RWFUNC(RW_SIZE_8|RW_WRITE|RW_REG) +#define AMBAPP_RMAP_RMEM DRVMGR_RWFUNC(RW_SIZE_ANY|RW_READ|RW_MEM) +#define AMBAPP_RMAP_WMEM DRVMGR_RWFUNC(RW_SIZE_ANY|RW_WRITE|RW_MEM) +#define AMBAPP_RMAP_MEMSET DRVMGR_RWFUNC(RW_SIZE_ANY|RW_SET|RW_MEM) +#define AMBAPP_RMAP_RW_ARG DRVMGR_RWFUNC(RW_ARG) +#define AMBAPP_RMAP_RW_ERR DRVMGR_RWFUNC(RW_ERR) + +/* Read/Write function types with additional argument */ +typedef uint8_t (*ambapp_rmap_r8)(uint8_t *srcadr, struct drvmgr_rw_arg *a); +typedef uint16_t (*ambapp_rmap_r16)(uint16_t *srcadr, struct drvmgr_rw_arg *a); +typedef uint32_t (*ambapp_rmap_r32)(uint32_t *srcadr, struct drvmgr_rw_arg *a); +typedef uint64_t (*ambapp_rmap_r64)(uint64_t *srcadr, struct drvmgr_rw_arg *a); +typedef void (*ambapp_rmap_w8)(uint8_t *dstadr, uint8_t data, struct drvmgr_rw_arg *a); +typedef void (*ambapp_rmap_w16)(uint16_t *dstadr, uint16_t data, struct drvmgr_rw_arg *a); +typedef void (*ambapp_rmap_w32)(uint32_t *dstadr, uint32_t data, struct drvmgr_rw_arg *a); +typedef void (*ambapp_rmap_w64)(uint64_t *dstadr, uint64_t data, struct drvmgr_rw_arg *a); +/* READ/COPY a memory area located on bus into CPU memory. + * From 'src' (remote) to the destination 'dest' (local), n=number of bytes + */ +typedef int (*ambapp_rmap_rmem)(void *dest, const void *src, int n, struct drvmgr_rw_arg *a); +/* WRITE/COPY a user buffer located in CPU memory to a location on the bus. + * From 'src' (local) to the destination 'dest' (remote), n=number of bytes + */ +typedef int (*ambapp_rmap_wmem)(void *dest, const void *src, int n, struct drvmgr_rw_arg *a); +/* Set a memory area to the byte value given in c, see LIBC memset(). Memset is + * implemented by calling wmem() multiple times with a "large" buffer. + */ +typedef int (*ambapp_rmap_memset)(void *dstadr, int c, size_t n, struct drvmgr_rw_arg *a); + +/* Allocate memory from the SpaceWire target's memory */ +extern void *ambapp_rmap_partition_memalign( + struct drvmgr_dev *dev, + int partition, + size_t boundary, + size_t size); + +/* Register a partition of memory in the SpaceWire target */ +extern int ambapp_rmap_partition_create( + struct drvmgr_dev *dev, + int partition, + unsigned int start, + size_t size); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/lib/libbsp/sparc/shared/include/drvmgr/leon2_amba_bus.h b/c/src/lib/libbsp/sparc/shared/include/drvmgr/leon2_amba_bus.h new file mode 100644 index 0000000000..539e0b4693 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/drvmgr/leon2_amba_bus.h @@ -0,0 +1,96 @@ +/* LEON2 Hardcoded bus driver interface. + * + * COPYRIGHT (c) 2008. + * Aeroflex Gaisler. + * + * Bus driver for a hardcoded setup. LEON2 systems have some + * cores always present, here called "Standard Cores". In + * addtion to the standard cores there are often extra cores + * that can be defined using the "Custom Cores" mechanism. + * + * A Core is described by assigning a base register and + * IRQ0..IRQ15 using the leon2_core structure. + * + * 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 __LEON2_AMBA_BUS_H__ +#define __LEON2_AMBA_BUS_H__ + +/*** Cores location and IRQs hardcoded ***/ + +#include <drvmgr/drvmgr.h> +#include <ambapp.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* LEON2 AMBA Driver ID generation */ +#define DRIVER_LEON2_AMBA(id) DRIVER_ID(DRVMGR_BUS_TYPE_LEON2_AMBA, id) + +/* LEON2 Cores (any unique 48-bit number will do) */ +#define LEON2_AMBA_NONE_ID 0 +#define LEON2_AMBA_TIMER_ID 1 +#define LEON2_AMBA_UART_ID 2 +#define LEON2_AMBA_GPIO_ID 3 +#define LEON2_AMBA_IRQCTRL_ID 4 + +#define LEON2_AMBA_AT697PCI_ID 100 +#define LEON2_AMBA_AMBAPP_ID 0xfff0 + +/* LEON2 driver IDs */ +#define DRIVER_LEON2_AMBA_TIMER DRIVER_LEON2_AMBA(LEON2_AMBA_TIMER_ID) +#define DRIVER_LEON2_AMBA_UART DRIVER_LEON2_AMBA(LEON2_AMBA_UART_ID) +#define DRIVER_LEON2_AMBA_AT697PCI DRIVER_LEON2_AMBA(LEON2_AMBA_AT697PCI_ID) +#define DRIVER_LEON2_AMBA_AMBAPP DRIVER_LEON2_AMBA(LEON2_AMBA_AMBAPP_ID) + +struct leon2_amba_dev_id { + unsigned short core_id; +}; + +#define EMPTY_LEON2_CORE {{LEON2_AMBA_NONE_ID}, NULL, NULL} +struct leon2_core { + struct leon2_amba_dev_id id; /* Core ID */ + char *name; /* Name of Core */ + struct drvmgr_key *keys; /* Core setup (location, IRQs) */ +}; + +struct leon2_bus { + struct leon2_core *std_cores; /* The LEON2 standard cores */ + struct leon2_core *custom_cores; /* Custom cores on the same bus */ + struct drvmgr_map_entry *maps_up; /* Memory map ip-stream */ + struct drvmgr_map_entry *maps_down; /* Memory map down-stream */ +}; + +extern struct leon2_core leon2_std_cores[]; + +/* Data structure drivers can access */ +struct leon2_amba_dev_info { + unsigned short core_id; /* Core ID */ + unsigned int reg_base; /* Register base */ + char irqs[16]; /* 16 irqs */ +}; + +struct leon2_amba_drv_info { + struct drvmgr_drv general; /* General bus info */ + /* AMBA specific bus information */ + struct leon2_amba_dev_id *ids; /* Supported hardware */ +}; + +/* Initialize LEON2 bus with a configuration + * bus_config - What cores, their location and irqs + * resources - Driver configuration for the cores specified bus_config + */ +int leon2_root_register( + struct leon2_bus *bus_config, + struct drvmgr_bus_res *resources); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/lib/libbsp/sparc/shared/include/drvmgr/spw_bus.h b/c/src/lib/libbsp/sparc/shared/include/drvmgr/spw_bus.h new file mode 100644 index 0000000000..870b7eb4a8 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/drvmgr/spw_bus.h @@ -0,0 +1,131 @@ +/* SpaceWire Network bus driver interface. + * + * COPYRIGHT (c) 2009. + * Aeroflex Gaisler. + * + * 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 __SPW_BUS_H__ +#define __SPW_BUS_H__ + +#include <drvmgr/drvmgr.h> +#include <drvmgr/spw_bus_ids.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* SpW Bus Driver ID generation */ +#define DRIVER_SPWBUS_ID(vendor, device) \ + DRIVER_ID(DRVMGR_BUS_TYPE_SPW_RMAP, ((((vendor) & 0xffff) << 16) | ((device) & 0xffff))) + +/* SpaceWire Driver IDs */ +#define DRIVER_SPW_RMAP_AMBAPP_ID 1 + +/**** SpaceWire Driver IDs *****/ +#define SPW_NODE_ID_GRLIB DRIVER_SPWBUS_ID(SPWBUS_VENDOR_GAISLER,SPWBUS_DEVICE_GAISLER_GRLIB) + + +/* SpaceWire Target ID */ +struct spw_id { + unsigned int spwid; /* SpaceWire Target ID */ +}; +#define SPW_NODE_ID_NONE 0 + +/* SpaceWire Target Setup */ +struct spw_node { + struct spw_id id; /* Node ID */ + char *name; /* Name of Target */ + struct drvmgr_key *keys; /* Target setup (Destination address, Destination Key) */ +}; + +/* spw_node.keys that must be defined for SpaceWire targets + * INTEGER DST_ADR SpaceWire Destination Address + * INTEGER DST_KEY RMAP Destination Key + */ + +#define EMPTY_SPW_NODE {{SPW_NODE_ID_NONE}, NULL, NULL} + +/* Virtual IRQ to GPIO Pin translation */ +struct spwbus_virq_config { + char *gpio_fsname; /* GPIO Filesystem Name */ + void *handle; /* If already opened, set "File descriptor" handle here */ +}; + +/* SpaceWire Bus driver configuration */ +struct spw_bus_config { + void *rmap; /* RMAP Stack handle, note that it should be thread-safe */ + int maxlen; /* Maximum length */ + struct spw_node *nodes; /* Bus configuration (SpaceWire nodes available) */ + char devName[32]; /* GRSPW Dev name bus is attached to */ + struct drvmgr_bus_res *resources; /* Driver resouces present on the bus */ + struct spwbus_virq_config virq_table[4]; /* Virtual IRQ number to GPIO translation table */ +}; + +/* Register SpW Bus */ +int drv_mgr_spw_init(struct spw_bus_config *config); + +struct spw_bus_dev_info { + unsigned int spwid; + + unsigned char dstadr; + unsigned char dstkey; + char virqs[4]; /* 4 virtual IRQs(VIRQ[1..4]) */ +}; + +#define SPWBUS_VIRQ1 1 +#define SPWBUS_VIRQ2 2 +#define SPWBUS_VIRQ3 3 +#define SPWBUS_VIRQ4 4 + +struct spw_bus_drv_info { + struct drvmgr_drv general; /* General bus info */ + /* SpW RMAP specific bus information */ + struct spw_id *ids; /* Supported target hardware */ +}; + +/*** Bus operations with READ/WRITE access operations ***/ +#define SPWBUS_RW_ARG DRVMGR_RWFUNC(RW_ARG) +#define SPWBUS_RW_ERR DRVMGR_RWFUNC(RW_ERR) +#define SPWBUS_R8 DRVMGR_RWFUNC(RW_SIZE_1|RW_READ|RW_REG) +#define SPWBUS_R16 DRVMGR_RWFUNC(RW_SIZE_2|RW_READ|RW_REG) +#define SPWBUS_R32 DRVMGR_RWFUNC(RW_SIZE_4|RW_READ|RW_REG) +#define SPWBUS_R64 DRVMGR_RWFUNC(RW_SIZE_8|RW_READ|RW_REG) +#define SPWBUS_W8 DRVMGR_RWFUNC(RW_SIZE_1|RW_WRITE|RW_REG) +#define SPWBUS_W16 DRVMGR_RWFUNC(RW_SIZE_2|RW_WRITE|RW_REG) +#define SPWBUS_W32 DRVMGR_RWFUNC(RW_SIZE_4|RW_WRITE|RW_REG) +#define SPWBUS_W64 DRVMGR_RWFUNC(RW_SIZE_8|RW_WRITE|RW_REG) +#define SPWBUS_RMEM DRVMGR_RWFUNC(RW_SIZE_ANY|RW_READ|RW_MEM) +#define SPWBUS_WMEM DRVMGR_RWFUNC(RW_SIZE_ANY|RW_WRITE|RW_MEM) +#define SPWBUS_MEMSET DRVMGR_RWFUNC(RW_SIZE_ANY|RW_SET|RW_MEM) + +/* Read/Write function types with additional argument */ +typedef uint8_t (*spwbus_r8)(uint8_t *srcadr, struct drvmgr_rw_arg *a); +typedef uint16_t (*spwbus_r16)(uint16_t *srcadr, struct drvmgr_rw_arg *a); +typedef uint32_t (*spwbus_r32)(uint32_t *srcadr, struct drvmgr_rw_arg *a); +typedef uint64_t (*spwbus_r64)(uint64_t *srcadr, struct drvmgr_rw_arg *a); +typedef void (*spwbus_w8)(uint8_t *dstadr, uint8_t data, struct drvmgr_rw_arg *a); +typedef void (*spwbus_w16)(uint16_t *dstadr, uint16_t data, struct drvmgr_rw_arg *a); +typedef void (*spwbus_w32)(uint32_t *dstadr, uint32_t data, struct drvmgr_rw_arg *a); +typedef void (*spwbus_w64)(uint64_t *dstadr, uint64_t data, struct drvmgr_rw_arg *a); + +/* READ/COPY a memory area located in 'src' to the destination 'dest', n=number of bytes */ +typedef int (*spwbus_rmem)(void *dest, const void *src, int n, struct drvmgr_rw_arg *a); + +/* WRITE/COPY a user buffer located in 'src' to the destination 'dest', n=number of bytes */ +typedef int (*spwbus_wmem)(void *dest, const void *src, int n, struct drvmgr_rw_arg *a); + +/* Set a memory area to the byte value given in c, see LIBC memset(). Memset is implemented by + * calling busops->write_mem multiple times with a zero buffer. + */ +typedef int (*spwbus_memset)(void *dstadr, int c, size_t n, struct drvmgr_rw_arg *a); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/lib/libbsp/sparc/shared/include/drvmgr/spw_bus_ids.h b/c/src/lib/libbsp/sparc/shared/include/drvmgr/spw_bus_ids.h new file mode 100644 index 0000000000..7ec5bdb46e --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/drvmgr/spw_bus_ids.h @@ -0,0 +1,22 @@ +/* SpaceWire bus device IDs, these are all made up, because there is no such thing as + * Plug & Play on the SpW bus. This is just an attempt to structure the SpW bus driver. + * + * COPYRIGHT (c) 2009. + * Aeroflex Gaisler. + * + * 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 __SPW_BUS_IDS_H__ +#define __SPW_BUS_IDS_H__ + +/* SpW bus VENDOR IDs */ +#define SPWBUS_VENDOR_GAISLER 1 /* Aeroflex Gaisler AB */ + +/* SpW bus DEVICE IDs for vendor GAISLER */ +#define SPWBUS_DEVICE_GAISLER_GRLIB 1 /* General GRLIB system */ + +#endif 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..50b3e0a5e4 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/genirq.h @@ -0,0 +1,94 @@ +#ifndef __GENIRQ_H__ +#define __GENIRQ_H__ + +/* 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. + * + * + */ + +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); + +#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..4b4ab1bc8f --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/gpiolib.h @@ -0,0 +1,95 @@ +/* GPIO Library 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 __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_mask(void *handle); +extern int gpiolib_irq_unmask(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 +#define GPIOLIB_IRQ_MASK 0x10 +#define GPIOLIB_IRQ_UNMASK 0x20 + +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..4b566a013a --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/gr1553b.h @@ -0,0 +1,369 @@ +#ifndef __GR1553B_H__ +#define __GR1553B_H__ + +/* GR1553B driver, used by BC, RT and/or BM driver + * + * COPYRIGHT (c) 2010. + * Aeroflex Gaisler. + * + * 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. + * + * 2010-03-15, Daniel Hellstrom <daniel@gaisler.com> + * Created + * + * + * 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. + * + */ + +#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..4157dc6cba --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/gr1553bc.h @@ -0,0 +1,254 @@ +#ifndef __GR1553BC_H__ +#define __GR1553BC_H__ + +/* GR1553B BC driver + * + * COPYRIGHT (c) 2010. + * Aeroflex Gaisler. + * + * 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. + * + * 2010-03-15, Daniel Hellstrom <daniel@gaisler.com> + * Created + * + * + * 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. + */ + +#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..be043f0b33 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/gr1553bc_list.h @@ -0,0 +1,709 @@ +#ifndef __GR1553BC_LIST_H__ +#define __GR1553BC_LIST_H__ +/* + * GR1553B BC driver, Descriptor LIST handling + * + * COPYRIGHT (c) 2010. + * Aeroflex Gaisler. + * + * 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. + * + * 2010-03-15, Daniel Hellstrom <daniel@gaisler.com> + * Created + */ + +/*!\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..4d241c5f0d --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/gr1553bm.h @@ -0,0 +1,212 @@ +#ifndef __GR1553BM_H__ +#define __GR1553BM_H__ + +/* GR1553B BM driver + * + * COPYRIGHT (c) 2010. + * Aeroflex Gaisler. + * + * 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. + * + * 2010-03-15, Daniel Hellstrom <daniel@gaisler.com> + * Created + * + * + * OVERVIEW + * ======== + * + */ + +#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..afa4ae2f4c --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/gr1553rt.h @@ -0,0 +1,443 @@ +#ifndef __GR1553RT_H__ +#define __GR1553RT_H__ + +/* GR1553B RT driver + * + * COPYRIGHT (c) 2010. + * Aeroflex Gaisler. + * + * 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. + * + * 2010-03-15, Daniel Hellstrom <daniel@gaisler.com> + * Created + * + * + * OVERVIEW + * ======== + * + * + */ + +/* 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/gr_701.h b/c/src/lib/libbsp/sparc/shared/include/gr_701.h new file mode 100644 index 0000000000..1bf16a7f5d --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/gr_701.h @@ -0,0 +1,49 @@ +/* GR-701 PCI Target driver. + * + * COPYRIGHT (c) 2008. + * Aeroflex Gaisler AB. + * + * Configures the GR-701 interface PCI board. + * This driver provides a AMBA PnP bus by using the general part + * of the AMBA PnP bus driver (ambapp_bus.c). + * + * Driver resources for the AMBA PnP bus provided can be set using + * gr701_set_resources(). + * + * 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 __GR_701_H__ +#define __GR_701_H__ + +#include <drvmgr/drvmgr.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* An array of pointers to GR-701 resources. The resources will be + * used by the drivers controlling the cores on the GR-701 target AMBA bus. + * + * The gr_rasta_io_resources is declared weak so that the user can override the + * default configuration. + */ +extern struct drvmgr_bus_res *gr701_resources[]; + +#define GR701_OPTIONS_AMBA 0x01 +#define GR701_OPTIONS_IRQ 0x02 + +/* Print information about GR-RASTA-IO PCI board */ +void gr701_print(int options); + +/* Register GR-701 driver */ +void gr701_register_drv(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/lib/libbsp/sparc/shared/include/gr_rasta_adcdac.h b/c/src/lib/libbsp/sparc/shared/include/gr_rasta_adcdac.h new file mode 100644 index 0000000000..6c58674aad --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/gr_rasta_adcdac.h @@ -0,0 +1,50 @@ +/* GR-RASTA-ADCDAC PCI Target driver. + * + * COPYRIGHT (c) 2008. + * Aeroflex Gaisler AB. + * + * Configures the GR-RASTA-ADCDAC interface PCI board. + * This driver provides a AMBA PnP bus by using the general part + * of the AMBA PnP bus driver (ambapp_bus.c). + * + * Driver resources for the AMBA PnP bus provided can be set using + * gr_rasta_adcdac_set_resources(). + * + * 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 __GR_RASTA_ADCDAC_H__ +#define __GR_RASTA_ADCDAC_H__ + +#include <drvmgr/drvmgr.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* An array of pointers to GR-RASTA-ADCDAC resources. The resources will be + * used by the drivers controlling the cores on the GR-RASTA-ADCDAC target AMBA bus. + * + * The gr_rasta_io_resources is declared weak so that the user can override the + * default configuration. + */ +extern struct drvmgr_bus_res *gr_rasta_adcdac_resources[]; + +/* Options to gr_rasta_io_print function */ +#define RASTA_ADCDAC_OPTIONS_AMBA 0x01 /* Print AMBA bus devices */ +#define RASTA_ADCDAC_OPTIONS_IRQ 0x02 /* Print current IRQ setup */ + +/* Print information about GR-RASTA-IO PCI board */ +void gr_rasta_adcdac_print(int options); + +/* Register GR-RASTA-IO driver */ +void gr_rasta_adcdac_register_drv(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/lib/libbsp/sparc/shared/include/gr_rasta_io.h b/c/src/lib/libbsp/sparc/shared/include/gr_rasta_io.h new file mode 100644 index 0000000000..b3e2a4166d --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/gr_rasta_io.h @@ -0,0 +1,50 @@ +/* GR-RASTA-IO PCI Target driver. + * + * COPYRIGHT (c) 2008. + * Aeroflex Gaisler AB. + * + * Configures the GR-RASTA-IO interface PCI board. + * This driver provides a AMBA PnP bus by using the general part + * of the AMBA PnP bus driver (ambapp_bus.c). + * + * Driver resources for the AMBA PnP bus provided can be set using + * gr_rasta_io_set_resources(). + * + * 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 __GR_RASTA_IO_H__ +#define __GR_RASTA_IO_H__ + +#include <drvmgr/drvmgr.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* An array of pointers to GR-RASTA-IO resources. The resources will be + * used by the drivers controlling the cores on the GR-RASTA-IO target AMBA bus. + * + * The gr_rasta_io_resources is declared weak so that the user can override the + * default configuration. + */ +extern struct drvmgr_bus_res *gr_rasta_io_resources[]; + +/* Options to gr_rasta_io_print function */ +#define RASTA_IO_OPTIONS_AMBA 0x01 /* Print AMBA bus devices */ +#define RASTA_IO_OPTIONS_IRQ 0x02 /* Print current IRQ setup */ + +/* Print information about GR-RASTA-IO PCI board */ +void gr_rasta_io_print(int options); + +/* Register GR-RASTA-IO driver */ +void gr_rasta_io_register_drv(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/lib/libbsp/sparc/shared/include/gr_rasta_tmtc.h b/c/src/lib/libbsp/sparc/shared/include/gr_rasta_tmtc.h new file mode 100644 index 0000000000..67ca7fca46 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/gr_rasta_tmtc.h @@ -0,0 +1,100 @@ +/* GR-RASTA-TMTC PCI Target driver. + * + * COPYRIGHT (c) 2008. + * Aeroflex Gaisler AB. + * + * Configures the GR-RASTA-TMTC interface PCI board. + * This driver provides a AMBA PnP bus by using the general part + * of the AMBA PnP bus driver (ambapp_bus.c). + * + * Driver resources for the AMBA PnP bus provided can be set using + * gr_rasta_tmtc_set_resources(). + * + * 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 __GR_RASTA_TMTC_H__ +#define __GR_RASTA_TMTC_H__ + +#include <drvmgr/drvmgr.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* GPIO TM/TC configuration pin definitions + * --31 PWRX (1=PW2APB, 0=TM VC3/4) + * --30 PWTC (1=APB2PW, 0=TC MAP1/2) + * --29 Redundant TM (1=enable, 0=disable) + * --28 Redundant TC (1=enable, 0=disable) + * --27 Select TM output (1=GRTM, 0=PTME) + * --26 Loop back PW (1=enable, 0=disable) + * --25 Transponder clock (1=PLL, 0=PLL bypass) + * --24 PWTX-SELECT (0=TX0-0, 1=TX0-1) + * --23 PDEC Map Switch (1=on, 0=off) + * --22 PDEC Ext CPDU (1=on, 0=off) + * --21 PDEC Super User (1=on, 0=off) + * --20 PDEC RM On (1=on, 0=off) + * --19 PDEC AU Enable (1=on, 0=off) + * --18 PDEC Dynamic Mode (1=on, 0=off) + * --17 PDEC Priority (1=on, 0=off) + * --16 TC PSS Support (1=on, 0=off) + * --15 TC Mark (1=on, 0=off) + * --14 TC Pseudo (1=on, 0=off) + * --13 TC Rising Clock (1=rise, 0=fall) + * --12 TC Active High (1=high, 0=low) + * --11 Bit Lock Positive (1=high, 0=low) + * --10 RF Avail Positive (1=high, 0=low) + * -- 9 : 0 SpaceCraft ID + */ + +#define GR_TMTC_GPIO_PWRX (1<<31) +#define GR_TMTC_GPIO_PWTC (1<<30) +#define GR_TMTC_GPIO_RED_TM (1<<29) +#define GR_TMTC_GPIO_RED_TC (1<<28) +#define GR_TMTC_GPIO_GRTM_SEL (1<<27) +#define GR_TMTC_GPIO_LB_PW (1<<26) +#define GR_TMTC_GPIO_TRANSP_CLK (1<<25) +#define GR_TMTC_GPIO_PWTX_SEL (1<<24) +#define GR_TMTC_GPIO_PDEC_MAP (1<<23) +#define GR_TMTC_GPIO_PDEC_CPDU (1<<22) +#define GR_TMTC_GPIO_PDEC_SU (1<<21) +#define GR_TMTC_GPIO_PDEC_RM (1<<20) +#define GR_TMTC_GPIO_PDEC_AU (1<<19) +#define GR_TMTC_GPIO_PDEC_DYN_MODE (1<<18) +#define GR_TMTC_GPIO_PDEC_PRIO (1<<17) +#define GR_TMTC_GPIO_TC_PSS (1<<16) +#define GR_TMTC_GPIO_TC_MARK (1<<15) +#define GR_TMTC_GPIO_TC_PSEUDO (1<<14) +#define GR_TMTC_GPIO_TC_RISING_CLK (1<<13) +#define GR_TMTC_GPIO_TC_ACTIVE_HIGH (1<<12) +#define GR_TMTC_GPIO_TC_BIT_LOCK (1<<11) +#define GR_TMTC_GPIO_TC_RF_AVAIL (1<<10) +#define GR_TMTC_GPIO_SCID (0x000003ff) + +/* An array of pointers to GR-RASTA-TMTC bus resources. The resources will be + * used by the device drivers controlling the cores on the GR-RASTA-IO target + * AMBA bus. + * + * The array is defined weak, and defualts to no resources. + */ +extern struct drvmgr_bus_res *gr_rasta_tmtc_resources[]; + +/* Options to gr_rasta_io_print function */ +#define RASTA_TMTC_OPTIONS_AMBA 0x01 /* Print AMBA bus devices */ +#define RASTA_TMTC_OPTIONS_IRQ 0x02 /* Print current IRQ setup */ + +/* Print information about GR-RASTA-TMTC PCI board */ +void gr_rasta_tmtc_print(int options); + +/* Register GR-RASTA-TMTC driver */ +void gr_rasta_tmtc_register_drv(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/lib/libbsp/sparc/shared/include/gr_tmtc_1553.h b/c/src/lib/libbsp/sparc/shared/include/gr_tmtc_1553.h new file mode 100644 index 0000000000..32615907f1 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/gr_tmtc_1553.h @@ -0,0 +1,50 @@ +/* GR-TMTC-1553 PCI Target driver. + * + * COPYRIGHT (c) 2010. + * Aeroflex Gaisler AB. + * + * Configures the GR-TMTC-1553 interface PCI board. + * This driver provides a AMBA PnP bus by using the general part + * of the AMBA PnP bus driver (ambapp_bus.c). + * + * Driver resources for the AMBA PnP bus provided can be set using + * gr_tmtc_1553_set_resources(). + * + * 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 __GR_TMTC_1553_H__ +#define __GR_TMTC_1553_H__ + +#include <drvmgr/drvmgr.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* An array of pointers to GR-TMTC-1553 resources. The resources will be + * used by the drivers controlling the cores on the GR-TMTC-1553 target AMBA bus. + * + * The gr_rasta_io_resources is declared weak so that the user can override the + * default configuration. + */ +extern struct drvmgr_bus_res *gr_tmtc_1553_resources[]; + +/* Options to gr_rasta_io_print function */ +#define TMTC_1553_OPTIONS_AMBA 0x01 /* Print AMBA bus devices */ +#define TMTC_1553_OPTIONS_IRQ 0x02 /* Print current IRQ setup */ + +/* Print information about GR-RASTA-IO PCI board */ +void gr_tmtc_1553_print(int options); + +/* Register GR-RASTA-IO driver */ +void gr_tmtc_1553_register_drv(void); + +#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..04b55edcf0 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/gradcdac.h @@ -0,0 +1,230 @@ +/* ADC / DAC (GRADCDAC) interface +/* + * Driver interface for APBUART + * + * 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 __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/graes.h b/c/src/lib/libbsp/sparc/shared/include/graes.h new file mode 100644 index 0000000000..e79df9d1c6 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/graes.h @@ -0,0 +1,136 @@ +/* GRAES Packetwire + * + * -------------------------------------------------------------------------- + * -- This file is a part of GAISLER RESEARCH source code. + * -- Copyright (C) 2009, Gaisler Research AB - all rights reserved. + * -- + * -- ANY USE OR REDISTRIBUTION IN PART OR IN WHOLE MUST BE HANDLED IN + * -- ACCORDANCE WITH THE GAISLER LICENSE AGREEMENT AND MUST BE APPROVED + * -- IN ADVANCE IN WRITING. + * -- + * -- BY DEFAULT, DISTRIBUTION OR DISCLOSURE IS NOT PERMITTED. + * -------------------------------------------------------------------------- + * + */ + +#ifndef __GRAES_H__ +#define __GRAES_H__ + +#include <rtems.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define GRAES_IOC_UNUSED 0 + +/* Driver operation controlling commands */ +#define GRAES_IOC_START 1 +#define GRAES_IOC_STOP 2 +#define GRAES_IOC_ISSTARTED 3 +#define GRAES_IOC_SET_BLOCKING_MODE 4 +#define GRAES_IOC_SET_TIMEOUT 5 + +/* Available only in RUNNING mode */ +#define GRAES_IOC_ENCRYPT 17 + +/* Available only in STOPPED mode */ +#define GRAES_IOC_SET_CONFIG 32 + +/* Available in both running and stopped mode */ +#define GRAES_IOC_RECLAIM 64 +#define GRAES_IOC_GET_CONFIG 65 +#define GRAES_IOC_GET_HW_IMPL 66 +#define GRAES_IOC_GET_HW_STATUS 67 /* Not implemented */ +#define GRAES_IOC_GET_STATS 69 +#define GRAES_IOC_CLR_STATS 70 + +#define GRAES_IOC_PRINT_STATUS 71 + + + +/* Args to GRTC_IOC_SET_BLOCKING_MODE */ +enum { + GRAES_BLKMODE_POLL = 0, /* Never block (polling mode) */ + GRAES_BLKMODE_BLK = 1, /* Block until at least 1 byte can be read */ +}; + +struct graes_ioc_hw { + unsigned short key_size; /* KEY Size */ +}; + +struct graes_print_status { + unsigned short printbd; +}; + + +/* Argument of GRAES_IOC_SET_CONFIG and GRAES_IOC_GET_CONFIG. + * Driver and Hardware configuration. + * + * Pointer to: + */ +struct graes_ioc_config { + + unsigned short key_size; + + /* 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 graes_block; + +struct graes_list { + struct graes_block *head; /* First Frame in list */ + struct graes_block *tail; /* Last Frame in list */ +}; + +#define GRAES_FLAGS_PROCESSED 0x01 +#define GRRM_FLAGS_ERR 0x02 + +#define GRAES_FLAGS_TRANSLATE (1<<31) /* Translate block payload address from CPU address to remote bus (the bus GRAES is resident on) */ +#define GRAES_FLAGS_TRANSLATE_AND_REMEMBER (1<<30) /* As GRAES_FLAGS_TRANSLATE, however if the translated payload address equals the payload address + * the GRAES_FLAGS_TRANSLATE_AND_REMEMBER bit is cleared and the GRAES_FLAGS_TRANSLATE bit is set */ +#define GRAES_FLAGS_COPY_DATA (1<<29) /* Where available: Transfer Frame payload to target, may be used for SpaceWire, where the GRAES driver transfer + * the payload to a buffer on the SpaceWire target. + */ + +#define GRAES_FLAGS_MASK (GRAES_BD_ED) + +#define GRAES_BD_ED_BIT 4 + +#define GRAES_BD_ED (1<<GRAES_BD_ED_BIT) + + +/* The GRAES software representation of a Frame */ +struct graes_block { + /* Options and status */ + unsigned int flags; /* bypass options, and sent/error status */ + struct graes_block *next; /* Next packet in chain */ + int length; + unsigned char *key; + unsigned char *iv; + unsigned char *payload; /* in */ + unsigned char *out; /* out */ +}; + +#define FRAME_SIZE(payloadlen) (sizeof(struct graes_block)+payloadlen) + +struct graes_ioc_stats { + unsigned long long blocks_processed; + unsigned int err_underrun; + unsigned int err_tx; + unsigned int err_ahb; + unsigned int err_transfer_frame; +}; + +/* Register GRAES driver at driver manager */ +void graes_register_drv(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __GRAES_H__ */ 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..84afc942a8 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/grascs.h @@ -0,0 +1,97 @@ +/* + * Header file for GRASCS RTEMS driver + * + * -------------------------------------------------------------------------- + * -- This file is a part of GAISLER RESEARCH source code. + * -- Copyright (C) 2008, Gaisler Research AB - all rights reserved. + * -- + * -- ANY USE OR REDISTRIBUTION IN PART OR IN WHOLE MUST BE HANDLED IN + * -- ACCORDANCE WITH THE GAISLER LICENSE AGREEMENT AND MUST BE APPROVED + * -- IN ADVANCE IN WRITING. + * -- + * -- BY DEFAULT, DISTRIBUTION OR DISCLOSURE IS NOT PERMITTED. + * -------------------------------------------------------------------------- + * + */ + +#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/grcan.h b/c/src/lib/libbsp/sparc/shared/include/grcan.h index 8b3ed15e55..3f5dcbee72 100644 --- a/c/src/lib/libbsp/sparc/shared/include/grcan.h +++ b/c/src/lib/libbsp/sparc/shared/include/grcan.h @@ -1,8 +1,8 @@ /* * Macros used for grcan controller * - * COPYRIGHT (c) 2007. - * Gaisler Research + * COPYRIGHT (c) 2008. + * Aeroflex Gaisler. * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at @@ -13,8 +13,6 @@ #ifndef __GRCAN_H__ #define __GRCAN_H__ -#include <ambapp.h> - #ifdef __cplusplus extern "C" { #endif @@ -181,20 +179,8 @@ typedef struct { #define GRCAN_IOC_SET_SFILTER 40 /* Set Sync Messages RX/TX filters, NULL disables the IRQ completely */ #define GRCAN_IOC_GET_STATUS 41 /* Get status register of GRCAN core */ -struct grcan_device_info { - unsigned int base_address; - int irq; -}; - -/* Use hard coded addresses and IRQs to find hardware */ -int grcan_register_abs(struct grcan_device_info *devices, int dev_cnt); -/* Use prescanned AMBA Plug&Play information to find all GRFIFO cores */ -int grcan_register(amba_confarea_type *abus); -#if 0 -void grcan_register(unsigned int baseaddr, unsigned int ram_base); -void grcan_interrupt_handler(rtems_vector_number v); -#endif +void grcan_register_drv(void); #ifdef __cplusplus } diff --git a/c/src/lib/libbsp/sparc/shared/include/grcan_rasta.h b/c/src/lib/libbsp/sparc/shared/include/grcan_rasta.h deleted file mode 100644 index 1f96da6bef..0000000000 --- a/c/src/lib/libbsp/sparc/shared/include/grcan_rasta.h +++ /dev/null @@ -1,24 +0,0 @@ - -#ifndef __GRCAN_RASTA_H__ -#define __GRCAN_RASTA_H__ - -#include <grcan.h> - -#ifdef __cplusplus -extern "C" { -#endif - -/* Registers the GRCAN for RASTA - * - * rambase is address of the first GRCAN core has it's TX buffer, followed by - * it's RX buffer - */ -int grcan_rasta_ram_register(amba_confarea_type *abus, int rambase); - -extern void (*grcan_rasta_int_reg)(void *handler, int irq, void *arg); - -#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..5f8c104681 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/grctm.h @@ -0,0 +1,169 @@ +/* GRCTM - CCSDS Time Manager - register 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 __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..021d2fc6bb --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/greth.h @@ -0,0 +1,159 @@ +/* + * 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. + * + * $Id: greth.h,v 1.4 2007/09/07 15:01:15 joel Exp $ + */ + + +#ifndef __GRETH_H__ +#define __GRETH_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Configuration Information */ + +typedef struct { + uint32_t base_address; + uint32_t vector; + uint32_t txd_count; + uint32_t rxd_count; +} greth_configuration_t; + +/* 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 */ +#define GRETH_CTRL_DD 0x00001000 /* Disable EDCL Duplex Detection */ + +/* 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..b9900c83f5 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/grgpio.h @@ -0,0 +1,26 @@ +/* + * GRGPIO GPIO Driver interface. + * + * COPYRIGHT (c) 2009. + * Gaisler Research + * + * 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/grlib.h b/c/src/lib/libbsp/sparc/shared/include/grlib.h new file mode 100644 index 0000000000..00373452f3 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/grlib.h @@ -0,0 +1,92 @@ +/* + * Common GRLIB AMBA Core Register definitions + * + * COPYRIGHT (c) 2008. + * Aeroflex Gaisler. + * + * 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 __GRLIB_H__ +#define __GRLIB_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* ESA MEMORY CONTROLLER */ +struct mctrl_regs { + unsigned int mcfg1; + unsigned int mcfg2; + unsigned int mcfg3; +}; + +/* APB UART */ +struct apbuart_regs { + volatile unsigned int data; + volatile unsigned int status; + volatile unsigned int ctrl; + volatile unsigned int scaler; +}; + +/* IRQMP and IRQAMP interrupt controllers */ +struct irqmp_regs { + volatile unsigned int ilevel; /* 0x00 */ + volatile unsigned int ipend; /* 0x04 */ + volatile unsigned int iforce; /* 0x08 */ + volatile unsigned int iclear; /* 0x0c */ + volatile unsigned int mpstat; /* 0x10 */ + volatile unsigned int bcast; /* 0x14 */ + volatile unsigned int notused02; /* 0x18 */ + volatile unsigned int notused03; /* 0x1c */ + volatile unsigned int ampctrl; /* 0x20 */ + volatile unsigned int icsel[2]; /* 0x24,0x28 */ + volatile unsigned int notused13; /* 0x2c */ + volatile unsigned int notused20; /* 0x30 */ + volatile unsigned int notused21; /* 0x34 */ + volatile unsigned int notused22; /* 0x38 */ + volatile unsigned int notused23; /* 0x3c */ + volatile unsigned int mask[16]; /* 0x40 */ + volatile unsigned int force[16]; /* 0x80 */ + /* Extended IRQ registers */ + volatile unsigned int intid[16]; /* 0xc0 */ + /* 0x100, align to 4Kb boundary */ + volatile unsigned int resv1[(0x1000-0x100)/4]; +}; + +/* GPTIMER Timer instance */ +struct gptimer_timer_regs { + volatile unsigned int value; + volatile unsigned int reload; + volatile unsigned int ctrl; + volatile unsigned int notused; +}; + +/* GPTIMER common registers */ +struct gptimer_regs { + volatile unsigned int scaler_value; /* common timer registers */ + volatile unsigned int scaler_reload; + volatile unsigned int cfg; + volatile unsigned int notused; + struct gptimer_timer_regs timer[7]; +}; + +/* GRGPIO GPIO */ +struct grgpio_regs { + volatile unsigned int data; /* 0x00 I/O port data register */ + volatile unsigned int output; /* 0x04 I/O port output register */ + volatile unsigned int dir; /* 0x08 I/O port direction register */ + volatile unsigned int imask; /* 0x0C Interrupt mask register */ + volatile unsigned int ipol; /* 0x10 Interrupt polarity register */ + volatile unsigned int iedge; /* 0x14 Interrupt edge register */ + volatile unsigned int bypass; /* 0x18 Bypass register */ +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/lib/libbsp/sparc/shared/include/grpci2.h b/c/src/lib/libbsp/sparc/shared/include/grpci2.h new file mode 100644 index 0000000000..c2c178f41b --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/grpci2.h @@ -0,0 +1,51 @@ +#ifndef __GRPCI2_H__ +#define __GRPCI2_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +extern void grpci2_register_drv(void); + +/* Driver Resources: + * + * PCI Interrupts + * ============== + * The interrupt settings are normally autodetected from Plyg&Play, however + * if IRQs are routed using custom GPIO pins in order to reduce the PIN count + * reserved for PCI, the options below can be used to tell GRPCI2 driver which + * System IRQ a PCI interrupt is connected to. + * Name="INTA#", Type=INT, System Interrupt number that PCI INTA is connected to + * Name="INTB#", Type=INT, System Interrupt number that PCI INTB is connected to + * Name="INTC#", Type=INT, System Interrupt number that PCI INTC is connected to + * Name="INTD#", Type=INT, System Interrupt number that PCI INTD is connected to + * + * Name="IRQmask", Type=INT, + * + * PCI Bytetwisting (endianess) + * ============================ + * Name="byteTwisting", Type=INT, Enable/Disable Bytetwisting by hardware + * + * PCI Host's Target BARs setup + * ============================ + * The Host's BARs are not configured by the configuration routines, by default + * the BARs are configured disabled (BAR=0) except for BAR0 which is mapped to + * the Main Memory for the Host. + * Name="tgtBarCfg", Type=PTR (*grpci2_pcibar_cfg), Target PCI BARs of Host + */ + +/* When the Host acts as a target on the PCI bus, the PCI BARs of the host's + * configuration space determine at which PCI address the Host will be accessed + * at and when accessing a BAR which AMBA address it will be translated to. + */ +struct grpci2_pcibar_cfg { + unsigned int pciadr; /* PCI address of BAR (BAR content) */ + unsigned int ahbadr; /* 'pciadr' translated to this AHB Address */ + unsigned int barsize; /* PCI BAR Size, must be a power of 2 */ +}; + +#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..6d84462394 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/grpwm.h @@ -0,0 +1,128 @@ +/* + * GRPWM PWM Driver interface. + * + * COPYRIGHT (c) 2009. + * Gaisler Research + * + * 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/grpwrx.h b/c/src/lib/libbsp/sparc/shared/include/grpwrx.h new file mode 100644 index 0000000000..2f287181e6 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/grpwrx.h @@ -0,0 +1,139 @@ +/* GRPWRX Packetwire + * + * -------------------------------------------------------------------------- + * -- This file is a part of GAISLER RESEARCH source code. + * -- Copyright (C) 2009, Gaisler Research AB - all rights reserved. + * -- + * -- ANY USE OR REDISTRIBUTION IN PART OR IN WHOLE MUST BE HANDLED IN + * -- ACCORDANCE WITH THE GAISLER LICENSE AGREEMENT AND MUST BE APPROVED + * -- IN ADVANCE IN WRITING. + * -- + * -- BY DEFAULT, DISTRIBUTION OR DISCLOSURE IS NOT PERMITTED. + * -------------------------------------------------------------------------- + * + */ + +#ifndef __GRPWRX_H__ +#define __GRPWRX_H__ + +#include <rtems.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define GRPWRX_IOC_UNUSED 0 + +/* Driver operation controlling commands */ +#define GRPWRX_IOC_START 1 +#define GRPWRX_IOC_STOP 2 +#define GRPWRX_IOC_ISSTARTED 3 +#define GRPWRX_IOC_SET_BLOCKING_MODE 4 +#define GRPWRX_IOC_SET_TIMEOUT 5 + +/* Available only in RUNNING mode */ +#define GRPWRX_IOC_RECV 17 + +/* Available only in STOPPED mode */ +#define GRPWRX_IOC_SET_CONFIG 32 + +/* Available in both running and stopped mode */ +#define GRPWRX_IOC_RECLAIM 64 +#define GRPWRX_IOC_GET_CONFIG 65 +#define GRPWRX_IOC_GET_HW_IMPL 66 +#define GRPWRX_IOC_GET_HW_STATUS 67 /* Not implemented */ +#define GRPWRX_IOC_GET_STATS 69 +#define GRPWRX_IOC_CLR_STATS 70 + +#define GRPWRX_IOC_PRINT_STATUS 71 + + +/* Args to GRTC_IOC_SET_BLOCKING_MODE */ +enum { + GRPWRX_BLKMODE_POLL = 0, /* Never block (polling mode) */ + GRPWRX_BLKMODE_BLK = 1, /* Block until at least 1 byte can be read */ +}; + +struct grpwrx_ioc_hw { + + unsigned short fifo_size; /* FIFO Size */ + unsigned short mode; /* frame mode = 1, packet mode = 0 */ + unsigned short clkdivide; /* Clock divide */ + +}; + +struct grpwrx_print_status { + unsigned short printbd; + +}; + +/* Argument of GRPWRX_IOC_SET_CONFIG and GRPWRX_IOC_GET_CONFIG. + * Driver and Hardware configuration. + * + * Pointer to: + */ +struct grpwrx_ioc_config { + + int framing; + + /* Physical layer options */ + unsigned short phy_clkrise; + unsigned short phy_validpos; + unsigned short phy_readypos; + unsigned short phy_busypos; + + /* 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 grpwrx_packet; + +struct grpwrx_list { + struct grpwrx_packet *head; /* First Frame in list */ + struct grpwrx_packet *tail; /* Last Frame in list */ +}; + +#define GRPWRX_FLAGS_RECEIVED 0x01 +#define GRRM_FLAGS_ERR 0x02 + +#define GRPWRX_FLAGS_TRANSLATE (1<<31) /* Translate frame payload address from CPU address to remote bus (the bus GRPWRX is resident on) */ +#define GRPWRX_FLAGS_TRANSLATE_AND_REMEMBER (1<<30) /* As GRPWRX_FLAGS_TRANSLATE, however if the translated payload address equals the payload address + * the GRPWRX_FLAGS_TRANSLATE_AND_REMEMBER bit is cleared and the GRPWRX_FLAGS_TRANSLATE bit is set */ +#define GRPWRX_FLAGS_COPY_DATA (1<<29) /* Where available: Transfer Frame payload to target, may be used for SpaceWire, where the GRPWRX driver transfer + * the payload to a buffer on the SpaceWire target. + */ + +#define GRPWRX_FLAGS_FHP (1<<3) + +#define GRPWRX_FLAGS_MASK (GRPWRX_FLAGS_FHP) + +/* The GRPWRX software representation of a Frame */ +struct grpwrx_packet { + /* Options and status */ + unsigned int flags; /* bypass options, and sent/error status */ + struct grpwrx_packet *next; /* Next packet in chain */ + int length; + unsigned char *payload; /* The Headers and Payload, Frame data and header must be word aligned */ +}; + +#define FRAME_SIZE(payloadlen) (sizeof(struct grpwrx_packet)+payloadlen) + +struct grpwrx_ioc_stats { + unsigned long long packets_received; + unsigned int err_underrun; + unsigned int err_tx; + unsigned int err_ahb; + unsigned int err_transfer_frame; +}; + +/* Register GRPWRX driver at driver manager */ +void grpwrx_register_drv(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __GRPWRX_H__ */ 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..41b44772c1 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/grslink.h @@ -0,0 +1,153 @@ +/* + * Header file for RTEMS GRSLINK SLINK master driver + * + * -------------------------------------------------------------------------- + * -- This file is a part of GAISLER RESEARCH source code. + * -- Copyright (C) 2008, Gaisler Research AB - all rights reserved. + * -- + * -- ANY USE OR REDISTRIBUTION IN PART OR IN WHOLE MUST BE HANDLED IN + * -- ACCORDANCE WITH THE GAISLER LICENSE AGREEMENT AND MUST BE APPROVED + * -- IN ADVANCE IN WRITING. + * -- + * -- BY DEFAULT, DISTRIBUTION OR DISCLOSURE IS NOT PERMITTED. + * -------------------------------------------------------------------------- + * + */ + +#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.h b/c/src/lib/libbsp/sparc/shared/include/grspw.h index 4c6c869d61..6e5ffe5cfb 100644 --- a/c/src/lib/libbsp/sparc/shared/include/grspw.h +++ b/c/src/lib/libbsp/sparc/shared/include/grspw.h @@ -1,7 +1,7 @@ /* * Macros used for Spacewire bus * - * COPYRIGHT (c) 2007. + * COPYRIGHT (c) 2008. * Gaisler Research * * The license and distribution terms for this file may be @@ -26,12 +26,18 @@ typedef struct { unsigned int txhsize; } spw_ioctl_packetsize; +#define GRSPW_PKTSEND_OPTION_HDR_CRC 0x1 +#define GRSPW_PKTSEND_OPTION_DATA_CRC 0x2 +#define GRSPW_PKTSEND_OPTION_NOCRCLEN(len) ((len & 0xf) << 8) +#define GRSPW_PKTSEND_OPTION_NOCRCLEN_MASK 0xf00 + typedef struct { unsigned int hlen; char *hdr; unsigned int dlen; char *data; unsigned int sent; + unsigned int options; } spw_ioctl_pkt_send; typedef struct { @@ -78,6 +84,11 @@ typedef struct { unsigned int is_rmapcrc; unsigned int nodemask; + unsigned int keep_source; /* copy source address to user-buffer in read() operations + * Note that rm_prot_id has no effect when keep_source is + * set. + */ + unsigned int rtimeout; /* Read timeout if != 0 */ } spw_config; #define SPACEWIRE_IOCTRL_SET_NODEADDR 1 @@ -107,30 +118,42 @@ typedef struct { #define SPACEWIRE_IOCTRL_SET_COREFREQ 32 #define SPACEWIRE_IOCTRL_SET_CLKDIVSTART 33 #define SPACEWIRE_IOCTRL_SET_NODEMASK 34 +#define SPACEWIRE_IOCTRL_SET_KEEP_SOURCE 35 +#define SPACEWIRE_IOCTRL_SET_TCODE_CTRL 36 +#define SPACEWIRE_IOCTRL_SET_TCODE 37 +#define SPACEWIRE_IOCTRL_GET_TCODE 38 +#define SPACEWIRE_IOCTRL_SET_READ_TIMEOUT 39 #define SPACEWIRE_IOCTRL_START 64 #define SPACEWIRE_IOCTRL_STOP 65 -int grspw_register(amba_confarea_type *bus); +/* Defines what register bits that will be touched + * for SPACEWIRE_IOCTRL_SET_TCODE_CTRL + */ +#define SPACEWIRE_TCODE_CTRL_IE_MSK 0x001 +#define SPACEWIRE_TCODE_CTRL_TT_MSK 0x004 +#define SPACEWIRE_TCODE_CTRL_TR_MSK 0x008 +/* Defines what register bits that should be set + * for SPACEWIRE_IOCTRL_SET_TCODE_CTRL + */ +#define SPACEWIRE_TCODE_CTRL_IE 0x100 +#define SPACEWIRE_TCODE_CTRL_TT 0x400 +#define SPACEWIRE_TCODE_CTRL_TR 0x800 -#if 0 -struct grspw_buf; +/* SPACEWIRE_IOCTRL_SET_TCODE argument mask */ +#define SPACEWIRE_TCODE_TCODE 0x0ff +#define SPACEWIRE_TCODE_SET 0x100 /* Set Timecode register */ +#define SPACEWIRE_TCODE_TX 0x400 -struct grspw_buf { - grspw_buf *next; /* next packet in chain */ +void grspw_register_drv (void); - /* Always used */ - unsigned int dlen; /* data length of '*data' */ - unsigned int max_dlen; /* allocated length of '*data' */ - void *data; /* pointer to beginning of cargo data */ +void grspw_print(int options); + +/* Global GRSPW Function pointer called upon timecode receive interrupt */ +extern void (*grspw_timecode_callback) + (void *pDev, void *regs, int minor, unsigned int tc); - /* Only used when transmitting */ - unsigned int hlen; /* length of header '*header' */ - unsigned int max_hlen; /* allocated length of '*header' */ - void *header; /* pointer to beginning of header data */ -}; -#endif #ifdef __cplusplus } diff --git a/c/src/lib/libbsp/sparc/shared/include/grspw_pci.h b/c/src/lib/libbsp/sparc/shared/include/grspw_pci.h deleted file mode 100644 index aea50f3791..0000000000 --- a/c/src/lib/libbsp/sparc/shared/include/grspw_pci.h +++ /dev/null @@ -1,49 +0,0 @@ - /* - * Macros used for GRSPW controller - * - * COPYRIGHT (c) 2006. - * Gaisler Research - * - * 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_PCI_H__ -#define __GRSPW_PCI_H__ - -#include <grspw.h> - -#ifdef __cplusplus -extern "C" { -#endif - -/* Register GRSPW Driver - * bus = &amba_conf for LEON3 - * - * Memory setup: - * memarea = 128k aligned pointer to memory (if zero malloc will be used) (as the CPU sees it) - * hw_address = address that HW must use to access memarea. (used in the translation process) - */ - -int grspw_pci_register (amba_confarea_type * bus, - unsigned int memarea, unsigned int hw_address); - - -/* This function must be called on BRM interrupt. Called from the - * PCI interrupt handler. irq = AMBA IRQ MASK assigned to the BRM device, - * is found by reading pending register on IRQMP connected to BRM - * device. - * - * Return 0=not handled. nono-zero=handled - */ -unsigned int grspw_pci_interrupt_handler (int irq, void *arg); - -extern void (*grspw_pci_int_reg) (void *handler, int irq, void *arg); - -#ifdef __cplusplus -} -#endif - -#endif /* __GRSPW_PCI_H__ */ diff --git a/c/src/lib/libbsp/sparc/shared/include/grspw_pkt.h b/c/src/lib/libbsp/sparc/shared/include/grspw_pkt.h new file mode 100644 index 0000000000..c52e2a1666 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/grspw_pkt.h @@ -0,0 +1,599 @@ +/* + * GRSPW/GRSPW2 SpaceWire Kernel Library Interface + * + * COPYRIGHT (c) 2011 + * 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 __GRSPW_PKT_H__ +#define __GRSPW_PKT_H__ + +struct grspw_pkt; + +/* Maximum number of GRSPW devices supported by driver */ +#define GRSPW_MAX 32 + +/* Weak overridable variable the user can use to define the worker-task + * priority (0..255) or to disable (-1) the creation of the worker-task + * and the message queue to save space */ +extern int grspw_work_task_priority; + +#ifndef GRSPW_PKT_FLAGS +#define GRSPW_PKT_FLAGS +/*** TX Packet flags ***/ + +/* Enable IRQ generation */ +#define TXPKT_FLAG_IE 0x0040 + +/* Enable Header CRC generation (if CRC is available in HW) + * Header CRC will be appended (one byte at end of header) + */ +#define TXPKT_FLAG_HCRC 0x0100 + +/* Enable Data CRC generation (if CRC is available in HW) + * Data CRC will be appended (one byte at end of packet) + */ +#define TXPKT_FLAG_DCRC 0x0200 + +/* Control how many bytes the beginning of the Header + * the CRC should not be calculated for */ +#define TXPKT_FLAG_NOCRC_MASK 0x0000000f +#define TXPKT_FLAG_NOCRC_LEN0 0x00000000 +#define TXPKT_FLAG_NOCRC_LEN1 0x00000001 +#define TXPKT_FLAG_NOCRC_LEN2 0x00000002 +#define TXPKT_FLAG_NOCRC_LEN3 0x00000003 +#define TXPKT_FLAG_NOCRC_LEN4 0x00000004 +#define TXPKT_FLAG_NOCRC_LEN5 0x00000005 +#define TXPKT_FLAG_NOCRC_LEN6 0x00000006 +#define TXPKT_FLAG_NOCRC_LEN7 0x00000007 +#define TXPKT_FLAG_NOCRC_LEN8 0x00000008 +#define TXPKT_FLAG_NOCRC_LEN9 0x00000009 +#define TXPKT_FLAG_NOCRC_LENa 0x0000000a +#define TXPKT_FLAG_NOCRC_LENb 0x0000000b +#define TXPKT_FLAG_NOCRC_LENc 0x0000000c +#define TXPKT_FLAG_NOCRC_LENd 0x0000000d +#define TXPKT_FLAG_NOCRC_LENe 0x0000000e +#define TXPKT_FLAG_NOCRC_LENf 0x0000000f + +/* Marks if packet was transmitted or not */ +#define TXPKT_FLAG_TX 0x8000 + +#define TXPKT_FLAG_INPUT_MASK (TXPKT_FLAG_NOCRC_MASK | TXPKT_FLAG_IE | \ + TXPKT_FLAG_HCRC | TXPKT_FLAG_DCRC) + +/* Link Error */ +#define TXPKT_FLAG_LINKERR 0x4000 + +#define TXPKT_FLAG_OUTPUT_MASK (TXPKT_FLAG_LINKERR) + +/*** RX Packet Flags ***/ + +/* Enable IRQ generation */ +#define RXPKT_FLAG_IE 0x0010 + +#define RXPKT_FLAG_INPUT_MASK (RXPKT_FLAG_IE) + +/* Packet was truncated */ +#define RXPKT_FLAG_TRUNK 0x0800 +/* Data CRC error (only valid if RMAP CRC is enabled) */ +#define RXPKT_FLAG_DCRC 0x0400 +/* Header CRC error (only valid if RMAP CRC is enabled) */ +#define RXPKT_FLAG_HCRC 0x0200 +/* Error in End-of-Packet */ +#define RXPKT_FLAG_EEOP 0x0100 +/* Marks if packet was recevied or not */ +#define RXPKT_FLAG_RX 0x8000 + +#define RXPKT_FLAG_OUTPUT_MASK (RXPKT_FLAG_TRUNK | RXPKT_FLAG_DCRC | \ + RXPKT_FLAG_HCRC | RXPKT_FLAG_EEOP) + +/*** General packet flag options ***/ + +/* Translate Hdr and/or Payload address */ +#define PKT_FLAG_TR_DATA 0x1000 +#define PKT_FLAG_TR_HDR 0x2000 +/* All General options */ +#define PKT_FLAG_MASK 0x3000 + +#endif +/* GRSPW RX/TX Packet structure. + * + * - For RX the 'hdr' and 'hlen' fields are not used, they are not written + * by driver. + * + * - The 'pkt_id' field is untouched by driver, it is intended for packet + * numbering or user-custom data. + * + * - The last packet in a list must have 'next' set to NULL. + * + * - data and hdr pointers are written without modification to hardware, + * this means that caller must do address translation to hardware + * address itself. + * + * - the 'flags' field are interpreted differently depending on transfer + * type (RX/TX). See XXPKT_FLAG_* options above. + */ +struct grspw_pkt { + struct grspw_pkt *next; + unsigned int pkt_id; /* User assigned ID */ + unsigned short flags; /* RX/TX Options */ + unsigned char reserved; /* Reserved, must be zero */ + unsigned char hlen; /* Length of Header Buffer */ + unsigned int dlen; /* Length of Data Buffer */ + void *data; /* 4-byte or byte aligned depends on HW */ + void *hdr; /* 4-byte or byte aligned depends on HW */ +}; + +/* GRSPW SpaceWire Packet List */ +struct grspw_list { + struct grspw_pkt *head; + struct grspw_pkt *tail; +}; + +/* SpaceWire Link State */ +typedef enum { + SPW_LS_ERRRST = 0, + SPW_LS_ERRWAIT = 1, + SPW_LS_READY = 2, + SPW_LS_CONNECTING = 3, + SPW_LS_STARTED = 4, + SPW_LS_RUN = 5 +} spw_link_state_t; + +/* Address Configuration */ +struct grspw_addr_config { + /* Ignore address field and put all received packets to first + * DMA channel. + */ + int promiscuous; + + /* Default Node Address and Mask */ + unsigned char def_addr; + unsigned char def_mask; + /* DMA Channel custom Node Address and Mask */ + struct { + char node_en; /* Enable Separate Addr */ + unsigned char node_addr; /* Node address */ + unsigned char node_mask; /* Node address mask */ + } dma_nacfg[4]; +}; + +/* Hardware Support in GRSPW Core */ +struct grspw_hw_sup { + char rmap; /* If RMAP in HW is available */ + char rmap_crc; /* If RMAP CRC is available */ + char rx_unalign; /* RX unaligned (byte boundary) access allowed*/ + char nports; /* Number of Ports (1 or 2) */ + char ndma_chans; /* Number of DMA Channels (1..4) */ + char strip_adr; /* Hardware can strip ADR from packet data */ + char strip_pid; /* Hardware can strip PID from packet data */ + int hw_version; /* GRSPW Hardware Version */ + char reserved[2]; +}; + +struct grspw_core_stats { + int irq_cnt; + int err_credit; + int err_eeop; + int err_addr; + int err_parity; + int err_escape; + int err_wsync; /* only in GRSPW1 */ +}; + +/* grspw_link_ctrl() options */ +#define LINKOPTS_ENABLE 0x0000 +#define LINKOPTS_DISABLE 0x0001 +#define LINKOPTS_START 0x0002 +#define LINKOPTS_AUTOSTART 0x0004 +#define LINKOPTS_DIS_ONERR 0x0008 +/*#define LINKOPTS_TICK_OUT_IRQ 0x0100*//* Enable Tick-out IRQ */ +#define LINKOPTS_IRQ 0x0200 /* Enable Error Link IRQ */ +#define LINKOPTS_MASK 0x020f /* All above options */ + +/* grspw_tc_ctrl() options */ +#define TCOPTS_EN_RXIRQ 0x0001 /* Tick-Out IRQ */ +#define TCOPTS_EN_TX 0x0004 +#define TCOPTS_EN_RX 0x0008 + +/* grspw_rmap_ctrl() options */ +#define RMAPOPTS_EN_RMAP 0x0001 +#define RMAPOPTS_EN_BUF 0x0002 + + +/* grspw_dma_config.flags options */ +#define DMAFLAG_NO_SPILL 0x0001 /* See HW doc DMA-CTRL NS bit */ +#define DMAFLAG_RESV1 0x0002 /* HAS NO EFFECT */ +#define DMAFLAG_STRIP_ADR 0x0004 /* See HW doc DMA-CTRL SA bit */ +#define DMAFLAG_STRIP_PID 0x0008 /* See HW doc DMA-CTRL SP bit */ +#define DMAFLAG_RESV2 0x0010 /* HAS NO EFFECT */ +#define DMAFLAG_MASK (DMAFLAG_NO_SPILL|DMAFLAG_STRIP_ADR|DMAFLAG_STRIP_PID) + +struct grspw_dma_config { + int flags; + + int rxmaxlen; /* RX Max Packet Length */ + int rx_irq_en_cnt; /* Enable RX IRQ every cnt descriptors */ + int tx_irq_en_cnt; /* Enable TX IRQ every cnt descriptors */ +}; + +/* Statistics per DMA channel */ +struct grspw_dma_stats { + /* IRQ Statistics */ + int irq_cnt; /* Number of DMA IRQs generated by channel */ + + /* Descriptor Statistics */ + int tx_pkts; /* Number of Transmitted packets */ + int tx_err_link; /* Number of Transmitted packets with Link Error*/ + int rx_pkts; /* Number of Received packets */ + int rx_err_trunk; /* Number of Received Truncated packets */ + int rx_err_endpkt; /* Number of Received packets with bad ending */ + + /* Diagnostics to help developers sizing their number buffers to avoid + * out-of-buffers or other phenomenons. + */ + int send_cnt_min; /* Minimum number of packets in TX SEND Q */ + int send_cnt_max; /* Maximum number of packets in TX SEND Q */ + int tx_sched_cnt_min; /* Minimum number of packets in TX SCHED Q */ + int tx_sched_cnt_max; /* Maximum number of packets in TX SCHED Q */ + int sent_cnt_max; /* Maximum number of packets in TX SENT Q */ + int tx_work_cnt; /* Times the work thread processed TX BDs */ + int tx_work_enabled; /* No. RX BDs enabled by work thread */ + + int ready_cnt_min; /* Minimum number of packets in RX READY Q */ + int ready_cnt_max; /* Maximum number of packets in RX READY Q */ + int rx_sched_cnt_min; /* Minimum number of packets in RX SCHED Q */ + int rx_sched_cnt_max; /* Maximum number of packets in RX SCHED Q */ + int recv_cnt_max; /* Maximum number of packets in RX RECV Q */ + int rx_work_cnt; /* Times the work thread processed RX BDs */ + int rx_work_enabled; /* No. RX BDs enabled by work thread */ +}; + +extern void grspw_initialize_user( + /* Callback every time a GRSPW device is found. Args: DeviceIndex */ + void *(*devfound)(int), + /* Callback every time a GRSPW device is removed. Args: + * int = DeviceIndex + * void* = Return Value from devfound() + */ + void (*devremove)(int,void*) + ); +extern int grspw_dev_count(void); +extern void *grspw_open(int dev_no); +extern void grspw_close(void *d); +extern void grspw_hw_support(void *d, struct grspw_hw_sup *hw); +extern void grspw_stats_read(void *d, struct grspw_core_stats *sts); +extern void grspw_stats_clr(void *d); + +/* Set and Read current node address configuration. The dma_nacfg[N] field + * represents the configuration for DMA Channel N. + * + * Set cfg->promiscous to -1 in order to only read current configuration. + */ +extern void grspw_addr_ctrl(void *d, struct grspw_addr_config *cfg); + +/*** Link Control interface ***/ +/* Read Link State */ +extern spw_link_state_t grspw_link_state(void *d); +/* options [in/out]: set to -1 to only read current config + * + * CLKDIV register contain: + * bits 7..0 : Clock Div RUN (only run-state) + * bits 15..8 : Clock Div During Startup (all link states except run-state) + */ +extern void grspw_link_ctrl(void *d, int *options, int *clkdiv); +/* Read the current value of the status register */ +extern unsigned int grspw_status(void *d); + +/*** Time Code Interface ***/ +/* Generate Tick-In (increment Time Counter, Send Time Code) */ +extern void grspw_tc_tx(void *d); +/* Control Timcode settings of core */ +extern void grspw_tc_ctrl(void *d, int *options); +/* Assign ISR Function to TimeCode RX IRQ */ +extern void grspw_tc_isr(void *d, void (*tcisr)(void *data, int tc), void *data); +/* Read/Write TCTRL and TIMECNT. Write if not -1, always read current value + * TCTRL = bits 7 and 6 + * TIMECNT = bits 5 to 0 + */ +extern void grspw_tc_time(void *d, int *time); + +/*** RMAP Control Interface ***/ +/* Set (not -1) and/or read RMAP options. */ +extern int grspw_rmap_ctrl(void *d, int *options, int *dstkey); +extern void grspw_rmap_support(void *d, char *rmap, char *rmap_crc); + +/*** SpW Port Control Interface ***/ + +/* Select port, if + * -1=The current selected port is returned + * 0=Port 0 + * 1=Port 1 + * Other positive values=Both Port0 and Port1 + */ +extern int grspw_port_ctrl(void *d, int *port); +/* Returns Number ports available in hardware */ +extern int grspw_port_count(void *d); +/* Returns the current active port */ +extern int grspw_port_active(void *d); + +/*** DMA Interface ***/ +extern void *grspw_dma_open(void *d, int chan_no); +extern void grspw_dma_close(void *c); + +extern int grspw_dma_start(void *c); +extern void grspw_dma_stop(void *c); + +/* Schedule List of packets for transmission at some point in + * future. + * + * 1. Move transmitted packets to SENT List (SCHED->SENT) + * 2. Add the requested packets to the SEND List (USER->SEND) + * 3. Schedule as many packets as possible for transmission (SEND->SCHED) + * + * Call this function with pkts=NULL to just do step 1 and 3. This may be + * required in Polling-mode. + * + * The above steps 1 and 3 may be skipped by setting 'opts': + * bit0 = 1: Skip Step 1. + * bit1 = 1: Skip Step 3. + * Skipping both step 1 and 3 may be usefull when IRQ is enabled, then + * the work queue will be totaly responsible for handling descriptors. + * + * The fastest solution in retreiving sent TX packets and sending new frames + * is to call: + * A. grspw_dma_tx_reclaim(opts=0) + * B. grspw_dma_tx_send(opts=1) + * + * NOTE: the TXPKT_FLAG_TX flag must not be set. + * + * Return Code + * -1 Error + * 0 Successfully added pkts to send/sched list + * 1 DMA stopped. No operation. + */ +extern int grspw_dma_tx_send(void *c, int opts, struct grspw_list *pkts, int count); + +/* Reclaim TX packet buffers that has previously been scheduled for transmission + * with grspw_dma_tx_send(). + * + * 1. Move transmitted packets to SENT List (SCHED->SENT) + * 2. Move all SENT List to pkts list (SENT->USER) + * 3. Schedule as many packets as possible for transmission (SEND->SCHED) + * + * The above steps 1 may be skipped by setting 'opts': + * bit0 = 1: Skip Step 1. + * bit1 = 1: Skip Step 3. + * + * The fastest solution in retreiving sent TX packets and sending new frames + * is to call: + * A. grspw_dma_tx_reclaim(opts=2) (Skip step 3) + * B. grspw_dma_tx_send(opts=1) (Skip step 1) + * + * Return Code + * -1 Error + * 0 Successful. pkts list filled with all packets from sent list + * 1 Same as 0, but indicates that DMA stopped + */ +extern int grspw_dma_tx_reclaim(void *c, int opts, struct grspw_list *pkts, int *count); + +/* Get current number of Packets in respective TX Queue. */ +extern void grspw_dma_tx_count(void *c, int *send, int *sched, int *sent); + +#define GRSPW_OP_AND 0 +#define GRSPW_OP_OR 1 +/* Block until ready_cnt or fewer packets are Queued in "Send and Scheduled" Q, + * op (AND or OR), sent_cnt or more packet "have been sent" (Sent Q) condition + * is met. + * If a link error occurs and the Stop on Link error is defined, this function + * will also return to caller. + * The timeout argument is used to return after timeout ticks, regardless of + * the other conditions. If timeout is zero, the function will wait forever + * until the condition is satisfied. + * + * NOTE: if IRQ of TX descriptors are not enabled conditions are never + * checked, this may hang infinitely unless a timeout has been specified + * + * Return Code + * -1 Error + * 0 Returing to caller because specified conditions are now fullfilled + * 1 DMA stopped + * 2 Timeout, conditions are not met + */ +extern int grspw_dma_tx_wait(void *c, int send_cnt, int op, int sent_cnt, int timeout); + +/* Get received RX packet buffers that has previously been scheduled for + * reception with grspw_dma_rx_prepare(). + * + * 1. Move Scheduled packets to RECV List (SCHED->RECV) + * 2. Move all RECV packet to the callers list (RECV->USER) + * 3. Schedule as many free packet buffers as possible (READY->SCHED) + * + * The above steps 1 may be skipped by setting 'opts': + * bit0 = 1: Skip Step 1. + * bit1 = 1: Skip Step 3. + * + * The fastest solution in retreiving received RX packets and preparing new + * packet buffers for future receive, is to call: + * A. grspw_dma_rx_recv(opts=2, &recvlist) (Skip step 3) + * B. grspw_dma_rx_prepare(opts=1, &freelist) (Skip step 1) + * + * Return Code + * -1 Error + * 0 Successfully filled pkts list with packets from recv list. + * 1 DMA stopped + */ +extern int grspw_dma_rx_recv(void *c, int opts, struct grspw_list *pkts, int *count); + +/* Add more RX packet buffers for future for reception. The received packets + * can later be read out with grspw_dma_rx_recv(). + * + * 1. Move Received packets to RECV List (SCHED->RECV) + * 2. Add the "free/ready" packet buffers to the READY List (USER->READY) + * 3. Schedule as many packets as possible (READY->SCHED) + * + * The above steps 1 may be skipped by setting 'opts': + * bit0 = 1: Skip Step 1. + * bit1 = 1: Skip Step 3. + * + * The fastest solution in retreiving received RX packets and preparing new + * packet buffers for future receive, is to call: + * A. grspw_dma_rx_recv(opts=2, &recvlist) (Skip step 3) + * B. grspw_dma_rx_prepare(opts=1, &freelist) (Skip step 1) + * + * Return Code + * -1 Error + * 0 Successfully added packet buffers from pkt list into the ready queue + * 1 DMA stopped + */ +extern int grspw_dma_rx_prepare(void *c, int opts, struct grspw_list *pkts, int count); + +/* Get current number of Packets in respective RX Queue. */ +extern void grspw_dma_rx_count(void *c, int *ready, int *sched, int *recv); + +/* Block until recv_cnt or more packets are Queued in RECV Q, op (AND or OR), + * ready_cnt or fewer packet buffers are available in the "READY and Scheduled" Q, + * condition is met. + * If a link error occurs and the Stop on Link error is defined, this function + * will also return to caller, however with an error. + * The timeout argument is used to return after timeout ticks, regardless of + * the other conditions. If timeout is zero, the function will wait forever + * until the condition is satisfied. + * + * NOTE: if IRQ of TX descriptors are not enabled conditions are never + * checked, this may hang infinitely unless a timeout has been specified + * + * Return Code + * -1 Error + * 0 Returing to caller because specified conditions are now fullfilled + * 1 DMA stopped + * 2 Timeout, conditions are not met + */ +extern int grspw_dma_rx_wait(void *c, int recv_cnt, int op, int ready_cnt, int timeout); + +extern int grspw_dma_config(void *c, struct grspw_dma_config *cfg); +extern void grspw_dma_config_read(void *c, struct grspw_dma_config *cfg); + +extern void grspw_dma_stats_read(void *c, struct grspw_dma_stats *sts); +extern void grspw_dma_stats_clr(void *c); + +/*** GRSPW SpaceWire Packet List Handling Routines ***/ + +static inline void grspw_list_clr(struct grspw_list *list) +{ + list->head = NULL; + list->tail = NULL; +} + +static inline int grspw_list_is_empty(struct grspw_list *list) +{ + return (list->head == NULL); +} + +/* Return Number of entries in list */ +static inline int grspw_list_cnt(struct grspw_list *list) +{ + struct grspw_pkt *lastpkt = NULL, *pkt = list->head; + int cnt = 0; + while ( pkt ) { + cnt++; + lastpkt = pkt; + pkt = pkt->next; + } + if ( lastpkt && (list->tail != lastpkt) ) + return -1; + return cnt; +} + +static inline void +grspw_list_append(struct grspw_list *list, struct grspw_pkt *pkt) +{ + pkt->next = NULL; + if ( list->tail == NULL ) { + list->head = pkt; + } else { + list->tail->next = pkt; + } + list->tail = pkt; +} + +static inline void +grspw_list_prepend(struct grspw_list *list, struct grspw_pkt *pkt) +{ + pkt->next = list->head; + if ( list->head == NULL ) { + list->tail = pkt; + } + list->head = pkt; +} + +static inline void +grspw_list_append_list(struct grspw_list *list, struct grspw_list *alist) +{ + alist->tail->next = NULL; + if ( list->tail == NULL ) { + list->head = alist->head; + } else { + list->tail->next = alist->head; + } + list->tail = alist->tail; +} + +static inline void +grspw_list_prepend_list(struct grspw_list *list, struct grspw_list *alist) +{ + if ( list->head == NULL ) { + list->tail = alist->tail; + alist->tail->next = NULL; + } else { + alist->tail->next = list->head; + } + list->head = alist->head; +} + +/* Remove dlist (delete-list) from head of list */ +static inline void +grspw_list_remove_head_list(struct grspw_list *list, struct grspw_list *dlist) +{ + list->head = dlist->tail->next; + if ( list->head == NULL ) { + list->tail = NULL; + } + dlist->tail->next = NULL; +} + +/* Take A number of entries from head of list 'list' and put the entires + * to rlist (result list). + */ +static inline int +grspw_list_take_head_list(struct grspw_list *list, struct grspw_list *rlist, int max) +{ + int cnt; + struct grspw_pkt *pkt, *last; + + pkt = list->head; + + if ( (max < 1) || (pkt == NULL) ) { + grspw_list_clr(rlist); + return 0; + } + + cnt = 0; + rlist->head = pkt; + last = pkt; + while ((cnt < max) && pkt) { + last = pkt; + pkt = pkt->next; + cnt++; + } + rlist->tail = last; + grspw_list_remove_head_list(list, rlist); + return cnt; +} + +#endif diff --git a/c/src/lib/libbsp/sparc/shared/include/grspw_rasta.h b/c/src/lib/libbsp/sparc/shared/include/grspw_rasta.h deleted file mode 100644 index 0e4e5bee4d..0000000000 --- a/c/src/lib/libbsp/sparc/shared/include/grspw_rasta.h +++ /dev/null @@ -1,49 +0,0 @@ - /* - * Macros used for RASTA PCI GRSPW controller - * - * COPYRIGHT (c) 2006. - * Gaisler Research - * - * 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_RASTA_H__ -#define __GRSPW_RASTA_H__ - -#include <grspw.h> - -#ifdef __cplusplus -extern "C" { -#endif - -/* Register GRSPW Driver - * bus = &amba_conf for LEON3 - * - * Memory setup: - * ram_base = 128k aligned pointer to memory (as the CPU sees it) - */ - -int grspw_rasta_register( - amba_confarea_type *bus, - unsigned int ram_base - ); - -/* This function must be called on GRSPW interrupt. Called from the - * PCI interrupt handler. irq = AMBA IRQ MASK assigned to the GRSPW device, - * is found by reading pending register on IRQMP connected to GRSPW - * device. - * - */ -void grspw_rasta_interrupt_handler(unsigned int status); - -/* callback to register interrupt handler */ -extern void (*grspw_rasta_int_reg)(void *handler, int irq, void *arg); - -#ifdef __cplusplus -} -#endif - -#endif /* __GRSPW_RASTA_PCI_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..f126f2f267 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/grspw_router.h @@ -0,0 +1,93 @@ +#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..cfd23dd525 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/grtc.h @@ -0,0 +1,157 @@ +/* GRTC Telecommand (TC) decoder driver interface + * + * -------------------------------------------------------------------------- + * -- This file is a part of GAISLER RESEARCH source code. + * -- Copyright (C) 2009, Gaisler Research AB - all rights reserved. + * -- + * -- ANY USE OR REDISTRIBUTION IN PART OR IN WHOLE MUST BE HANDLED IN + * -- ACCORDANCE WITH THE GAISLER LICENSE AGREEMENT AND MUST BE APPROVED + * -- IN ADVANCE IN WRITING. + * -- + * -- BY DEFAULT, DISTRIBUTION OR DISCLOSURE IS NOT PERMITTED. + * -------------------------------------------------------------------------- + * + */ + +#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..8eca52819d --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/grtm.h @@ -0,0 +1,246 @@ +/* GRTM Telemetry (TM) driver interface + * + * -------------------------------------------------------------------------- + * -- This file is a part of GAISLER RESEARCH source code. + * -- Copyright (C) 2009, Gaisler Research AB - all rights reserved. + * -- + * -- ANY USE OR REDISTRIBUTION IN PART OR IN WHOLE MUST BE HANDLED IN + * -- ACCORDANCE WITH THE GAISLER LICENSE AGREEMENT AND MUST BE APPROVED + * -- IN ADVANCE IN WRITING. + * -- + * -- BY DEFAULT, DISTRIBUTION OR DISCLOSURE IS NOT PERMITTED. + * -------------------------------------------------------------------------- + * + */ + +#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/i2cmst.h b/c/src/lib/libbsp/sparc/shared/include/i2cmst.h index 21780ee143..36febc4b4a 100644 --- a/c/src/lib/libbsp/sparc/shared/include/i2cmst.h +++ b/c/src/lib/libbsp/sparc/shared/include/i2cmst.h @@ -50,26 +50,6 @@ typedef struct gr_i2cmst_regs { #define GRI2C_STATUS_IDLE 0x00000000 -/* The OC I2C core will perform a write after a start unless the RD bit - in the command register has been set. Since the rtems framework has - a send_start function we buffer that command and use it when the first - data is written. The START is buffered in the sendstart member below */ -typedef struct gr_i2cmst_prv { - gr_i2cmst_regs_t *reg_ptr; - unsigned int sysfreq; /* System clock frequency in kHz */ - unsigned char sendstart; /* START events are buffered here */ - /* rtems_irq_number irq_number; */ - /* rtems_id irq_sema_id; */ -} gr_i2cmst_prv_t; - -typedef struct gr_i2cmst_desc { - rtems_libi2c_bus_t bus_desc; - gr_i2cmst_prv_t prv; -} gr_i2cmst_desc_t; - -/* Scans for I2CMST core and initalizes i2c library */ -rtems_status_code leon_register_i2c(amba_confarea_type *abus); - #ifdef __cplusplus } #endif diff --git a/c/src/lib/libbsp/sparc/shared/include/network_interface_add.h b/c/src/lib/libbsp/sparc/shared/include/network_interface_add.h new file mode 100644 index 0000000000..50deb696d8 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/network_interface_add.h @@ -0,0 +1,48 @@ +/* Network interface register help function + * + * COPYRIGHT (c) 2008. + * Aeroflex Gaisler AB. + * + * This function adds a network interface to the + * rtems_bsdnet_config.ifconfig linked list of interfaces. + * The interface configuration is taken from the user defined + * array interface_configs. This function is useful for PnP + * systems when an unknown number of interfaces are available. + * + * 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 __NETWORK_INTERFACE_ADD_H__ +#define __NETWORK_INTERFACE_ADD_H__ + +#include <rtems/rtems_bsdnet.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* Interface configuration description */ +struct ethernet_config { + char *ip_addr; /* IP address */ + char *ip_netmask; /* IP Netmask */ + char eth_adr[6]; /* Ethernet hardware MAC address */ +}; + +/* Array with configurations for all interfaces in the system + * Must be defined by the user. + */ +extern struct ethernet_config interface_configs[]; + +/* Routine adding interface to rtems_bsdnet_config.ifconfig linked + * list of interfaces. + */ +int network_interface_add(struct rtems_bsdnet_ifconfig *interface); + +#ifdef __cplusplus +} +#endif + +#endif /* _RTEMS_NETWORKCONFIG_H_ */ diff --git a/c/src/lib/libbsp/sparc/shared/include/occan.h b/c/src/lib/libbsp/sparc/shared/include/occan.h index 12ec42a01c..13d707d761 100644 --- a/c/src/lib/libbsp/sparc/shared/include/occan.h +++ b/c/src/lib/libbsp/sparc/shared/include/occan.h @@ -1,7 +1,7 @@ /* Gaisler wrapper to OpenCores CAN, driver interface * - * COPYRIGHT (c) 2007. - * Gaisler Research. + * COPYRIGHT (c) 2008. + * Aeroflex Gaisler. * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at @@ -11,10 +11,8 @@ */ -#ifndef __OCCAN_H__ -#define __OCCAN_H__ - -#include <ambapp.h> +#ifndef __OCCAN_DRIVER_H__ +#define __OCCAN_DRIVER_H__ #ifdef __cplusplus extern "C" { @@ -147,7 +145,7 @@ struct occan_afilter { #define OCCAN_BLK_MODE_RX 0x1 #define OCCAN_BLK_MODE_TX 0x2 -int occan_register(amba_confarea_type *bus); +void occan_register_drv (void); #define OCCAN_SPEED_500K 500000 diff --git a/c/src/lib/libbsp/sparc/shared/include/occan_pci.h b/c/src/lib/libbsp/sparc/shared/include/occan_pci.h deleted file mode 100644 index 2f46293af2..0000000000 --- a/c/src/lib/libbsp/sparc/shared/include/occan_pci.h +++ /dev/null @@ -1,42 +0,0 @@ - /* - * OC_CAN controller via PCI - driver interface - * - * COPYRIGHT (c) 2007. - * Gaisler Research - * - * 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 __OCCAN_PCI_H__ -#define __OCCAN_PCI_H__ - -#include <occan.h> - -#ifdef __cplusplus -extern "C" { -#endif - -/* Register OC_CAN driver - * bus = pointer to AMBA bus description used to search for OC_CAN contrller(s). - */ - -int occan_pci_register(amba_confarea_type *bus); - -/* This function must be called on OC_CAN interrupt. Called from the - * PCI interrupt handler. irq = AMBA IRQ assigned to the OC_CAN device, - * is found by reading pending register on IRQMP connected to the OC_CAN - * device. - * - */ -void occanpci_interrupt_handler(int irq, void *arg); - -extern void (*occan_pci_int_reg)(void *handler, int irq, void *arg); - -#ifdef __cplusplus -} -#endif - -#endif /* __OCCAN_PCI_H__ */ diff --git a/c/src/lib/libbsp/sparc/shared/include/rmap.h b/c/src/lib/libbsp/sparc/shared/include/rmap.h new file mode 100644 index 0000000000..c18dafed5e --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/rmap.h @@ -0,0 +1,292 @@ +/* RMAP stack and RMAP driver interface + * + * COPYRIGHT (c) 2009 + * Aeroflex Gaisler. + * + * 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 __RMAP_H__ +#define __RMAP_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/*** RMAP Driver interface ***/ + +#define PKT_OPTION_HDR_CRC 0x1 /* Enable Header CRC by Hardware */ +#define PKT_OPTION_DATA_CRC 0x2 /* Enable Data CRC by Hardware */ +/* skip len of header when calculating CRC by hardware */ +#define PKT_OPTION_HDR_CRC_SKIPLEN(len) (((len) & 0xf) << 8) +#define PKT_OPTION_HDR_CRC_SKIPLEN_MASK 0xf00 + +/* RMAP SpW packet */ +struct rmap_spw_pkt { + int options; /* Data CRC | Header CRC */ + int hlen; + unsigned char *hdr; + int dlen; + unsigned char *data; + int reserved[2]; /* May be used internally by driver layer */ +}; + +struct rmap_drv_ops { + int (*init)(void *cookie); /* Initialize driver */ + int (*ioctl)(void *cookie, int command, void *arg); /* Configure driver */ + int (*send)(void *cookie, struct rmap_spw_pkt *pkt); /* Schedule one packet for transmission */ + int (*recv)(void *cookie, struct rmap_spw_pkt *pkt); /* Receive one packet */ +}; + +/* RMAP driver description */ +struct rmap_drv { + struct rmap_drv_ops ops; + void *cookie; +}; + +#define RMAP_TIMEOUT_SET_RTIME 0x01 +#define RMAP_TIMEOUT_SET_WTIME 0x02 +struct rmap_drv_timeout { + unsigned int options; + unsigned int rtimeout; + unsigned int wtimeout; +}; + +/* RMAP driver ioctl commands */ +#define RMAP_DRV_IOCTL_START 1 /* Start operation, packets will be sent/received after this command */ +#define RMAP_DRV_IOCTL_STOP 2 /* Stop operation, no more packets will be sent before next START */ +#define RMAP_DRV_IOCTL_BLOCK 3 /* Set RX/TX blocking */ +#define RMAP_DRV_IOCTL_TIMEOUT 4 /* Set timeout in blocing mode */ +#define RMAP_DRV_IOCTL_GET_CAP 5 /* Get capabilities of the driver/hardware */ +#define RMAP_IOCTL_GET_CONFIG 6 /* Get Stack Configuration */ + +/* Capabilities */ +#define DRV_CAP_HDR_CRC 0x1 /* Hardware supports HEADER CRC generation */ +#define DRV_CAP_DATA_CRC 0x2 /* Hardware supports DATA CRC generation */ + + +/*** RMAP Stack interface ***/ + +/* Function that will generate path addressing. + * + * \param cookie Identifies RMAP stack, + * \param dir Direction (0=to dst, 1=from dst) + * \param srcadr Source address (the SpW address of the interface used) + * \param dstadr Destination address to translate. Note that it is actually + * a "unsigned short" given here, so this is rather a Node-ID. + * \param buf Where path address is put + * \param len On calling it indicates max length. On return it must be set + * reflect the number of bytes was written to buf. + */ +typedef int (*rmap_route_t)(void *cookie, int dir, int srcadr, int dstadr, void *buf, int *len); + +struct rmap_config { + rmap_route_t route_func; /* Function that will generate path addressing */ + int tid_msb; /* 8 most significant bits in TID. + * Set to -1 for normal operation using + * all bits in TID for sequence counting. + */ + int spw_adr; /* The SpW Address of the SpW interface used */ + struct rmap_drv *drv; /* Driver used for transmission */ + int max_rx_len; /* Maximum data length of received packets */ + int max_tx_len; /* Maximum data length of transmitted packets */ + int thread_safe; /* Set this to non-zero to enable the RMAP stack to create a + * semaphore used to protect from multiple tasks entering the + * transfer function(s) of the stack at the same time */ +}; + +struct rmap_command { + char type; /* READ/WRITE/MODIFY ... */ + unsigned char dstkey; /* Destination key */ + unsigned char status; /* Error/Status response. 0 if no response is expected */ + unsigned short dstadr; /* Destination address */ + unsigned short tid; /* TID assigned to packet */ + unsigned long long address; /* READ/WRITE address, 32-bit + 8-bit extended */ + union { + struct { + unsigned int length; /* 24-bit data length */ + unsigned char *data; /* Data bytes, NOTE that 1 byte must be available + * for the stack to write the DATA CRC in the end + * of the data buffer. Stack write data[length] = CRC + */ + } write; + struct { + unsigned int length; /* 24-bit length */ + unsigned int datalength;/* Response 24-bit length, may be different from requested length */ + unsigned int *data; /* Response data is stored in this buffer */ + } read; + struct { + unsigned int length; /* length 1,2 or 4 valid */ + unsigned int data; /* 1,2 or 4 bytes data */ + unsigned int mask; /* 1,2 or 4 bytes data mask */ + unsigned int oldlength; /* Response, old data length */ + unsigned int olddata; /* Response, old data before write */ + } read_m_write; + } data; +}; + +struct rmap_command_write { + char type; /* WRITE (Increment, Single, ACK, Verify) */ + unsigned char dstkey; /* Destination key */ + unsigned char status; /* Status response, set if ACK, non-zero indicates error */ + unsigned short dstadr; /* Destination address */ + unsigned short tid; /* TID assigned to packet */ + unsigned long long address; /* READ/WRITE address */ + unsigned int length; /* 24-bit data length */ + unsigned char *data; /* Data bytes, NOTE that 1 byte must be available + * for the stack to write the DATA CRC in the end + * of the data buffer. Stack write data[length] = CRC + */ +}; + +struct rmap_command_read { + char type; /* READ (Single, Increment) */ + unsigned char dstkey; /* Destination key */ + unsigned char status; /* Status response, always set, non-zero indicates error */ + unsigned short dstadr; /* Destination address */ + unsigned short tid; /* TID assigned to packet */ + unsigned long long address; /* READ/WRITE address */ + unsigned int length; /* 24-bit data length */ + unsigned int datalength; /* Response 24-bit length, may be different from requested length */ + unsigned int *data; /* Response data is stored in this buffer */ +}; + +struct rmap_command_rmw { + char type; /* RMAP_CMD_RMWI */ + unsigned char dstkey; /* Destination key */ + unsigned char status; /* Status response, always set, non-zero indicates error */ + unsigned short dstadr; /* Destination address */ + unsigned short tid; /* TID assigned to packet */ + unsigned long long address; /* READ/WRITE address */ + unsigned int length; /* length 1,2 or 4 valid */ + unsigned int data; /* 1,2 or 4 bytes data */ + unsigned int mask; /* 1,2 or 4 bytes data mask */ + unsigned int oldlength; /* Response, old data length */ + unsigned int olddata; /* Response, old data before write */ +}; + +/* Command definition. + * + * R = READ + * W = WRITE + * M = MODIFY + * S = SINGLE + * I = INCREMENTING ADDRESSES + * V = VERIFY + * A = ACKNOWLEDGE + */ +enum { + RMAP_CMD_UNUSED1 = 0x0, /* Not used */ + RMAP_CMD_UNUSED2 = 0x1, /* Not used */ + RMAP_CMD_RS = 0x2, /* Read single address */ + RMAP_CMD_RI = 0x3, /* Read incrementing addresses */ + RMAP_CMD_UNUSED3 = 0x4, /* Not used */ + RMAP_CMD_UNUSED4 = 0x5, /* Not used */ + RMAP_CMD_UNUSED5 = 0x6, /* Not used */ + RMAP_CMD_RMWI = 0x7, /* Read-Modify-Write incrementing addresses */ + RMAP_CMD_WS = 0x8, /* Write, single address, don't verify before writing, no acknowledge */ + RMAP_CMD_WI = 0x9, /* Write, incrementing addresses, don't verify before writing, no acknowledge */ + RMAP_CMD_WSA = 0xa, /* Write, single address, don't verify before writing, send acknowledge */ + RMAP_CMD_WIA = 0xb, /* Write, incrementing addresses, don't verify before writing, send acknowledge */ + RMAP_CMD_WSV = 0xc, /* Write, single address, verify before writing, no acknowledge */ + RMAP_CMD_WIV = 0xd, /* Write, incrementing addresses, verify before writing, no acknowledge */ + RMAP_CMD_WSVA = 0xe, /* Write, single address, verify before writing, send acknowledge */ + RMAP_CMD_WIVA = 0xf, /* Write, incrementing addresses, verify before writing, send acknowledge */ +}; + +#define RMAP_CMD_INCREMENT 0x1 +#define RMAP_CMD_ACKNOWLEDGE 0x2 +#define RMAP_CMD_VERIFY 0x4 +#define RMAP_CMD_WRITE 0x8 + +/* Initialize the stack + * + * \param route_func Function that will generate path addressing + * \param drv Set driver used for transmission + */ +void *rmap_init(struct rmap_config *config); + +/* Enables the RMAP stack to send/receive commands */ + +/* Disables the RMAP stack to send/receive commands */ + +/* Configure stack (blocking etc)*/ +extern int rmap_ioctl(void *cookie, int command, void *arg); + +/* Send a command and optionally block waiting for response. + * + * The transaction ID is incremented + * + * \param cmd RMAP command to transmit + * \param resp Response (stack blocking mode only) + * \param tid Pointer to where the Transaction ID is stored (stack non-blocking mode only) + */ +extern int rmap_send(void *cookie, struct rmap_command *cmd); + +#if NOT_IMPLEMENTED +/** OPTIONAL PART 1 **/ + +/* Wait for response on a previously sent RMAP command, the command was sent + * using rmap_send and was assigned a transaction ID (tid). The TID is used' + * to separate different RMAP commands from each other. + * + * This is a blocking call. + * + * Only available when, + * - stack in non-blocking mode + * - driver in blocking mode. + * + * \param timeout Timeout in ticks. Currently NOT used. + * \param resp Response on the Transaction ID + * \param tid Transaction ID + */ +extern int rmap_resp_wait(void *cookie, int timeout, struct rmap_command *resp, unsigned int tid); + +/** OPTIONAL PART 2 **/ + +/* Check for response on a previously sent RMAP command, the command was sent + * using rmap_send and was assigned a transaction ID (tid). The TID is used' + * to separate different RMAP commands from each other. + * + * This function will never block. + * + * Only available when, + * - stack in non-blocking mode + * - driver in non-blocking mode. + * + * \param resp Response on the Transaction ID + * \param tid Transaction ID + */ +extern int rmap_resp_poll(void *cookie, struct rmap_command *resp, unsigned int tid); +#endif + +/* rmap_ioctl commands */ +#define RMAP_IOCTL_START 0 /* Start RMAP stack, enables stack to send/receive packets */ +#define RMAP_IOCTL_STOP 1 /* Stop RMAP stack, no packets will be sent/received */ +#define RMAP_IOCTL_BLOCK 2 /* Set blocking mode */ +#define RMAP_IOCTL_TIMEOUT 3 /* Set timeout for command responses in number of ticks */ + +#define RMAP_IOCTRL_BLOCK_ALL 0 + + +/*** RMAP Stack help interface ***/ + +/* Parse a Write command */ + +/* Return the CRC of a RMAP data buffer of length len */ +extern unsigned char rmap_crc_calc(unsigned char *data, unsigned int len); + +/* A handy write function using the RMAP stack */ +extern int rmap_write(void *cookie, void *dst, void *buf, int length, int dstadr, int dstkey); + +/* A handy read function using the RMAP stack */ +extern int rmap_read(void *cookie, void *src, void *buf, int length, int dstadr, int dstkey); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/lib/libbsp/sparc/shared/include/rmap_drv_grspw.h b/c/src/lib/libbsp/sparc/shared/include/rmap_drv_grspw.h new file mode 100644 index 0000000000..1ba71db2b0 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/rmap_drv_grspw.h @@ -0,0 +1,31 @@ +/* GRSPW RMAP driver, used by RMAP stack to talk to the GRSPW driver. + * + * COPYRIGHT (c) 2009 + * Aeroflex Gaisler. + * + * 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 __RMAP_DRV_GRSPW_H__ +#define __RMAP_DRV_GRSPW_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct rmap_drv_ops rmap_grspw_ops; + +struct rmap_drv_grspw_config { + int fd; +}; + +void *rmap_drv_grspw_init(struct rmap_drv_grspw_config *config); + +#ifdef __cplusplus +} +#endif + +#endif 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..048890d4b9 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/satcan.h @@ -0,0 +1,147 @@ +/* + * Header file for RTEMS SATCAN FPGA driver + * + * -------------------------------------------------------------------------- + * -- This file is a part of GAISLER RESEARCH source code. + * -- Copyright (C) 2008, Gaisler Research AB - all rights reserved. + * -- + * -- ANY USE OR REDISTRIBUTION IN PART OR IN WHOLE MUST BE HANDLED IN + * -- ACCORDANCE WITH THE GAISLER LICENSE AGREEMENT AND MUST BE APPROVED + * -- IN ADVANCE IN WRITING. + * -- + * -- BY DEFAULT, DISTRIBUTION OR DISCLOSURE IS NOT PERMITTED. + * -------------------------------------------------------------------------- + * + */ + +#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..d298f5be68 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/spictrl.h @@ -0,0 +1,127 @@ +/* + * SPICTRL SPI Driver interface. + * + * COPYRIGHT (c) 2009. + * Gaisler Research + * + * 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..5b50b43125 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/spwcuc.h @@ -0,0 +1,189 @@ +/* SPWCUC - SpaceWire - CCSDS unsegmented Code Transfer Protocol GRLIB core + * register 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 __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/include/tlib.h b/c/src/lib/libbsp/sparc/shared/include/tlib.h new file mode 100644 index 0000000000..7b253b7813 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/tlib.h @@ -0,0 +1,177 @@ +/* + * Timer Library (TLIB) + * + * The Library rely on timer drivers, the timer presented by the + * timer driver must look like a down-counter timer, which generates + * interrupt (if configured) when underflown. + * + * If Timer hardware is an up-counter the Timer driver must recalculate + * into values that would match as if it was a down-counter. + * + * COPYRIGHT (c) 2011. + * Aeroflex Gaisler. + * + * 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. + */ + +struct tlib_dev; + +typedef void (*tlib_isr_t)(void *data); + +struct tlib_drv { + /*** Functions ***/ + void (*reset)(struct tlib_dev *hand); + void (*get_freq)( + struct tlib_dev *hand, + unsigned int *basefreq, + unsigned int *tickrate); + int (*set_freq)(struct tlib_dev *hand, unsigned int tickrate); + void (*irq_reg)(struct tlib_dev *hand, tlib_isr_t func, void *data); + void (*irq_unreg)(struct tlib_dev *hand, tlib_isr_t func,void *data); + void (*start)(struct tlib_dev *hand, int once); + void (*stop)(struct tlib_dev *hand); + void (*restart)(struct tlib_dev *hand); + void (*get_counter)(struct tlib_dev *hand, unsigned int *counter); + int (*custom)(struct tlib_dev *hand, int cmd, void *arg); + int (*int_pend)(struct tlib_dev *hand, int ack); +}; + +struct tlib_dev { + struct tlib_dev *next; + char status; /* 0=closed, 1=open, 2=timer started */ + char index; /* Timer Index */ + tlib_isr_t isr_func; + void *isr_data; + struct tlib_drv *drv; +}; + +#ifdef RTEMS_DRVMGR_STARTUP +/* Clock Driver Timer register function. Only used when the TLIB-Clock + * driver is used. A specific Timer is registered as the System Clock + * timer. + */ +extern void Clock_timer_register(int timer_number); +#endif + +/* Register Timer. Called by Timer Drivers in order to register + * a Timer to the Timer Library. The registration order determines + * the Timer Number used in tlib_open() to identify a specific + * Timer. + */ +extern int tlib_dev_reg(struct tlib_dev *newdev); + +/* Allocate a Timer. + * + * A Timer handle is returned identifying the timer in later calls. + */ +extern void *tlib_open(int timer_no); + +/* Close Timer */ +extern void tlib_close(void *hand); + +/* Returns Number of Timers currently registered to Timer Library */ +extern int tlib_ntimer(void); + +static inline void tlib_reset(void *hand) +{ + struct tlib_dev *dev = hand; + + dev->drv->reset(dev); +} +/* Get Frequencies: + * - Base Frequency (unchangable base freq rate of timer, prescaler, clkinput) + * - Current Tick Rate [in multiples of Base Frequency] + */ +static inline void tlib_get_freq( + void *hand, + unsigned int *basefreq, + unsigned int *tickrate) +{ + struct tlib_dev *dev = hand; + + dev->drv->get_freq(dev, basefreq, tickrate); +} + +/* Set current Tick Rate in number of "Base-Frequency ticks" */ +static inline int tlib_set_freq(void *hand, unsigned int tickrate) +{ + struct tlib_dev *dev = hand; + + return dev->drv->set_freq(dev, tickrate); +} + +/* Register ISR at Timer ISR */ +static inline void tlib_irq_unregister(void *hand) +{ + struct tlib_dev *dev = hand; + + if ( dev->isr_func ) { + dev->drv->irq_unreg(dev, dev->isr_func, dev->isr_data); + dev->isr_func = NULL; + } +} + +/* Register ISR at Timer ISR */ +static inline void tlib_irq_register(void *hand, tlib_isr_t func, void *data) +{ + struct tlib_dev *dev = hand; + + /* Unregister previous ISR if installed */ + tlib_irq_unregister(hand); + dev->isr_func = func; + dev->isr_data = data; + dev->drv->irq_reg(dev, func, data); +} + +/* Start Timer, ISRs will be generated if enabled. + * + * once determines if timer should restart (=0) on underflow automatically, + * or stop when underflow is reached (=1). + */ +static inline void tlib_start(void *hand, int once) +{ + struct tlib_dev *dev = hand; + + dev->drv->start(dev, once); +} + +/* Stop Timer, no more ISRs will be generated */ +static inline void tlib_stop(void *hand) +{ + struct tlib_dev *dev = hand; + + dev->drv->stop(dev); +} + +/* Restart/Reload Timer, may be usefull if a Watchdog Timer */ +static inline void tlib_restart(void *hand) +{ + struct tlib_dev *dev = hand; + + dev->drv->restart(dev); +} + +/* Get current counter value (since last tick) */ +static inline void tlib_get_counter(void *hand, unsigned int *counter) +{ + struct tlib_dev *dev = hand; + + dev->drv->get_counter(dev, counter); +} + +/* Do a custom operation */ +static inline void tlib_custom(void *hand, int cmd, void *arg) +{ + struct tlib_dev *dev = hand; + + dev->drv->custom(dev, cmd, arg); +} + +static inline int tlib_interrupt_pending(void *hand, int ack) +{ + struct tlib_dev *dev = hand; + + return dev->drv->int_pend(dev, ack); +} 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..f4e72909da --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/irq/genirq.c @@ -0,0 +1,230 @@ +#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/irq/irq-shared.c b/c/src/lib/libbsp/sparc/shared/irq/irq-shared.c new file mode 100644 index 0000000000..99480e451e --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/irq/irq-shared.c @@ -0,0 +1,110 @@ +#include <rtems.h> +#include <bsp.h> +#include <bsp/irq-generic.h> +#include <leon.h> + +static inline void leon_dispatch_irq(int irq) +{ + bsp_interrupt_handler_entry *e = + &bsp_interrupt_handler_table[bsp_interrupt_handler_index(irq)]; + + while (e != NULL) { + (*e->handler)(e->arg); + e = e->next; + } +} + +/* Called directly from IRQ trap handler TRAP[0x10..0x1F] = IRQ[0..15] */ +void LEON_ISR_handler(rtems_vector_number vector) +{ + int irq = LEON_TRAP_SOURCE(vector); + + /* Let BSP fixup and/or handle incomming IRQ */ + irq = leon_irq_fixup(irq); + + leon_dispatch_irq(irq); +} + +/* Initialize interrupts */ +int BSP_shared_interrupt_init(void) +{ + rtems_vector_number vector; + rtems_isr_entry previous_isr; + int sc, i; + + for (i=0; i <= BSP_INTERRUPT_VECTOR_MAX_STD; i++) { + vector = LEON_TRAP_TYPE(i); + rtems_interrupt_catch(LEON_ISR_handler, vector, &previous_isr); + } + + /* Initalize interrupt support */ + sc = bsp_interrupt_initialize(); + if (sc != RTEMS_SUCCESSFUL) + return -1; + + return 0; +} + +/* Callback from bsp_interrupt_initialize() */ +rtems_status_code bsp_interrupt_facility_initialize(void) +{ + return RTEMS_SUCCESSFUL; +} + +/* Spurious IRQ handler */ +void bsp_interrupt_handler_default(rtems_vector_number vector) +{ + printk("Spurious IRQ %d\n", (int)vector); +} + +rtems_status_code bsp_interrupt_vector_enable(rtems_vector_number vector) +{ + rtems_interrupt_level level; + + rtems_interrupt_disable(level); + LEON_Unmask_interrupt((int)vector); + rtems_interrupt_enable(level); + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code bsp_interrupt_vector_disable(rtems_vector_number vector) +{ + rtems_interrupt_level level; + + rtems_interrupt_disable(level); + LEON_Mask_interrupt((int)vector); + rtems_interrupt_enable(level); + + return RTEMS_SUCCESSFUL; +} + +void BSP_shared_interrupt_mask(int irq) +{ + rtems_interrupt_level level; + + rtems_interrupt_disable(level); + + LEON_Mask_interrupt(irq); + + rtems_interrupt_enable(level); +} + +void BSP_shared_interrupt_unmask(int irq) +{ + rtems_interrupt_level level; + + rtems_interrupt_disable(level); + + LEON_Unmask_interrupt(irq); + + rtems_interrupt_enable(level); +} + +void BSP_shared_interrupt_clear(int irq) +{ + /* We don't have to interrupt lock here, because the register is only + * written and self clearing + */ + LEON_Clear_interrupt(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..587dc57de1 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/mem/mctrl.c @@ -0,0 +1,214 @@ +/* Memory Controller driver (FTMTRL, MCTRL) + * + * COPYRIGHT (c) 2008. + * Aeroflex 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. + * + * 2008-12-08, Daniel Hellstrom <daniel@gaisler.com> + * Created + * + */ + +/******************* 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/mem/mctrl_rmap.c b/c/src/lib/libbsp/sparc/shared/mem/mctrl_rmap.c new file mode 100644 index 0000000000..19e85dabff --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/mem/mctrl_rmap.c @@ -0,0 +1,248 @@ +/* Memory Controller driver (FTMTRL, FTSRCTRL, MCTRL) + * + * COPYRIGHT (c) 2008. + * Aeroflex 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. + * + * 2009-11-19, Daniel Hellstrom <daniel@gaisler.com> + * Created from on-chip memory controller driver + * + */ + +/******************* Driver manager interface ***********************/ +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> +#include <drvmgr/ambapp_bus_rmap.h> + +/* This call will take 128 bytes of buffer at stack */ +#define MEMSET(pDev, adr, c, length) pDev->rw_memset(adr, c, length, &pDev->rw_arg) + +#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 */ + + /* Read/Write access operations */ + struct drvmgr_rw_arg rw_arg; + ambapp_rmap_w32 rw_w32; + ambapp_rmap_memset rw_memset; +}; + +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); + +static struct mctrl_ops std_mctrl_ops = +{ + mctrl_set_std +}; + +static struct drvmgr_drv_ops mctrl_ops = +{ + .init = {mctrl_init1,NULL, NULL, NULL}, + .remove = mctrl_remove, + .info = NULL +}; + +static struct amba_dev_id mctrl_ids[] = +{ + {VENDOR_ESA, ESA_MCTRL}, + {VENDOR_GAISLER, GAISLER_FTMCTRL}, + {VENDOR_GAISLER, GAISLER_FTSRCTRL}, + {0, 0} /* Mark end of table */ +}; + +static 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_RMAP, /* Bus Type */ + &mctrl_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + 0, + }, + &mctrl_ids[0] +}; + +void mctrl_rmap_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 Read/Write operations for bus */ + priv->rw_arg.dev = dev; + priv->rw_arg.arg = drvmgr_func_call(dev->parent, AMBAPP_RMAP_RW_ARG, dev, NULL, NULL, NULL); + drvmgr_func_get(dev->parent, AMBAPP_RMAP_W32, (void **)&priv->rw_w32); + drvmgr_func_get(dev->parent, AMBAPP_RMAP_MEMSET, (void **)&priv->rw_memset); + + /* 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-RMAP: Washing 0x%08x-0x%08x\n", start, start+length-1); + + MEMSET(priv, (void *)start, 0, length); + } + } + } + } + + /* Register memory partitions if user wants */ + for (i=0; i<9; i++) { + strcpy(res_name, "partXStart"); + 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, "partXLength"); + res_name[4] = '0' + i; + value = drvmgr_dev_key_get(priv->dev, res_name, KEY_TYPE_INT); + if ( value ) { + length = value->i; + + DBG("MCTRL-RMAP: registering partition %d: 0x%08x-0x%08x\n", i, start, length); + + /* Register partition */ + ambapp_rmap_partition_create(dev, i, start, 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 */ + priv->rw_w32((uint32_t *)&pregs->mcfg[index], (uint32_t)regval, &priv->rw_arg); +} 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..fea8890c72 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/net/greth.c @@ -0,0 +1,1451 @@ +/* + * 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 + +/* #define GRETH_DEBUG_MII */ + +#ifdef GRETH_DEBUG_MII +#define MIIDBG(args...) printk(args) +#else +#define MIIDBG(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 phy_read_access; + int phy_write_access; + 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) +{ + char *tmp; + tmp = calloc(1,2*sz); + tmp = (char *) (((int)tmp+sz) & ~(sz -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) +{ + sc->phy_read_access++; + 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)) { + MIIDBG("greth%d: mii read[%d] OK to %x.%x (0x%08x,0x%08x)\n", + sc->minor, sc->phy_read_access, phy_addr, reg_addr, + sc->regs->ctrl, sc->regs->mdio_ctrl); + + return((sc->regs->mdio_ctrl >> 16) & 0xFFFF); + } else { + printf("greth%d: mii read[%d] failed to %x.%x (0x%08x,0x%08x)\n", + sc->minor, sc->phy_read_access, phy_addr, reg_addr, + sc->regs->ctrl, sc->regs->mdio_ctrl); + return (0xffff); + } +} + +static void write_mii(struct greth_softc *sc, uint32_t phy_addr, uint32_t reg_addr, uint32_t data) +{ + sc->phy_write_access++; + 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) {} + if (!(sc->regs->mdio_ctrl & GRETH_MDIO_LINKFAIL)) { + MIIDBG("greth%d: mii write[%d] OK to %x.%x (0x%08x,0x%08x)\n", + sc->minor, sc->phy_write_access, phy_addr, reg_addr, + sc->regs->ctrl, sc->regs->mdio_ctrl); + } else { + printf("greth%d: mii write[%d] failed to %x.%x (0x%08x,0x%08x)\n", + sc->minor, sc->phy_write_access, phy_addr, reg_addr, + sc->regs->ctrl, sc->regs->mdio_ctrl); + } +} + +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 = GRETH_CTRL_RST; /* Reset ON */ + for (i = 0; i<100 && (regs->ctrl & GRETH_CTRL_RST); i++) + ; + regs->ctrl = GRETH_CTRL_DD; /* Reset OFF. SW do PHY Init */ + + /* 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; + } + sc->phy_read_access = 0; + sc->phy_write_access = 0; + + /* As I understand the PHY comes back to a good default state after + * Power-down or Reset, so we do both just in case. Power-down bit should + * be cleared. + * Wait for old reset (if asserted by boot loader) to complete, otherwise + * power-down instruction might not have any effect. + */ + while (read_mii(sc, phyaddr, 0) & 0x8000) {} + write_mii(sc, phyaddr, 0, 0x0800); /* Power-down */ + write_mii(sc, phyaddr, 0, 0x0000); /* Power-Up */ + write_mii(sc, phyaddr, 0, 0x8000); /* Reset */ + + /* We wait about 30ms */ + rtems_task_wake_after(rtems_clock_get_ticks_per_second()/32); + + /* Wait for reset to complete and get default values */ + while ((phyctrl = read_mii(sc, phyaddr, 0)) & 0x8000) {} + + /* Enable/Disable GBit auto-neg advetisement so that the link partner + * know that we have/haven't GBit capability. The MAC may not support + * Gbit even though PHY does... + */ + phystatus = read_mii(sc, phyaddr, 1); + if (phystatus & 0x0100) { + tmp1 = read_mii(sc, phyaddr, 9); + if (sc->gbit_mac) + write_mii(sc, phyaddr, 9, tmp1 | 0x300); + else + write_mii(sc, phyaddr, 9, tmp1 & ~(0x300)); + } + + /* If autonegotiation implemented we start it */ + if (phystatus & 0x0008) { + write_mii(sc, phyaddr, 0, phyctrl | 0x1200); + phyctrl = read_mii(sc, phyaddr, 0); + } + + /* 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 */ + if (((sc->gb) && !(sc->gbit_mac)) || !((phyctrl >> 12) & 1)) { + write_mii(sc, phyaddr, 0, sc->sp << 13); + + /* check if marvell 88EE1111 PHY. Needs special reset handling */ + if ((phystatus & 1) && (sc->phydev.vendor == 0x005043) && + (sc->phydev.device == 0x0C)) + write_mii(sc, phyaddr, 0, 0x8000); + + sc->gb = 0; + sc->sp = 0; + sc->fd = 0; + } + while ((read_mii(sc, phyaddr, 0)) & 0x8000) {} + + regs->ctrl = GRETH_CTRL_RST; /* Reset ON */ + for (i = 0; i < 100 && (regs->ctrl & GRETH_CTRL_RST); i++) + ; + regs->ctrl = GRETH_CTRL_DD; + + /* Initialize rx/tx descriptor pointers */ + sc->txdesc = (greth_rxtxdesc *) almalloc(1024); + sc->rxdesc = (greth_rxtxdesc *) almalloc(1024); + sc->tx_ptr = 0; + sc->tx_dptr = 0; + sc->tx_cnt = 0; + sc->rx_ptr = 0; + + /* Translate the base address into an address that the GRETH core can understand */ + drvmgr_translate(sc->dev, 0, 0, (void *)sc->txdesc, (void **)&sc->txdesc_remote); + drvmgr_translate(sc->dev, 0, 0, (void *)sc->rxdesc, (void **)&sc->rxdesc_remote); + 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(sc->dev, 0, 0, (void *)malloc(GRETH_MAXBUF_LEN), (void **)&sc->txdesc[i].addr); + } +#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(sc->dev, 0, 0, (void *)mtod(m, uint32_t *), (void **)&sc->rxdesc[i].addr); + 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(dp->dev, 0, 0, (void *)mtod (m, uint32_t *), (void **)&dp->rxdesc[dp->rx_ptr].addr); + 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, 1, 1, (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(dp->dev, 0, 0, (void *)(uint32_t *)m->m_data, (void **)&dp->txdesc[dp->tx_ptr].addr); + + /* 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; + char name[4] = {'E', 'T', 'H', '0'}; + + if (sc->daemonTid == 0) + { + + /* + * Start driver tasks + */ + name[3] += sc->minor; + sc->daemonTid = rtems_bsdnet_newproc (name, 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/net/network_interface_add.c b/c/src/lib/libbsp/sparc/shared/net/network_interface_add.c new file mode 100644 index 0000000000..7c7ce31b4a --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/net/network_interface_add.c @@ -0,0 +1,66 @@ +/* Network interface register help function + * + * COPYRIGHT (c) 2008. + * Aeroflex Gaisler AB. + * + * This function adds a network interface to the + * rtems_bsdnet_config.ifconfig linked list of interfaces. + * The interface configuration is taken from the user defined + * array interface_configs. This function is useful for PnP + * systems when an unknown number of interfaces are available. + * + * 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-08, Daniel Hellstrom <daniel@gaisler.com> + * Created + * + */ + +#include <rtems/rtems_bsdnet.h> +#include <stdio.h> + +#include <network_interface_add.h> + +extern struct rtems_bsdnet_config rtems_bsdnet_config; + +/* Number of interfaces taken */ +int network_interface_cnt = 0; + +int network_interface_add(struct rtems_bsdnet_ifconfig *interface) +{ + struct ethernet_config *cfg = NULL; + int i, last_entry = 1; + + /* Init interface description */ + interface->next = NULL; + + cfg = &interface_configs[network_interface_cnt]; + for(i=0; i<6; i++) { + if ( cfg->eth_adr[i] != 0 ) { + last_entry = 0; + break; + } + } + /* Do we have a valid configuration? */ + if ( last_entry == 0 ) { + cfg = &interface_configs[network_interface_cnt]; + + interface->ip_address = cfg->ip_addr; + interface->ip_netmask = cfg->ip_netmask; + interface->hardware_address = cfg->eth_adr; + + network_interface_cnt++; + } else { + interface->ip_address = NULL; + interface->ip_netmask = NULL; + interface->hardware_address = NULL; + } + + /* Insert interface first into list */ + interface->next = rtems_bsdnet_config.ifconfig; + rtems_bsdnet_config.ifconfig = interface; + + return 0; +} diff --git a/c/src/lib/libbsp/sparc/shared/pci/gr_701.c b/c/src/lib/libbsp/sparc/shared/pci/gr_701.c new file mode 100644 index 0000000000..01c6b88f1c --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/pci/gr_701.c @@ -0,0 +1,596 @@ +/* GR-701 PCI Target driver. + * + * COPYRIGHT (c) 2008. + * Aeroflex Gaisler AB. + * + * Configures the GR-701 interface PCI board. + * This driver provides a AMBA PnP bus by using the general part + * of the AMBA PnP bus driver (ambapp_bus.c). + * + * Driver resources for the AMBA PnP bus provided can be set using + * gr701_set_resources(). + * + * 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-05, Daniel Hellstrom <daniel@gaisler.com> + * Created + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <bsp.h> +#include <rtems/bspIo.h> +#include <pci.h> +#include <pci/access.h> + +#include <ambapp.h> + +#include <ambapp.h> +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> +#include <drvmgr/pci_bus.h> +#include <genirq.h> + +#include <gr_701.h> + +/* Offset from 0x80000000 (dual bus version) */ +#define AHB1_BASE_ADDR 0x80000000 +#define AHB1_IOAREA_BASE_ADDR 0x80100000 + +/* #define DEBUG 1 */ + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +int gr701_init1(struct drvmgr_dev *dev); +int gr701_init2(struct drvmgr_dev *dev); + +#define READ_REG(address) (*(volatile unsigned int *)address) + +/* PCI bride reg layout on AMBA side */ +struct amba_bridge_regs { + volatile unsigned int bar0; + volatile unsigned int bar1; + volatile unsigned int bar2; + volatile unsigned int bar3; + volatile unsigned int bar4;/* 0x10 */ + + volatile unsigned int unused[4*3-1]; + + volatile unsigned int ambabars[1]; /* 0x40 */ +}; + +/* PCI bride reg layout on PCI side */ +struct pci_bridge_regs { + volatile unsigned int bar0; + volatile unsigned int bar1; + volatile unsigned int bar2; + volatile unsigned int bar3; + volatile unsigned int bar4; /* 0x10 */ + + volatile unsigned int ilevel; + volatile unsigned int ipend; + volatile unsigned int iforce; + volatile unsigned int istatus; + volatile unsigned int iclear; + volatile unsigned int imask; +}; + +/* Private data structure for driver */ +struct gr701_priv { + /* Driver management */ + struct drvmgr_dev *dev; + char prefix[16]; + + struct pci_bridge_regs *pcib; + struct amba_bridge_regs *ambab; + + /* PCI */ + pci_dev_t pcidev; + struct pci_dev_info *devinfo; + + /* IRQ */ + genirq_t genirq; + int interrupt_cnt; + + /* GR-701 Address translation */ + struct drvmgr_map_entry bus_maps_up[2]; + struct drvmgr_map_entry bus_maps_down[2]; + + /* AMBA Plug&Play information on GR-701 */ + struct ambapp_bus abus; + struct ambapp_mmap amba_maps[3]; + struct ambapp_config config; +}; + +int ambapp_gr701_int_register( + struct drvmgr_dev *dev, + int irq, + const char *info, + drvmgr_isr handler, + void *arg); +int ambapp_gr701_int_unregister( + struct drvmgr_dev *dev, + int irq, + drvmgr_isr isr, + void *arg); +int ambapp_gr701_int_unmask( + struct drvmgr_dev *dev, + int irq); +int ambapp_gr701_int_mask( + struct drvmgr_dev *dev, + int irq); +int ambapp_gr701_int_clear( + struct drvmgr_dev *dev, + int irq); +int ambapp_gr701_get_params( + struct drvmgr_dev *dev, + struct drvmgr_bus_params *params); + +struct ambapp_ops ambapp_gr701_ops = { + .int_register = ambapp_gr701_int_register, + .int_unregister = ambapp_gr701_int_unregister, + .int_unmask = ambapp_gr701_int_unmask, + .int_mask = ambapp_gr701_int_mask, + .int_clear = ambapp_gr701_int_clear, + .get_params = ambapp_gr701_get_params +}; + +struct drvmgr_drv_ops gr701_ops = +{ + .init = {gr701_init1, gr701_init2, NULL, NULL}, + .remove = NULL, + .info = NULL +}; + +struct pci_dev_id_match gr701_ids[] = +{ + PCIID_DEVVEND(PCIID_VENDOR_GAISLER, PCIID_DEVICE_GR_701), + PCIID_END_TABLE /* Mark end of table */ +}; + +struct pci_drv_info gr701_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_PCI_GAISLER_GR701_ID, /* Driver ID */ + "GR-701_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_PCI, /* Bus Type */ + &gr701_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + 0, + }, + &gr701_ids[0] +}; + +/* Driver resources configuration for the AMBA bus on the GR-701 board. + * It is declared weak so that the user may override it from the project file, + * if the default settings are not enough. + * + * The configuration consists of an array of configuration pointers, each + * pointer determine the configuration of one GR-701 board. Pointer + * zero is for board0, pointer 1 for board1 and so on. + * + * The array must end with a NULL pointer. + */ +struct drvmgr_bus_res *gr701_resources[] __attribute__((weak)) = +{ + NULL +}; +int gr701_resources_cnt = 0; + +void gr701_register_drv(void) +{ + DBG("Registering GR-701 PCI driver\n"); + drvmgr_drv_register(&gr701_info.general); +} + +void gr701_interrupt(void *arg) +{ + struct gr701_priv *priv = arg; + unsigned int status; + int irq = 0; + + while ( (status=priv->pcib->istatus) != 0 ) { + priv->interrupt_cnt++; /* An interrupt was generated */ + irq = status; + genirq_doirq(priv->genirq, irq); + /* ACK interrupt */ + priv->pcib->istatus = 0; + } + + /* ACK interrupt, this is because PCI is Level, so the IRQ Controller still drives the IRQ. */ + if ( irq ) + drvmgr_interrupt_clear(priv->dev, 0); +} + +int gr701_hw_init(struct gr701_priv *priv) +{ + uint32_t com1; + struct pci_bridge_regs *pcib; + struct amba_bridge_regs *ambab; + int mst; + unsigned int pci_freq_hz; + pci_dev_t pcidev = priv->pcidev; + struct pci_dev_info *devinfo = priv->devinfo; + + /* Set up PCI ==> AMBA */ + priv->pcib = pcib = (void *)devinfo->resources[0].address; + pcib->bar0 = 0xfc000000; + + /* Set up GR701 AMBA Masters connection to PCI */ + priv->ambab = ambab = (struct amba_bridge_regs *)( + devinfo->resources[1].address + 0x400); + + /* Init all msters, max 16 */ + for (mst=0; mst<16; mst++) { + ambab->ambabars[mst] = 0x40000000; + if (READ_REG(&ambab->ambabars[mst]) != 0x40000000) + break; + } + + /* Setup Address translation for AMBA bus, assume that PCI BAR + * are mapped 1:1 to CPU. + */ + + priv->amba_maps[0].size = 0x04000000; + priv->amba_maps[0].local_adr = devinfo->resources[1].address; + priv->amba_maps[0].remote_adr = 0xfc000000; + + /* Mark end of table */ + priv->amba_maps[1].size=0; + priv->amba_maps[1].local_adr = 0; + priv->amba_maps[1].remote_adr = 0; + + /* Setup DOWN-streams address translation */ + priv->bus_maps_down[0].name = "PCI BAR1 -> AMBA"; + priv->bus_maps_down[0].size = priv->amba_maps[0].size; + priv->bus_maps_down[0].from_adr = (void *)devinfo->resources[1].address; + priv->bus_maps_down[0].to_adr = (void *)0xfc000000; + + /* Setup UP-streams address translation */ + priv->bus_maps_up[0].name = "AMBA PCIF Window"; + priv->bus_maps_up[0].size = 0x10000000; + priv->bus_maps_up[0].from_adr = (void *)0xe0000000; + priv->bus_maps_up[0].to_adr = (void *)0x40000000; + + /* Mark end of translation tables */ + priv->bus_maps_down[1].size = 0; + priv->bus_maps_up[1].size = 0; + + /* Enable I/O and Mem accesses */ + pci_cfg_r32(pcidev, PCI_COMMAND, &com1); + com1 |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY; + pci_cfg_w32(pcidev, PCI_COMMAND, com1); + + /* Start AMBA PnP scan at first AHB bus */ + ambapp_scan(&priv->abus, devinfo->resources[1].address + 0x3f00000, + NULL, &priv->amba_maps[0]); + + /* Frequency is the same as the PCI bus frequency */ + drvmgr_freq_get(priv->dev, NULL, &pci_freq_hz); + + /* Initialize Frequency of AMBA bus */ + ambapp_freq_init(&priv->abus, NULL, pci_freq_hz); + + /* Init IRQ controller (avoid IRQ generation) */ + pcib->imask = 0x0000; + pcib->ipend = 0; + pcib->iclear = 0xffff; + pcib->iforce = 0; + pcib->ilevel = 0x0; + + /* Successfully registered the GR-701 board */ + return 0; +} + +void gr701_hw_init2(struct gr701_priv *priv) +{ + /* Enable PCI Master (for DMA) */ + pci_master_enable(priv->pcidev); +} + +/* Called when a PCI target is found with the PCI device and vendor ID + * given in gr701_ids[]. + */ +int gr701_init1(struct drvmgr_dev *dev) +{ + struct gr701_priv *priv; + struct pci_dev_info *devinfo; + uint32_t bar0, bar1, bar0_size, bar1_size; + + priv = malloc(sizeof(struct gr701_priv)); + if ( !priv ) + return DRVMGR_NOMEM; + + memset(priv, 0, sizeof(*priv)); + dev->priv = priv; + priv->dev = dev; + + /* Determine number of configurations */ + if ( gr701_resources_cnt == 0 ) { + while ( gr701_resources[gr701_resources_cnt] ) + gr701_resources_cnt++; + } + + /* Generate Device prefix */ + + strcpy(priv->prefix, "/dev/gr701_0"); + priv->prefix[11] += dev->minor_drv; + mkdir(priv->prefix, S_IRWXU | S_IRWXG | S_IRWXO); + priv->prefix[12] = '/'; + priv->prefix[13] = '\0'; + + priv->devinfo = devinfo = (struct pci_dev_info *)dev->businfo; + priv->pcidev = devinfo->pcidev; + bar0 = devinfo->resources[0].address; + bar0_size = devinfo->resources[0].size; + bar1 = devinfo->resources[1].address; + bar1_size = devinfo->resources[1].size; + printf("\n\n--- GR-701[%d] ---\n", dev->minor_drv); + printf(" PCI BUS: 0x%x, SLOT: 0x%x, FUNCTION: 0x%x\n", + PCI_DEV_EXPAND(priv->pcidev)); + printf(" PCI VENDOR: 0x%04x, DEVICE: 0x%04x\n\n\n", + devinfo->id.vendor, devinfo->id.device); + printf(" PCI BAR[0]: 0x%lx - 0x%lx\n", bar0, bar0 + bar0_size - 1); + printf(" PCI BAR[1]: 0x%lx - 0x%lx\n", bar1, bar1 + bar1_size - 1); + printf(" IRQ: %d\n\n\n", devinfo->irq); + + /* all neccessary space assigned to GR-701 target? */ + if ((bar0_size == 0) || (bar1_size == 0)) + return DRVMGR_ENORES; + + priv->genirq = genirq_init(16); + if ( priv->genirq == NULL ) { + free(priv); + dev->priv = NULL; + return DRVMGR_FAIL; + } + + if ( gr701_hw_init(priv) ) { + genirq_destroy(priv->genirq); + free(priv); + dev->priv = NULL; + printf(" Failed to initialize GR-701 HW\n"); + return DRVMGR_FAIL; + } + + /* Init amba bus */ + priv->config.abus = &priv->abus; + priv->config.ops = &ambapp_gr701_ops; + priv->config.maps_up = &priv->bus_maps_up[0]; + priv->config.maps_down = &priv->bus_maps_down[0]; + if ( priv->dev->minor_drv < gr701_resources_cnt ) { + priv->config.resources = gr701_resources[priv->dev->minor_drv]; + } else { + priv->config.resources = NULL; + } + + /* Create and register AMBA PnP bus. */ + return ambapp_bus_register(dev, &priv->config); +} + +/* Called when a PCI target is found with the PCI device and vendor ID + * given in gr701_ids[]. + */ +int gr701_init2(struct drvmgr_dev *dev) +{ + struct gr701_priv *priv = dev->priv; + + /* Clear any old interrupt requests */ + drvmgr_interrupt_clear(dev, 0); + + /* Enable System IRQ so that GR-701 PCI target interrupt goes through. + * + * It is important to enable it in stage init2. If interrupts were + * enabled in init1 this might hang the system when more than one PCI + * board is connected, this is because PCI interrupts might be shared + * and PCI target 2 have not initialized and might therefore drive + * interrupt already when entering init1(). + */ + drvmgr_interrupt_register(dev, 0, "gr701", gr701_interrupt, priv); + + gr701_hw_init2(priv); + + return DRVMGR_OK; +} + +int ambapp_gr701_int_register( + struct drvmgr_dev *dev, + int irq, + const char *info, + drvmgr_isr handler, + void *arg) +{ + struct gr701_priv *priv = dev->parent->dev->priv; + rtems_interrupt_level level; + int status; + + rtems_interrupt_disable(level); + + status = genirq_register(priv->genirq, irq, handler, arg); + if ( status == 0 ) { + /* Clear IRQ for first registered handler */ + priv->pcib->iclear = (1<<irq); + } else if ( status == 1 ) + status = 0; + + if (status != 0) { + rtems_interrupt_enable(level); + return DRVMGR_FAIL; + } + + status = genirq_enable(priv->genirq, irq, handler, arg); + if ( status == 0 ) { + /* Enable IRQ for first enabled handler only */ + priv->pcib->imask |= (1<<irq); /* unmask interrupt source */ + } else if ( status == 1 ) + status = DRVMGR_OK; + + rtems_interrupt_enable(level); + + return status; +} + +int ambapp_gr701_int_unregister( + struct drvmgr_dev *dev, + int irq, + drvmgr_isr isr, + void *arg) +{ + struct gr701_priv *priv = dev->parent->dev->priv; + rtems_interrupt_level level; + int status; + + rtems_interrupt_disable(level); + + status = genirq_disable(priv->genirq, irq, isr, arg); + if ( status == 0 ) { + /* Disable IRQ only when no enabled handler exists */ + priv->pcib->imask &= ~(1<<irq); /* mask interrupt source */ + } + + status = genirq_unregister(priv->genirq, irq, isr, arg); + if ( status != 0 ) + status = DRVMGR_FAIL; + + rtems_interrupt_enable(level); + + return status; +} + +int ambapp_gr701_int_unmask( + struct drvmgr_dev *dev, + int irq) +{ + struct gr701_priv *priv = dev->parent->dev->priv; + rtems_interrupt_level level; + + DBG("GR-701 IRQ %d: enable\n", irq); + + if ( genirq_check(priv->genirq, irq) ) + return DRVMGR_FAIL; + + rtems_interrupt_disable(level); + + /* Enable IRQ */ + priv->pcib->imask |= (1<<irq); /* unmask interrupt source */ + + rtems_interrupt_enable(level); + + return DRVMGR_OK; +} + +int ambapp_gr701_int_mask( + struct drvmgr_dev *dev, + int irq) +{ + struct gr701_priv *priv = dev->parent->dev->priv; + rtems_interrupt_level level; + + DBG("GR-701 IRQ %d: disable\n", irq); + + if ( genirq_check(priv->genirq, irq) ) + return DRVMGR_FAIL; + + rtems_interrupt_disable(level); + + /* Disable IRQ */ + priv->pcib->imask &= ~(1<<irq); /* mask interrupt source */ + + rtems_interrupt_enable(level); + + return DRVMGR_OK; +} + +int ambapp_gr701_int_clear( + struct drvmgr_dev *dev, + int irq) +{ + struct gr701_priv *priv = dev->parent->dev->priv; + + if ( genirq_check(priv->genirq, irq) ) + return DRVMGR_FAIL; + + priv->pcib->iclear = (1<<irq); + + return DRVMGR_OK; +} + +int ambapp_gr701_get_params(struct drvmgr_dev *dev, struct drvmgr_bus_params *params) +{ + struct gr701_priv *priv = dev->parent->dev->priv; + + /* Device name prefix pointer, skip /dev */ + params->dev_prefix = &priv->prefix[5]; + + return 0; +} + +void gr701_print_dev(struct drvmgr_dev *dev, int options) +{ + struct gr701_priv *priv = dev->priv; + struct pci_dev_info *devinfo = priv->devinfo; + unsigned int freq_hz; + uint32_t bar0, bar1, bar0_size, bar1_size; + + /* Print */ + printf("--- GR-701 [bus 0x%x, dev 0x%x, fun 0x%x] ---\n", + PCI_DEV_EXPAND(priv->pcidev)); + bar0 = devinfo->resources[0].address; + bar0_size = devinfo->resources[0].size; + bar1 = devinfo->resources[1].address; + bar1_size = devinfo->resources[1].size; + + printf(" PCI BAR[0]: 0x%lx - 0x%lx\n", bar0, bar0 + bar0_size - 1); + printf(" PCI BAR[1]: 0x%lx - 0x%lx\n", bar1, bar1 + bar1_size - 1); + printf(" IRQ: %d\n", devinfo->irq); + + /* Frequency is the same as the PCI bus frequency */ + drvmgr_freq_get(dev, 0, &freq_hz); + + printf(" FREQ: %u Hz\n", freq_hz); + printf(" IMASK: 0x%08x\n", priv->pcib->imask); + printf(" IPEND: 0x%08x\n", priv->pcib->ipend); + + /* Print amba config */ + if ( options & GR701_OPTIONS_AMBA ) { + ambapp_print(&priv->abus, 10); + } + +#if 0 + /* Print IRQ handlers and their arguments */ + if ( options & GR701_OPTIONS_IRQ ) { + int i; + for(i=0; i<16; i++) { + printf(" IRQ[%02d]: 0x%x, arg: 0x%x\n", + i, (unsigned int)priv->isrs[i].handler, (unsigned int)priv->isrs[i].arg); + } + } +#endif +} + +void gr701_print(int options) +{ + struct pci_drv_info *drv = &gr701_info; + struct drvmgr_dev *dev; + + dev = drv->general.dev; + while(dev) { + gr701_print_dev(dev, options); + dev = dev->next_in_drv; + } +} diff --git a/c/src/lib/libbsp/sparc/shared/pci/gr_rasta_adcdac.c b/c/src/lib/libbsp/sparc/shared/pci/gr_rasta_adcdac.c new file mode 100644 index 0000000000..163ccfb98c --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/pci/gr_rasta_adcdac.c @@ -0,0 +1,671 @@ +/* GR-RASTA-ADCDAC PCI Target driver. + * + * COPYRIGHT (c) 2008. + * Aeroflex Gaisler AB. + * + * Configures the GR-RASTA-ADCDAC interface PCI board. + * This driver provides a AMBA PnP bus by using the general part + * of the AMBA PnP bus driver (ambapp_bus.c). + * + * Driver resources for the AMBA PnP bus provided can be set using + * gr_rasta_adcdac_set_resources(). + * + * 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-05, Daniel Hellstrom <daniel@gaisler.com> + * Created + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <bsp.h> +#include <rtems/bspIo.h> +#include <pci.h> + +#include <ambapp.h> +#include <grlib.h> +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> +#include <drvmgr/pci_bus.h> +#include <genirq.h> + +#include <gr_rasta_adcdac.h> + +/*#define DEBUG 1*/ + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +/* Determines which PCI address the AHB masters will access, it should be + * set so that the masters can access the CPU RAM. Default is base of CPU RAM, + * CPU RAM is mapped 1:1 to PCI space. + */ +extern unsigned int _RAM_START; +#define AHBMST2PCIADR (((unsigned int)&_RAM_START) & 0xf0000000) + +/* PCI ID */ +#define PCIID_VENDOR_GAISLER 0x1AC8 +#define PCIID_DEVICE_GR_RASTA_ADCDAC 0x0014 + +int gr_rasta_adcdac_init1(struct drvmgr_dev *dev); +int gr_rasta_adcdac_init2(struct drvmgr_dev *dev); + +struct grpci_regs { + volatile unsigned int cfg_stat; + volatile unsigned int bar0; + volatile unsigned int page0; + volatile unsigned int bar1; + volatile unsigned int page1; + volatile unsigned int iomap; + volatile unsigned int stat_cmd; +}; + +struct gr_rasta_adcdac_ver { + const unsigned int amba_freq_hz; /* The frequency */ + const unsigned int amba_ioarea; /* The address where the PnP IOAREA starts at */ +}; + +/* Private data structure for driver */ +struct gr_rasta_adcdac_priv { + /* Driver management */ + struct drvmgr_dev *dev; + char prefix[20]; + + /* PCI */ + pci_dev_t pcidev; + struct pci_dev_info *devinfo; + uint32_t ahbmst2pci_map; + + /* IRQ */ + genirq_t genirq; + + /* GR-RASTA-ADCDAC */ + struct gr_rasta_adcdac_ver *version; + struct irqmp_regs *irq; + struct grpci_regs *grpci; + struct drvmgr_map_entry bus_maps_down[3]; + struct drvmgr_map_entry bus_maps_up[2]; + + /* AMBA Plug&Play information on GR-RASTA-ADCDAC */ + struct ambapp_bus abus; + struct ambapp_mmap amba_maps[4]; + struct ambapp_config config; +}; + +struct gr_rasta_adcdac_ver gr_rasta_adcdac_ver0 = { + .amba_freq_hz = 50000000, + .amba_ioarea = 0x80100000, +}; + +int ambapp_rasta_adcdac_int_register( + struct drvmgr_dev *dev, + int irq, + const char *info, + drvmgr_isr handler, + void *arg); +int ambapp_rasta_adcdac_int_unregister( + struct drvmgr_dev *dev, + int irq, + drvmgr_isr isr, + void *arg); +int ambapp_rasta_adcdac_int_unmask( + struct drvmgr_dev *dev, + int irq); +int ambapp_rasta_adcdac_int_mask( + struct drvmgr_dev *dev, + int irq); +int ambapp_rasta_adcdac_int_clear( + struct drvmgr_dev *dev, + int irq); +int ambapp_rasta_adcdac_get_params( + struct drvmgr_dev *dev, + struct drvmgr_bus_params *params); + +struct ambapp_ops ambapp_rasta_adcdac_ops = { + .int_register = ambapp_rasta_adcdac_int_register, + .int_unregister = ambapp_rasta_adcdac_int_unregister, + .int_unmask = ambapp_rasta_adcdac_int_unmask, + .int_mask = ambapp_rasta_adcdac_int_mask, + .int_clear = ambapp_rasta_adcdac_int_clear, + .get_params = ambapp_rasta_adcdac_get_params +}; + +struct drvmgr_drv_ops gr_rasta_adcdac_ops = +{ .init = {gr_rasta_adcdac_init1, gr_rasta_adcdac_init2, NULL, NULL}, + .remove = NULL, + .info = NULL +}; + +struct pci_dev_id_match gr_rasta_adcdac_ids[] = +{ + PCIID_DEVVEND(PCIID_VENDOR_GAISLER, PCIID_DEVICE_GR_RASTA_ADCDAC), + PCIID_END_TABLE /* Mark end of table */ +}; + +struct pci_drv_info gr_rasta_adcdac_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_PCI_GAISLER_RASTAADCDAC_ID,/* Driver ID */ + "GR-RASTA-ADCDAC_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_PCI, /* Bus Type */ + &gr_rasta_adcdac_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + 0, + }, + &gr_rasta_adcdac_ids[0] +}; + +/* Driver resources configuration for the AMBA bus on the GR-RASTA-ADCDAC board. + * It is declared weak so that the user may override it from the project file, + * if the default settings are not enough. + * + * The configuration consists of an array of configuration pointers, each + * pointer determine the configuration of one GR-RASTA-ADCDAC board. Pointer + * zero is for board0, pointer 1 for board1 and so on. + * + * The array must end with a NULL pointer. + */ +struct drvmgr_bus_res *gr_rasta_adcdac_resources[] __attribute__((weak)) = +{ + NULL +}; +int gr_rasta_adcdac_resources_cnt = 0; + +void gr_rasta_adcdac_register_drv(void) +{ + DBG("Registering GR-RASTA-ADCDAC PCI driver\n"); + drvmgr_drv_register(&gr_rasta_adcdac_info.general); +} + +void gr_rasta_adcdac_isr (void *arg) +{ + struct gr_rasta_adcdac_priv *priv = arg; + unsigned int status, tmp; + int irq; + tmp = status = priv->irq->ipend; + + /* DBG("GR-RASTA-ADCDAC: IRQ 0x%x\n",status); */ + + for(irq=0; irq<16; irq++) { + if ( status & (1<<irq) ) { + genirq_doirq(priv->genirq, irq); + priv->irq->iclear = (1<<irq); + status &= ~(1<<irq); + if ( status == 0 ) + break; + } + } + + /* ACK interrupt, this is because PCI is Level, so the IRQ Controller still drives the IRQ. */ + if ( tmp ) + drvmgr_interrupt_clear(priv->dev, 0); + + DBG("RASTA-ADCDAC-IRQ: 0x%x\n", tmp); +} + +int gr_rasta_adcdac_hw_init1(struct gr_rasta_adcdac_priv *priv) +{ + uint32_t data; + unsigned int *page0 = NULL; + struct ambapp_dev *tmp; + int status; + struct ambapp_ahb_info *ahb; + struct pci_dev_info *devinfo = priv->devinfo; + uint32_t bar0, bar0_size; + + /* Select version of GR-RASTA-ADCDAC board */ + switch (devinfo->rev) { + case 0: + priv->version = &gr_rasta_adcdac_ver0; + break; + default: + return -2; + } + + bar0 = devinfo->resources[0].address; + bar0_size = devinfo->resources[0].size; + page0 = (unsigned int *)(bar0 + bar0_size/2); + + /* Point PAGE0 to start of Plug and Play information */ + *page0 = priv->version->amba_ioarea & 0xf0000000; + + /* set parity error response */ + pci_cfg_r32(priv->pcidev, PCI_COMMAND, &data); + pci_cfg_w32(priv->pcidev, PCI_COMMAND, (data|PCI_COMMAND_PARITY)); + + /* Setup cache line size. Default cache line size will result in + * poor performance (256 word fetches), 0xff will set it according + * to the max size of the PCI FIFO. + */ + pci_cfg_w8(priv->pcidev, PCI_CACHE_LINE_SIZE, 0xff); + + /* Scan AMBA Plug&Play */ + + /* AMBA MAP bar0 (in CPU) ==> 0x80000000(remote amba address) */ + priv->amba_maps[0].size = bar0_size/2; + priv->amba_maps[0].local_adr = bar0; + priv->amba_maps[0].remote_adr = 0x80000000; + + /* AMBA MAP bar1 (in CPU) ==> 0x40000000(remote amba address) */ + priv->amba_maps[1].size = devinfo->resources[1].size; + priv->amba_maps[1].local_adr = devinfo->resources[1].address; + priv->amba_maps[1].remote_adr = 0x40000000; + + /* Addresses not matching with map be untouched */ + priv->amba_maps[2].size = 0xfffffff0; + priv->amba_maps[2].local_adr = 0; + priv->amba_maps[2].remote_adr = 0; + + /* Mark end of table */ + priv->amba_maps[3].size=0; + priv->amba_maps[3].local_adr = 0; + priv->amba_maps[3].remote_adr = 0; + + /* Start AMBA PnP scan at first AHB bus */ + /*ambapp_scan(priv->bar0 + (priv->version->amba_ioarea & ~0xf0000000), + NULL, &priv->amba_maps[0], NULL, &priv->abus.root, NULL);*/ + ambapp_scan(&priv->abus, + bar0 + (priv->version->amba_ioarea & ~0xf0000000), + NULL, &priv->amba_maps[0]); + + /* Initialize Frequency of AMBA bus */ + ambapp_freq_init(&priv->abus, NULL, priv->version->amba_freq_hz); + + /* Point PAGE0 to start of APB area */ + *page0 = 0x80000000; + + /* Find GRPCI controller */ + tmp = (void *)ambapp_for_each(&priv->abus, + (OPTIONS_ALL|OPTIONS_APB_SLVS), + VENDOR_GAISLER, GAISLER_PCIFBRG, + ambapp_find_by_idx, NULL); + if ( !tmp ) { + return -3; + } + priv->grpci = (struct grpci_regs *)((struct ambapp_apb_info *)tmp->devinfo)->start; + + /* Set GRPCI mmap so that AMBA masters can access CPU-RAM over + * the PCI window. + */ + priv->grpci->cfg_stat = (priv->grpci->cfg_stat & 0x0fffffff) | + (priv->ahbmst2pci_map & 0xf0000000); + priv->grpci->page1 = 0x40000000; + + /* Find IRQ controller */ + tmp = (void *)ambapp_for_each(&priv->abus, + (OPTIONS_ALL|OPTIONS_APB_SLVS), + VENDOR_GAISLER, GAISLER_IRQMP, + ambapp_find_by_idx, NULL); + if ( !tmp ) { + return -4; + } + priv->irq = (struct irqmp_regs *)DEV_TO_APB(tmp)->start; + /* Set up GR-RASTA-ADCDAC irq controller */ + priv->irq->iclear = 0xffff; + priv->irq->ilevel = 0; + priv->irq->mask[0] = 0; + + /* DOWN streams translation table */ + priv->bus_maps_down[0].name = "PCI BAR0 -> AMBA"; + priv->bus_maps_down[0].size = priv->amba_maps[0].size; + priv->bus_maps_down[0].from_adr = (void *)priv->amba_maps[0].local_adr; + priv->bus_maps_down[0].to_adr = (void *)priv->amba_maps[0].remote_adr; + + priv->bus_maps_down[1].name = "PCI BAR1 -> AMBA"; + priv->bus_maps_down[1].size = priv->amba_maps[1].size; + priv->bus_maps_down[1].from_adr = (void *)priv->amba_maps[1].local_adr; + priv->bus_maps_down[1].to_adr = (void *)priv->amba_maps[1].remote_adr; + + /* Mark end of translation table */ + priv->bus_maps_down[2].size = 0; + + /* Find GRPCI controller AHB Slave interface */ + tmp = (void *)ambapp_for_each(&priv->abus, + (OPTIONS_ALL|OPTIONS_AHB_SLVS), + VENDOR_GAISLER, GAISLER_PCIFBRG, + ambapp_find_by_idx, NULL); + if ( !tmp ) { + return -5; + } + ahb = (struct ambapp_ahb_info *)tmp->devinfo; + + /* UP streams translation table */ + priv->bus_maps_up[0].name = "AMBA GRPCI Window"; + priv->bus_maps_up[0].size = ahb->mask[0]; /* AMBA->PCI Window on GR-RASTA-ADCDAC board */ + priv->bus_maps_up[0].from_adr = (void *)ahb->start[0]; + priv->bus_maps_up[0].to_adr = (void *) + (priv->ahbmst2pci_map & 0xf0000000); + + /* Mark end of translation table */ + priv->bus_maps_up[1].size = 0; + + /* Successfully registered the RASTA board */ + return 0; +} + +int gr_rasta_adcdac_hw_init2(struct gr_rasta_adcdac_priv *priv) +{ + /* Enable DMA by enabling PCI target as master */ + pci_master_enable(priv->pcidev); + + return DRVMGR_OK; +} + +/* Called when a PCI target is found with the PCI device and vendor ID + * given in gr_rasta_adcdac_ids[]. + */ +int gr_rasta_adcdac_init1(struct drvmgr_dev *dev) +{ + struct gr_rasta_adcdac_priv *priv; + struct pci_dev_info *devinfo; + int status; + uint32_t bar0, bar1, bar0_size, bar1_size; + union drvmgr_key_value *value; + + priv = malloc(sizeof(struct gr_rasta_adcdac_priv)); + if ( !priv ) + return DRVMGR_NOMEM; + + memset(priv, 0, sizeof(*priv)); + dev->priv = priv; + priv->dev = dev; + + /* Determine number of configurations */ + if ( gr_rasta_adcdac_resources_cnt == 0 ) { + while ( gr_rasta_adcdac_resources[gr_rasta_adcdac_resources_cnt] ) + gr_rasta_adcdac_resources_cnt++; + } + + /* Generate Device prefix */ + + strcpy(priv->prefix, "/dev/rastaadcdac0"); + priv->prefix[16] += dev->minor_drv; + mkdir(priv->prefix, S_IRWXU | S_IRWXG | S_IRWXO); + priv->prefix[17] = '/'; + priv->prefix[18] = '\0'; + + priv->devinfo = devinfo = (struct pci_dev_info *)dev->businfo; + priv->pcidev = devinfo->pcidev; + bar0 = devinfo->resources[0].address; + bar0_size = devinfo->resources[0].size; + bar1 = devinfo->resources[1].address; + bar1_size = devinfo->resources[1].size; + printf("\n\n--- GR-RASTA-ADCDAC[%d] ---\n", dev->minor_drv); + printf(" PCI BUS: 0x%x, SLOT: 0x%x, FUNCTION: 0x%x\n", + PCI_DEV_EXPAND(priv->pcidev)); + printf(" PCI VENDOR: 0x%04x, DEVICE: 0x%04x\n", + devinfo->id.vendor, devinfo->id.device); + printf(" PCI BAR[0]: 0x%lx - 0x%lx\n", bar0, bar0 + bar0_size - 1); + printf(" PCI BAR[1]: 0x%lx - 0x%lx\n", bar1, bar1 + bar1_size - 1); + printf(" IRQ: %d\n\n\n", devinfo->irq); + + /* all neccessary space assigned to GR-RASTA-ADCDAC target? */ + if ((bar0_size == 0) || (bar1_size == 0)) + return DRVMGR_ENORES; + + /* Let user override which PCI address the AHB masters of the + * RASTA-ADCDAC board access when doing DMA to CPU RAM. The AHB masters + * access the PCI Window of the AMBA bus, the MSB 4-bits of that address + * is translated according this config option before the address + * goes out on the PCI bus. + * Only the 4 MSB bits have an effect; + */ + value = drvmgr_dev_key_get(priv->dev, "ahbmst2pci", KEY_TYPE_INT); + if (value) + priv->ahbmst2pci_map = value->i; + else + priv->ahbmst2pci_map = AHBMST2PCIADR; /* default */ + + priv->genirq = genirq_init(16); + if ( priv->genirq == NULL ) { + free(priv); + dev->priv = NULL; + return DRVMGR_FAIL; + } + + if ( (status = gr_rasta_adcdac_hw_init1(priv)) != 0 ) { + genirq_destroy(priv->genirq); + free(priv); + dev->priv = NULL; + printf(" Failed to initialize GR-RASTA-ADCDAC HW: %d\n", status); + return DRVMGR_FAIL; + } + + /* Init amba bus */ + priv->config.abus = &priv->abus; + priv->config.ops = &ambapp_rasta_adcdac_ops; + priv->config.maps_up = &priv->bus_maps_up[0]; + priv->config.maps_down = &priv->bus_maps_down[0]; + if ( priv->dev->minor_drv < gr_rasta_adcdac_resources_cnt ) { + priv->config.resources = gr_rasta_adcdac_resources[priv->dev->minor_drv]; + } else { + priv->config.resources = NULL; + } + + /* Create and register AMBA PnP bus. */ + return ambapp_bus_register(dev, &priv->config); +} + +int gr_rasta_adcdac_init2(struct drvmgr_dev *dev) +{ + struct gr_rasta_adcdac_priv *priv = dev->priv; + + /* Clear any old interrupt requests */ + drvmgr_interrupt_clear(dev, 0); + + /* Enable System IRQ so that GR-RASTA-ADCDAC PCI target interrupt + * goes through. + * + * It is important to enable it in stage init2. If interrupts were + * enabled in init1 this might hang the system when more than one + * PCI board is connected, this is because PCI interrupts might + * be shared and PCI board 2 have not initialized and might + * therefore drive interrupt already when entering init1(). + */ + drvmgr_interrupt_register( + dev, + 0, + "gr_rasta_adcdac", + gr_rasta_adcdac_isr, + (void *)priv); + + return gr_rasta_adcdac_hw_init2(priv); +} + +int ambapp_rasta_adcdac_int_register( + struct drvmgr_dev *dev, + int irq, + const char *info, + drvmgr_isr handler, + void *arg) +{ + struct gr_rasta_adcdac_priv *priv = dev->parent->dev->priv; + rtems_interrupt_level level; + int status; + + rtems_interrupt_disable(level); + + status = genirq_register(priv->genirq, irq, handler, arg); + if ( status == 0 ) { + /* Clear IRQ for first registered handler */ + priv->irq->iclear = (1<<irq); + } else if ( status == 1 ) + status = 0; + + if (status != 0) { + rtems_interrupt_enable(level); + return DRVMGR_FAIL; + } + + status = genirq_enable(priv->genirq, irq, handler, arg); + if ( status == 0 ) { + /* Enable IRQ for first enabled handler only */ + priv->irq->mask[0] |= (1<<irq); /* unmask interrupt source */ + } else if ( status == 1 ) + status = 0; + + rtems_interrupt_enable(level); + + return status; +} + +int ambapp_rasta_adcdac_int_unregister( + struct drvmgr_dev *dev, + int irq, + drvmgr_isr isr, + void *arg) +{ + struct gr_rasta_adcdac_priv *priv = dev->parent->dev->priv; + rtems_interrupt_level level; + int status; + + rtems_interrupt_disable(level); + + status = genirq_disable(priv->genirq, irq, isr, arg); + if ( status == 0 ) { + /* Disable IRQ only when no enabled handler exists */ + priv->irq->mask[0] &= ~(1<<irq); /* mask interrupt source */ + } + + status = genirq_unregister(priv->genirq, irq, isr, arg); + if ( status != 0 ) + status = DRVMGR_FAIL; + + rtems_interrupt_enable(level); + + return status; +} + +int ambapp_rasta_adcdac_int_unmask( + struct drvmgr_dev *dev, + int irq) +{ + struct gr_rasta_adcdac_priv *priv = dev->parent->dev->priv; + rtems_interrupt_level level; + + DBG("RASTA-ADCDAC IRQ %d: unmask\n", irq); + + if ( genirq_check(priv->genirq, irq) ) + return DRVMGR_EINVAL; + + rtems_interrupt_disable(level); + + /* Enable IRQ for first enabled handler only */ + priv->irq->mask[0] |= (1<<irq); /* unmask interrupt source */ + + rtems_interrupt_enable(level); + + return DRVMGR_OK; +} + +int ambapp_rasta_adcdac_int_mask( + struct drvmgr_dev *dev, + int irq) +{ + struct gr_rasta_adcdac_priv *priv = dev->parent->dev->priv; + rtems_interrupt_level level; + + DBG("RASTA-ADCDAC IRQ %d: mask\n", irq); + + if ( genirq_check(priv->genirq, irq) ) + return DRVMGR_EINVAL; + + rtems_interrupt_disable(level); + + /* Disable/mask IRQ */ + priv->irq->mask[0] &= ~(1<<irq); /* mask interrupt source */ + + rtems_interrupt_enable(level); + + return DRVMGR_OK; +} + +int ambapp_rasta_adcdac_int_clear( + struct drvmgr_dev *dev, + int irq) +{ + struct gr_rasta_adcdac_priv *priv = dev->parent->dev->priv; + + if ( genirq_check(priv->genirq, irq) ) + return DRVMGR_FAIL; + + priv->irq->iclear = (1<<irq); + + return DRVMGR_OK; +} + +int ambapp_rasta_adcdac_get_params(struct drvmgr_dev *dev, struct drvmgr_bus_params *params) +{ + struct gr_rasta_adcdac_priv *priv = dev->parent->dev->priv; + + /* Device name prefix pointer, skip /dev */ + params->dev_prefix = &priv->prefix[5]; + + return 0; +} + +void gr_rasta_adcdac_print_dev(struct drvmgr_dev *dev, int options) +{ + struct gr_rasta_adcdac_priv *priv = dev->priv; + struct pci_dev_info *devinfo = priv->devinfo; + uint32_t bar0, bar1, bar0_size, bar1_size; + + /* Print */ + printf("--- GR-RASTA-ADCDAC [bus 0x%x, dev 0x%x, fun 0x%x] ---\n", + PCI_DEV_EXPAND(priv->pcidev)); + + bar0 = devinfo->resources[0].address; + bar0_size = devinfo->resources[0].size; + bar1 = devinfo->resources[1].address; + bar1_size = devinfo->resources[1].size; + + printf(" PCI BAR[0]: 0x%lx - 0x%lx\n", bar0, bar0 + bar0_size - 1); + printf(" PCI BAR[1]: 0x%lx - 0x%lx\n", bar1, bar1 + bar1_size - 1); + printf(" IRQ REGS: 0x%x\n", (unsigned int)priv->irq); + printf(" IRQ: %d\n", devinfo->irq); + printf(" PCI REVISION: %d\n", devinfo->rev); + printf(" FREQ: %d Hz\n", priv->version->amba_freq_hz); + printf(" IMASK: 0x%08x\n", priv->irq->mask[0]); + printf(" IPEND: 0x%08x\n", priv->irq->ipend); + + /* Print amba config */ + if ( options & RASTA_ADCDAC_OPTIONS_AMBA ) { + ambapp_print(&priv->abus, 10); + } +#if 0 + /* Print IRQ handlers and their arguments */ + if ( options & RASTA_ADCDAC_OPTIONS_IRQ ) { + int i; + for(i=0; i<16; i++) { + printf(" IRQ[%02d]: 0x%x, arg: 0x%x\n", + i, (unsigned int)priv->isrs[i].handler, (unsigned int)priv->isrs[i].arg); + } + } +#endif +} + +void gr_rasta_adcdac_print(int options) +{ + struct pci_drv_info *drv = &gr_rasta_adcdac_info; + struct drvmgr_dev *dev; + + dev = drv->general.dev; + while(dev) { + gr_rasta_adcdac_print_dev(dev, options); + dev = dev->next_in_drv; + } +} diff --git a/c/src/lib/libbsp/sparc/shared/pci/gr_rasta_io.c b/c/src/lib/libbsp/sparc/shared/pci/gr_rasta_io.c new file mode 100644 index 0000000000..071636ba45 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/pci/gr_rasta_io.c @@ -0,0 +1,688 @@ +/* GR-RASTA-IO PCI Target driver. + * + * COPYRIGHT (c) 2008. + * Aeroflex Gaisler AB. + * + * Configures the GR-RASTA-IO interface PCI board. + * This driver provides a AMBA PnP bus by using the general part + * of the AMBA PnP bus driver (ambapp_bus.c). + * + * Driver resources for the AMBA PnP bus provided can be set using + * gr_rasta_io_set_resources(). + * + * 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-05, Daniel Hellstrom <daniel@gaisler.com> + * Created + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <bsp.h> +#include <rtems/bspIo.h> +#include <pci.h> + +#include <ambapp.h> +#include <grlib.h> +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> +#include <drvmgr/pci_bus.h> +#include <genirq.h> + +#include <gr_rasta_io.h> + +/* Determines which PCI address the AHB masters will access, it should be + * set so that the masters can access the CPU RAM. Default is base of CPU RAM, + * CPU RAM is mapped 1:1 to PCI space. + */ +extern unsigned int _RAM_START; +#define AHBMST2PCIADR (((unsigned int)&_RAM_START) & 0xf0000000) + +/* Offset from 0x80000000 (dual bus version) */ +#define AHB1_BASE_ADDR 0x80000000 +#define AHB1_IOAREA_BASE_ADDR 0x80100000 + +/* #define DEBUG 1 */ + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +/* PCI ID */ +#define PCIID_VENDOR_GAISLER 0x1AC8 + +int gr_rasta_io_init1(struct drvmgr_dev *dev); +int gr_rasta_io_init2(struct drvmgr_dev *dev); + +struct grpci_regs { + volatile unsigned int cfg_stat; + volatile unsigned int bar0; + volatile unsigned int page0; + volatile unsigned int bar1; + volatile unsigned int page1; + volatile unsigned int iomap; + volatile unsigned int stat_cmd; +}; + +struct gr_rasta_io_ver { + const unsigned int amba_freq_hz; /* The frequency */ + const unsigned int amba_ioarea; /* The address where the PnP IOAREA starts at */ +}; + +/* Private data structure for driver */ +struct gr_rasta_io_priv { + /* Driver management */ + struct drvmgr_dev *dev; + char prefix[16]; + + /* PCI */ + pci_dev_t pcidev; + struct pci_dev_info *devinfo; + uint32_t ahbmst2pci_map; + + /* IRQ */ + genirq_t genirq; + + /* GR-RASTA-IO */ + struct gr_rasta_io_ver *version; + struct irqmp_regs *irq; + struct grpci_regs *grpci; + struct drvmgr_map_entry bus_maps_down[3]; + struct drvmgr_map_entry bus_maps_up[2]; + + /* AMBA Plug&Play information on GR-RASTA-IO */ + struct ambapp_bus abus; + struct ambapp_mmap amba_maps[4]; + struct ambapp_config config; +}; + +struct gr_rasta_io_ver gr_rasta_io_ver0 = { + .amba_freq_hz = 30000000, + .amba_ioarea = 0x80100000, +}; + +struct gr_rasta_io_ver gr_rasta_io_ver1 = { + .amba_freq_hz = 50000000, + .amba_ioarea = 0x80100000, +}; + +int ambapp_rasta_io_int_register( + struct drvmgr_dev *dev, + int irq, + const char *info, + drvmgr_isr handler, + void *arg); +int ambapp_rasta_io_int_unregister( + struct drvmgr_dev *dev, + int irq, + drvmgr_isr handler, + void *arg); +int ambapp_rasta_io_int_unmask( + struct drvmgr_dev *dev, + int irq); +int ambapp_rasta_io_int_mask( + struct drvmgr_dev *dev, + int irq); +int ambapp_rasta_io_int_clear( + struct drvmgr_dev *dev, + int irq); +int ambapp_rasta_io_get_params( + struct drvmgr_dev *dev, + struct drvmgr_bus_params *params); + +struct ambapp_ops ambapp_rasta_io_ops = { + .int_register = ambapp_rasta_io_int_register, + .int_unregister = ambapp_rasta_io_int_unregister, + .int_unmask = ambapp_rasta_io_int_unmask, + .int_mask = ambapp_rasta_io_int_mask, + .int_clear = ambapp_rasta_io_int_clear, + .get_params = ambapp_rasta_io_get_params +}; + +struct drvmgr_drv_ops gr_rasta_io_ops = +{ + .init = {gr_rasta_io_init1, gr_rasta_io_init2, NULL, NULL}, + .remove = NULL, + .info = NULL +}; + +struct pci_dev_id_match gr_rasta_io_ids[] = +{ + PCIID_DEVVEND(PCIID_VENDOR_GAISLER, PCIID_DEVICE_GR_RASTA_IO), + PCIID_DEVVEND(PCIID_VENDOR_GAISLER_OLD, PCIID_DEVICE_GR_RASTA_IO_OLD), + PCIID_END_TABLE /* Mark end of table */ +}; + +struct pci_drv_info gr_rasta_io_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_PCI_GAISLER_RASTAIO_ID, /* Driver ID */ + "GR-RASTA-IO_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_PCI, /* Bus Type */ + &gr_rasta_io_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + 0, + }, + &gr_rasta_io_ids[0] +}; + +/* Driver resources configuration for the AMBA bus on the GR-RASTA-IO board. + * It is declared weak so that the user may override it from the project file, + * if the default settings are not enough. + * + * The configuration consists of an array of configuration pointers, each + * pointer determine the configuration of one GR-RASTA-IO board. Pointer + * zero is for board0, pointer 1 for board1 and so on. + * + * The array must end with a NULL pointer. + */ +struct drvmgr_bus_res *gr_rasta_io_resources[] __attribute__((weak)) = +{ + NULL +}; +int gr_rasta_io_resources_cnt = 0; + +void gr_rasta_io_register_drv(void) +{ + DBG("Registering GR-RASTA-IO PCI driver\n"); + drvmgr_drv_register(&gr_rasta_io_info.general); +} + +void gr_rasta_io_isr (void *arg) +{ + struct gr_rasta_io_priv *priv = arg; + unsigned int status, tmp; + int irq; + tmp = status = priv->irq->ipend; + + /* DBG("GR-RASTA-IO: IRQ 0x%x\n",status); */ + + for(irq=0; irq<16; irq++) { + if ( status & (1<<irq) ) { + genirq_doirq(priv->genirq, irq); + priv->irq->iclear = (1<<irq); + status &= ~(1<<irq); + if ( status == 0 ) + break; + } + } + + /* ACK interrupt, this is because PCI is Level, so the IRQ Controller still drives the IRQ. */ + if ( tmp ) + drvmgr_interrupt_clear(priv->dev, 0); + + DBG("RASTA-IO-IRQ: 0x%x\n", tmp); +} + +int gr_rasta_io_hw_init(struct gr_rasta_io_priv *priv) +{ + unsigned int *page0 = NULL; + struct ambapp_dev *tmp; + int status; + struct ambapp_ahb_info *ahb; + struct pci_dev_info *devinfo = priv->devinfo; + uint32_t bar0, bar0_size; + + /* Select version of GR-RASTA-IO board */ + switch (devinfo->rev) { + case 0: + priv->version = &gr_rasta_io_ver0; + break; + case 1: + priv->version = &gr_rasta_io_ver1; + break; + default: + return -2; + } + + bar0 = devinfo->resources[0].address; + bar0_size = devinfo->resources[0].size; + page0 = (unsigned int *)(bar0 + bar0_size/2); + + /* Point PAGE0 to start of Plug and Play information */ + *page0 = priv->version->amba_ioarea & 0xf0000000; + +#if 0 + { + uint32_t data; + /* set parity error response */ + pci_cfg_r32(priv->pcidev, PCI_COMMAND, &data); + pci_cfg_w32(priv->pcidev, PCI_COMMAND, (data|PCI_COMMAND_PARITY)); + } +#endif + + /* Setup cache line size. Default cache line size will result in + * poor performance (256 word fetches), 0xff will set it according + * to the max size of the PCI FIFO. + */ + pci_cfg_w8(priv->pcidev, PCI_CACHE_LINE_SIZE, 0xff); + + /* Scan AMBA Plug&Play */ + + /* AMBA MAP bar0 (in CPU) ==> 0x80000000(remote amba address) */ + priv->amba_maps[0].size = bar0_size/2; + priv->amba_maps[0].local_adr = bar0; + priv->amba_maps[0].remote_adr = 0x80000000; + + /* AMBA MAP bar1 (in CPU) ==> 0x40000000(remote amba address) */ + priv->amba_maps[1].size = devinfo->resources[1].size; + priv->amba_maps[1].local_adr = devinfo->resources[1].address; + priv->amba_maps[1].remote_adr = 0x40000000; + + /* Addresses not matching with map be untouched */ + priv->amba_maps[2].size = 0xfffffff0; + priv->amba_maps[2].local_adr = 0; + priv->amba_maps[2].remote_adr = 0; + + /* Mark end of table */ + priv->amba_maps[3].size=0; + priv->amba_maps[3].local_adr = 0; + priv->amba_maps[3].remote_adr = 0; + + /* Start AMBA PnP scan at first AHB bus */ + ambapp_scan(&priv->abus, + bar0 + (priv->version->amba_ioarea & ~0xf0000000), + NULL, &priv->amba_maps[0]); + + /* Initialize Frequency of AMBA bus */ + ambapp_freq_init(&priv->abus, NULL, priv->version->amba_freq_hz); + + /* Point PAGE0 to start of APB area */ + *page0 = 0x80000000; + + /* Find GRPCI controller */ + tmp = (void *)ambapp_for_each(&priv->abus, + (OPTIONS_ALL|OPTIONS_APB_SLVS), + VENDOR_GAISLER, GAISLER_PCIFBRG, + ambapp_find_by_idx, NULL); + if ( !tmp ) { + return -3; + } + priv->grpci = (struct grpci_regs *)((struct ambapp_apb_info *)tmp->devinfo)->start; + + /* Set GRPCI mmap so that AMBA masters can access CPU-RAM over + * the PCI window. + */ + priv->grpci->cfg_stat = (priv->grpci->cfg_stat & 0x0fffffff) | + (priv->ahbmst2pci_map & 0xf0000000); + priv->grpci->page1 = 0x40000000; + + /* Find IRQ controller, Clear all current IRQs */ + tmp = (void *)ambapp_for_each(&priv->abus, + (OPTIONS_ALL|OPTIONS_APB_SLVS), + VENDOR_GAISLER, GAISLER_IRQMP, + ambapp_find_by_idx, NULL); + if ( !tmp ) { + return -4; + } + priv->irq = (struct irqmp_regs *)DEV_TO_APB(tmp)->start; + /* Set up GR-RASTA-IO irq controller */ + priv->irq->mask[0] = 0; + priv->irq->iclear = 0xffff; + priv->irq->ilevel = 0; + + /* DOWN streams translation table */ + priv->bus_maps_down[0].name = "PCI BAR0 -> AMBA"; + priv->bus_maps_down[0].size = priv->amba_maps[0].size; + priv->bus_maps_down[0].from_adr = (void *)priv->amba_maps[0].local_adr; + priv->bus_maps_down[0].to_adr = (void *)priv->amba_maps[0].remote_adr; + + priv->bus_maps_down[1].name = "PCI BAR1 -> AMBA"; + priv->bus_maps_down[1].size = priv->amba_maps[1].size; + priv->bus_maps_down[1].from_adr = (void *)priv->amba_maps[1].local_adr; + priv->bus_maps_down[1].to_adr = (void *)priv->amba_maps[1].remote_adr; + + /* Mark end of translation table */ + priv->bus_maps_down[2].size = 0; + + /* Find GRPCI controller AHB Slave interface */ + tmp = (void *)ambapp_for_each(&priv->abus, + (OPTIONS_ALL|OPTIONS_AHB_SLVS), + VENDOR_GAISLER, GAISLER_PCIFBRG, + ambapp_find_by_idx, NULL); + if ( !tmp ) { + return -5; + } + ahb = (struct ambapp_ahb_info *)tmp->devinfo; + + /* UP streams translation table */ + priv->bus_maps_up[0].name = "AMBA GRPCI Window"; + priv->bus_maps_up[0].size = ahb->mask[0]; /* AMBA->PCI Window on GR-RASTA-IO board */ + priv->bus_maps_up[0].from_adr = (void *)ahb->start[0]; + priv->bus_maps_up[0].to_adr = (void *) + (priv->ahbmst2pci_map & 0xf0000000); + + /* Mark end of translation table */ + priv->bus_maps_up[1].size = 0; + + /* Successfully registered the RASTA board */ + return 0; +} + +int gr_rasta_io_hw_init2(struct gr_rasta_io_priv *priv) +{ + /* Enable DMA by enabling PCI target as master */ + pci_master_enable(priv->pcidev); + + return DRVMGR_OK; +} + +/* Called when a PCI target is found with the PCI device and vendor ID + * given in gr_rasta_io_ids[]. + */ +int gr_rasta_io_init1(struct drvmgr_dev *dev) +{ + struct gr_rasta_io_priv *priv; + struct pci_dev_info *devinfo; + int status; + uint32_t bar0, bar1, bar0_size, bar1_size; + union drvmgr_key_value *value; + + priv = malloc(sizeof(struct gr_rasta_io_priv)); + if ( !priv ) + return DRVMGR_NOMEM; + + memset(priv, 0, sizeof(*priv)); + dev->priv = priv; + priv->dev = dev; + + /* Determine number of configurations */ + if ( gr_rasta_io_resources_cnt == 0 ) { + while ( gr_rasta_io_resources[gr_rasta_io_resources_cnt] ) + gr_rasta_io_resources_cnt++; + } + + /* Generate Device prefix */ + + strcpy(priv->prefix, "/dev/rastaio0"); + priv->prefix[12] += dev->minor_drv; + mkdir(priv->prefix, S_IRWXU | S_IRWXG | S_IRWXO); + priv->prefix[13] = '/'; + priv->prefix[14] = '\0'; + + priv->devinfo = devinfo = (struct pci_dev_info *)dev->businfo; + priv->pcidev = devinfo->pcidev; + bar0 = devinfo->resources[0].address; + bar0_size = devinfo->resources[0].size; + bar1 = devinfo->resources[1].address; + bar1_size = devinfo->resources[1].size; + printf("\n\n--- GR-RASTA-IO[%d] ---\n", dev->minor_drv); + printf(" PCI BUS: 0x%x, SLOT: 0x%x, FUNCTION: 0x%x\n", + PCI_DEV_EXPAND(priv->pcidev)); + printf(" PCI VENDOR: 0x%04x, DEVICE: 0x%04x\n", + devinfo->id.vendor, devinfo->id.device); + printf(" PCI BAR[0]: 0x%lx - 0x%lx\n", bar0, bar0 + bar0_size - 1); + printf(" PCI BAR[1]: 0x%lx - 0x%lx\n", bar1, bar1 + bar1_size - 1); + printf(" IRQ: %d\n\n\n", devinfo->irq); + + /* all neccessary space assigned to GR-RASTA-IO target? */ + if ((bar0_size == 0) || (bar1_size == 0)) + return DRVMGR_ENORES; + + /* Let user override which PCI address the AHB masters of the + * GR-RASTA-IO board access when doing DMA to CPU RAM. The AHB masters + * access the PCI Window of the AMBA bus, the MSB 4-bits of that address + * is translated according this config option before the address + * goes out on the PCI bus. + * Only the 4 MSB bits have an effect; + */ + value = drvmgr_dev_key_get(priv->dev, "ahbmst2pci", KEY_TYPE_INT); + if (value) + priv->ahbmst2pci_map = value->i; + else + priv->ahbmst2pci_map = AHBMST2PCIADR; /* default */ + + priv->genirq = genirq_init(16); + if ( priv->genirq == NULL ) { + free(priv); + dev->priv = NULL; + return DRVMGR_FAIL; + } + + status = gr_rasta_io_hw_init(priv); + if ( status != 0 ) { + genirq_destroy(priv->genirq); + free(priv); + dev->priv = NULL; + printf(" Failed to initialize GR-RASTA-IO HW: %d\n", status); + return DRVMGR_FAIL; + } + + /* Init amba bus */ + priv->config.abus = &priv->abus; + priv->config.ops = &ambapp_rasta_io_ops; + priv->config.maps_up = &priv->bus_maps_up[0]; + priv->config.maps_down = &priv->bus_maps_down[0]; + if ( priv->dev->minor_drv < gr_rasta_io_resources_cnt ) { + priv->config.resources = gr_rasta_io_resources[priv->dev->minor_drv]; + } else { + priv->config.resources = NULL; + } + + /* Create and register AMBA PnP bus. */ + return ambapp_bus_register(dev, &priv->config); +} + +int gr_rasta_io_init2(struct drvmgr_dev *dev) +{ + struct gr_rasta_io_priv *priv = dev->priv; + + /* Clear any old interrupt requests */ + drvmgr_interrupt_clear(dev, 0); + + /* Enable System IRQ so that GR-RASTA-IO PCI target interrupt goes + * through. + * + * It is important to enable it in stage init2. If interrupts were + * enabled in init1 this might hang the system when more than one + * PCI board is connected, this is because PCI interrupts might + * be shared and PCI board 2 have not initialized and + * might therefore drive interrupt already when entering init1(). + */ + drvmgr_interrupt_register( + dev, + 0, + "gr_rasta_io", + gr_rasta_io_isr, + (void *)priv); + + return gr_rasta_io_hw_init2(priv); +} + +int ambapp_rasta_io_int_register( + struct drvmgr_dev *dev, + int irq, + const char *info, + drvmgr_isr handler, + void *arg) +{ + struct gr_rasta_io_priv *priv = dev->parent->dev->priv; + rtems_interrupt_level level; + int status; + + rtems_interrupt_disable(level); + + status = genirq_register(priv->genirq, irq, handler, arg); + if ( status == 0 ) { + /* Clear IRQ for first registered handler */ + priv->irq->iclear = (1<<irq); + } else if ( status == 1 ) + status = 0; + + if (status != 0) { + rtems_interrupt_enable(level); + return DRVMGR_FAIL; + } + + status = genirq_enable(priv->genirq, irq, handler, arg); + if ( status == 0 ) { + /* Enable IRQ for first enabled handler only */ + priv->irq->mask[0] |= (1<<irq); /* unmask interrupt source */ + } else if ( status == 1 ) + status = 0; + + rtems_interrupt_enable(level); + + return status; +} + +int ambapp_rasta_io_int_unregister( + struct drvmgr_dev *dev, + int irq, + drvmgr_isr isr, + void *arg) +{ + struct gr_rasta_io_priv *priv = dev->parent->dev->priv; + rtems_interrupt_level level; + int status; + + rtems_interrupt_disable(level); + + status = genirq_disable(priv->genirq, irq, isr, arg); + if ( status == 0 ) { + /* Disable IRQ only when no enabled handler exists */ + priv->irq->mask[0] &= ~(1<<irq); /* mask interrupt source */ + } + + status = genirq_unregister(priv->genirq, irq, isr, arg); + if ( status != 0 ) + status = DRVMGR_FAIL; + + rtems_interrupt_enable(level); + + return status; +} + +int ambapp_rasta_io_int_unmask( + struct drvmgr_dev *dev, + int irq) +{ + struct gr_rasta_io_priv *priv = dev->parent->dev->priv; + rtems_interrupt_level level; + + DBG("RASTA-IO IRQ %d: unmask\n", irq); + + if ( genirq_check(priv->genirq, irq) ) + return DRVMGR_EINVAL; + + rtems_interrupt_disable(level); + + /* Enable IRQ for first enabled handler only */ + priv->irq->mask[0] |= (1<<irq); /* unmask interrupt source */ + + rtems_interrupt_enable(level); + + return DRVMGR_OK; +} + +int ambapp_rasta_io_int_mask( + struct drvmgr_dev *dev, + int irq) +{ + struct gr_rasta_io_priv *priv = dev->parent->dev->priv; + rtems_interrupt_level level; + + DBG("RASTA-IO IRQ %d: mask\n", irq); + + if ( genirq_check(priv->genirq, irq) ) + return DRVMGR_EINVAL; + + rtems_interrupt_disable(level); + + /* Disable/mask IRQ */ + priv->irq->mask[0] &= ~(1<<irq); /* mask interrupt source */ + + rtems_interrupt_enable(level); + + return DRVMGR_OK; +} + +int ambapp_rasta_io_int_clear( + struct drvmgr_dev *dev, + int irq) +{ + struct gr_rasta_io_priv *priv = dev->parent->dev->priv; + + if ( genirq_check(priv->genirq, irq) ) + return DRVMGR_EINVAL; + + priv->irq->iclear = (1<<irq); + + return DRVMGR_OK; +} + +int ambapp_rasta_io_get_params(struct drvmgr_dev *dev, struct drvmgr_bus_params *params) +{ + struct gr_rasta_io_priv *priv = dev->parent->dev->priv; + + /* Device name prefix pointer, skip /dev */ + params->dev_prefix = &priv->prefix[5]; + + return 0; +} + +void gr_rasta_io_print_dev(struct drvmgr_dev *dev, int options) +{ + struct gr_rasta_io_priv *priv = dev->priv; + struct pci_dev_info *devinfo = priv->devinfo; + uint32_t bar0, bar1, bar0_size, bar1_size; + + /* Print */ + printf("--- GR-RASTA-IO [bus 0x%x, dev 0x%x, fun 0x%x] ---\n", + PCI_DEV_EXPAND(priv->pcidev)); + + bar0 = devinfo->resources[0].address; + bar0_size = devinfo->resources[0].size; + bar1 = devinfo->resources[1].address; + bar1_size = devinfo->resources[1].size; + + printf(" PCI BAR[0]: 0x%lx - 0x%lx\n", bar0, bar0 + bar0_size - 1); + printf(" PCI BAR[1]: 0x%lx - 0x%lx\n", bar1, bar1 + bar1_size - 1); + printf(" IRQ REGS: 0x%x\n", (unsigned int)priv->irq); + printf(" IRQ: %d\n", devinfo->irq); + printf(" PCI REVISION: %d\n", devinfo->rev); + printf(" FREQ: %d Hz\n", priv->version->amba_freq_hz); + printf(" IMASK: 0x%08x\n", priv->irq->mask[0]); + printf(" IPEND: 0x%08x\n", priv->irq->ipend); + + /* Print amba config */ + if ( options & RASTA_IO_OPTIONS_AMBA ) { + ambapp_print(&priv->abus, 10); + } + +#if 0 + /* Print IRQ handlers and their arguments */ + if ( options & RASTA_IO_OPTIONS_IRQ ) { + int i; + for(i=0; i<16; i++) { + printf(" IRQ[%02d]: 0x%x, arg: 0x%x\n", + i, (unsigned int)priv->isrs[i].handler, (unsigned int)priv->isrs[i].arg); + } + } +#endif +} + +void gr_rasta_io_print(int options) +{ + struct pci_drv_info *drv = &gr_rasta_io_info; + struct drvmgr_dev *dev; + + dev = drv->general.dev; + while(dev) { + gr_rasta_io_print_dev(dev, options); + dev = dev->next_in_drv; + } +} diff --git a/c/src/lib/libbsp/sparc/shared/pci/gr_rasta_spw_router.c b/c/src/lib/libbsp/sparc/shared/pci/gr_rasta_spw_router.c new file mode 100644 index 0000000000..c6a17f0ecf --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/pci/gr_rasta_spw_router.c @@ -0,0 +1,681 @@ +/* GR-RASTA-SPW-ROUTER PCI Target driver. + * + * COPYRIGHT (c) 2011. + * Aeroflex Gaisler AB. + * + * 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. + * + * Configures the GR-RASTA-SPW-ROUTER interface PCI board. + * This driver provides a AMBA PnP bus by using the general part + * of the AMBA PnP bus driver (ambapp_bus.c). Based on the + * GR-RASTA-IO driver. + * + * 2011-06-07, Marko Isomaki <marko@gaisler.com> + * Created + * 2011-07-14, Daniel Hellstrom <daniel@gaisler.com> + * Converted to new driver-manger/libpci layers + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <bsp.h> +#include <rtems/bspIo.h> +#include <pci.h> + +#include <ambapp.h> +#include <grlib.h> +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> +#include <drvmgr/pci_bus.h> +#include <genirq.h> + +/*#include <gr_rasta_spw_router.h> */ + +/* Determines which PCI address the AHB masters will access, it should be + * set so that the masters can access the CPU RAM. Default is base of CPU RAM, + * CPU RAM is mapped 1:1 to PCI space. + */ +extern unsigned int _RAM_START; +#define AHBMST2PCIADR (((unsigned int)&_RAM_START) & 0xf0000000) + +/* Offset from 0x80000000 (dual bus version) */ +#define AHB1_BASE_ADDR 0x80000000 +#define AHB1_IOAREA_BASE_ADDR 0x80100000 + +#define GRPCI2_BAR0_TO_AHB_MAP 0x04 /* Fixme */ +#define GRPCI2_PCI_CONFIG 0x20 /* Fixme */ +#define RASTA_SPW_ROUTER_OPTIONS_AMBA 0x01 /* Print AMBA bus devices */ /* Fixme */ +#define RASTA_SPW_ROUTER_OPTIONS_IRQ 0x02 /* Print current IRQ setup */ /* Fixme */ + + +/* #define DEBUG 1 */ + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +/* PCI ID */ +#define PCIID_VENDOR_GAISLER 0x1AC8 + +int gr_rasta_spw_router_init1(struct drvmgr_dev *dev); +int gr_rasta_spw_router_init2(struct drvmgr_dev *dev); + +struct grpci2_regs { + volatile unsigned int ctrl; + volatile unsigned int statcap; + volatile unsigned int pcimstprefetch; + volatile unsigned int ahbtopciiomap; + volatile unsigned int dmactrl; + volatile unsigned int dmadesc; + volatile unsigned int dmachanact; + volatile unsigned int reserved; + volatile unsigned int pcibartoahb[6]; + volatile unsigned int reserved2[2]; + volatile unsigned int ahbtopcimemmap[16]; + volatile unsigned int trcctrl; + volatile unsigned int trccntmode; + volatile unsigned int trcadpat; + volatile unsigned int trcadmask; + volatile unsigned int trcctrlsigpat; + volatile unsigned int trcctrlsigmask; + volatile unsigned int trcadstate; + volatile unsigned int trcctrlsigstate; +}; + +struct gr_rasta_spw_router_ver { + const unsigned int amba_freq_hz; /* The frequency */ + const unsigned int amba_ioarea; /* The address where the PnP IOAREA starts at */ +}; + +/* Private data structure for driver */ +struct gr_rasta_spw_router_priv { + /* Driver management */ + struct drvmgr_dev *dev; + char prefix[20]; + + /* PCI */ + pci_dev_t pcidev; + struct pci_dev_info *devinfo; + uint32_t ahbmst2pci_map; + + /* IRQ */ + genirq_t genirq; + + /* GR-RASTA-SPW-ROUTER */ + struct gr_rasta_spw_router_ver *version; + struct irqmp_regs *irq; + struct grpci2_regs *grpci2; + struct drvmgr_map_entry bus_maps_up[2]; + struct drvmgr_map_entry bus_maps_down[2]; + + /* AMBA Plug&Play information on GR-RASTA-SPW-ROUTER */ + struct ambapp_bus abus; + struct ambapp_mmap amba_maps[3]; + struct ambapp_config config; +}; + +struct gr_rasta_spw_router_ver gr_rasta_spw_router_ver0 = { + .amba_freq_hz = 50000000, + .amba_ioarea = 0xfff00000, +}; + +int ambapp_rasta_spw_router_int_register( + struct drvmgr_dev *dev, + int irq, + const char *info, + drvmgr_isr handler, + void *arg); +int ambapp_rasta_spw_router_int_unregister( + struct drvmgr_dev *dev, + int irq, + drvmgr_isr handler, + void *arg); +int ambapp_rasta_spw_router_int_unmask( + struct drvmgr_dev *dev, + int irq); +int ambapp_rasta_spw_router_int_mask( + struct drvmgr_dev *dev, + int irq); +int ambapp_rasta_spw_router_int_clear( + struct drvmgr_dev *dev, + int irq); +int ambapp_rasta_spw_router_get_params( + struct drvmgr_dev *dev, + struct drvmgr_bus_params *params); + +struct ambapp_ops ambapp_rasta_spw_router_ops = { + .int_register = ambapp_rasta_spw_router_int_register, + .int_unregister = ambapp_rasta_spw_router_int_unregister, + .int_unmask = ambapp_rasta_spw_router_int_unmask, + .int_mask = ambapp_rasta_spw_router_int_mask, + .int_clear = ambapp_rasta_spw_router_int_clear, + .get_params = ambapp_rasta_spw_router_get_params +}; + +struct drvmgr_drv_ops gr_rasta_spw_router_ops = +{ + .init = {gr_rasta_spw_router_init1, gr_rasta_spw_router_init2, NULL, NULL}, + .remove = NULL, + .info = NULL +}; + +struct pci_dev_id_match gr_rasta_spw_router_ids[] = +{ + PCIID_DEVVEND(PCIID_VENDOR_GAISLER, PCIID_DEVICE_GR_RASTA_SPW_RTR), + PCIID_END_TABLE /* Mark end of table */ +}; + +struct pci_drv_info gr_rasta_spw_router_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_PCI_GAISLER_RASTA_SPW_ROUTER_ID, /* Driver ID */ + "GR-RASTA-SPW_ROUTER_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_PCI, /* Bus Type */ + &gr_rasta_spw_router_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + sizeof(struct gr_rasta_spw_router_priv), + }, + &gr_rasta_spw_router_ids[0] +}; + +/* Driver resources configuration for the AMBA bus on the GR-RASTA-SPW-ROUTER board. + * It is declared weak so that the user may override it from the project file, + * if the default settings are not enough. + * + * The configuration consists of an array of configuration pointers, each + * pointer determine the configuration of one GR-RASTA-SPW-ROUTER board. Pointer + * zero is for board0, pointer 1 for board1 and so on. + * + * The array must end with a NULL pointer. + */ +struct drvmgr_bus_res *gr_rasta_spw_router_resources[] __attribute__((weak)) = +{ + NULL +}; +int gr_rasta_spw_router_resources_cnt = 0; + +void gr_rasta_spw_router_register_drv(void) +{ + DBG("Registering GR-RASTA-SPW-ROUTER PCI driver\n"); + drvmgr_drv_register(&gr_rasta_spw_router_info.general); +} + +void gr_rasta_spw_router_isr(void *arg) +{ + struct gr_rasta_spw_router_priv *priv = arg; + unsigned int status, tmp; + int irq; + tmp = status = priv->irq->ipend; + + /* DBG("GR-RASTA-SPW-ROUTER: IRQ 0x%x\n",status); */ + + for(irq=0; irq<16; irq++) { + if ( status & (1<<irq) ) { + genirq_doirq(priv->genirq, irq); + priv->irq->iclear = (1<<irq); + status &= ~(1<<irq); + if ( status == 0 ) + break; + } + } + + /* ACK interrupt, this is because PCI is Level, so the IRQ Controller + * still drives the IRQ + */ + if ( tmp ) + drvmgr_interrupt_clear(priv->dev, 0); + + DBG("RASTA-SPW_ROUTER-IRQ: 0x%x\n", tmp); +} + +int gr_rasta_spw_router_hw_init(struct gr_rasta_spw_router_priv *priv) +{ + int i; + uint32_t data; + unsigned int ctrl; + uint8_t tmp2; + struct ambapp_dev *tmp; + int status; + struct ambapp_ahb_info *ahb; + uint8_t cap_ptr; + pci_dev_t pcidev = priv->pcidev; + struct pci_dev_info *devinfo = priv->devinfo; + + /* Select version of GR-RASTA-SPW-ROUTER board. Currently only one + * version + */ + switch (devinfo->rev) { + case 0: + priv->version = &gr_rasta_spw_router_ver0; + break; + default: + return -2; + } + + /* Check capabilities list bit */ + pci_cfg_r8(pcidev, PCI_STATUS, &tmp2); + + if (!((tmp2 >> 4) & 1)) { + /* Capabilities list not available which it should be in the GRPCI2 */ + return -3; + } + + /* Read capabilities pointer */ + pci_cfg_r8(pcidev, PCI_CAP_PTR, &cap_ptr); + + /* Set AHB address mappings for target PCI bars */ + pci_cfg_w32(pcidev, cap_ptr+GRPCI2_BAR0_TO_AHB_MAP, 0xffe00000); /* APB bus, AHB I/O bus 2 MB */ + + /* Set PCI bus to be big endian */ + pci_cfg_r32(pcidev, cap_ptr+GRPCI2_PCI_CONFIG, &data); + data = data & 0xFFFFFFFE; + pci_cfg_w32(pcidev, cap_ptr+GRPCI2_PCI_CONFIG, data); + +#if 0 + /* set parity error response */ + pci_cfg_r32(pcidev, PCI_COMMAND, &data); + pci_cfg_w32(pcidev, PCI_COMMAND, (data|PCI_COMMAND_PARITY)); +#endif + + /* Scan AMBA Plug&Play */ + + /* AMBA MAP bar0 (in router) ==> 0xffe00000(remote amba address) */ + priv->amba_maps[0].size = devinfo->resources[0].size; + priv->amba_maps[0].local_adr = devinfo->resources[0].address; + priv->amba_maps[0].remote_adr = 0xffe00000; + + /* Addresses not matching with map be untouched */ + priv->amba_maps[1].size = 0xfffffff0; + priv->amba_maps[1].local_adr = 0; + priv->amba_maps[1].remote_adr = 0; + + /* Mark end of table */ + priv->amba_maps[2].size=0; + + /* Start AMBA PnP scan at first AHB bus */ + ambapp_scan( + &priv->abus, + devinfo->resources[0].address + 0x100000, + NULL, + &priv->amba_maps[0]); + + /* Initialize Frequency of AMBA bus */ + ambapp_freq_init(&priv->abus, NULL, priv->version->amba_freq_hz); + + /* Find IRQ controller, Clear all current IRQs */ + tmp = ambapp_for_each(&priv->abus, + (OPTIONS_ALL|OPTIONS_APB_SLVS), + VENDOR_GAISLER, GAISLER_IRQMP, + ambapp_find_by_idx, NULL); + if ( !tmp ) { + return -4; + } + priv->irq = (struct irqmp_regs *)DEV_TO_APB(tmp)->start; + /* Set up GR-RASTA-SPW-ROUTER irq controller */ + priv->irq->mask[0] = 0; + priv->irq->iclear = 0xffff; + priv->irq->ilevel = 0; + + priv->bus_maps_down[0].name = "PCI BAR0 -> AMBA"; + priv->bus_maps_down[0].size = priv->amba_maps[0].size; + priv->bus_maps_down[0].from_adr = (void *)priv->amba_maps[0].local_adr; + priv->bus_maps_down[0].to_adr = (void *)priv->amba_maps[0].remote_adr; + priv->bus_maps_down[1].size = 0; + + /* Find GRPCI2 controller AHB Slave interface */ + tmp = (void *)ambapp_for_each(&priv->abus, + (OPTIONS_ALL|OPTIONS_AHB_SLVS), + VENDOR_GAISLER, GAISLER_GRPCI2, + ambapp_find_by_idx, NULL); + if ( !tmp ) { + return -5; + } + ahb = (struct ambapp_ahb_info *)tmp->devinfo; + priv->bus_maps_up[0].name = "AMBA GRPCI2 Window"; + priv->bus_maps_up[0].size = ahb->mask[0]; /* AMBA->PCI Window on GR-RASTA-SPW-ROUTER board */ + priv->bus_maps_up[0].from_adr = (void *)ahb->start[0]; + priv->bus_maps_up[0].to_adr = (void *) + (priv->ahbmst2pci_map & ~(ahb->mask[0]-1)); + priv->bus_maps_up[1].size = 0; + + /* Find GRPCI2 controller APB Slave interface */ + tmp = (void *)ambapp_for_each(&priv->abus, + (OPTIONS_ALL|OPTIONS_APB_SLVS), + VENDOR_GAISLER, GAISLER_GRPCI2, + ambapp_find_by_idx, NULL); + if ( !tmp ) { + return -6; + } + priv->grpci2 = (struct grpci2_regs *) + ((struct ambapp_apb_info *)tmp->devinfo)->start; + + /* Set AHB to PCI mapping for all AMBA AHB masters */ + for(i = 0; i < 16; i++) { + priv->grpci2->ahbtopcimemmap[i] = priv->ahbmst2pci_map & + ~(ahb->mask[0]-1); + } + + /* Make sure dirq(0) sampling is enabled */ + ctrl = priv->grpci2->ctrl; + ctrl = (ctrl & 0xFFFFFF0F) | (1 << 4); + printf("data: 0x%x\n", ctrl); + priv->grpci2->ctrl = ctrl; + + /* Successfully registered the RASTA-SPW-ROUTER board */ + return 0; +} + +int gr_rasta_spw_router_hw_init2(struct gr_rasta_spw_router_priv *priv) +{ + /* Enable DMA by enabling PCI target as master */ + pci_master_enable(priv->pcidev); + + return DRVMGR_OK; +} + +/* Called when a PCI target is found with the PCI device and vendor ID + * given in gr_rasta_spw_router_ids[]. + */ +int gr_rasta_spw_router_init1(struct drvmgr_dev *dev) +{ + struct gr_rasta_spw_router_priv *priv; + struct pci_dev_info *devinfo; + int status; + uint32_t bar0, bar0_size; + union drvmgr_key_value *value; + + priv = dev->priv; + if (!priv) + return DRVMGR_NOMEM; + + memset(priv, 0, sizeof(*priv)); + dev->priv = priv; + priv->dev = dev; + + /* Determine number of configurations */ + if ( gr_rasta_spw_router_resources_cnt == 0 ) { + while ( gr_rasta_spw_router_resources[gr_rasta_spw_router_resources_cnt] ) + gr_rasta_spw_router_resources_cnt++; + } + + /* Generate Device prefix */ + + strcpy(priv->prefix, "/dev/spwrouter0"); + priv->prefix[14] += dev->minor_drv; + mkdir(priv->prefix, S_IRWXU | S_IRWXG | S_IRWXO); + priv->prefix[15] = '/'; + priv->prefix[16] = '\0'; + + priv->devinfo = devinfo = (struct pci_dev_info *)dev->businfo; + priv->pcidev = devinfo->pcidev; + bar0 = devinfo->resources[0].address; + bar0_size = devinfo->resources[0].size; + printf("\n\n--- GR-RASTA-SPW-ROUTER[%d] ---\n", dev->minor_drv); + printf(" PCI BUS: 0x%x, SLOT: 0x%x, FUNCTION: 0x%x\n", + PCI_DEV_EXPAND(priv->pcidev)); + printf(" PCI VENDOR: 0x%04x, DEVICE: 0x%04x\n", + devinfo->id.vendor, devinfo->id.device); + printf(" PCI BAR[0]: 0x%08lx - 0x%08lx\n", bar0, bar0 + bar0_size - 1); + printf(" IRQ: %d\n\n\n", devinfo->irq); + + /* all neccessary space assigned to GR-RASTA-SPW-ROUTER target? */ + if (bar0_size == 0) + return DRVMGR_ENORES; + + /* Let user override which PCI address the AHB masters of the + * GR-RASTA-SPW board access when doing DMA to CPU RAM. The AHB masters + * access the PCI Window of the AMBA bus, the MSB 4-bits of that address + * is translated according this config option before the address + * goes out on the PCI bus. + * Only the 4 MSB bits have an effect; + */ + value = drvmgr_dev_key_get(priv->dev, "ahbmst2pci", KEY_TYPE_INT); + if (value) + priv->ahbmst2pci_map = value->i; + else + priv->ahbmst2pci_map = AHBMST2PCIADR; /* default */ + + priv->genirq = genirq_init(16); + if ( priv->genirq == NULL ) + return DRVMGR_FAIL; + + if ((status = gr_rasta_spw_router_hw_init(priv)) != 0) { + genirq_destroy(priv->genirq); + printf(" Failed to initialize GR-RASTA-SPW-ROUTER HW: %d\n", status); + return DRVMGR_FAIL; + } + + /* Init amba bus */ + priv->config.abus = &priv->abus; + priv->config.ops = &ambapp_rasta_spw_router_ops; + priv->config.maps_up = &priv->bus_maps_up[0]; + priv->config.maps_down = &priv->bus_maps_down[0]; + if ( priv->dev->minor_drv < gr_rasta_spw_router_resources_cnt ) { + priv->config.resources = gr_rasta_spw_router_resources[priv->dev->minor_drv]; + } else { + priv->config.resources = NULL; + } + + /* Create and register AMBA PnP bus. */ + return ambapp_bus_register(dev, &priv->config); +} + +int gr_rasta_spw_router_init2(struct drvmgr_dev *dev) +{ + struct gr_rasta_spw_router_priv *priv = dev->priv; + + /* Clear any old interrupt requests */ + drvmgr_interrupt_clear(dev, 0); + + /* Enable System IRQ so that GR-RASTA-SPW-ROUTER PCI target interrupt + * goes through. + * + * It is important to enable it in stage init2. If interrupts were + * enabled in init1 this might hang the system when more than one + * PCI board is connected, this is because PCI interrupts might + * be shared and PCI board 2 have not initialized and + * might therefore drive interrupt already when entering init1(). + */ + drvmgr_interrupt_register( + dev, + 0, + "gr_rasta_spw_router", + gr_rasta_spw_router_isr, + (void *)priv); + + return gr_rasta_spw_router_hw_init2(priv); +} + +int ambapp_rasta_spw_router_int_register( + struct drvmgr_dev *dev, + int irq, + const char *info, + drvmgr_isr handler, + void *arg) +{ + struct gr_rasta_spw_router_priv *priv = dev->parent->dev->priv; + rtems_interrupt_level level; + int status; + + rtems_interrupt_disable(level); + + status = genirq_register(priv->genirq, irq, handler, arg); + if (status == 0) { + /* Clear IRQ for first registered handler */ + priv->irq->iclear = (1<<irq); + } else if (status == 1) + status = 0; + + if (status != 0) { + rtems_interrupt_enable(level); + return DRVMGR_FAIL; + } + + status = genirq_enable(priv->genirq, irq, handler, arg); + if ( status == 0 ) { + /* Enable IRQ for first enabled handler only */ + priv->irq->mask[0] |= (1<<irq); /* unmask interrupt source */ + } else if ( status == 1 ) + status = 0; + + rtems_interrupt_enable(level); + + return status; +} + +int ambapp_rasta_spw_router_int_unregister( + struct drvmgr_dev *dev, + int irq, + drvmgr_isr isr, + void *arg) +{ + struct gr_rasta_spw_router_priv *priv = dev->parent->dev->priv; + rtems_interrupt_level level; + int status; + + rtems_interrupt_disable(level); + + status = genirq_disable(priv->genirq, irq, isr, arg); + if ( status == 0 ) { + /* Disable IRQ only when no enabled handler exists */ + priv->irq->mask[0] &= ~(1<<irq); /* mask interrupt source */ + } + + status = genirq_unregister(priv->genirq, irq, isr, arg); + if ( status != 0 ) + status = DRVMGR_FAIL; + + rtems_interrupt_enable(level); + + return status; +} + +int ambapp_rasta_spw_router_int_unmask( + struct drvmgr_dev *dev, + int irq) +{ + struct gr_rasta_spw_router_priv *priv = dev->parent->dev->priv; + rtems_interrupt_level level; + + DBG("RASTA-SPW-ROUTER IRQ %d: unmask\n", irq); + + if ( genirq_check(priv->genirq, irq) ) + return DRVMGR_EINVAL; + + rtems_interrupt_disable(level); + + /* Enable IRQ for first enabled handler only */ + priv->irq->mask[0] |= (1<<irq); /* unmask interrupt source */ + + rtems_interrupt_enable(level); + + return DRVMGR_OK; +} + +int ambapp_rasta_spw_router_int_mask( + struct drvmgr_dev *dev, + int irq) +{ + struct gr_rasta_spw_router_priv *priv = dev->parent->dev->priv; + rtems_interrupt_level level; + + DBG("RASTA-SPW-ROUTER IRQ %d: mask\n", irq); + + if ( genirq_check(priv->genirq, irq) ) + return DRVMGR_EINVAL; + + rtems_interrupt_disable(level); + + /* Disable/mask IRQ */ + priv->irq->mask[0] &= ~(1<<irq); /* mask interrupt source */ + + rtems_interrupt_enable(level); + + return DRVMGR_OK; +} + +int ambapp_rasta_spw_router_int_clear( + struct drvmgr_dev *dev, + int irq) +{ + struct gr_rasta_spw_router_priv *priv = dev->parent->dev->priv; + + if ( genirq_check(priv->genirq, irq) ) + return DRVMGR_EINVAL; + + priv->irq->iclear = (1<<irq); + + return DRVMGR_OK; +} + +int ambapp_rasta_spw_router_get_params(struct drvmgr_dev *dev, struct drvmgr_bus_params *params) +{ + struct gr_rasta_spw_router_priv *priv = dev->parent->dev->priv; + + /* Device name prefix pointer, skip /dev */ + params->dev_prefix = &priv->prefix[5]; + + return 0; +} + +void gr_rasta_spw_router_print_dev(struct drvmgr_dev *dev, int options) +{ + struct gr_rasta_spw_router_priv *priv = dev->priv; + struct pci_dev_info *devinfo = priv->devinfo; + uint32_t bar0, bar0_size; + + /* Print */ + printf("--- GR-RASTA-SPW-ROUTER [bus 0x%x, dev 0x%x, fun 0x%x] ---\n", + PCI_DEV_EXPAND(priv->pcidev)); + + bar0 = devinfo->resources[0].address; + bar0_size = devinfo->resources[0].size; + printf(" PCI BAR[0]: 0x%lx - 0x%lx\n", bar0, bar0 + bar0_size - 1); + printf(" IRQ REGS: 0x%x\n", (unsigned int)priv->irq); + printf(" IRQ: %d\n", devinfo->irq); + printf(" PCI REVISION: %d\n", devinfo->rev); + printf(" FREQ: %d Hz\n", priv->version->amba_freq_hz); + printf(" IMASK: 0x%08x\n", priv->irq->mask[0]); + printf(" IPEND: 0x%08x\n", priv->irq->ipend); + + /* Print amba config */ + if (options & RASTA_SPW_ROUTER_OPTIONS_AMBA) + ambapp_print(&priv->abus, 10); + +#if 0 + /* Print IRQ handlers and their arguments */ + if (options & RASTA_SPW_ROUTER_OPTIONS_IRQ) { + int i; + for(i = 0; i < 16; i++) { + printf(" IRQ[%02d]: 0x%x, arg: 0x%x\n", + i, (unsigned int)priv->isrs[i].handler, + (unsigned int)priv->isrs[i].arg); + } + } +#endif +} + +void gr_rasta_spw_router_print(int options) +{ + struct pci_drv_info *drv = &gr_rasta_spw_router_info; + struct drvmgr_dev *dev; + + dev = drv->general.dev; + while(dev) { + gr_rasta_spw_router_print_dev(dev, options); + dev = dev->next_in_drv; + } +} diff --git a/c/src/lib/libbsp/sparc/shared/pci/gr_rasta_tmtc.c b/c/src/lib/libbsp/sparc/shared/pci/gr_rasta_tmtc.c new file mode 100644 index 0000000000..8e31b65f0e --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/pci/gr_rasta_tmtc.c @@ -0,0 +1,694 @@ +/* GR-RASTA-TMTC PCI Target driver. + * + * COPYRIGHT (c) 2008. + * Aeroflex Gaisler AB. + * + * Configures the GR-RASTA-TMTC interface PCI board. + * This driver provides a AMBA PnP bus by using the general part + * of the AMBA PnP bus driver (ambapp_bus.c). + * + * Driver resources for the AMBA PnP bus provided can be set by overriding + * the defaults by declaring gr_rasta_tmtc_resources[]. + * + * 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-05, Daniel Hellstrom <daniel@gaisler.com> + * Created + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <bsp.h> +#include <rtems/bspIo.h> +#include <pci.h> + +#include <ambapp.h> +#include <grlib.h> +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> +#include <drvmgr/pci_bus.h> +#include <genirq.h> + +#include <gr_rasta_tmtc.h> + +/* Determines which PCI address the AHB masters will access, it should be + * set so that the masters can access the CPU RAM. Default is base of CPU RAM, + * CPU RAM is mapped 1:1 to PCI space. + */ +extern unsigned int _RAM_START; +#define AHBMST2PCIADR (((unsigned int)&_RAM_START) & 0xf0000000) + +#define GAISLER_GPIO 0x01a +#define AHB1_BASE_ADDR 0x80000000 +#define AHB1_IOAREA_BASE_ADDR 0x80200000 + +/* #define DEBUG 1 */ + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +int gr_rasta_tmtc_init1(struct drvmgr_dev *dev); +int gr_rasta_tmtc_init2(struct drvmgr_dev *dev); + +struct grpci_regs { + volatile unsigned int cfg_stat; + volatile unsigned int bar0; + volatile unsigned int page0; + volatile unsigned int bar1; + volatile unsigned int page1; + volatile unsigned int iomap; + volatile unsigned int stat_cmd; +}; + +struct gr_rasta_tmtc_ver { + const unsigned int amba_freq_hz; /* The frequency */ + const unsigned int amba_ioarea; /* The address where the PnP IOAREA starts at */ +}; + +/* Private data structure for driver */ +struct gr_rasta_tmtc_priv { + /* Driver management */ + struct drvmgr_dev *dev; + char prefix[20]; + + /* PCI */ + pci_dev_t pcidev; + struct pci_dev_info *devinfo; + uint32_t ahbmst2pci_map; + + /* IRQ */ + genirq_t genirq; + + /* GR-RASTA-TMTC */ + struct gr_rasta_tmtc_ver *version; + struct irqmp_regs *irq; + struct grpci_regs *grpci; + struct grgpio_regs *gpio; + struct drvmgr_map_entry bus_maps_down[3]; + struct drvmgr_map_entry bus_maps_up[2]; + + /* AMBA Plug&Play information on GR-RASTA-TMTC */ + struct ambapp_bus abus; + struct ambapp_mmap amba_maps[4]; + struct ambapp_config config; +}; + +struct gr_rasta_tmtc_ver gr_rasta_tmtc_ver0 = { + .amba_freq_hz = 30000000, + .amba_ioarea = AHB1_IOAREA_BASE_ADDR, +}; + +int ambapp_rasta_tmtc_int_register( + struct drvmgr_dev *dev, + int irq, + const char *info, + drvmgr_isr handler, + void *arg); +int ambapp_rasta_tmtc_int_unregister( + struct drvmgr_dev *dev, + int irq, + drvmgr_isr handler, + void *arg); +int ambapp_rasta_tmtc_int_unmask( + struct drvmgr_dev *dev, + int irq); +int ambapp_rasta_tmtc_int_mask( + struct drvmgr_dev *dev, + int irq); +int ambapp_rasta_tmtc_int_clear( + struct drvmgr_dev *dev, + int irq); +int ambapp_rasta_tmtc_get_params( + struct drvmgr_dev *dev, + struct drvmgr_bus_params *params); + +struct ambapp_ops ambapp_rasta_tmtc_ops = { + .int_register = ambapp_rasta_tmtc_int_register, + .int_unregister = ambapp_rasta_tmtc_int_unregister, + .int_unmask = ambapp_rasta_tmtc_int_unmask, + .int_mask = ambapp_rasta_tmtc_int_mask, + .int_clear = ambapp_rasta_tmtc_int_clear, + .get_params = ambapp_rasta_tmtc_get_params +}; + +struct drvmgr_drv_ops gr_rasta_tmtc_ops = +{ + .init = {gr_rasta_tmtc_init1, gr_rasta_tmtc_init2, NULL, NULL}, + .remove = NULL, + .info = NULL, +}; + +struct pci_dev_id_match gr_rasta_tmtc_ids[] = +{ + PCIID_DEVVEND(PCIID_VENDOR_GAISLER, PCIID_DEVICE_GR_RASTA_TMTC), + PCIID_END_TABLE /* Mark end of table */ +}; + +struct pci_drv_info gr_rasta_tmtc_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_PCI_GAISLER_RASTATMTC_ID,/* Driver ID */ + "GR-RASTA-TMTC_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_PCI, /* Bus Type */ + &gr_rasta_tmtc_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + sizeof(struct gr_rasta_tmtc_priv) /* Let drvmgr alloc private */ + }, + &gr_rasta_tmtc_ids[0] +}; + +/* Driver resources configuration for the AMBA bus on the GR-RASTA-TMTC board. + * It is declared weak so that the user may override it from the project file, + * if the default settings are not enough. + * + * The configuration consists of an array of configuration pointers, each + * pointer determine the configuration of one GR-RASTA-TMTC board. Pointer + * zero is for board0, pointer 1 for board1 and so on. + * + * The array must end with a NULL pointer. + */ +struct drvmgr_bus_res *gr_rasta_tmtc_resources[] __attribute__((weak)) = +{ + NULL, +}; +int gr_rasta_tmtc_resources_cnt = 0; + +void gr_rasta_tmtc_register_drv(void) +{ + DBG("Registering GR-RASTA-TMTC PCI driver\n"); + drvmgr_drv_register(&gr_rasta_tmtc_info.general); +} + +void gr_rasta_tmtc_isr (void *arg) +{ + struct gr_rasta_tmtc_priv *priv = arg; + unsigned int status, tmp; + int irq; + tmp = status = priv->irq->ipend; + + /* printk("GR-RASTA-TMTC: IRQ 0x%x\n",status); */ + + for(irq=0; irq<32; irq++) { + if ( status & (1<<irq) ) { + genirq_doirq(priv->genirq, irq); + priv->irq->iclear = (1<<irq); + status &= ~(1<<irq); + if ( status == 0 ) + break; + } + } + + /* ACK interrupt, this is because PCI is Level, so the IRQ Controller still drives the IRQ. */ + if ( tmp ) + drvmgr_interrupt_clear(priv->dev, 0); + + DBG("RASTA-TMTC-IRQ: 0x%x\n", tmp); +} + +int gr_rasta_tmtc_hw_init(struct gr_rasta_tmtc_priv *priv) +{ + unsigned int *page0 = NULL; + struct ambapp_dev *tmp; + struct ambapp_ahb_info *ahb; + unsigned int pci_freq_hz; + pci_dev_t pcidev = priv->pcidev; + struct pci_dev_info *devinfo = priv->devinfo; + uint32_t bar0, bar0_size; + + /* Select version of GR-RASTA-TMTC board */ + switch (devinfo->rev) { + case 0: + priv->version = &gr_rasta_tmtc_ver0; + break; + default: + return -2; + } + + bar0 = devinfo->resources[0].address; + bar0_size = devinfo->resources[0].size; + page0 = (unsigned int *)(bar0 + bar0_size/2); + + /* Point PAGE0 to start of Plug and Play information */ + *page0 = priv->version->amba_ioarea & 0xf0000000; + +#if 0 + { + uint32_t data; + /* set parity error response */ + pci_cfg_r32(pcidev, PCI_COMMAND, &data); + pci_cfg_w32(pcidev, PCI_COMMAND, (data|PCI_COMMAND_PARITY)); + } +#endif + + /* Setup cache line size. Default cache line size will result in + * poor performance (256 word fetches), 0xff will set it according + * to the max size of the PCI FIFO. + */ + pci_cfg_w8(pcidev, PCI_CACHE_LINE_SIZE, 0xff); + + /* Scan AMBA Plug&Play */ + + /* AMBA MAP bar0 (in CPU) ==> 0x80000000(remote amba address) */ + priv->amba_maps[0].size = 0x10000000; + priv->amba_maps[0].local_adr = bar0; + priv->amba_maps[0].remote_adr = AHB1_BASE_ADDR; + + /* AMBA MAP bar1 (in CPU) ==> 0x40000000(remote amba address) */ + priv->amba_maps[1].size = devinfo->resources[1].size; + priv->amba_maps[1].local_adr = devinfo->resources[1].address; + priv->amba_maps[1].remote_adr = 0x40000000; + + /* Addresses not matching with map be untouched */ + priv->amba_maps[2].size = 0xfffffff0; + priv->amba_maps[2].local_adr = 0; + priv->amba_maps[2].remote_adr = 0; + + /* Mark end of table */ + priv->amba_maps[3].size=0; + priv->amba_maps[3].local_adr = 0; + priv->amba_maps[3].remote_adr = 0; + + /* Start AMBA PnP scan at first AHB bus */ + ambapp_scan(&priv->abus, + bar0 + (priv->version->amba_ioarea & ~0xf0000000), + NULL, &priv->amba_maps[0]); + + /* Frequency is the same as the PCI bus frequency */ + drvmgr_freq_get(priv->dev, 0, &pci_freq_hz); + + /* Initialize Frequency of AMBA bus */ + ambapp_freq_init(&priv->abus, NULL, pci_freq_hz); + + /* Point PAGE0 to start of APB area */ + *page0 = AHB1_BASE_ADDR; + + /* Find GRPCI controller */ + tmp = (void *)ambapp_for_each(&priv->abus, + (OPTIONS_ALL|OPTIONS_APB_SLVS), + VENDOR_GAISLER, GAISLER_PCIFBRG, + ambapp_find_by_idx, NULL); + if ( !tmp ) { + return -3; + } + priv->grpci = (struct grpci_regs *)((struct ambapp_apb_info *)tmp->devinfo)->start; + + /* Set GRPCI mmap so that AMBA masters can access CPU-RAM over + * the PCI window. + */ + priv->grpci->cfg_stat = (priv->grpci->cfg_stat & 0x0fffffff) | + (priv->ahbmst2pci_map & 0xf0000000); + priv->grpci->page1 = 0x40000000; + + /* Find IRQ controller, Clear all current IRQs */ + tmp = (void *)ambapp_for_each(&priv->abus, + (OPTIONS_ALL|OPTIONS_APB_SLVS), + VENDOR_GAISLER, GAISLER_IRQMP, + ambapp_find_by_idx, NULL); + if ( !tmp ) { + return -4; + } + priv->irq = (struct irqmp_regs *)DEV_TO_APB(tmp)->start; + /* Set up GR-RASTA-TMTC irq controller */ + priv->irq->mask[0] = 0; + priv->irq->iclear = 0xffffffff; + priv->irq->ilevel = 0; + + /* Find First GPIO controller */ + tmp = (void *)ambapp_for_each(&priv->abus, + (OPTIONS_ALL|OPTIONS_APB_SLVS), + VENDOR_GAISLER, GAISLER_GPIO, + ambapp_find_by_idx, NULL); + if ( !tmp ) { + return -5; + } + priv->gpio = (struct grgpio_regs *) (((struct ambapp_apb_info *)tmp->devinfo)->start); + /* Clear GR-RASTA-TMTC GPIO controller */ + priv->gpio->imask = 0; + priv->gpio->ipol = 0; + priv->gpio->iedge = 0; + priv->gpio->bypass = 0; + /* Set up GR-RASTA-TMTC GPIO controller to select GRTM and GRTC */ + priv->gpio->output = (GR_TMTC_GPIO_GRTM_SEL|GR_TMTC_GPIO_TRANSP_CLK) | (GR_TMTC_GPIO_TC_BIT_LOCK|GR_TMTC_GPIO_TC_RF_AVAIL|GR_TMTC_GPIO_TC_ACTIVE_HIGH|GR_TMTC_GPIO_TC_RISING_CLK); + priv->gpio->dir = 0xffffffff; + DBG("GR-TMTC GPIO: 0x%x\n", (unsigned int)priv->gpio); + + /* Enable DMA by enabling PCI target as master */ + pci_master_enable(pcidev); + + /* DOWN streams translation table */ + priv->bus_maps_down[0].name = "PCI BAR0 -> AMBA"; + priv->bus_maps_down[0].size = priv->amba_maps[0].size; + priv->bus_maps_down[0].from_adr = (void *)priv->amba_maps[0].local_adr; + priv->bus_maps_down[0].to_adr = (void *)priv->amba_maps[0].remote_adr; + + priv->bus_maps_down[1].name = "PCI BAR1 -> AMBA"; + priv->bus_maps_down[1].size = priv->amba_maps[1].size; + priv->bus_maps_down[1].from_adr = (void *)priv->amba_maps[1].local_adr; + priv->bus_maps_down[1].to_adr = (void *)priv->amba_maps[1].remote_adr; + + /* Mark end of translation table */ + priv->bus_maps_down[2].size = 0; + + /* Find GRPCI controller AHB Slave interface */ + tmp = (struct ambapp_dev *)ambapp_for_each(&priv->abus, + (OPTIONS_ALL|OPTIONS_AHB_SLVS), + VENDOR_GAISLER, GAISLER_PCIFBRG, + ambapp_find_by_idx, NULL); + if ( !tmp ) { + return -6; + } + ahb = (struct ambapp_ahb_info *)tmp->devinfo; + + /* UP streams translation table */ + priv->bus_maps_up[0].name = "AMBA GRPCI Window"; + priv->bus_maps_up[0].size = ahb->mask[0]; /* AMBA->PCI Window on GR-RASTA-TMTC board */ + priv->bus_maps_up[0].from_adr = (void *)ahb->start[0]; + priv->bus_maps_up[0].to_adr = (void *) + (priv->ahbmst2pci_map & 0xf0000000); + + /* Mark end of translation table */ + priv->bus_maps_up[1].size = 0; + + /* Successfully registered the RASTA board */ + return 0; +} + +void gr_rasta_tmtc_hw_init2(struct gr_rasta_tmtc_priv *priv) +{ + /* Enable DMA by enabling PCI target as master */ + pci_master_enable(priv->pcidev); +} + +/* Called when a PCI target is found with the PCI device and vendor ID + * given in gr_rasta_tmtc_ids[]. + */ +int gr_rasta_tmtc_init1(struct drvmgr_dev *dev) +{ + struct gr_rasta_tmtc_priv *priv; + struct pci_dev_info *devinfo; + int status; + uint32_t bar0, bar1, bar0_size, bar1_size; + union drvmgr_key_value *value; + + priv = dev->priv; + if (!priv) + return DRVMGR_NOMEM; + priv->dev = dev; + + /* Determine number of configurations */ + if ( gr_rasta_tmtc_resources_cnt == 0 ) { + while ( gr_rasta_tmtc_resources[gr_rasta_tmtc_resources_cnt] ) + gr_rasta_tmtc_resources_cnt++; + } + + /* Generate Device prefix */ + + strcpy(priv->prefix, "/dev/rastatmtc0"); + priv->prefix[14] += dev->minor_drv; + mkdir(priv->prefix, S_IRWXU | S_IRWXG | S_IRWXO); + priv->prefix[15] = '/'; + priv->prefix[16] = '\0'; + + priv->devinfo = devinfo = (struct pci_dev_info *)dev->businfo; + priv->pcidev = devinfo->pcidev; + bar0 = devinfo->resources[0].address; + bar0_size = devinfo->resources[0].size; + bar1 = devinfo->resources[1].address; + bar1_size = devinfo->resources[1].size; + printf("\n\n--- GR-RASTA-TMTC[%d] ---\n", dev->minor_drv); + printf(" PCI BUS: 0x%x, SLOT: 0x%x, FUNCTION: 0x%x\n", + PCI_DEV_EXPAND(priv->pcidev)); + printf(" PCI VENDOR: 0x%04x, DEVICE: 0x%04x\n", + devinfo->id.vendor, devinfo->id.device); + printf(" PCI BAR[0]: 0x%lx - 0x%lx\n", bar0, bar0 + bar0_size - 1); + printf(" PCI BAR[1]: 0x%lx - 0x%lx\n", bar1, bar1 + bar1_size - 1); + printf(" IRQ: %d\n\n\n", devinfo->irq); + + /* all neccessary space assigned to GR-RASTA-IO target? */ + if ((bar0_size == 0) || (bar1_size == 0)) + return DRVMGR_ENORES; + + /* Let user override which PCI address the AHB masters of the + * GR-RASTA-TMTC board access when doing DMA to CPU RAM. The AHB masters + * access the PCI Window of the AMBA bus, the MSB 4-bits of that address + * is translated according this config option before the address + * goes out on the PCI bus. + * Only the 4 MSB bits have an effect; + */ + value = drvmgr_dev_key_get(priv->dev, "ahbmst2pci", KEY_TYPE_INT); + if (value) + priv->ahbmst2pci_map = value->i; + else + priv->ahbmst2pci_map = AHBMST2PCIADR; /* default */ + + priv->genirq = genirq_init(32); + if ( priv->genirq == NULL ) + return DRVMGR_FAIL; + + status = gr_rasta_tmtc_hw_init(priv); + if ( status != 0 ) { + genirq_destroy(priv->genirq); + printf(" Failed to initialize GR-RASTA-TMTC HW: %d\n", status); + return DRVMGR_FAIL; + } + + /* Init amba bus */ + priv->config.abus = &priv->abus; + priv->config.ops = &ambapp_rasta_tmtc_ops; + priv->config.maps_up = &priv->bus_maps_up[0]; + priv->config.maps_down = &priv->bus_maps_down[0]; + if ( priv->dev->minor_drv < gr_rasta_tmtc_resources_cnt ) { + priv->config.resources = gr_rasta_tmtc_resources[priv->dev->minor_drv]; + } else { + priv->config.resources = NULL; + } + + return ambapp_bus_register(dev, &priv->config); +} + +int gr_rasta_tmtc_init2(struct drvmgr_dev *dev) +{ + struct gr_rasta_tmtc_priv *priv = dev->priv; + + /* Clear any old interrupt requests */ + drvmgr_interrupt_clear(priv->dev, 0); + + /* Enable System IRQ so that GR-RASTA-TMTC PCI target interrupt goes + * through. + * + * It is important to enable it in stage init2. If interrupts were + * enabled in init1 this might hang the system when more than one + * PCI target is connected, this is because PCI interrupts might + * be shared and PCI board 2 have not initialized and + * might therefore drive interrupt already when entering init1(). + */ + drvmgr_interrupt_register( + priv->dev, + 0, + "gr_rasta_tmtc", + gr_rasta_tmtc_isr, + (void *)priv); + + gr_rasta_tmtc_hw_init2(priv); + + return DRVMGR_OK; +} + +int ambapp_rasta_tmtc_int_register( + struct drvmgr_dev *dev, + int irq, + const char *info, + drvmgr_isr handler, + void *arg) +{ + struct gr_rasta_tmtc_priv *priv = dev->parent->dev->priv; + rtems_interrupt_level level; + int status; + + rtems_interrupt_disable(level); + + status = genirq_register(priv->genirq, irq, handler, arg); + if ( status == 0 ) { + /* Disable and clear IRQ for first registered handler */ + priv->irq->iclear = (1<<irq); + } else if ( status == 1 ) + status = 0; + + if (status != 0) { + rtems_interrupt_enable(level); + return DRVMGR_FAIL; + } + + status = genirq_enable(priv->genirq, irq, handler, arg); + if ( status == 0 ) { + /* Enable IRQ for first enabled handler only */ + priv->irq->mask[0] |= (1<<irq); /* unmask interrupt source */ + } else if ( status == 1 ) + status = 0; + + rtems_interrupt_enable(level); + + return status; +} + +int ambapp_rasta_tmtc_int_unregister( + struct drvmgr_dev *dev, + int irq, + drvmgr_isr isr, + void *arg) +{ + struct gr_rasta_tmtc_priv *priv = dev->parent->dev->priv; + rtems_interrupt_level level; + int status; + + rtems_interrupt_disable(level); + + status = genirq_disable(priv->genirq, irq, isr, arg); + if ( status == 0 ) { + /* Disable IRQ only when no enabled handler exists */ + priv->irq->mask[0] &= ~(1<<irq); /* mask interrupt source */ + } else if ( status == 1 ) + status = 0; + + status = genirq_unregister(priv->genirq, irq, isr, arg); + if ( status != 0 ) + status = DRVMGR_FAIL; + + rtems_interrupt_enable(level); + + return status; +} + +int ambapp_rasta_tmtc_int_unmask( + struct drvmgr_dev *dev, + int irq) +{ + struct gr_rasta_tmtc_priv *priv = dev->parent->dev->priv; + rtems_interrupt_level level; + + DBG("RASTA-TMTC IRQ %d: unmask\n", irq); + + if ( genirq_check(priv->genirq, irq) ) + return DRVMGR_EINVAL; + + rtems_interrupt_disable(level); + + /* Enable IRQ */ + priv->irq->mask[0] |= (1<<irq); /* unmask interrupt source */ + + rtems_interrupt_enable(level); + + return DRVMGR_OK; +} + +int ambapp_rasta_tmtc_int_mask( + struct drvmgr_dev *dev, + int irq) +{ + struct gr_rasta_tmtc_priv *priv = dev->parent->dev->priv; + rtems_interrupt_level level; + + DBG("RASTA-TMTC IRQ %d: mask\n", irq); + + if ( genirq_check(priv->genirq, irq) ) + return DRVMGR_EINVAL; + + rtems_interrupt_disable(level); + + /* Disable IRQ */ + priv->irq->mask[0] &= ~(1<<irq); /* mask interrupt source */ + + rtems_interrupt_enable(level); + + return DRVMGR_OK; +} + +int ambapp_rasta_tmtc_int_clear( + struct drvmgr_dev *dev, + int irq) +{ + struct gr_rasta_tmtc_priv *priv = dev->parent->dev->priv; + + if ( genirq_check(priv->genirq, irq) ) + return DRVMGR_FAIL; + + priv->irq->iclear = (1<<irq); + + return DRVMGR_OK; +} + +int ambapp_rasta_tmtc_get_params(struct drvmgr_dev *dev, struct drvmgr_bus_params *params) +{ + struct gr_rasta_tmtc_priv *priv = dev->parent->dev->priv; + + /* Device name prefix pointer, skip /dev */ + params->dev_prefix = &priv->prefix[5]; + + return 0; +} + +void gr_rasta_tmtc_print_dev(struct drvmgr_dev *dev, int options) +{ + struct gr_rasta_tmtc_priv *priv = dev->priv; + struct pci_dev_info *devinfo = priv->devinfo; + uint32_t bar0, bar1, bar0_size, bar1_size; + + /* Print */ + printf("--- GR-RASTA-TMTC [bus 0x%x, dev 0x%x, fun 0x%x] ---\n", + PCI_DEV_EXPAND(priv->pcidev)); + + bar0 = devinfo->resources[0].address; + bar0_size = devinfo->resources[0].size; + bar1 = devinfo->resources[1].address; + bar1_size = devinfo->resources[1].size; + + printf(" PCI BAR[0]: 0x%lx - 0x%lx\n", bar0, bar0 + bar0_size - 1); + printf(" PCI BAR[1]: 0x%lx - 0x%lx\n", bar1, bar1 + bar1_size - 1); + printf(" IRQ: %d\n", devinfo->irq); + printf(" PCI REVISION: %d\n", devinfo->rev); + printf(" FREQ: %d Hz\n", priv->version->amba_freq_hz); + printf(" IMASK: 0x%08x\n", priv->irq->mask[0]); + printf(" IPEND: 0x%08x\n", priv->irq->ipend); + + /* Print amba config */ + if ( options & RASTA_TMTC_OPTIONS_AMBA ) { + ambapp_print(&priv->abus, 10); + } + +#if 0 + /* Print IRQ handlers and their arguments */ + if ( options & RASTA_TMTC_OPTIONS_IRQ ) { + int i; + for(i=0; i<16; i++) { + printf(" IRQ[%02d]: 0x%x, arg: 0x%x\n", + i, (unsigned int)priv->isrs[i].handler, (unsigned int)priv->isrs[i].arg); + } + } +#endif +} + +void gr_rasta_tmtc_print(int options) +{ + struct pci_drv_info *drv = &gr_rasta_tmtc_info; + struct drvmgr_dev *dev; + + dev = drv->general.dev; + while(dev) { + gr_rasta_tmtc_print_dev(dev, options); + dev = dev->next_in_drv; + } +} diff --git a/c/src/lib/libbsp/sparc/shared/pci/gr_tmtc_1553.c b/c/src/lib/libbsp/sparc/shared/pci/gr_tmtc_1553.c new file mode 100644 index 0000000000..53f8ce998b --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/pci/gr_tmtc_1553.c @@ -0,0 +1,575 @@ +/* GR-TMTC-1553 PCI Target driver. + * + * COPYRIGHT (c) 2008. + * Aeroflex Gaisler AB. + * + * Configures the GR-TMTC-1553 interface PCI board. + * This driver provides a AMBA PnP bus by using the general part + * of the AMBA PnP bus driver (ambapp_bus.c). + * + * Driver resources for the AMBA PnP bus provided can be set using + * gr_tmtc_1553_set_resources(). + * + * 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. + * + * 2010-06-21, Kristoffer Glembo + * Created + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <bsp.h> +#include <rtems/bspIo.h> +#include <pci.h> +#include <pci/access.h> + +#include <ambapp.h> +#include <grlib.h> +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> +#include <drvmgr/pci_bus.h> +#include <genirq.h> + +#include <gr_tmtc_1553.h> + + +/*#define DEBUG 1 */ + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +/* PCI ID */ +#define PCIID_VENDOR_GAISLER 0x1AC8 + +int gr_tmtc_1553_init1(struct drvmgr_dev *dev); +int gr_tmtc_1553_init2(struct drvmgr_dev *dev); + +struct gr_tmtc_1553_ver { + const unsigned int amba_freq_hz; /* The frequency */ + const unsigned int amba_ioarea; /* The address where the PnP IOAREA starts at */ +}; + +/* Private data structure for driver */ +struct gr_tmtc_1553_priv { + /* Driver management */ + struct drvmgr_dev *dev; + char prefix[32]; + + /* PCI */ + pci_dev_t pcidev; + struct pci_dev_info *devinfo; + + /* IRQ */ + genirq_t genirq; + + struct gr_tmtc_1553_ver *version; + struct irqmp_regs *irq; + struct drvmgr_map_entry bus_maps_down[2]; + + struct ambapp_bus abus; + struct ambapp_mmap amba_maps[4]; + struct ambapp_config config; +}; + +struct gr_tmtc_1553_ver gr_tmtc_1553_ver0 = { + .amba_freq_hz = 33333333, + .amba_ioarea = 0xfff00000, +}; + + +int ambapp_tmtc_1553_int_register( + struct drvmgr_dev *dev, + int irq, + const char *info, + drvmgr_isr handler, + void *arg); +int ambapp_tmtc_1553_int_unregister( + struct drvmgr_dev *dev, + int irq, + drvmgr_isr handler, + void *arg); +int ambapp_tmtc_1553_int_unmask( + struct drvmgr_dev *dev, + int irq); +int ambapp_tmtc_1553_int_mask( + struct drvmgr_dev *dev, + int irq); +int ambapp_tmtc_1553_int_clear( + struct drvmgr_dev *dev, + int irq); +int ambapp_tmtc_1553_get_params( + struct drvmgr_dev *dev, + struct drvmgr_bus_params *params); + +struct ambapp_ops ambapp_tmtc_1553_ops = { + .int_register = ambapp_tmtc_1553_int_register, + .int_unregister = ambapp_tmtc_1553_int_unregister, + .int_unmask = ambapp_tmtc_1553_int_unmask, + .int_mask = ambapp_tmtc_1553_int_mask, + .int_clear = ambapp_tmtc_1553_int_clear, + .get_params = ambapp_tmtc_1553_get_params +}; + +struct drvmgr_drv_ops gr_tmtc_1553_ops = +{ + {gr_tmtc_1553_init1, gr_tmtc_1553_init2, NULL, NULL}, + NULL, + NULL +}; + +struct pci_dev_id_match gr_tmtc_1553_ids[] = +{ + PCIID_DEVVEND(PCIID_VENDOR_GAISLER, PCIID_DEVICE_GR_TMTC_1553), + PCIID_END_TABLE /* Mark end of table */ +}; + +struct pci_drv_info gr_tmtc_1553_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_PCI_GAISLER_TMTC_1553_ID, /* Driver ID */ + "GR-TMTC-1553_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_PCI, /* Bus Type */ + &gr_tmtc_1553_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + 0, + }, + &gr_tmtc_1553_ids[0] +}; + +/* Driver resources configuration for the AMBA bus on the GR-RASTA-IO board. + * It is declared weak so that the user may override it from the project file, + * if the default settings are not enough. + * + * The configuration consists of an array of configuration pointers, each + * pointer determine the configuration of one GR-RASTA-IO board. Pointer + * zero is for board0, pointer 1 for board1 and so on. + * + * The array must end with a NULL pointer. + */ +struct drvmgr_bus_res *gr_tmtc_1553_resources[] __attribute__((weak)) = +{ + NULL +}; +int gr_tmtc_1553_resources_cnt = 0; + +void gr_tmtc_1553_register_drv(void) +{ + DBG("Registering GR-TMTC-1553 PCI driver\n"); + drvmgr_drv_register(&gr_tmtc_1553_info.general); +} + +void gr_tmtc_1553_isr (void *arg) +{ + struct gr_tmtc_1553_priv *priv = arg; + unsigned int status, tmp; + int irq; + tmp = status = priv->irq->ipend; + + /* DBG("GR-RASTA-IO: IRQ 0x%x\n",status); */ + + for(irq=0; irq<16; irq++) { + if ( status & (1<<irq) ) { + genirq_doirq(priv->genirq, irq); + priv->irq->iclear = (1<<irq); + status &= ~(1<<irq); + if ( status == 0 ) + break; + } + } + + /* ACK interrupt, this is because PCI is Level, so the IRQ Controller still drives the IRQ. */ + if ( tmp ) + drvmgr_interrupt_clear(priv->dev, 0); + + DBG("GR-TMTC-1553-IRQ: 0x%x\n", tmp); +} + +int gr_tmtc_1553_hw_init(struct gr_tmtc_1553_priv *priv) +{ + unsigned int *page0 = NULL; + struct ambapp_dev *tmp; + int status; + unsigned int pci_freq_hz; + struct pci_dev_info *devinfo = priv->devinfo; + uint32_t bar0, bar0_size; + + /* Select version of GR-TMTC-1553 board */ + switch (devinfo->rev) { + case 0: + priv->version = &gr_tmtc_1553_ver0; + break; + default: + return -2; + } + + bar0 = devinfo->resources[0].address; + bar0_size = devinfo->resources[0].size; + page0 = (unsigned int *)(bar0 + bar0_size/2); + + /* Point PAGE0 to start of board address map. RAM at 0xff000000, APB at 0xffc00000, IOAREA at 0xfff000000 */ + /* XXX We assume little endian host with byte twisting enabled here */ + *page0 = 0x010000ff; /* Set little endian mode on peripheral. */ + + /* Scan AMBA Plug&Play */ + + /* AMBA MAP bar0 (in CPU) ==> 0x80000000(remote amba address) */ + priv->amba_maps[0].size = 0x1000000; + priv->amba_maps[0].local_adr = bar0; + priv->amba_maps[0].remote_adr = 0xff000000; + + /* Addresses not matching with map be untouched */ + priv->amba_maps[2].size = 0xfffffff0; + priv->amba_maps[2].local_adr = 0; + priv->amba_maps[2].remote_adr = 0; + + /* Mark end of table */ + priv->amba_maps[3].size=0; + priv->amba_maps[3].local_adr = 0; + priv->amba_maps[3].remote_adr = 0; + + /* Start AMBA PnP scan at first AHB bus */ + ambapp_scan(&priv->abus, + bar0 + (priv->version->amba_ioarea & ~0xff000000), + NULL, &priv->amba_maps[0]); + + /* Frequency is the hsame as the PCI bus frequency */ + drvmgr_freq_get(priv->dev, NULL, &pci_freq_hz); + + ambapp_freq_init(&priv->abus, NULL, pci_freq_hz); + + /* Find IRQ controller */ + tmp = (void *)ambapp_for_each(&priv->abus, + (OPTIONS_ALL|OPTIONS_APB_SLVS), + VENDOR_GAISLER, GAISLER_IRQMP, + ambapp_find_by_idx, NULL); + if ( !tmp ) { + return -4; + } + priv->irq = (struct irqmp_regs *)DEV_TO_APB(tmp)->start; + /* Set up irq controller */ + priv->irq->mask[0] = 0; + priv->irq->iclear = 0xffff; + priv->irq->ilevel = 0; + + /* DOWN streams translation table */ + priv->bus_maps_down[0].name = "PCI BAR0 -> AMBA"; + priv->bus_maps_down[0].size = priv->amba_maps[0].size; + priv->bus_maps_down[0].from_adr = (void *)priv->amba_maps[0].local_adr; + priv->bus_maps_down[0].to_adr = (void *)priv->amba_maps[0].remote_adr; + /* Mark end of translation table */ + priv->bus_maps_down[1].size = 0; + + /* Successfully registered the board */ + return 0; +} + + +/* Called when a PCI target is found with the PCI device and vendor ID + * given in gr_tmtc_1553_ids[]. + */ +int gr_tmtc_1553_init1(struct drvmgr_dev *dev) +{ + struct gr_tmtc_1553_priv *priv; + struct pci_dev_info *devinfo; + int status; + uint32_t bar0, bar0_size; + + /* PCI device does not have the IRQ line register, when PCI autoconf configures it the configuration + * is forgotten. We take the IRQ number from the PCI Host device (AMBA device), this works as long + * as PCI-IRQs are ored together on the bus. + * + * Note that this only works on LEON. + */ + ((struct pci_dev_info *)dev->businfo)->irq = ((struct amba_dev_info *)dev->parent->dev->businfo)->info.irq; + + priv = malloc(sizeof(struct gr_tmtc_1553_priv)); + if ( !priv ) + return DRVMGR_NOMEM; + + memset(priv, 0, sizeof(*priv)); + dev->priv = priv; + priv->dev = dev; + + /* Determine number of configurations */ + if ( gr_tmtc_1553_resources_cnt == 0 ) { + while ( gr_tmtc_1553_resources[gr_tmtc_1553_resources_cnt] ) + gr_tmtc_1553_resources_cnt++; + } + + /* Generate Device prefix */ + + strcpy(priv->prefix, "/dev/tmtc1553_0/"); + priv->prefix[19] += dev->minor_drv; + mkdir(priv->prefix, S_IRWXU | S_IRWXG | S_IRWXO); + priv->prefix[20] = '/'; + priv->prefix[21] = '\0'; + + priv->devinfo = devinfo = (struct pci_dev_info *)dev->businfo; + priv->pcidev = devinfo->pcidev; + bar0 = devinfo->resources[0].address; + bar0_size = devinfo->resources[0].size; + printf("\n\n--- GR-TMTC-1553[%d] ---\n", dev->minor_drv); + printf(" PCI BUS: 0x%x, SLOT: 0x%x, FUNCTION: 0x%x\n", + PCI_DEV_EXPAND(priv->pcidev)); + printf(" PCI VENDOR: 0x%04x, DEVICE: 0x%04x\n", + devinfo->id.vendor, devinfo->id.device); + printf(" PCI BAR[0]: 0x%lx - 0x%lx\n", bar0, bar0 + bar0_size - 1); + printf(" IRQ: %d\n\n\n", devinfo->irq); + + /* all neccessary space assigned to GR-TMTC-1553 target? */ + if (bar0_size == 0) + return DRVMGR_ENORES; + + priv->genirq = genirq_init(16); + if ( priv->genirq == NULL ) { + free(priv); + dev->priv = NULL; + return DRVMGR_FAIL; + } + + status = gr_tmtc_1553_hw_init(priv); + if ( status != 0 ) { + genirq_destroy(priv->genirq); + free(priv); + dev->priv = NULL; + printf(" Failed to initialize GR-TMTC-1553 HW: %d\n", status); + return DRVMGR_FAIL; + } + + /* Init amba bus */ + priv->config.abus = &priv->abus; + priv->config.ops = &ambapp_tmtc_1553_ops; + priv->config.maps_down = &priv->bus_maps_down[0]; + /* This PCI device has only target interface so DMA is not supported, + * which means that translation from AMBA->PCI should fail if attempted. + */ + priv->config.maps_up = DRVMGR_TRANSLATE_NO_BRIDGE; + if ( priv->dev->minor_drv < gr_tmtc_1553_resources_cnt ) { + priv->config.resources = gr_tmtc_1553_resources[priv->dev->minor_drv]; + } else { + priv->config.resources = NULL; + } + + /* Create And Register AMBA PnP Bus */ + return ambapp_bus_register(dev, &priv->config); +} + +int gr_tmtc_1553_init2(struct drvmgr_dev *dev) +{ + struct gr_tmtc_1553_priv *priv = dev->priv; + + /* Clear any old interrupt requests */ + drvmgr_interrupt_clear(dev, 0); + + /* Enable System IRQ so that GR-TMTC-1553 PCI target interrupt goes through. + * + * It is important to enable it in stage init2. If interrupts were enabled in init1 + * this might hang the system when more than one PCI target is connected, this is + * because PCI interrupts might be shared and PCI target 2 have not initialized and + * might therefore drive interrupt already when entering init1(). + */ + drvmgr_interrupt_register( + dev, + 0, + "gr_tmtc_1553", + gr_tmtc_1553_isr, + (void *)priv); + + return DRVMGR_OK; +} + +int ambapp_tmtc_1553_int_register( + struct drvmgr_dev *dev, + int irq, + const char *info, + drvmgr_isr handler, + void *arg) +{ + struct gr_tmtc_1553_priv *priv = dev->parent->dev->priv; + rtems_interrupt_level level; + int status; + + rtems_interrupt_disable(level); + + status = genirq_register(priv->genirq, irq, handler, arg); + if ( status == 0 ) { + /* Disable and clear IRQ for first registered handler */ + priv->irq->iclear = (1<<irq); + priv->irq->mask[0] &= ~(1<<irq); /* mask interrupt source */ + } else if ( status == 1 ) + status = 0; + + if (status != 0) { + rtems_interrupt_enable(level); + return DRVMGR_FAIL; + } + + status = genirq_enable(priv->genirq, irq, handler, arg); + if ( status == 0 ) { + /* Enable IRQ for first enabled handler only */ + priv->irq->mask[0] |= (1<<irq); /* unmask interrupt source */ + } else if ( status == 1 ) + status = 0; + + rtems_interrupt_enable(level); + + return status; +} + +int ambapp_tmtc_1553_int_unregister( + struct drvmgr_dev *dev, + int irq, + drvmgr_isr isr, + void *arg) +{ + struct gr_tmtc_1553_priv *priv = dev->parent->dev->priv; + rtems_interrupt_level level; + int status; + + rtems_interrupt_disable(level); + + status = genirq_disable(priv->genirq, irq, isr, arg); + if ( status == 0 ) { + /* Disable IRQ only when no enabled handler exists */ + priv->irq->mask[0] &= ~(1<<irq); /* mask interrupt source */ + } else if ( status == 1 ) + status = 0; + + status = genirq_unregister(priv->genirq, irq, isr, arg); + if ( status != 0 ) + status = DRVMGR_FAIL; + + rtems_interrupt_enable(level); + + return status; +} + +int ambapp_tmtc_1553_int_unmask( + struct drvmgr_dev *dev, + int irq) +{ + struct gr_tmtc_1553_priv *priv = dev->parent->dev->priv; + rtems_interrupt_level level; + + DBG("TMTC-1553 IRQ %d: enable\n", irq); + + if ( genirq_check(priv->genirq, irq) ) + return DRVMGR_FAIL; + + rtems_interrupt_disable(level); + + /* Enable IRQ */ + priv->irq->mask[0] |= (1<<irq); /* unmask interrupt source */ + + rtems_interrupt_enable(level); + + return DRVMGR_OK; +} + +int ambapp_tmtc_1553_int_mask( + struct drvmgr_dev *dev, + int irq) +{ + struct gr_tmtc_1553_priv *priv = dev->parent->dev->priv; + rtems_interrupt_level level; + + DBG("TMTC-1553 IRQ %d: disable\n", irq); + + if ( genirq_check(priv->genirq, irq) ) + return DRVMGR_FAIL; + + rtems_interrupt_disable(level); + + /* Disable IRQ */ + priv->irq->mask[0] &= ~(1<<irq); /* mask interrupt source */ + + rtems_interrupt_enable(level); + + return DRVMGR_OK; +} + +int ambapp_tmtc_1553_int_clear( + struct drvmgr_dev *dev, + int irq) +{ + struct gr_tmtc_1553_priv *priv = dev->parent->dev->priv; + + if ( genirq_check(priv->genirq, irq) ) + return DRVMGR_FAIL; + + priv->irq->iclear = (1<<irq); + + return DRVMGR_OK; +} + +int ambapp_tmtc_1553_get_params(struct drvmgr_dev *dev, struct drvmgr_bus_params *params) +{ + struct gr_tmtc_1553_priv *priv = dev->parent->dev->priv; + + /* Device name prefix pointer, skip /dev */ + params->dev_prefix = &priv->prefix[5]; + + return 0; +} + +void gr_tmtc_1553_print_dev(struct drvmgr_dev *dev, int options) +{ + struct gr_tmtc_1553_priv *priv = dev->priv; + struct pci_dev_info *devinfo = priv->devinfo; + uint32_t bar0, bar0_size; + + /* Print */ + printf("--- GR-TMTC-1553 [bus 0x%x, dev 0x%x, fun 0x%x] ---\n", + PCI_DEV_EXPAND(priv->pcidev)); + + bar0 = devinfo->resources[0].address; + bar0_size = devinfo->resources[0].size; + + printf(" PCI BAR[0]: 0x%lx - 0x%lx\n", bar0, bar0 + bar0_size - 1); + printf(" IRQ REGS: 0x%x\n", (unsigned int)priv->irq); + printf(" IRQ: %d\n", devinfo->irq); + printf(" FREQ: %d Hz\n", priv->version->amba_freq_hz); + printf(" IMASK: 0x%08x\n", priv->irq->mask[0]); + printf(" IPEND: 0x%08x\n", priv->irq->ipend); + + /* Print amba config */ + if ( options & TMTC_1553_OPTIONS_AMBA ) { + ambapp_print(&priv->abus, 10); + } +#if 0 + /* Print IRQ handlers and their arguments */ + if ( options & TMTC_1553_OPTIONS_IRQ ) { + int i; + for(i=0; i<16; i++) { + printf(" IRQ[%02d]: 0x%x, arg: 0x%x\n", + i, (unsigned int)priv->isrs[i].handler, (unsigned int)priv->isrs[i].arg); + } + } +#endif +} + +void gr_tmtc_1553_print(int options) +{ + struct pci_drv_info *drv = &gr_tmtc_1553_info; + struct drvmgr_dev *dev; + + dev = drv->general.dev; + while(dev) { + gr_tmtc_1553_print_dev(dev, options); + dev = dev->next_in_drv; + } +} diff --git a/c/src/lib/libbsp/sparc/shared/pci/grpci.c b/c/src/lib/libbsp/sparc/shared/pci/grpci.c new file mode 100644 index 0000000000..b649b61aee --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/pci/grpci.c @@ -0,0 +1,698 @@ +/* GRLIB GRPCI PCI HOST driver. + * + * COPYRIGHT (c) 2008. + * Aeroflex Gaisler AB. + * + * Configures the GRPCI core and initialize, + * - the PCI Library (pci.c) + * - the general part of the PCI Bus driver (pci_bus.c) + * + * System interrupt assigned to PCI interrupt (INTA#..INTD#) is by + * default taken from Plug and Play, but may be overridden by the + * driver resources INTA#..INTD#. + * + * 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-06, Daniel Hellstrom <daniel@gaisler.com> + * Created + * + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <rtems/bspIo.h> +#include <libcpu/byteorder.h> +#include <libcpu/access.h> +#include <pci.h> +#include <pci/cfg.h> + +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> +#include <ambapp.h> +#include <drvmgr/pci_bus.h> + +#define DMAPCI_ADDR 0x80000500 + +/* Configuration options */ +#define SYSTEM_MAINMEM_START 0x40000000 + +/* If defined to 1 - byte twisting is enabled by default */ +#define DEFAULT_BT_ENABLED 0 + +/* Interrupt assignment. Set to other value than 0xff in order to + * override defaults and plug&play information + */ +#ifndef GRPCI_INTA_SYSIRQ + #define GRPCI_INTA_SYSIRQ 0xff +#endif +#ifndef GRPCI_INTB_SYSIRQ + #define GRPCI_INTB_SYSIRQ 0xff +#endif +#ifndef GRPCI_INTC_SYSIRQ + #define GRPCI_INTC_SYSIRQ 0xff +#endif +#ifndef GRPCI_INTD_SYSIRQ + #define GRPCI_INTD_SYSIRQ 0xff +#endif + +#define PAGE0_BTEN_BIT 0 +#define PAGE0_BTEN (1<<PAGE0_BTEN_BIT) + +#define CFGSTAT_HOST_BIT 13 +#define CFGSTAT_HOST (1<<CFGSTAT_HOST_BIT) + +/*#define DEBUG 1*/ + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +#define PCI_INVALID_VENDORDEVICEID 0xffffffff +#define PCI_MULTI_FUNCTION 0x80 + +/* + * Bit encode for PCI_CONFIG_HEADER_TYPE register + */ +struct grpci_regs { + volatile unsigned int cfg_stat; + volatile unsigned int bar0; + volatile unsigned int page0; + volatile unsigned int bar1; + volatile unsigned int page1; + volatile unsigned int iomap; + volatile unsigned int stat_cmd; + volatile unsigned int irq; +}; + +struct grpci_priv *grpcipriv = NULL; +static int grpci_minor = 0; +static unsigned int *pcidma = (unsigned int *)DMAPCI_ADDR; + +/* PCI Interrupt assignment. Connects an PCI interrupt pin (INTA#..INTD#) + * to a system interrupt number. + */ +unsigned char grpci_pci_irq_table[4] = +{ + /* INTA# */ GRPCI_INTA_SYSIRQ, + /* INTB# */ GRPCI_INTB_SYSIRQ, + /* INTC# */ GRPCI_INTC_SYSIRQ, + /* INTD# */ GRPCI_INTD_SYSIRQ +}; + +/* Driver private data struture */ +struct grpci_priv { + struct drvmgr_dev *dev; + struct grpci_regs *regs; + int irq; + int minor; + + uint32_t bar1_pci_adr; + uint32_t bar1_size; + + int bt_enabled; + unsigned int pci_area; + unsigned int pci_area_end; + unsigned int pci_io; + unsigned int pci_conf; + unsigned int pci_conf_end; + + uint32_t devVend; /* Host PCI Vendor/Device ID */ + + struct drvmgr_map_entry maps_up[2]; + struct drvmgr_map_entry maps_down[2]; + struct pcibus_config config; +}; + +int grpci_init1(struct drvmgr_dev *dev); + +/* GRPCI DRIVER */ + +struct drvmgr_drv_ops grpci_ops = +{ + .init = {grpci_init1, NULL, NULL, NULL}, + .remove = NULL, + .info = NULL +}; + +struct amba_dev_id grpci_ids[] = +{ + {VENDOR_GAISLER, GAISLER_PCIFBRG}, + {0, 0} /* Mark end of table */ +}; + +struct amba_drv_info grpci_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_GAISLER_GRPCI_ID, /* Driver ID */ + "GRPCI_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &grpci_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + sizeof(struct grpci_priv), /* Make drvmgr alloc private */ + }, + &grpci_ids[0] +}; + +void grpci_register_drv(void) +{ + DBG("Registering GRPCI driver\n"); + drvmgr_drv_register(&grpci_info.general); +} + +int grpci_cfg_r32(pci_dev_t dev, int ofs, uint32_t *val) +{ + struct grpci_priv *priv = grpcipriv; + volatile uint32_t *pci_conf; + unsigned int devfn = PCI_DEV_DEVFUNC(dev); + int retval; + int bus = PCI_DEV_BUS(dev); + + if (ofs & 3) + return PCISTS_EINVAL; + + if (PCI_DEV_SLOT(dev) > 21) { + *val = 0xffffffff; + return PCISTS_OK; + } + + /* Select bus */ + priv->regs->cfg_stat = (priv->regs->cfg_stat & ~(0xf<<23)) | (bus<<23); + + pci_conf = (volatile uint32_t *)(priv->pci_conf | (devfn << 8) | ofs); + + if (priv->bt_enabled) { + *val = CPU_swap_u32(*pci_conf); + } else { + *val = *pci_conf; + } + + if (priv->regs->cfg_stat & 0x100) { + *val = 0xffffffff; + retval = PCISTS_MSTABRT; + } else + retval = PCISTS_OK; + + DBG("pci_read: [%x:%x:%x] reg: 0x%x => addr: 0x%x, val: 0x%x\n", + PCI_DEV_EXPAND(dev), ofs, pci_conf, *val); + + return retval; +} + + +int grpci_cfg_r16(pci_dev_t dev, int ofs, uint16_t *val) +{ + uint32_t v; + int retval; + + if (ofs & 1) + return PCISTS_EINVAL; + + retval = grpci_cfg_r32(dev, ofs & ~0x3, &v); + *val = 0xffff & (v >> (8*(ofs & 0x3))); + + return retval; +} + +int grpci_cfg_r8(pci_dev_t dev, int ofs, uint8_t *val) +{ + uint32_t v; + int retval; + + retval = grpci_cfg_r32(dev, ofs & ~0x3, &v); + + *val = 0xff & (v >> (8*(ofs & 3))); + + return retval; +} + +int grpci_cfg_w32(pci_dev_t dev, int ofs, uint32_t val) +{ + struct grpci_priv *priv = grpcipriv; + volatile uint32_t *pci_conf; + uint32_t value, devfn = PCI_DEV_DEVFUNC(dev); + int bus = PCI_DEV_BUS(dev); + + if (ofs & 0x3) + return PCISTS_EINVAL; + + if (PCI_DEV_SLOT(dev) > 21) + return PCISTS_MSTABRT; + + /* Select bus */ + priv->regs->cfg_stat = (priv->regs->cfg_stat & ~(0xf<<23)) | (bus<<23); + + pci_conf = (volatile uint32_t *)(priv->pci_conf | (devfn << 8) | ofs); + + if ( priv->bt_enabled ) { + value = CPU_swap_u32(val); + } else { + value = val; + } + + *pci_conf = value; + + DBG("pci_write - [%x:%x:%x] reg: 0x%x => addr: 0x%x, val: 0x%x\n", + PCI_DEV_EXPAND(dev), ofs, pci_conf, value); + + return PCISTS_OK; +} + +int grpci_cfg_w16(pci_dev_t dev, int ofs, uint16_t val) +{ + uint32_t v; + int retval; + + if (ofs & 1) + return PCISTS_EINVAL; + + retval = grpci_cfg_r32(dev, ofs & ~0x3, &v); + if (retval != PCISTS_OK) + return retval; + + v = (v & ~(0xffff << (8*(ofs&3)))) | ((0xffff&val) << (8*(ofs&3))); + + return grpci_cfg_w32(dev, ofs & ~0x3, v); +} + +int grpci_cfg_w8(pci_dev_t dev, int ofs, uint8_t val) +{ + uint32_t v; + int retval; + + retval = grpci_cfg_r32(dev, ofs & ~0x3, &v); + if (retval != PCISTS_OK) + return retval; + + v = (v & ~(0xff << (8*(ofs&3)))) | ((0xff&val) << (8*(ofs&3))); + + return grpci_cfg_w32(dev, ofs & ~0x3, v); +} + +/* Return the assigned system IRQ number that corresponds to the PCI + * "Interrupt Pin" information from configuration space. + * + * The IRQ information is stored in the grpci_pci_irq_table configurable + * by the user. + * + * Returns the "system IRQ" for the PCI INTA#..INTD# pin in irq_pin. Returns + * 0xff if not assigned. + */ +uint8_t grpci_bus0_irq_map(pci_dev_t dev, int irq_pin) +{ + uint8_t sysIrqNr = 0; /* not assigned */ + int irq_group; + + if ( (irq_pin >= 1) && (irq_pin <= 4) ) { + /* Use default IRQ decoding on PCI BUS0 according slot numbering */ + irq_group = PCI_DEV_SLOT(dev) & 0x3; + irq_pin = ((irq_pin - 1) + irq_group) & 0x3; + /* Valid PCI "Interrupt Pin" number */ + sysIrqNr = grpci_pci_irq_table[irq_pin]; + } + return sysIrqNr; +} + +int grpci_translate(uint32_t *address, int type, int dir) +{ + uint32_t adr; + struct grpci_priv *priv = grpcipriv; + + if (type == 1) { + /* I/O */ + if (dir != 0) { + /* The PCI bus can not access the CPU bus from I/O + * because GRPCI core does not support I/O BARs + */ + return -1; + } + + /* We have got a PCI BAR address that the CPU want to access... + * Check that it is within the PCI I/O window, I/O adresses + * are mapped 1:1 with GRPCI driver... no translation needed. + */ + adr = *(uint32_t *)address; + if (adr < priv->pci_io || adr >= priv->pci_conf) + return -1; + } else { + /* MEMIO and MEM. + * Memory space is mapped 1:1 so no translation is needed. + * Check that address is within accessible windows. + */ + adr = *(uint32_t *)address; + if (dir == 0) { + /* PCI BAR to AMBA-CPU address.. check that it is + * located within GRPCI PCI Memory Window + * adr = PCI address. + */ + if (adr < priv->pci_area || adr >= priv->pci_area_end) + return -1; + } else { + /* We have a CPU address and want to get access to it + * from PCI space, typically when doing DMA into CPU + * RAM. The GRPCI core has two target BARs that PCI + * masters can access, we check here that the address + * is accessible from PCI. + * adr = AMBA address. + */ + if (adr < priv->bar1_pci_adr || + adr >= (priv->bar1_pci_adr + priv->bar1_size)) + return -1; + } + } + + return 0; +} + +extern struct pci_memreg_ops pci_memreg_sparc_le_ops; +extern struct pci_memreg_ops pci_memreg_sparc_be_ops; + +/* GRPCI PCI access routines, default to Little-endian PCI Bus */ +struct pci_access_drv grpci_access_drv = { + .cfg = + { + grpci_cfg_r8, + grpci_cfg_r16, + grpci_cfg_r32, + grpci_cfg_w8, + grpci_cfg_w16, + grpci_cfg_w32, + }, + .io = + { + _ld8, + _ld_le16, + _ld_le32, + _st8, + _st_le16, + _st_le32, + }, + .memreg = &pci_memreg_sparc_le_ops, + .translate = grpci_translate, +}; + +struct pci_io_ops grpci_io_ops_be = +{ + _ld8, + _ld_be16, + _ld_be32, + _st8, + _st_be16, + _st_be32, +}; + +int grpci_hw_init(struct grpci_priv *priv) +{ + volatile unsigned int *mbar0, *page0; + uint32_t data, addr, mbar0size; + pci_dev_t host = PCI_DEV(0, 0, 0); + + mbar0 = (volatile unsigned int *)priv->pci_area; + + if ( !priv->bt_enabled && ((priv->regs->page0 & PAGE0_BTEN) == PAGE0_BTEN) ) { + /* Byte twisting is on, turn it off */ + grpci_cfg_w32(host, PCI_BASE_ADDRESS_0, 0xffffffff); + grpci_cfg_r32(host, PCI_BASE_ADDRESS_0, &addr); + /* Setup bar0 to nonzero value */ + grpci_cfg_w32(host, PCI_BASE_ADDRESS_0, + CPU_swap_u32(0x80000000)); + /* page0 is accessed through upper half of bar0 */ + addr = (~CPU_swap_u32(addr)+1)>>1; + mbar0size = addr*2; + DBG("GRPCI: Size of MBAR0: 0x%x, MBAR0: 0x%x(lower) 0x%x(upper)\n",mbar0size,((unsigned int)mbar0),((unsigned int)mbar0)+mbar0size/2); + page0 = &mbar0[mbar0size/8]; + DBG("GRPCI: PAGE0 reg address: 0x%x (0x%x)\n",((unsigned int)mbar0)+mbar0size/2,page0); + priv->regs->cfg_stat = (priv->regs->cfg_stat & (~0xf0000000)) | 0x80000000; /* Setup mmap reg so we can reach bar0 */ + *page0 = 0<<PAGE0_BTEN_BIT; /* Disable bytetwisting ... */ + } + + /* Get the GRPCI Host PCI ID */ + grpci_cfg_r32(host, PCI_VENDOR_ID, &priv->devVend); + + /* set 1:1 mapping between AHB -> PCI memory */ + priv->regs->cfg_stat = (priv->regs->cfg_stat & 0x0fffffff) | priv->pci_area; + + /* determine size of target BAR1 */ + grpci_cfg_w32(host, PCI_BASE_ADDRESS_1, 0xffffffff); + grpci_cfg_r32(host, PCI_BASE_ADDRESS_1, &addr); + priv->bar1_size = (~(addr & ~0xf)) + 1; + + /* and map system RAM at pci address 0x40000000 */ + priv->bar1_pci_adr &= ~(priv->bar1_size - 1); /* Fix alignment of BAR1 */ + grpci_cfg_w32(host, PCI_BASE_ADDRESS_1, priv->bar1_pci_adr); + priv->regs->page1 = priv->bar1_pci_adr; + + /* Translate I/O accesses 1:1 */ + priv->regs->iomap = priv->pci_io & 0xffff0000; + + /* Setup Latency Timer and cache line size. Default cache line + * size will result in poor performance (256 word fetches), 0xff + * will set it according to the max size of the PCI FIFO. + */ + grpci_cfg_w8(host, PCI_CACHE_LINE_SIZE, 0xff); + grpci_cfg_w8(host, PCI_LATENCY_TIMER, 0x40); + + /* set as bus master and enable pci memory responses */ + grpci_cfg_r32(host, PCI_COMMAND, &data); + data |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + grpci_cfg_w32(host, PCI_COMMAND, data); + + /* unmask all PCI interrupts at PCI Core, not all GRPCI cores support + * this + */ + priv->regs->irq = 0xf0000; + + /* Successful */ + return 0; +} + +/* Initializes the GRPCI core and driver, must be called before calling init_pci() + * + * Return values + * 0 Successful initalization + * -1 Error during initialization, for example "PCI core not found". + * -2 Error PCI controller not HOST (targets not supported) + * -3 Error due to GRPCI hardware initialization + * -4 Error registering driver to PCI layer + */ +int grpci_init(struct grpci_priv *priv) +{ + struct ambapp_apb_info *apb; + struct ambapp_ahb_info *ahb; + int pin; + union drvmgr_key_value *value; + char keyname[6]; + struct amba_dev_info *ainfo = priv->dev->businfo; + + /* Find PCI core from Plug&Play information */ + apb = ainfo->info.apb_slv; + ahb = ainfo->info.ahb_slv; + + /* Found PCI core, init private structure */ + priv->irq = apb->irq; + priv->regs = (struct grpci_regs *)apb->start; + priv->bt_enabled = DEFAULT_BT_ENABLED; + + /* Calculate the PCI windows + * AMBA->PCI Window: AHB SLAVE AREA0 + * AMBA->PCI I/O cycles Window: AHB SLAVE AREA1 Lower half + * AMBA->PCI Configuration cycles Window: AHB SLAVE AREA1 Upper half + */ + priv->pci_area = ahb->start[0]; + priv->pci_area_end = ahb->start[0] + ahb->mask[0]; + priv->pci_io = ahb->start[1]; + priv->pci_conf = ahb->start[1] + (ahb->mask[1] >> 1); + priv->pci_conf_end = ahb->start[1] + ahb->mask[1]; + + /* On systems where PCI I/O area and configuration area is apart of the "PCI Window" + * the PCI Window stops at the start of the PCI I/O area + */ + if ( (priv->pci_io > priv->pci_area) && (priv->pci_io < (priv->pci_area_end-1)) ) { + priv->pci_area_end = priv->pci_io; + } + + /* Init PCI interrupt assignment table to all use the interrupt routed through + * the GRPCI core. + */ + strcpy(keyname, "INTX#"); + for (pin=1; pin<5; pin++) { + if ( grpci_pci_irq_table[pin-1] == 0xff ) { + grpci_pci_irq_table[pin-1] = priv->irq; + + /* User may override Both hardcoded IRQ setup and Plug & Play IRQ */ + keyname[3] = 'A' + (pin-1); + value = drvmgr_dev_key_get(priv->dev, keyname, KEY_TYPE_INT); + if ( value ) + grpci_pci_irq_table[pin-1] = value->i; + } + } + + /* User may override DEFAULT_BT_ENABLED to enable/disable byte twisting */ + value = drvmgr_dev_key_get(priv->dev, "byteTwisting", KEY_TYPE_INT); + if ( value ) + priv->bt_enabled = value->i; + + /* Use GRPCI target BAR1 to map CPU RAM to PCI, this is to make it + * possible for PCI peripherals to do DMA directly to CPU memory. + */ + value = drvmgr_dev_key_get(priv->dev, "tgtbar1", KEY_TYPE_INT); + if (value) + priv->bar1_pci_adr = value->i; + else + priv->bar1_pci_adr = SYSTEM_MAINMEM_START; /* default */ + + /* This driver only support HOST systems, we check for HOST */ + if ( !(priv->regs->cfg_stat & CFGSTAT_HOST) ) { + /* Target not supported */ + return -2; + } + + /* Init the PCI Core */ + if ( grpci_hw_init(priv) ) { + return -3; + } + + /* Down streams translation table */ + priv->maps_down[0].name = "AMBA -> PCI MEM Window"; + priv->maps_down[0].size = priv->pci_area_end - priv->pci_area; + priv->maps_down[0].from_adr = (void *)priv->pci_area; + priv->maps_down[0].to_adr = (void *)priv->pci_area; + /* End table */ + priv->maps_down[1].size = 0; + + /* Up streams translation table */ + priv->maps_up[0].name = "Target BAR1 -> AMBA"; + priv->maps_up[0].size = priv->bar1_size; + priv->maps_up[0].from_adr = (void *)priv->bar1_pci_adr; + priv->maps_up[0].to_adr = (void *)priv->bar1_pci_adr; + /* End table */ + priv->maps_up[1].size = 0; + + return 0; +} + +/* Called when a core is found with the AMBA device and vendor ID + * given in grpci_ids[]. IRQ, Console does not work here + */ +int grpci_init1(struct drvmgr_dev *dev) +{ + int status; + struct grpci_priv *priv; + struct pci_auto_setup grpci_auto_cfg; + + DBG("GRPCI[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name); + + if ( grpci_minor != 0 ) { + DBG("Driver only supports one PCI core\n"); + return DRVMGR_FAIL; + } + + if ( (strcmp(dev->parent->dev->drv->name, "AMBAPP_GRLIB_DRV") != 0) && + (strcmp(dev->parent->dev->drv->name, "AMBAPP_LEON2_DRV") != 0) ) { + /* We only support GRPCI driver on local bus */ + return DRVMGR_FAIL; + } + + priv = dev->priv; + if ( !priv ) + return DRVMGR_NOMEM; + + priv->dev = dev; + priv->minor = grpci_minor++; + + grpcipriv = priv; + status = grpci_init(priv); + if (status) { + printf("Failed to initialize grpci driver %d\n", status); + return DRVMGR_FAIL; + } + + + /* Register the PCI core at the PCI layers */ + + if (priv->bt_enabled == 0) { + /* Host is Big-Endian */ + pci_endian = PCI_BIG_ENDIAN; + + memcpy(&grpci_access_drv.io, &grpci_io_ops_be, + sizeof(grpci_io_ops_be)); + grpci_access_drv.memreg = &pci_memreg_sparc_be_ops; + } + + if (pci_access_drv_register(&grpci_access_drv)) { + /* Access routines registration failed */ + return DRVMGR_FAIL; + } + + /* Prepare memory MAP */ + grpci_auto_cfg.options = 0; + grpci_auto_cfg.mem_start = 0; + grpci_auto_cfg.mem_size = 0; + grpci_auto_cfg.memio_start = priv->pci_area; + grpci_auto_cfg.memio_size = priv->pci_area_end - priv->pci_area; + grpci_auto_cfg.io_start = priv->pci_io; + grpci_auto_cfg.io_size = priv->pci_conf - priv->pci_io; + grpci_auto_cfg.irq_map = grpci_bus0_irq_map; + grpci_auto_cfg.irq_route = NULL; /* use standard routing */ + pci_config_register(&grpci_auto_cfg); + + if (pci_config_init()) { + /* PCI configuration failed */ + return DRVMGR_FAIL; + } + + priv->config.maps_down = &priv->maps_down[0]; + priv->config.maps_up = &priv->maps_up[0]; + return pcibus_register(dev, &priv->config); +} + +/* DMA functions which uses GRPCIs optional DMA controller (len in words) */ +int grpci_dma_to_pci(unsigned int ahb_addr, unsigned int pci_addr, unsigned int len) { + int ret = 0; + + pcidma[0] = 0x82; + pcidma[1] = ahb_addr; + pcidma[2] = pci_addr; + pcidma[3] = len; + pcidma[0] = 0x83; + + while ( (pcidma[0] & 0x4) == 0) + ; + + if (pcidma[0] & 0x8) { /* error */ + ret = -1; + } + + pcidma[0] |= 0xC; + return ret; + +} + +int grpci_dma_from_pci(unsigned int ahb_addr, unsigned int pci_addr, unsigned int len) { + int ret = 0; + + pcidma[0] = 0x80; + pcidma[1] = ahb_addr; + pcidma[2] = pci_addr; + pcidma[3] = len; + pcidma[0] = 0x81; + + while ( (pcidma[0] & 0x4) == 0) + ; + + if (pcidma[0] & 0x8) { /* error */ + ret = -1; + } + + pcidma[0] |= 0xC; + return ret; + +} diff --git a/c/src/lib/libbsp/sparc/shared/pci/grpci2.c b/c/src/lib/libbsp/sparc/shared/pci/grpci2.c new file mode 100644 index 0000000000..0745b3a741 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/pci/grpci2.c @@ -0,0 +1,906 @@ +/* GRLIB GRPCI2 PCI HOST driver. + * + * COPYRIGHT (c) 2011 + * Aeroflex Gaisler AB. + * + * Configures the GRPCI2 core and initialize, + * - the PCI Library (pci.c) + * - the general part of the PCI Bus driver (pci_bus.c) + * + * System interrupt assigned to PCI interrupt (INTA#..INTD#) is by + * default taken from Plug and Play, but may be overridden by the + * driver resources INTA#..INTD#. GRPCI2 handles differently depending + * on the design (4 different ways). + * + * 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. + * + * 2011-02-04, Daniel Hellstrom <daniel@gaisler.com> + * Created + * + * + * GRPCI2 IRQ implementation notes + * ------------------------------- + * Since the Driver Manager pci_bus layer implements IRQ by calling + * pci_interrupt_* which translates into BSP_shared_interrupt_*, and the + * root-bus also relies on BSP_shared_interrupt_*, it is safe for the GRPCI2 + * driver to use the drvmgr_interrupt_* routines since they will be + * accessing the same routines in the end. Otherwise the GRPCI2 driver must + * have used the pci_interrupt_* routines. + * + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <rtems/bspIo.h> +#include <libcpu/byteorder.h> +#include <libcpu/access.h> +#include <pci.h> +#include <pci/cfg.h> + +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> +#include <ambapp.h> +#include <drvmgr/pci_bus.h> +#include <grpci2.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 + +/* If defined to 1 - byte twisting is enabled by default */ +#define DEFAULT_BT_ENABLED 0 + +/* Interrupt assignment. Set to other value than 0xff in order to + * override defaults and plug&play information + */ +#ifndef GRPCI2_INTA_SYSIRQ + #define GRPCI2_INTA_SYSIRQ 0xff +#endif +#ifndef GRPCI2_INTB_SYSIRQ + #define GRPCI2_INTB_SYSIRQ 0xff +#endif +#ifndef GRPCI2_INTC_SYSIRQ + #define GRPCI2_INTC_SYSIRQ 0xff +#endif +#ifndef GRPCI2_INTD_SYSIRQ + #define GRPCI2_INTD_SYSIRQ 0xff +#endif + +/*#define DEBUG 1*/ + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +#define PCI_INVALID_VENDORDEVICEID 0xffffffff +#define PCI_MULTI_FUNCTION 0x80 + +/* + * GRPCI2 APB Register MAP + */ +struct grpci2_regs { + volatile unsigned int ctrl; /* 0x00 */ + volatile unsigned int sts_cap; /* 0x04 */ + int res1; /* 0x08 */ + volatile unsigned int io_map; /* 0x0C */ + volatile unsigned int dma_ctrl; /* 0x10 */ + volatile unsigned int dma_bdbase; /* 0x14 */ + int res2[2]; /* 0x18 */ + volatile unsigned int bars[6]; /* 0x20 */ + int res3[2]; /* 0x38 */ + volatile unsigned int ahbmst_map[16]; /* 0x40 */ +}; + +#define CTRL_BUS_BIT 16 + +#define CTRL_SI (1<<27) +#define CTRL_PE (1<<26) +#define CTRL_EI (1<<25) +#define CTRL_ER (1<<24) +#define CTRL_BUS (0xff<<CTRL_BUS_BIT) +#define CTRL_HOSTINT 0xf + +#define STS_HOST_BIT 31 +#define STS_MST_BIT 30 +#define STS_TAR_BIT 29 +#define STS_DMA_BIT 28 +#define STS_DI_BIT 27 +#define STS_HI_BIT 26 +#define STS_IRQMODE_BIT 24 +#define STS_TRACE_BIT 23 +#define STS_CFGERRVALID_BIT 20 +#define STS_CFGERR_BIT 19 +#define STS_INTTYPE_BIT 12 +#define STS_INTSTS_BIT 8 +#define STS_FDEPTH_BIT 2 +#define STS_FNUM_BIT 0 + +#define STS_HOST (1<<STS_HOST_BIT) +#define STS_MST (1<<STS_MST_BIT) +#define STS_TAR (1<<STS_TAR_BIT) +#define STS_DMA (1<<STS_DMA_BIT) +#define STS_DI (1<<STS_DI_BIT) +#define STS_HI (1<<STS_HI_BIT) +#define STS_IRQMODE (0x3<<STS_IRQMODE_BIT) +#define STS_TRACE (1<<STS_TRACE_BIT) +#define STS_CFGERRVALID (1<<STS_CFGERRVALID_BIT) +#define STS_CFGERR (1<<STS_CFGERR_BIT) +#define STS_INTTYPE (0x3f<<STS_INTTYPE_BIT) +#define STS_INTSTS (0xf<<STS_INTSTS_BIT) +#define STS_FDEPTH (0x7<<STS_FDEPTH_BIT) +#define STS_FNUM (0x3<<STS_FNUM_BIT) + +#define STS_ISYSERR (1<<17) +#define STS_IDMA (1<<16) +#define STS_IDMAERR (1<<15) +#define STS_IMSTABRT (1<<14) +#define STS_ITGTABRT (1<<13) +#define STS_IPARERR (1<<12) + +struct grpci2_bd_chan { + volatile unsigned int ctrl; /* 0x00 DMA Control */ + volatile unsigned int nchan; /* 0x04 Next DMA Channel Address */ + volatile unsigned int nbd; /* 0x08 Next Data Descriptor in channel */ + volatile unsigned int res; /* 0x0C Reserved */ +}; + +#define BD_CHAN_EN 0x80000000 +#define BD_CHAN_TYPE 0x00300000 +#define BD_CHAN_BDCNT 0x0000ffff +#define BD_CHAN_EN_BIT 31 +#define BD_CHAN_TYPE_BIT 20 +#define BD_CHAN_BDCNT_BIT 0 + +struct grpci2_bd_data { + volatile unsigned int ctrl; /* 0x00 DMA Data Control */ + volatile unsigned int pci_adr; /* 0x04 PCI Start Address */ + volatile unsigned int ahb_adr; /* 0x08 AHB Start address */ + volatile unsigned int next; /* 0x0C Next Data Descriptor in channel */ +}; + +#define BD_DATA_EN 0x80000000 +#define BD_DATA_IE 0x40000000 +#define BD_DATA_DR 0x20000000 +#define BD_DATA_TYPE 0x00300000 +#define BD_DATA_ER 0x00080000 +#define BD_DATA_LEN 0x0000ffff +#define BD_DATA_EN_BIT 31 +#define BD_DATA_IE_BIT 30 +#define BD_DATA_DR_BIT 29 +#define BD_DATA_TYPE_BIT 20 +#define BD_DATA_ER_BIT 19 +#define BD_DATA_LEN_BIT 0 + +/* GRPCI2 Capability */ +struct grpci2_cap_first { + unsigned int ctrl; + unsigned int pci2ahb_map[6]; + unsigned int ext2ahb_map; + unsigned int io_map; + unsigned int pcibar_size[6]; +}; +#define CAP9_CTRL_OFS 0 +#define CAP9_BAR_OFS 0x4 +#define CAP9_IOMAP_OFS 0x20 +#define CAP9_BARSIZE_OFS 0x24 + +struct grpci2_priv *grpci2priv = NULL; + +/* PCI Interrupt assignment. Connects an PCI interrupt pin (INTA#..INTD#) + * to a system interrupt number. + */ +unsigned char grpci2_pci_irq_table[4] = +{ + /* INTA# */ GRPCI2_INTA_SYSIRQ, + /* INTB# */ GRPCI2_INTB_SYSIRQ, + /* INTC# */ GRPCI2_INTC_SYSIRQ, + /* INTD# */ GRPCI2_INTD_SYSIRQ +}; + +/* Start of workspace/dynamical area */ +extern unsigned int _end; +#define DMA_START ((unsigned int) &_end) + +/* Default BAR mapping, set BAR0 256MB 1:1 mapped base of CPU RAM */ +struct grpci2_pcibar_cfg grpci2_default_bar_mapping[6] = { + /* BAR0 */ {DMA_START, DMA_START, 0x10000000}, + /* BAR1 */ {0, 0, 0}, + /* BAR2 */ {0, 0, 0}, + /* BAR3 */ {0, 0, 0}, + /* BAR4 */ {0, 0, 0}, + /* BAR5 */ {0, 0, 0}, +}; + +/* Driver private data struture */ +struct grpci2_priv { + struct drvmgr_dev *dev; + struct grpci2_regs *regs; + char irq; + char irq_mode; /* IRQ Mode from CAPSTS REG */ + char bt_enabled; + unsigned int irq_mask; + + struct grpci2_pcibar_cfg *barcfg; + + unsigned int pci_area; + unsigned int pci_area_end; + unsigned int pci_io; + unsigned int pci_conf; + unsigned int pci_conf_end; + + uint32_t devVend; /* Host PCI Device/Vendor ID */ + + struct drvmgr_map_entry maps_up[7]; + struct drvmgr_map_entry maps_down[2]; + struct pcibus_config config; +}; + +int grpci2_init1(struct drvmgr_dev *dev); +int grpci2_init3(struct drvmgr_dev *dev); + +/* GRPCI2 DRIVER */ + +struct drvmgr_drv_ops grpci2_ops = +{ + .init = {grpci2_init1, NULL, grpci2_init3, NULL}, + .remove = NULL, + .info = NULL +}; + +struct amba_dev_id grpci2_ids[] = +{ + {VENDOR_GAISLER, GAISLER_GRPCI2}, + {0, 0} /* Mark end of table */ +}; + +struct amba_drv_info grpci2_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_GAISLER_GRPCI2_ID,/* Driver ID */ + "GRPCI2_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &grpci2_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + sizeof(struct grpci2_priv), /* Make drvmgr alloc private */ + }, + &grpci2_ids[0] +}; + +void grpci2_register_drv(void) +{ + DBG("Registering GRPCI2 driver\n"); + drvmgr_drv_register(&grpci2_info.general); +} + +int grpci2_cfg_r32(pci_dev_t dev, int ofs, uint32_t *val) +{ + struct grpci2_priv *priv = grpci2priv; + volatile uint32_t *pci_conf; + unsigned int tmp, devfn; + IRQ_GLOBAL_PREPARE(oldLevel); + int retval, bus = PCI_DEV_BUS(dev); + + if ((unsigned int)ofs & 0xffffff03) { + retval = PCISTS_EINVAL; + goto out2; + } + + if (PCI_DEV_SLOT(dev) > 15) { + retval = PCISTS_MSTABRT; + goto out; + } + + /* GRPCI2 can access "non-standard" devices on bus0 (on AD11.AD16), + * we skip them. + */ + if (bus == 0 && PCI_DEV_SLOT(dev) != 0) + devfn = PCI_DEV_DEVFUNC(dev) + PCI_DEV(0, 6, 0); + else + devfn = PCI_DEV_DEVFUNC(dev); + + pci_conf = (volatile uint32_t *) (priv->pci_conf | (devfn << 8) | ofs); + + IRQ_GLOBAL_DISABLE(oldLevel); /* protect regs */ + + /* Select bus */ + priv->regs->ctrl = (priv->regs->ctrl & ~(0xff<<16)) | (bus<<16); + /* clear old status */ + priv->regs->sts_cap = (STS_CFGERR | STS_CFGERRVALID); + + tmp = *pci_conf; + + /* Wait until GRPCI2 signals that CFG access is done, it should be + * done instantaneously unless a DMA operation is ongoing... + */ + while ((priv->regs->sts_cap & STS_CFGERRVALID) == 0) + ; + + if (priv->regs->sts_cap & STS_CFGERR) { + retval = PCISTS_MSTABRT; + } else { + /* Bus always little endian (unaffected by byte-swapping) */ + *val = CPU_swap_u32(tmp); + retval = PCISTS_OK; + } + + IRQ_GLOBAL_ENABLE(oldLevel); + +out: + if (retval != PCISTS_OK) + *val = 0xffffffff; + + DBG("pci_read: [%x:%x:%x] reg: 0x%x => addr: 0x%x, val: 0x%x (%d)\n", + PCI_DEV_EXPAND(dev), ofs, pci_conf, *val, retval); + +out2: + return retval; +} + +int grpci2_cfg_r16(pci_dev_t dev, int ofs, uint16_t *val) +{ + uint32_t v; + int retval; + + if (ofs & 1) + return PCISTS_EINVAL; + + retval = grpci2_cfg_r32(dev, ofs & ~0x3, &v); + *val = 0xffff & (v >> (8*(ofs & 0x3))); + + return retval; +} + +int grpci2_cfg_r8(pci_dev_t dev, int ofs, uint8_t *val) +{ + uint32_t v; + int retval; + + retval = grpci2_cfg_r32(dev, ofs & ~0x3, &v); + + *val = 0xff & (v >> (8*(ofs & 3))); + + return retval; +} + +int grpci2_cfg_w32(pci_dev_t dev, int ofs, uint32_t val) +{ + struct grpci2_priv *priv = grpci2priv; + volatile uint32_t *pci_conf; + uint32_t value, devfn; + int retval, bus = PCI_DEV_BUS(dev); + IRQ_GLOBAL_PREPARE(oldLevel); + + if ((unsigned int)ofs & 0xffffff03) + return PCISTS_EINVAL; + + if (PCI_DEV_SLOT(dev) > 15) + return PCISTS_MSTABRT; + + value = CPU_swap_u32(val); + + /* GRPCI2 can access "non-standard" devices on bus0 (on AD11.AD16), + * we skip them. + */ + if (bus == 0 && PCI_DEV_SLOT(dev) != 0) + devfn = PCI_DEV_DEVFUNC(dev) + PCI_DEV(0, 6, 0); + else + devfn = PCI_DEV_DEVFUNC(dev); + + pci_conf = (volatile uint32_t *) (priv->pci_conf | (devfn << 8) | ofs); + + IRQ_GLOBAL_DISABLE(oldLevel); /* protect regs */ + + /* Select bus */ + priv->regs->ctrl = (priv->regs->ctrl & ~(0xff<<16)) | (bus<<16); + /* clear old status */ + priv->regs->sts_cap = (STS_CFGERR | STS_CFGERRVALID); + + *pci_conf = value; + + /* Wait until GRPCI2 signals that CFG access is done, it should be + * done instantaneously unless a DMA operation is ongoing... + */ + while ((priv->regs->sts_cap & STS_CFGERRVALID) == 0) + ; + + if (priv->regs->sts_cap & STS_CFGERR) + retval = PCISTS_MSTABRT; + else + retval = PCISTS_OK; + + IRQ_GLOBAL_ENABLE(oldLevel); + + DBG("pci_write - [%x:%x:%x] reg: 0x%x => addr: 0x%x, val: 0x%x (%d)\n", + PCI_DEV_EXPAND(dev), ofs, pci_conf, value, retval); + + return retval; +} + +int grpci2_cfg_w16(pci_dev_t dev, int ofs, uint16_t val) +{ + uint32_t v; + int retval; + + if (ofs & 1) + return PCISTS_EINVAL; + + retval = grpci2_cfg_r32(dev, ofs & ~0x3, &v); + if (retval != PCISTS_OK) + return retval; + + v = (v & ~(0xffff << (8*(ofs&3)))) | ((0xffff&val) << (8*(ofs&3))); + + return grpci2_cfg_w32(dev, ofs & ~0x3, v); +} + +int grpci2_cfg_w8(pci_dev_t dev, int ofs, uint8_t val) +{ + uint32_t v; + int retval; + + retval = grpci2_cfg_r32(dev, ofs & ~0x3, &v); + if (retval != PCISTS_OK) + return retval; + + v = (v & ~(0xff << (8*(ofs&3)))) | ((0xff&val) << (8*(ofs&3))); + + return grpci2_cfg_w32(dev, ofs & ~0x3, v); +} + +/* Return the assigned system IRQ number that corresponds to the PCI + * "Interrupt Pin" information from configuration space. + * + * The IRQ information is stored in the grpci2_pci_irq_table configurable + * by the user. + * + * Returns the "system IRQ" for the PCI INTA#..INTD# pin in irq_pin. Returns + * 0xff if not assigned. + */ +uint8_t grpci2_bus0_irq_map(pci_dev_t dev, int irq_pin) +{ + uint8_t sysIrqNr = 0; /* not assigned */ + int irq_group; + + if ( (irq_pin >= 1) && (irq_pin <= 4) ) { + /* Use default IRQ decoding on PCI BUS0 according slot numbering */ + irq_group = PCI_DEV_SLOT(dev) & 0x3; + irq_pin = ((irq_pin - 1) + irq_group) & 0x3; + /* Valid PCI "Interrupt Pin" number */ + sysIrqNr = grpci2_pci_irq_table[irq_pin]; + } + return sysIrqNr; +} + +int grpci2_translate(uint32_t *address, int type, int dir) +{ + uint32_t adr, start, end; + struct grpci2_priv *priv = grpci2priv; + int i; + + if (type == 1) { + /* I/O */ + if (dir != 0) { + /* The PCI bus can not access the CPU bus from I/O + * because GRPCI2 core does not support I/O BARs + */ + return -1; + } + + /* We have got a PCI IO BAR address that the CPU want to access. + * Check that it is within the PCI I/O window, I/O adresses + * are NOT mapped 1:1 with GRPCI2 driver... translation needed. + */ + adr = *(uint32_t *)address; + if (adr < 0x100 || adr > 0x10000) + return -1; + *address = adr + priv->pci_io; + } else { + /* MEMIO and MEM. + * Memory space is mapped 1:1 so no translation is needed. + * Check that address is within accessible windows. + */ + adr = *(uint32_t *)address; + if (dir == 0) { + /* PCI BAR to AMBA-CPU address.. check that it is + * located within GRPCI2 PCI Memory Window + * adr = PCI address. + */ + if (adr < priv->pci_area || adr >= priv->pci_area_end) + return -1; + } else { + /* We have a CPU address and want to get access to it + * from PCI space, typically when doing DMA into CPU + * RAM. The GRPCI2 core may have multiple target BARs + * that PCI masters can access, the BARs are user + * configurable in the following ways: + * BAR_SIZE, PCI_BAR Address and MAPPING (AMBA ADR) + * + * The below code tries to find a BAR for which the + * AMBA bar may have been mapped onto, and translate + * the AMBA-CPU address into a PCI address using the + * given mapping. + * + * adr = AMBA address. + */ + for(i=0; i<6; i++) { + start = priv->barcfg[i].ahbadr; + end = priv->barcfg[i].ahbadr + + priv->barcfg[i].barsize; + if (adr >= start && adr < end) { + /* BAR match: Translate address */ + *address = (adr - start) + + priv->barcfg[i].pciadr; + return 0; + } + } + return -1; + } + } + + return 0; +} + +extern struct pci_memreg_ops pci_memreg_sparc_le_ops; +extern struct pci_memreg_ops pci_memreg_sparc_be_ops; + +/* GRPCI2 PCI access routines, default to Little-endian PCI Bus */ +struct pci_access_drv grpci2_access_drv = { + .cfg = + { + grpci2_cfg_r8, + grpci2_cfg_r16, + grpci2_cfg_r32, + grpci2_cfg_w8, + grpci2_cfg_w16, + grpci2_cfg_w32, + }, + .io = + { + _ld8, + _ld_le16, + _ld_le32, + _st8, + _st_le16, + _st_le32, + }, + .memreg = &pci_memreg_sparc_le_ops, + .translate = grpci2_translate, +}; + +struct pci_io_ops grpci2_io_ops_be = +{ + _ld8, + _ld_be16, + _ld_be32, + _st8, + _st_be16, + _st_be32, +}; + +/* PCI Error Interrupt handler, called when there may be a PCI Target/Master + * Abort. + */ +void grpci2_err_isr(void *arg) +{ + struct grpci2_priv *priv = arg; + unsigned int sts = priv->regs->sts_cap; + + if (sts & (STS_IMSTABRT | STS_ITGTABRT | STS_IPARERR | STS_ISYSERR)) { + /* A PCI error IRQ ... Error handler unimplemented + * add your code here... + */ + if (sts & STS_IMSTABRT) { + printk("GRPCI2: unhandled Master Abort IRQ\n"); + } + if (sts & STS_ITGTABRT) { + printk("GRPCI2: unhandled Target Abort IRQ\n"); + } + if (sts & STS_IPARERR) { + printk("GRPCI2: unhandled Parity Error IRQ\n"); + } + if (sts & STS_ISYSERR) { + printk("GRPCI2: unhandled System Error IRQ\n"); + } + } +} + +int grpci2_hw_init(struct grpci2_priv *priv) +{ + struct grpci2_regs *regs = priv->regs; + int i; + uint8_t capptr; + uint32_t data, io_map, ahbadr, pciadr, size; + pci_dev_t host = PCI_DEV(0, 0, 0); + struct grpci2_pcibar_cfg *barcfg = priv->barcfg; + + /* Reset any earlier setup */ + regs->ctrl = 0; + regs->sts_cap = ~0; /* Clear Status */ + regs->dma_ctrl = 0; + regs->dma_bdbase = 0; + + /* Translate I/O accesses 1:1, (will not work for PCI 2.3) */ + regs->io_map = priv->pci_io & 0xffff0000; + + /* set 1:1 mapping between AHB -> PCI memory space, for all Masters + * Each AHB master has it's own mapping registers. Max 16 AHB masters. + */ + for (i=0; i<16; i++) + regs->ahbmst_map[i] = priv->pci_area; + + /* Get the GRPCI2 Host PCI ID */ + grpci2_cfg_r32(host, PCI_VENDOR_ID, &priv->devVend); + + /* Get address to first (always defined) capability structure */ + grpci2_cfg_r8(host, PCI_CAP_PTR, &capptr); + if (capptr == 0) + return -1; + + /* Enable/Disable Byte twisting */ + grpci2_cfg_r32(host, capptr+CAP9_IOMAP_OFS, &io_map); + io_map = (io_map & ~0x1) | (priv->bt_enabled ? 1 : 0); + grpci2_cfg_w32(host, capptr+CAP9_IOMAP_OFS, io_map); + + /* Setup the Host's PCI Target BARs for others to access (DMA) */ + for (i=0; i<6; i++) { + /* Make sure address is properly aligned */ + size = ~(barcfg[i].barsize-1); + barcfg[i].pciadr &= size; + barcfg[i].ahbadr &= size; + + pciadr = barcfg[i].pciadr; + ahbadr = barcfg[i].ahbadr; + size |= PCI_BASE_ADDRESS_MEM_PREFETCH; + + grpci2_cfg_w32(host, capptr+CAP9_BARSIZE_OFS+i*4, size); + grpci2_cfg_w32(host, capptr+CAP9_BAR_OFS+i*4, ahbadr); + grpci2_cfg_w32(host, PCI_BASE_ADDRESS_0+i*4, pciadr); + } + + /* set as bus master and enable pci memory responses */ + grpci2_cfg_r32(host, PCI_COMMAND, &data); + data |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + grpci2_cfg_w32(host, PCI_COMMAND, data); + + /* Enable Error respone (CPU-TRAP) on illegal memory access */ + regs->ctrl = CTRL_ER | CTRL_PE; + + /* Successful */ + return 0; +} + +/* Initializes the GRPCI2 core and driver, must be called before calling + * init_pci() + * + * Return values + * 0 Successful initalization + * -1 Error during initialization, for example "PCI core not found". + * -2 Error PCI controller not HOST (targets not supported) + * -3 Error due to GRPCI2 hardware initialization + */ +int grpci2_init(struct grpci2_priv *priv) +{ + struct ambapp_apb_info *apb; + struct ambapp_ahb_info *ahb; + int pin, i, j; + union drvmgr_key_value *value; + char keyname[6]; + struct amba_dev_info *ainfo = priv->dev->businfo; + struct grpci2_pcibar_cfg *barcfg; + unsigned int size; + + /* Find PCI core from Plug&Play information */ + apb = ainfo->info.apb_slv; + ahb = ainfo->info.ahb_slv; + + /* Found PCI core, init private structure */ + priv->irq = apb->irq; + priv->regs = (struct grpci2_regs *)apb->start; + priv->bt_enabled = DEFAULT_BT_ENABLED; + priv->irq_mode = (priv->regs->sts_cap & STS_IRQMODE) >> STS_IRQMODE_BIT; + + /* Calculate the PCI windows + * AMBA->PCI Window: AHB SLAVE AREA0 + * AMBA->PCI I/O cycles Window: AHB SLAVE AREA1 Lower half + * AMBA->PCI Configuration cycles Window: AHB SLAVE AREA1 Upper half + */ + priv->pci_area = ahb->start[0]; + priv->pci_area_end = ahb->start[0] + ahb->mask[0]; + priv->pci_io = ahb->start[1]; + priv->pci_conf = ahb->start[1] + 0x10000; + priv->pci_conf_end = priv->pci_conf + 0x10000; + + /* On systems where PCI I/O area and configuration area is apart of the + * "PCI Window" the PCI Window stops at the start of the PCI I/O area + */ + if ((priv->pci_io > priv->pci_area) && + (priv->pci_io < (priv->pci_area_end-1))) { + priv->pci_area_end = priv->pci_io; + } + + /* Init PCI interrupt assignment table to all use the interrupt routed + * through the GRPCI2 core. + */ + strcpy(keyname, "INTX#"); + for (pin=1; pin<5; pin++) { + if (grpci2_pci_irq_table[pin-1] == 0xff) { + if (priv->irq_mode < 2) { + /* PCI Interrupts are shared */ + grpci2_pci_irq_table[pin-1] = priv->irq; + } else { + /* Unique IRQ per PCI INT Pin */ + grpci2_pci_irq_table[pin-1] = priv->irq + pin-1; + } + + /* User may override Both hardcoded IRQ setup and Plug & Play IRQ */ + keyname[3] = 'A' + (pin-1); + value = drvmgr_dev_key_get(priv->dev, keyname, KEY_TYPE_INT); + if (value) + grpci2_pci_irq_table[pin-1] = value->i; + } + + /* Remember which IRQs are enabled */ + if (grpci2_pci_irq_table[pin-1] != 0) + priv->irq_mask |= 1 << (pin-1); + } + + /* User may override DEFAULT_BT_ENABLED to enable/disable byte twisting */ + value = drvmgr_dev_key_get(priv->dev, "byteTwisting", KEY_TYPE_INT); + if (value) + priv->bt_enabled = value->i; + + /* Let user Configure the 6 target BARs */ + value = drvmgr_dev_key_get(priv->dev, "tgtBarCfg", KEY_TYPE_POINTER); + if (value) + priv->barcfg = value->ptr; + else + priv->barcfg = grpci2_default_bar_mapping; + + /* This driver only support HOST systems, we check that it can act as a + * PCI Master and that it is in the Host slot. */ + if ((priv->regs->sts_cap&STS_HOST) || !(priv->regs->sts_cap&STS_MST)) + return -2; /* Target not supported */ + + /* Init the PCI Core */ + if (grpci2_hw_init(priv)) + return -3; + + /* Down streams translation table */ + priv->maps_down[0].name = "AMBA -> PCI MEM Window"; + priv->maps_down[0].size = priv->pci_area_end - priv->pci_area; + priv->maps_down[0].from_adr = (void *)priv->pci_area; + priv->maps_down[0].to_adr = (void *)priv->pci_area; + /* End table */ + priv->maps_down[1].size = 0; + + /* Up streams translation table */ + /* Setup the Host's PCI Target BARs for others to access (DMA) */ + barcfg = priv->barcfg; + for (i=0,j=0; i<6; i++) { + size = barcfg[i].barsize; + if (size == 0) + continue; + + /* Make sure address is properly aligned */ + priv->maps_up[j].name = "Target BAR[I] -> AMBA"; + priv->maps_up[j].size = size; + priv->maps_up[j].from_adr = (void *) + (barcfg[i].pciadr & ~(size - 1)); + priv->maps_up[j].to_adr = (void *) + (barcfg[i].ahbadr & ~(size - 1)); + j++; + } + + /* End table */ + priv->maps_up[j].size = 0; + + return 0; +} + +/* Called when a core is found with the AMBA device and vendor ID + * given in grpci2_ids[]. IRQ, Console does not work here + */ +int grpci2_init1(struct drvmgr_dev *dev) +{ + int status; + struct grpci2_priv *priv; + struct pci_auto_setup grpci2_auto_cfg; + + DBG("GRPCI2[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name); + + if (grpci2priv) { + DBG("Driver only supports one PCI core\n"); + return DRVMGR_FAIL; + } + + if ((strcmp(dev->parent->dev->drv->name, "AMBAPP_GRLIB_DRV") != 0) && + (strcmp(dev->parent->dev->drv->name, "AMBAPP_LEON2_DRV") != 0)) { + /* We only support GRPCI2 driver on local bus */ + return DRVMGR_FAIL; + } + + priv = dev->priv; + if (!priv) + return DRVMGR_NOMEM; + + priv->dev = dev; + grpci2priv = priv; + + /* Initialize GRPCI2 Hardware */ + status = grpci2_init(priv); + if (status) { + printf("Failed to initialize grpci2 driver %d\n", status); + return -1; + } + + /* Register the PCI core at the PCI layers */ + + if (priv->bt_enabled == 0) { + /* Host is Big-Endian */ + pci_endian = PCI_BIG_ENDIAN; + + memcpy(&grpci2_access_drv.io, &grpci2_io_ops_be, + sizeof(grpci2_io_ops_be)); + grpci2_access_drv.memreg = &pci_memreg_sparc_be_ops; + } + + if (pci_access_drv_register(&grpci2_access_drv)) { + /* Access routines registration failed */ + return DRVMGR_FAIL; + } + + /* Prepare memory MAP */ + grpci2_auto_cfg.options = 0; + grpci2_auto_cfg.mem_start = 0; + grpci2_auto_cfg.mem_size = 0; + grpci2_auto_cfg.memio_start = priv->pci_area; + grpci2_auto_cfg.memio_size = priv->pci_area_end - priv->pci_area; + grpci2_auto_cfg.io_start = 0x100; /* avoid PCI address 0 */ + grpci2_auto_cfg.io_size = 0x10000 - 0x100; /* lower 64kB I/O 16 */ + grpci2_auto_cfg.irq_map = grpci2_bus0_irq_map; + grpci2_auto_cfg.irq_route = NULL; /* use standard routing */ + pci_config_register(&grpci2_auto_cfg); + + if (pci_config_init()) { + /* PCI configuration failed */ + return DRVMGR_FAIL; + } + + /* Initialize/Register Driver Manager PCI Bus */ + priv->config.maps_down = &priv->maps_down[0]; + priv->config.maps_up = &priv->maps_up[0]; + return pcibus_register(dev, &priv->config); +} + +int grpci2_init3(struct drvmgr_dev *dev) +{ + struct grpci2_priv *priv = dev->priv; + + /* Install and Enable PCI Error interrupt handler */ + drvmgr_interrupt_register(dev, 0, "grpci2", grpci2_err_isr, priv); + + /* Unmask Error IRQ and all PCI interrupts at PCI Core. For this to be + * safe every PCI board have to be resetted (no IRQ generation) before + * Global IRQs are enabled (Init is reached or similar) + */ + priv->regs->ctrl |= (CTRL_EI | priv->irq_mask); + + return DRVMGR_OK; +} diff --git a/c/src/lib/libbsp/sparc/shared/pci/pci_memreg_sparc_be.c b/c/src/lib/libbsp/sparc/shared/pci/pci_memreg_sparc_be.c new file mode 100644 index 0000000000..714c978dcb --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/pci/pci_memreg_sparc_be.c @@ -0,0 +1,19 @@ +/* Registers-over-Memory Space - SPARC Big endian PCI bus definitions */ + +#include <pci.h> +#include <libcpu/access.h> + +struct pci_memreg_ops pci_memreg_sparc_be_ops = { + .ld8 = _ld8, + .st8 = _st8, + + .ld_le16 = _ld_be16, + .st_le16 = _st_be16, + .ld_be16 = _ld_le16, + .st_be16 = _st_le16, + + .ld_le32 = _ld_be32, + .st_le32 = _st_be32, + .ld_be32 = _ld_le32, + .st_be32 = _st_le16, +}; diff --git a/c/src/lib/libbsp/sparc/shared/pci/pci_memreg_sparc_le.c b/c/src/lib/libbsp/sparc/shared/pci/pci_memreg_sparc_le.c new file mode 100644 index 0000000000..357643ed39 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/pci/pci_memreg_sparc_le.c @@ -0,0 +1,19 @@ +/* Registers-over-Memory Space - SPARC Little endian PCI bus definitions */ + +#include <pci.h> +#include <libcpu/access.h> + +struct pci_memreg_ops pci_memreg_sparc_le_ops = { + .ld8 = _ld8, + .st8 = _st8, + + .ld_le16 = _ld_le16, + .st_le16 = _st_le16, + .ld_be16 = _ld_be16, + .st_be16 = _st_be16, + + .ld_le32 = _ld_le32, + .st_le32 = _st_le32, + .ld_be32 = _ld_be32, + .st_be32 = _st_be16, +}; diff --git a/c/src/lib/libbsp/sparc/shared/pci/pcif.c b/c/src/lib/libbsp/sparc/shared/pci/pcif.c new file mode 100644 index 0000000000..3f37107293 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/pci/pcif.c @@ -0,0 +1,567 @@ +/* GRLIB PCIF PCI HOST driver. + * + * COPYRIGHT (c) 2008. + * Aeroflex Gaisler AB. + * + * Configures the PCIF core and initialize, + * - the PCI Library (pci.c) + * - the general part of the PCI Bus driver (pci_bus.c) + * + * System interrupt assigned to PCI interrupt (INTA#..INTD#) is by + * default taken from Plug and Play, but may be overridden by the + * driver resources INTA#..INTD#. + * + * 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-06, Daniel Hellstrom <daniel@gaisler.com> + * Created + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <libcpu/byteorder.h> +#include <libcpu/access.h> +#include <pci.h> +#include <pci/cfg.h> + +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> +#include <ambapp.h> +#include <drvmgr/pci_bus.h> + +#include <pci.h> +#include <rtems/bspIo.h> +#include <string.h> + +/* Configuration options */ +#define SYSTEM_MAINMEM_START 0x40000000 + +/* Interrupt assignment. Set to other value than 0xff in order to + * override defaults and plug&play information + */ +#ifndef PCIF_INTA_SYSIRQ + #define PCIF_INTA_SYSIRQ 0xff +#endif +#ifndef PCIF_INTB_SYSIRQ + #define PCIF_INTB_SYSIRQ 0xff +#endif +#ifndef PCIF_INTC_SYSIRQ + #define PCIF_INTC_SYSIRQ 0xff +#endif +#ifndef PCIF_INTD_SYSIRQ + #define PCIF_INTD_SYSIRQ 0xff +#endif + +/*#define DEBUG 1 */ + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +/* + * Bit encode for PCI_CONFIG_HEADER_TYPE register + */ +struct pcif_regs { + volatile unsigned int bars[4]; /* 0x00-0x10 */ + volatile unsigned int bus; /* 0x10 */ + volatile unsigned int map_io; /* 0x14 */ + volatile unsigned int status; /* 0x18 */ + volatile unsigned int intr; /* 0x1c */ + int unused[(0x40-0x20)/4]; /* 0x20-0x40 */ + volatile unsigned int maps[(0x80-0x40)/4]; /* 0x40-0x80*/ +}; + +struct pcif_priv *pcifpriv = NULL; +static int pcif_minor = 0; + +/* PCI Interrupt assignment. Connects an PCI interrupt pin (INTA#..INTD#) + * to a system interrupt number. + */ +unsigned char pcif_pci_irq_table[4] = +{ + /* INTA# */ PCIF_INTA_SYSIRQ, + /* INTB# */ PCIF_INTB_SYSIRQ, + /* INTC# */ PCIF_INTC_SYSIRQ, + /* INTD# */ PCIF_INTD_SYSIRQ +}; + +/* Driver private data struture */ +struct pcif_priv { + struct drvmgr_dev *dev; + struct pcif_regs *regs; + int irq; + int minor; + int irq_mask; + + unsigned int pci_area; + unsigned int pci_area_end; + unsigned int pci_io; + unsigned int pci_conf; + unsigned int pci_conf_end; + + uint32_t devVend; /* Host PCI Vendor/Device ID */ + uint32_t bar1_size; + + struct drvmgr_map_entry maps_up[2]; + struct drvmgr_map_entry maps_down[2]; + struct pcibus_config config; +}; + +int pcif_init1(struct drvmgr_dev *dev); +int pcif_init3(struct drvmgr_dev *dev); + +/* PCIF DRIVER */ + +struct drvmgr_drv_ops pcif_ops = +{ + .init = {pcif_init1, NULL, pcif_init3, NULL}, + .remove = NULL, + .info = NULL +}; + +struct amba_dev_id pcif_ids[] = +{ + {VENDOR_GAISLER, GAISLER_PCIF}, + {0, 0} /* Mark end of table */ +}; + +struct amba_drv_info pcif_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_GAISLER_PCIF_ID, /* Driver ID */ + "PCIF_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &pcif_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + sizeof(struct pcif_priv), /* Let drvmgr alloc private */ + }, + &pcif_ids[0] +}; + +void pcif_register_drv(void) +{ + DBG("Registering PCIF driver\n"); + drvmgr_drv_register(&pcif_info.general); +} + +int pcif_cfg_r32(pci_dev_t dev, int ofs, uint32_t *val) +{ + struct pcif_priv *priv = pcifpriv; + volatile uint32_t *pci_conf; + unsigned int devfn = PCI_DEV_DEVFUNC(dev); + int retval; + int bus = PCI_DEV_BUS(dev); + + if (ofs & 3) + return PCISTS_EINVAL; + + if (PCI_DEV_SLOT(dev) > 21) { + *val = 0xffffffff; + return PCISTS_OK; + } + + /* Select bus */ + priv->regs->bus = bus << 16; + + pci_conf = (volatile uint32_t *)(priv->pci_conf | (devfn << 8) | ofs); + + *val = *pci_conf; + + if (priv->regs->status & 0x30000000) { + *val = 0xffffffff; + retval = PCISTS_MSTABRT; + } else + retval = PCISTS_OK; + + DBG("pci_read: [%x:%x:%x] reg: 0x%x => addr: 0x%x, val: 0x%x\n", + PCI_DEV_EXPAND(dev), ofs, pci_conf, *val); + + return retval; +} +int pcif_cfg_r16(pci_dev_t dev, int ofs, uint16_t *val) +{ + uint32_t v; + int retval; + + if (ofs & 1) + return PCISTS_EINVAL; + + retval = pcif_cfg_r32(dev, ofs & ~0x3, &v); + *val = 0xffff & (v >> (8*(ofs & 0x3))); + + return retval; +} + +int pcif_cfg_r8(pci_dev_t dev, int ofs, uint8_t *val) +{ + uint32_t v; + int retval; + + retval = pcif_cfg_r32(dev, ofs & ~0x3, &v); + + *val = 0xff & (v >> (8*(ofs & 3))); + + return retval; +} + +int pcif_cfg_w32(pci_dev_t dev, int ofs, uint32_t val) +{ + struct pcif_priv *priv = pcifpriv; + volatile uint32_t *pci_conf; + uint32_t devfn = PCI_DEV_DEVFUNC(dev); + int bus = PCI_DEV_BUS(dev); + + if (ofs & ~0xfc) + return PCISTS_EINVAL; + + if (PCI_DEV_SLOT(dev) > 21) + return PCISTS_MSTABRT; + + /* Select bus */ + priv->regs->bus = bus << 16; + + pci_conf = (volatile uint32_t *)(priv->pci_conf | (devfn << 8) | ofs); + + *pci_conf = val; + + DBG("pci_write - [%x:%x:%x] reg: 0x%x => addr: 0x%x, val: 0x%x\n", + PCI_DEV_EXPAND(dev), ofs, pci_conf, value); + + return PCISTS_OK; +} + +int pcif_cfg_w16(pci_dev_t dev, int ofs, uint16_t val) +{ + uint32_t v; + int retval; + + if (ofs & 1) + return PCISTS_EINVAL; + + retval = pcif_cfg_r32(dev, ofs & ~0x3, &v); + if (retval != PCISTS_OK) + return retval; + + v = (v & ~(0xffff << (8*(ofs&3)))) | ((0xffff&val) << (8*(ofs&3))); + + return pcif_cfg_w32(dev, ofs & ~0x3, v); +} + +int pcif_cfg_w8(pci_dev_t dev, int ofs, uint8_t val) +{ + uint32_t v; + int retval; + + retval = pcif_cfg_r32(dev, ofs & ~0x3, &v); + if (retval != PCISTS_OK) + return retval; + + v = (v & ~(0xff << (8*(ofs&3)))) | ((0xff&val) << (8*(ofs&3))); + + return pcif_cfg_w32(dev, ofs & ~0x3, v); +} + + +/* Return the assigned system IRQ number that corresponds to the PCI + * "Interrupt Pin" information from configuration space. + * + * The IRQ information is stored in the pcif_pci_irq_table configurable + * by the user. + * + * Returns the "system IRQ" for the PCI INTA#..INTD# pin in irq_pin. Returns + * 0xff if not assigned. + */ +uint8_t pcif_bus0_irq_map(pci_dev_t dev, int irq_pin) +{ + uint8_t sysIrqNr = 0; /* not assigned */ + int irq_group; + + if ( (irq_pin >= 1) && (irq_pin <= 4) ) { + /* Use default IRQ decoding on PCI BUS0 according slot numbering */ + irq_group = PCI_DEV_SLOT(dev) & 0x3; + irq_pin = ((irq_pin - 1) + irq_group) & 0x3; + /* Valid PCI "Interrupt Pin" number */ + sysIrqNr = pcif_pci_irq_table[irq_pin]; + } + return sysIrqNr; +} + +int pcif_translate(uint32_t *address, int type, int dir) +{ + /* No address translation implmented at this point */ + return 0; +} + +extern struct pci_memreg_ops pci_memreg_sparc_be_ops; + +/* PCIF Big-Endian PCI access routines */ +struct pci_access_drv pcif_access_drv = { + .cfg = + { + pcif_cfg_r8, + pcif_cfg_r16, + pcif_cfg_r32, + pcif_cfg_w8, + pcif_cfg_w16, + pcif_cfg_w32, + }, + .io = /* PCIF only supports Big-endian */ + { + _ld8, + _ld_be16, + _ld_be32, + _st8, + _st_be16, + _st_be32, + }, + .memreg = &pci_memreg_sparc_be_ops, + .translate = pcif_translate, +}; + +/* Initializes the PCIF core hardware + * + */ +int pcif_hw_init(struct pcif_priv *priv) +{ + struct pcif_regs *regs; + uint32_t data, size; + int mst; + pci_dev_t host = PCI_DEV(0, 0, 0); + + regs = priv->regs; + + /* Mask PCI interrupts */ + regs->intr = 0; + + /* Get the PCIF Host PCI ID */ + pcif_cfg_r32(host, PCI_VENDOR_ID, &priv->devVend); + + /* set 1:1 mapping between AHB -> PCI memory space, for all Master cores */ + for ( mst=0; mst<16; mst++) { + regs->maps[mst] = priv->pci_area; + + /* Check if this register is implemented */ + if ( regs->maps[mst] != priv->pci_area ) + break; + } + + /* and map system RAM at pci address SYSTEM_MAINMEM_START. This way + * PCI targets can do DMA directly into CPU main memory. + */ + regs->bars[0] = SYSTEM_MAINMEM_START; + regs->bars[1] = 0; + regs->bars[2] = 0; + regs->bars[3] = 0; + + /* determine size of target BAR1 */ + pcif_cfg_w32(host, PCI_BASE_ADDRESS_1, 0xffffffff); + pcif_cfg_r32(host, PCI_BASE_ADDRESS_1, &size); + priv->bar1_size = (~(size & ~0xf)) + 1; + + pcif_cfg_w32(host, PCI_BASE_ADDRESS_0, 0); + pcif_cfg_w32(host, PCI_BASE_ADDRESS_1, SYSTEM_MAINMEM_START); + pcif_cfg_w32(host, PCI_BASE_ADDRESS_2, 0); + pcif_cfg_w32(host, PCI_BASE_ADDRESS_3, 0); + pcif_cfg_w32(host, PCI_BASE_ADDRESS_4, 0); + pcif_cfg_w32(host, PCI_BASE_ADDRESS_5, 0); + + /* set as bus master and enable pci memory responses */ + pcif_cfg_r32(host, PCI_COMMAND, &data); + data |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + pcif_cfg_w32(host, PCI_COMMAND, data); + + /* Successful */ + return 0; +} + +/* Initializes the PCIF core and driver, must be called before calling init_pci() + * + * Return values + * 0 Successful initalization + * -1 Error during initialization, for example "PCI core not found". + * -2 Error PCI controller not HOST (targets not supported) + * -3 Error due to PCIF hardware initialization + * -4 Error registering driver to PCI layer + */ +int pcif_init(struct pcif_priv *priv) +{ + struct ambapp_apb_info *apb; + struct ambapp_ahb_info *ahb; + int pin; + union drvmgr_key_value *value; + char keyname[6]; + struct amba_dev_info *ainfo = priv->dev->businfo; + + /* Find PCI core from Plug&Play information */ + apb = ainfo->info.apb_slv; + ahb = ainfo->info.ahb_slv; + + /* Found PCI core, init private structure */ + priv->irq = apb->irq; + priv->regs = (struct pcif_regs *)apb->start; + + /* Calculate the PCI windows + * AMBA->PCI Window: AHB SLAVE AREA0 + * AMBA->PCI I/O cycles Window: AHB SLAVE AREA1 Lower half + * AMBA->PCI Configuration cycles Window: AHB SLAVE AREA1 Upper half + */ + priv->pci_area = ahb->start[0]; + priv->pci_area_end = ahb->start[0] + ahb->mask[0]; + priv->pci_io = ahb->start[1]; + priv->pci_conf = ahb->start[1] + (ahb->mask[1] >> 1); + priv->pci_conf_end = ahb->start[1] + ahb->mask[1]; + + /* On systems where PCI I/O area and configuration area is apart of the "PCI Window" + * the PCI Window stops at the start of the PCI I/O area + */ + if ( (priv->pci_io > priv->pci_area) && (priv->pci_io < (priv->pci_area_end-1)) ) { + priv->pci_area_end = priv->pci_io; + } + + /* Init PCI interrupt assignment table to all use the interrupt routed through + * the PCIF core. + */ + strcpy(keyname, "INTX#"); + for (pin=1; pin<5; pin++) { + if ( pcif_pci_irq_table[pin-1] == 0xff ) { + pcif_pci_irq_table[pin-1] = priv->irq; + + /* User may override Plug & Play IRQ */ + keyname[3] = 'A' + (pin-1); + value = drvmgr_dev_key_get(priv->dev, keyname, KEY_TYPE_INT); + if ( value ) + pcif_pci_irq_table[pin-1] = value->i; + } + } + + priv->irq_mask = 0xf; + value = drvmgr_dev_key_get(priv->dev, "", KEY_TYPE_INT); + if ( value ) + priv->irq_mask = value->i & 0xf; + + /* This driver only support HOST systems, we check for HOST */ + if ( priv->regs->status & 0x00000001 ) { + /* Target not supported */ + return -2; + } + + /* Init the PCI Core */ + if ( pcif_hw_init(priv) ) { + return -3; + } + + /* Down streams translation table */ + priv->maps_down[0].name = "AMBA -> PCI MEM Window"; + priv->maps_down[0].size = priv->pci_area_end - priv->pci_area; + priv->maps_down[0].from_adr = (void *)priv->pci_area; + priv->maps_down[0].to_adr = (void *)priv->pci_area; + /* End table */ + priv->maps_down[1].size = 0; + + /* Up streams translation table */ + priv->maps_up[0].name = "Target BAR1 -> AMBA"; + priv->maps_up[0].size = priv->bar1_size; + priv->maps_up[0].from_adr = (void *)SYSTEM_MAINMEM_START; + priv->maps_up[0].to_adr = (void *)SYSTEM_MAINMEM_START; + /* End table */ + priv->maps_up[1].size = 0; + + return 0; +} + +/* Called when a core is found with the AMBA device and vendor ID + * given in pcif_ids[]. + */ +int pcif_init1(struct drvmgr_dev *dev) +{ + struct pcif_priv *priv; + struct pci_auto_setup pcif_auto_cfg; + + DBG("PCIF[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name); + + if ( pcif_minor != 0 ) { + printf("Driver only supports one PCI core\n"); + return DRVMGR_FAIL; + } + + priv = dev->priv; + if ( !priv ) + return DRVMGR_NOMEM; + + dev->priv = priv; + priv->dev = dev; + priv->minor = pcif_minor++; + + pcifpriv = priv; + if ( pcif_init(priv) ) { + printf("Failed to initialize PCIF driver\n"); + free(priv); + dev->priv = NULL; + return DRVMGR_FAIL; + } + + /* Host is always Big-Endian */ + pci_endian = PCI_BIG_ENDIAN; + + /* Register the PCI core at the PCI layer */ + + if (pci_access_drv_register(&pcif_access_drv)) { + /* Access routines registration failed */ + return DRVMGR_FAIL; + } + + /* Prepare memory MAP */ + pcif_auto_cfg.options = 0; + pcif_auto_cfg.mem_start = 0; + pcif_auto_cfg.mem_size = 0; + pcif_auto_cfg.memio_start = priv->pci_area; + pcif_auto_cfg.memio_size = priv->pci_area_end - priv->pci_area; + pcif_auto_cfg.io_start = priv->pci_io; + pcif_auto_cfg.io_size = priv->pci_conf - priv->pci_io; + pcif_auto_cfg.irq_map = pcif_bus0_irq_map; + pcif_auto_cfg.irq_route = NULL; /* use standard routing */ + pci_config_register(&pcif_auto_cfg); + + if (pci_config_init()) { + /* PCI configuration failed */ + return DRVMGR_FAIL; + } + + priv->config.maps_down = &priv->maps_down[0]; + priv->config.maps_up = &priv->maps_up[0]; + return pcibus_register(dev, &priv->config); +} + +int pcif_init3(struct drvmgr_dev *dev) +{ + struct pcif_priv *priv = dev->priv; + + /* Unmask all interrupts, on some sytems this + * might be problematic because all PCI IRQs are + * not connected on the PCB or used for something + * else. The irqMask driver resource can be used to + * control which PCI IRQs are used to generate the + * PCI system IRQ, example: + * + * 0xf - enable all (DEFAULT) + * 0x8 - enable one PCI irq + * + * Before unmasking PCI IRQ, all PCI boards must + * have been initialized and IRQ turned off to avoid + * system hang. + */ + + priv->regs->intr = priv->irq_mask; + + return DRVMGR_OK; +} diff --git a/c/src/lib/libbsp/sparc/shared/pci/pcifinddevice.c b/c/src/lib/libbsp/sparc/shared/pci/pcifinddevice.c deleted file mode 100644 index 0047d07784..0000000000 --- a/c/src/lib/libbsp/sparc/shared/pci/pcifinddevice.c +++ /dev/null @@ -1,57 +0,0 @@ - -/* Author: Till Straumann <strauman@slac.stanford.edu>, 2001 */ - -/* find a particular PCI device - * (we assume, the firmware configured the PCI bus[es] for us) - * - * pcifinddevice.c,v 1.1.4.2 2003/07/18 15:48:54 joel Exp - */ - -#define PCI_INVALID_VENDORDEVICEID 0xffffffff -#define PCI_MULTI_FUNCTION 0x80 - -#include <pci.h> -#include <rtems/bspIo.h> - -int -BSP_pciFindDevice( unsigned short vendorid, unsigned short deviceid, - int instance, int *pbus, int *pdev, int *pfun ) -{ - unsigned int d; - unsigned short s; - unsigned char bus,dev,fun,hd; - - for (bus=0; bus<BusCountPCI(); bus++) { - for (dev=0; dev<PCI_MAX_DEVICES; dev++) { - - pci_read_config_byte(bus,dev,0, PCI_HEADER_TYPE, &hd); - hd = (hd & PCI_MULTI_FUNCTION ? PCI_MAX_FUNCTIONS : 1); - - for (fun=0; fun<hd; fun++) { - /* - * The last devfn id/slot is special; must skip it - */ - if (PCI_MAX_DEVICES-1==dev && PCI_MAX_FUNCTIONS-1 == fun) - break; - (void)pci_read_config_dword(bus,dev,fun,PCI_VENDOR_ID,&d); - if (PCI_INVALID_VENDORDEVICEID == d) - continue; -#ifdef PCI_DEBUG - printk("BSP_pciFindDevice: found 0x%08x at %d/%d/%d\n",d,bus,dev,fun); -#endif - (void) pci_read_config_word(bus,dev,fun,PCI_VENDOR_ID,&s); - if (vendorid != s) - continue; - (void) pci_read_config_word(bus,dev,fun,PCI_DEVICE_ID,&s); - if (deviceid == s) { - if (instance--) continue; - *pbus=bus; *pdev=dev; *pfun=fun; - return 0; - } - } - } - } - return -1; -} - -/* eof */ 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..0727cdff4f --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/pwm/grpwm.c @@ -0,0 +1,848 @@ +/* + * GRPWM PWM Driver interface. + * + * COPYRIGHT (c) 2009. + * Gaisler Research + * + * 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..99e59b89c7 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/slink/grslink.c @@ -0,0 +1,665 @@ +/* + * This file contains the RTEMS GRSLINK SLINK master driver + * + * -------------------------------------------------------------------------- + * -- This file is a part of GAISLER RESEARCH source code. + * -- Copyright (C) 2008, Gaisler Research AB - all rights reserved. + * -- + * -- ANY USE OR REDISTRIBUTION IN PART OR IN WHOLE MUST BE HANDLED IN + * -- ACCORDANCE WITH THE GAISLER LICENSE AGREEMENT AND MUST BE APPROVED + * -- IN ADVANCE IN WRITING. + * -- + * -- BY DEFAULT, DISTRIBUTION OR DISCLOSURE IS NOT PERMITTED. + * -------------------------------------------------------------------------- + * + * 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..8dddf0c88f --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/spi/spictrl.c @@ -0,0 +1,1011 @@ +/* + * SPICTRL SPI driver implmenetation + * + * COPYRIGHT (c) 2009. + * Aeroflex Gaisler. + * + * 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.c b/c/src/lib/libbsp/sparc/shared/spw/grspw.c index cd6c801cb6..42dbeae887 100644 --- a/c/src/lib/libbsp/sparc/shared/spw/grspw.c +++ b/c/src/lib/libbsp/sparc/shared/spw/grspw.c @@ -1,14 +1,17 @@ /* * This file contains the GRSPW SpaceWire Driver for LEON2 and LEON3. * - * COPYRIGHT (c) 2007 - * Gaisler Research. + * COPYRIGHT (c) 2008 + * Aeroflex Gaisler. * * 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. * * Changes: + * + * 2008-12-09, Daniel Hellstrom <daniel@gaisler.com> + * Converted driver to support driver manager * * 2007-09-27, Daniel Hellstrom <daniel@gaisler.com> * Added basic support for GRSPW2 core. @@ -23,23 +26,23 @@ * Typical LEON3 register: grspw_register(&amba_conf); * * 2007-05-28, Daniel Hellstrom <daniel@gaisler.com> - * Changed errno return values, compatible with RASTA + * Changed errno return values, compatible with RASTA * Spacewire driver * * 2007-05-25, Daniel Hellstrom <daniel@gaisler.com> - * Changed name from /dev/spacewire,/dev/spacewire_b... + * Changed name from /dev/spacewire,/dev/spacewire_b... * to /dev/grspw0,/dev/grspw1... * * 2007-05-24, Daniel Hellstrom <daniel@gaisler.com> * Merged LEON3, LEON2 and RASTA driver to one - this. - * The driver is included and configured from grspw_pci.c + * The driver is included and configured from grspw_pci.c * and grspw_rasta.c. * * 2007-05-23, Daniel Hellstrom <daniel@gaisler.com> * Changed open call, now one need to first call open * and then ioctl(fd,START,timeout) in order to setup * hardware for communication. - * + * * 2007-05-23, Daniel Hellstrom <daniel@gaisler.com> * Added ioctl(fd,SET_COREFREQ,freq_arg), the command * can autodetect the register values disconnect and @@ -48,32 +51,6 @@ * */ -/* default name to /dev/grspw0 */ -#if !defined(GRSPW_DEVNAME) || !defined(GRSPW_DEVNAME_NO) - #undef GRSPW_DEVNAME - #undef GRSPW_DEVNAME_NO - #define GRSPW_DEVNAME "/dev/grspw0" - #define GRSPW_DEVNAME_NO(devstr,no) ((devstr)[10]='0'+(no)) -#endif - -#ifndef GRSPW_PREFIX - #define GRSPW_PREFIX(name) grspw##name -#endif - -/* default to no translation */ -#ifndef GRSPW_ADR_TO - #define memarea_to_hw(x) ((unsigned int)(x)) -#endif -#ifndef GRSPW_ADR_FROM - #define hw_to_memarea(x) ((unsigned int)(x)) -#endif - -#ifndef GRSPW_REG_INT - #define GRSPW_REG_INT(handler,irqno,arg) set_vector(handler,irqno+0x10,1) - #undef GRSPW_DEFINE_INTHANDLER - #define GRSPW_DEFINE_INTHANDLER -#endif - #include <bsp.h> #include <rtems/libio.h> #include <stdlib.h> @@ -83,6 +60,9 @@ #include <ctype.h> #include <rtems/bspIo.h> #include <ambapp.h> + +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> #include <grspw.h> #define DBGSPW_IOCALLS 1 @@ -93,7 +73,7 @@ #define DEBUG_SPACEWIRE_FLAGS (DBGSPW_IOCALLS | DBGSPW_TX | DBGSPW_RX ) /* #define DEBUG_SPACEWIRE_ONOFF */ - + #ifdef DEBUG_SPACEWIRE_ONOFF #define SPACEWIRE_DBG(fmt, args...) do { { printk(" : %03d @ %18s()]:" fmt , __LINE__,__FUNCTION__,## args); }} while(0) #define SPACEWIRE_DBG2(fmt) do { { printk(" : %03d @ %18s()]:" fmt , __LINE__,__FUNCTION__); }} while(0) @@ -113,15 +93,15 @@ typedef struct { volatile unsigned int time; volatile unsigned int timer; volatile unsigned int pad; - - volatile unsigned int dma0ctrl; + + volatile unsigned int dma0ctrl; volatile unsigned int dma0rxmax; volatile unsigned int dma0txdesc; volatile unsigned int dma0rxdesc; - + /* For GRSPW core 2 and onwards */ volatile unsigned int dma0addr; - + } LEON3_SPACEWIRE_Regs_Map; typedef struct { @@ -149,7 +129,10 @@ typedef struct { #define SPW_ALIGN(p,c) ((((unsigned int)(p))+((c)-1))&~((c)-1)) typedef struct { - /* configuration parameters */ + /* configuration parameters */ + struct drvmgr_dev *dev; /* Driver manager device */ + char devName[32]; /* Device Name */ + LEON3_SPACEWIRE_Regs_Map *regs; spw_config config; unsigned int tx_all_in_use; @@ -163,12 +146,24 @@ typedef struct { unsigned int txbufcnt; unsigned int rxbufcnt; + /* DMA Area set by user */ + unsigned int rx_dma_area; + unsigned int tx_data_dma_area; + unsigned int tx_hdr_dma_area; + unsigned int bd_dma_area; + /* statistics */ spw_stats stat; + unsigned int _ptr_rxbuf0; char *ptr_rxbuf0; char *ptr_txdbuf0; char *ptr_txhbuf0; + char *ptr_bd0; + + char *ptr_rxbuf0_remote; + char *ptr_txdbuf0_remote; + char *ptr_txhbuf0_remote; unsigned int irq; int minor; @@ -176,7 +171,7 @@ typedef struct { int open; int running; unsigned int core_freq_khz; - + unsigned int rtimeout; /* semaphores*/ rtems_id txsp; @@ -185,48 +180,53 @@ typedef struct { SPACEWIRE_RXBD *rx; SPACEWIRE_TXBD *tx; -#ifdef GRSPW_STATIC_MEM - unsigned int membase, memend, mem_bdtable; -#else - char _rxtable[SPACEWIRE_BDTABLE_SIZE*2]; - char _txtable[SPACEWIRE_BDTABLE_SIZE*2]; -#endif - - LEON3_SPACEWIRE_Regs_Map *regs; + unsigned int rx_remote; + unsigned int tx_remote; } GRSPW_DEV; -static int spw_cores; -static int spw_cores2; -static unsigned int sys_freq_khz; -static GRSPW_DEV *grspw_devs; +/* Function pointer called upon timecode receive */ +void (*grspw_timecode_callback) + (void *pDev, void *regs, int minor, unsigned int tc) = NULL; #ifdef GRSPW_DONT_BYPASS_CACHE #define _SPW_READ(address) (*(volatile unsigned int *)(address)) -#define _MEM_READ(address) (*(volatile unsigned char *)(address)) +#define _MEM_READ8(address) (*(volatile unsigned char *)(address)) +#define _MEM_READ32(address) (*(volatile unsigned int *)(address)) #else -static unsigned int _SPW_READ(void *addr) { +static inline unsigned int _SPW_READ(volatile void *addr) { unsigned int tmp; - asm(" lda [%1]1, %0 " + asm volatile (" lda [%1]1, %0 " : "=r"(tmp) : "r"(addr) ); return tmp; } -static unsigned int _MEM_READ(void *addr) { +static inline unsigned int _MEM_READ8(volatile void *addr) { unsigned int tmp; - asm(" lduba [%1]1, %0 " + asm volatile (" lduba [%1]1, %0 " : "=r"(tmp) : "r"(addr) ); - return tmp; + return tmp; + +} + +static inline unsigned int _MEM_READ32(volatile void *addr) { + unsigned int tmp; + asm volatile (" lda [%1]1, %0 " + : "=r"(tmp) + : "r"(addr) + ); + return tmp; } #endif -#define MEM_READ(addr) _MEM_READ((void *)(addr)) -#define SPW_READ(addr) _SPW_READ((void *)(addr)) -#define SPW_WRITE(addr,v) *addr=v +#define MEM_READ8(addr) _MEM_READ8((volatile void *)(addr)) +#define MEM_READ32(addr) _MEM_READ32((volatile void *)(addr)) +#define SPW_READ(addr) _SPW_READ((volatile void *)(addr)) +#define SPW_WRITE(addr,v) (*(volatile unsigned int *)addr)=v #define SPW_REG(c,r) (c->regs->r) #define SPW_REG_CTRL(c) SPW_REG(c,ctrl) @@ -264,6 +264,8 @@ static unsigned int _MEM_READ(void *addr) { #define SPW_TXBD_WR (1 << 13) #define SPW_TXBD_IE (1 << 14) #define SPW_TXBD_LE (1 << 15) +#define SPW_TXBD_HC (1 << 16) +#define SPW_TXBD_DC (1 << 17) #define SPW_TXBD_ERROR (SPW_TXBD_LE) @@ -312,7 +314,7 @@ static unsigned int _MEM_READ(void *addr) { #define SPW_PREPAREMASK_RX (SPW_DMACTRL_TXEN | SPW_DMACTRL_TXIE | SPW_DMACTRL_AI | SPW_DMACTRL_PR | SPW_DMACTRL_RA) static int grspw_hw_init(GRSPW_DEV *pDev); -static int grspw_hw_send(GRSPW_DEV *pDev, unsigned int hlen, char *hdr, unsigned int dlen, char *data); +static int grspw_hw_send(GRSPW_DEV *pDev, unsigned int hlen, char *hdr, unsigned int dlen, char *data, unsigned int options); static int grspw_hw_receive(GRSPW_DEV *pDev,char *b,int c); static int grspw_hw_startup (GRSPW_DEV *pDev, int timeout); static int grspw_hw_stop (GRSPW_DEV *pDev, int rx, int tx); @@ -323,7 +325,8 @@ static void grspw_hw_read_config(GRSPW_DEV *pDev); static void check_rx_errors(GRSPW_DEV *pDev, int ctrl); static void grspw_rxnext(GRSPW_DEV *pDev); -static void grspw_interrupt(GRSPW_DEV *pDev); +static void grspw_interrupt(void *arg); +static int grspw_buffer_alloc(GRSPW_DEV *pDev); static rtems_device_driver grspw_initialize( rtems_device_major_number major, @@ -370,74 +373,272 @@ static rtems_device_driver grspw_control( grspw_control } static rtems_driver_address_table grspw_driver = GRSPW_DRIVER_TABLE_ENTRY; -static amba_confarea_type *amba_bus; +static int grspw_driver_io_registered = 0; +static rtems_device_major_number grspw_driver_io_major = 0; -int GRSPW_PREFIX(_register)(amba_confarea_type *bus) -{ - rtems_status_code r; - rtems_device_major_number m; +/******************* Driver manager interface ***********************/ - /* Get System clock frequency */ - sys_freq_khz = 0; +/* Driver prototypes */ +int grspw_register_io(rtems_device_major_number *m); +int grspw_device_init(GRSPW_DEV *pDev); - amba_bus = bus; +int grspw_init2(struct drvmgr_dev *dev); +int grspw_init3(struct drvmgr_dev *dev); - /* Auto Detect the GRSPW core frequency by assuming that the system frequency is - * is the same as the GRSPW core frequency. - */ -#ifndef SYS_FREQ_KHZ -#ifdef LEON3 - /* LEON3: find timer address via AMBA Plug&Play info */ +struct drvmgr_drv_ops grspw_ops = +{ + .init = {NULL, grspw_init2, grspw_init3, NULL}, + .remove = NULL, + .info = NULL +}; + +struct amba_dev_id grspw_ids[] = +{ + {VENDOR_GAISLER, GAISLER_SPW}, + {VENDOR_GAISLER, GAISLER_SPW2}, + {VENDOR_GAISLER, GAISLER_SPW2_DMA}, + {0, 0} /* Mark end of table */ +}; + +struct amba_drv_info grspw_drv_info = +{ { - amba_apb_device gptimer; - LEON3_Timer_Regs_Map *tregs; - - if ( amba_find_apbslv(&amba_conf,VENDOR_GAISLER,GAISLER_GPTIMER,&gptimer) == 1 ){ - tregs = (LEON3_Timer_Regs_Map *)gptimer.start; - sys_freq_khz = (tregs->scaler_reload+1)*1000; - SPACEWIRE_DBG("GRSPW: detected %dkHZ system frequency\n\r",sys_freq_khz); - }else{ - sys_freq_khz = 40000; /* Default to 40MHz */ - printk("GRSPW: Failed to detect system frequency\n\r"); + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_GAISLER_GRSPW_ID, /* Driver ID */ + "GRSPW_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &grspw_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + 0, + }, + &grspw_ids[0] +}; + +void grspw_register_drv (void) +{ + SPACEWIRE_DBG("Registering GRSPW driver\n"); + drvmgr_drv_register(&grspw_drv_info.general); +} + +int grspw_init2(struct drvmgr_dev *dev) +{ + GRSPW_DEV *priv; + + SPACEWIRE_DBG("GRSPW[%d] on bus %s\n", dev->minor_drv, + dev->parent->dev->name); + priv = dev->priv = malloc(sizeof(GRSPW_DEV)); + 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 grspw_init3(struct drvmgr_dev *dev) +{ + GRSPW_DEV *priv; + char prefix[32]; + rtems_status_code status; + + priv = dev->priv; + + /* Do initialization */ + + if ( grspw_driver_io_registered == 0) { + /* Register the I/O driver only once for all cores */ + if ( grspw_register_io(&grspw_driver_io_major) ) { + /* Failed to register I/O driver */ + free(dev->priv); + dev->priv = NULL; + return DRVMGR_FAIL; } + grspw_driver_io_registered = 1; } -#elif defined(LEON2) - /* LEON2: use hardcoded address to get to timer */ - { - LEON_Register_Map *regs = (LEON_Register_Map *)0x80000000; - sys_freq_khz = (regs->Scaler_Reload+1)*1000; - } -#else - #error CPU not supported by GRSPW driver -#endif -#else - /* Use hardcoded frequency */ - sys_freq_khz = SYS_FREQ_KHZ; -#endif - SPACEWIRE_DBG2("register driver\n"); - if ((r = rtems_io_register_driver(0, &grspw_driver, &m)) == RTEMS_SUCCESSFUL) { - SPACEWIRE_DBG2("success\n"); - return 0; - } else { - switch(r) { - case RTEMS_TOO_MANY: - SPACEWIRE_DBG2("failed RTEMS_TOO_MANY\n"); - break; - case RTEMS_INVALID_NUMBER: - SPACEWIRE_DBG2("failed RTEMS_INVALID_NUMBER\n"); - break; - case RTEMS_RESOURCE_IN_USE: - SPACEWIRE_DBG2("failed RTEMS_RESOURCE_IN_USE\n"); - break; - default: - SPACEWIRE_DBG("failed %i\n",r); - break; - } - return 1; - } + /* I/O system registered and initialized + * Now we take care of device initialization. + */ + + /* Get frequency in Hz */ + if ( drvmgr_freq_get(dev, DEV_APB_SLV, &priv->core_freq_khz) ) { + return DRVMGR_FAIL; + } + /* Convert from Hz -> kHz */ + priv->core_freq_khz = priv->core_freq_khz / 1000; + + if ( grspw_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/grspw%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/%sgrspw%d", prefix, dev->minor_bus); + } + + /* Register Device */ + status = rtems_io_register_name(priv->devName, grspw_driver_io_major, dev->minor_drv); + if (status != RTEMS_SUCCESSFUL) { + return DRVMGR_FAIL; + } + return DRVMGR_OK; +} + +/******************* Driver Implementation ***********************/ + +int grspw_register_io(rtems_device_major_number *m) +{ + rtems_status_code r; + + if ((r = rtems_io_register_driver(0, &grspw_driver, m)) == RTEMS_SUCCESSFUL) { + SPACEWIRE_DBG("GRSPW driver successfully registered, major: %d\n", *m); + } else { + switch(r) { + case RTEMS_TOO_MANY: + printk("GRSPW rtems_io_register_driver failed: RTEMS_TOO_MANY\n"); + return -1; + case RTEMS_INVALID_NUMBER: + printk("GRSPW rtems_io_register_driver failed: RTEMS_INVALID_NUMBER\n"); + return -1; + case RTEMS_RESOURCE_IN_USE: + printk("GRSPW rtems_io_register_driver failed: RTEMS_RESOURCE_IN_USE\n"); + return -1; + default: + printk("GRSPW rtems_io_register_driver failed\n"); + return -1; + } + } + return 0; +} + +int grspw_device_init(GRSPW_DEV *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 = (LEON3_SPACEWIRE_Regs_Map *)pnpinfo->apb_slv->start; + pDev->minor = pDev->dev->minor_drv; + + /* Get SpaceWire core version */ + switch( pnpinfo->device ) { + case GAISLER_SPW: + pDev->core_ver = 1; + break; + case GAISLER_SPW2: + pDev->core_ver = 2; + break; + case GAISLER_SPW2_DMA: + pDev->core_ver = 3; + break; + default: + return -1; + } + + /* initialize the code with some resonable values, + * actual initialization is done later using ioctl(fd) + * on the opened device */ + pDev->config.rxmaxlen = SPACEWIRE_RXPCK_SIZE; + pDev->txdbufsize = SPACEWIRE_TXD_SIZE; + pDev->txhbufsize = SPACEWIRE_TXH_SIZE; + pDev->rxbufsize = SPACEWIRE_RXPCK_SIZE; + pDev->txbufcnt = SPACEWIRE_TXBUFS_NR; + pDev->rxbufcnt = SPACEWIRE_RXBUFS_NR; + + pDev->_ptr_rxbuf0 = 0; + pDev->ptr_rxbuf0 = 0; + pDev->ptr_txdbuf0 = 0; + pDev->ptr_txhbuf0 = 0; + pDev->ptr_bd0 = 0; + pDev->rx_dma_area = 0; + pDev->tx_data_dma_area = 0; + pDev->tx_hdr_dma_area = 0; + pDev->bd_dma_area = 0; + + /* Get Configuration from Bus resources (Let user override defaults) */ + + value = drvmgr_dev_key_get(pDev->dev, "txBdCnt", KEY_TYPE_INT); + if ( value ) + pDev->txbufcnt = value->i; + + value = drvmgr_dev_key_get(pDev->dev, "rxBdCnt", KEY_TYPE_INT); + if ( value ) + pDev->rxbufcnt = value->i; + + value = drvmgr_dev_key_get(pDev->dev, "txDataSize", KEY_TYPE_INT); + if ( value ) + pDev->txdbufsize = value->i; + + value = drvmgr_dev_key_get(pDev->dev, "txHdrSize", KEY_TYPE_INT); + if ( value ) + pDev->txhbufsize = value->i; + + value = drvmgr_dev_key_get(pDev->dev, "rxPktSize", KEY_TYPE_INT); + if ( value ) + pDev->rxbufsize = value->i; + + value = drvmgr_dev_key_get(pDev->dev, "rxDmaArea", KEY_TYPE_INT); + if ( value ) + pDev->rx_dma_area = value->i; + + value = drvmgr_dev_key_get(pDev->dev, "txDataDmaArea", KEY_TYPE_INT); + if ( value ) + pDev->tx_data_dma_area = value->i; + + value = drvmgr_dev_key_get(pDev->dev, "txHdrDmaArea", KEY_TYPE_INT); + if ( value ) + pDev->tx_hdr_dma_area = value->i; + + value = drvmgr_dev_key_get(pDev->dev, "bdDmaArea", KEY_TYPE_INT); + if ( value ) + pDev->bd_dma_area = value->i; + + if (grspw_buffer_alloc(pDev)) + return RTEMS_NO_MEMORY; + + /* Create semaphores */ + rtems_semaphore_create( + rtems_build_name('T', 'x', 'S', '0' + pDev->minor), + 0, + RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | \ + RTEMS_NO_PRIORITY_CEILING, + 0, + &(pDev->txsp)); + + rtems_semaphore_create( + rtems_build_name('R', 'x', 'S', '0' + pDev->minor), + 0, + RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | \ + RTEMS_NO_PRIORITY_CEILING, + 0, + &(pDev->rxsp)); + + grspw_hw_init(pDev); + + return 0; } /* Get a value at least 6.4us in number of clock cycles */ @@ -452,1184 +653,1212 @@ static unsigned int grspw_calc_disconnect(int freq_khz){ return disconnect & 0x3ff; } -#if 0 -static int grspw_buffer_alloc(GRSPW_DEV *pDev) { - if (pDev->ptr_rxbuf0) { - free(pDev->ptr_rxbuf0); - } - if (pDev->ptr_txdbuf0) { - free(pDev->ptr_txdbuf0); - } - if (pDev->ptr_txhbuf0) { - free(pDev->ptr_txhbuf0); - } - pDev->ptr_rxbuf0 = (char *) malloc(pDev->rxbufsize * pDev->rxbufcnt); - pDev->ptr_txdbuf0 = (char *) malloc(pDev->txdbufsize * pDev->txbufcnt); - pDev->ptr_txhbuf0 = (char *) malloc(pDev->txhbufsize * pDev->txbufcnt); - if ((pDev->ptr_rxbuf0 == NULL) || - (pDev->ptr_txdbuf0 == NULL) || (pDev->ptr_txhbuf0 == NULL)) { - return 1; - } else { - return 0; - } -} -#endif - static int grspw_buffer_alloc(GRSPW_DEV *pDev) { -#ifndef GRSPW_STATIC_MEM - if (pDev->ptr_rxbuf0) { - free(pDev->ptr_rxbuf0); - } - if (pDev->ptr_txdbuf0) { - free(pDev->ptr_txdbuf0); - } - if (pDev->ptr_txhbuf0) { - free(pDev->ptr_txhbuf0); - } - - pDev->ptr_rxbuf0 = (char *) malloc(pDev->rxbufsize * pDev->rxbufcnt); - pDev->ptr_txdbuf0 = (char *) malloc(pDev->txdbufsize * pDev->txbufcnt); - pDev->ptr_txhbuf0 = (char *) malloc(pDev->txhbufsize * pDev->txbufcnt); - if ((pDev->ptr_rxbuf0 == NULL) || - (pDev->ptr_txdbuf0 == NULL) || (pDev->ptr_txhbuf0 == NULL)) { - return 1; - } else { - return 0; - } -#else - if ( (pDev->membase + pDev->rxbufsize*pDev->rxbufcnt + pDev->txdbufsize*pDev->txbufcnt) >= pDev->memend ) { - return -1; - } - pDev->ptr_rxbuf0 = (char *) pDev->membase; - pDev->ptr_txdbuf0 = pDev->ptr_rxbuf0 + pDev->rxbufsize * pDev->rxbufcnt; - pDev->ptr_txhbuf0 = pDev->ptr_txdbuf0 + pDev->txdbufsize * pDev->txbufcnt; - return 0; -#endif + if ( pDev->rx_dma_area ) { +#warning Check size? + if ( pDev->rx_dma_area & 1 ) { + /* Address given in remote address */ + drvmgr_translate(pDev->dev, 1, 1, (void *)(pDev->rx_dma_area & ~1), (void **)&pDev->ptr_rxbuf0); + } else { + pDev->ptr_rxbuf0 = pDev->rx_dma_area; + } + } else { + if (pDev->_ptr_rxbuf0) { + free(pDev->_ptr_rxbuf0); + } + pDev->_ptr_rxbuf0 = (unsigned int) malloc(pDev->rxbufsize * pDev->rxbufcnt+4); + pDev->ptr_rxbuf0 = (char *)((pDev->_ptr_rxbuf0+7)&~7); + if ( !pDev->ptr_rxbuf0 ) + return 1; + } + if ( pDev->tx_data_dma_area ) { + if ( pDev->tx_data_dma_area & 1 ) { + /* Address given in remote address */ + drvmgr_translate(pDev->dev, 1, 1, (void *)(pDev->tx_data_dma_area & ~1), (void **)&pDev->ptr_txdbuf0); + } else { + pDev->ptr_txdbuf0 = pDev->tx_data_dma_area; + } + } else { + if (pDev->ptr_txdbuf0) { + free(pDev->ptr_txdbuf0); + } + pDev->ptr_txdbuf0 = (char *) malloc(pDev->txdbufsize * pDev->txbufcnt); + if ( !pDev->ptr_txdbuf0 ) + return 1; + } + if ( pDev->tx_hdr_dma_area ) { + if ( pDev->tx_hdr_dma_area & 1 ) { + /* Address given in remote address */ + drvmgr_translate(pDev->dev, 1, 1, (void *)(pDev->tx_hdr_dma_area & ~1), (void **)&pDev->ptr_txhbuf0); + } else { + pDev->ptr_txhbuf0 = pDev->tx_hdr_dma_area; + } + } else { + if (pDev->ptr_txhbuf0) { + free(pDev->ptr_txhbuf0); + } + pDev->ptr_txhbuf0 = (char *) malloc(pDev->txhbufsize * pDev->txbufcnt); + if ( !pDev->ptr_txhbuf0 ) + return 1; + } + if ( pDev->bd_dma_area ) { + if ( pDev->bd_dma_area & 1 ) { + /* Address given in remote address */ + drvmgr_translate(pDev->dev, 1, 1, (void *)(pDev->bd_dma_area & ~1), (void **)&pDev->ptr_bd0); + } else { + pDev->ptr_bd0 = pDev->bd_dma_area; + } + } else { + if (pDev->ptr_bd0) { + free(pDev->ptr_bd0); + } + pDev->ptr_bd0 = (char *) malloc(3*SPACEWIRE_BDTABLE_SIZE); + if ( !pDev->ptr_bd0 ) + return 1; + } + /* Translate into remote address */ + drvmgr_translate(pDev->dev, 0, 0, (void *)pDev->ptr_rxbuf0, (void **)&pDev->ptr_rxbuf0_remote); + drvmgr_translate(pDev->dev, 0, 0, (void *)pDev->ptr_txdbuf0,(void **)&pDev->ptr_txdbuf0_remote); + drvmgr_translate(pDev->dev, 0, 0, (void *)pDev->ptr_txhbuf0, (void **)&pDev->ptr_txhbuf0_remote); + return 0; } -#ifdef GRSPW_DEFINE_INTHANDLER -/* - * Standard Interrupt handler - */ -static rtems_isr grspw_interrupt_handler(rtems_vector_number v) +static void grspw_interrupt(void *arg) { - int minor; - - for(minor = 0; minor < spw_cores+spw_cores2; minor++) { - if (v == (grspw_devs[minor].irq+0x10) ) { - grspw_interrupt(&grspw_devs[minor]); - break; - } - } -} -#endif - -static void grspw_interrupt(GRSPW_DEV *pDev){ - int dmactrl; - int status; - int ctrl; - - status = SPW_STATUS_READ(pDev); - SPW_STATUS_WRITE(pDev, SPW_STATUS_CE | SPW_STATUS_ER | SPW_STATUS_DE | SPW_STATUS_PE | SPW_STATUS_WE | SPW_STATUS_IA | SPW_STATUS_EE); - dmactrl = SPW_READ(&pDev->regs->dma0ctrl); - SPW_WRITE(&pDev->regs->dma0ctrl, dmactrl | SPW_DMACTRL_PR); - /* If linkinterrupts are enabled check if it was a linkerror irq and then send an event to the - process set in the config */ - if (pDev->config.link_err_irq) { - if (status & (SPW_STATUS_CE | SPW_STATUS_ER | SPW_STATUS_DE | SPW_STATUS_PE | SPW_STATUS_WE)) { - rtems_event_send(pDev->config.event_id, SPW_LINKERR_EVENT); - if (pDev->config.disable_err) { - /* disable link*/ - SPW_CTRL_WRITE(pDev, (SPW_CTRL_READ(pDev) & 0xFFFFFFFC) | SPW_CTRL_LINKDISABLED); - pDev->config.linkdisabled = 1; - pDev->config.linkstart = 0; - pDev->running = 0; - } - } - } - if (status & SPW_STATUS_CE) { - pDev->stat.credit_err++; - } - if (status & SPW_STATUS_ER) { - pDev->stat.escape_err++; - } - if (status & SPW_STATUS_DE) { - pDev->stat.disconnect_err++; - } - if (status & SPW_STATUS_PE) { - pDev->stat.parity_err++; - } - if (status & SPW_STATUS_WE) { - pDev->stat.write_sync_err++; - } - if (status & SPW_STATUS_IA) { - pDev->stat.invalid_address++; - } - if (status & SPW_STATUS_EE) { - pDev->stat.early_ep++; - } - - /* Check for tx interrupts */ - while( (pDev->tx_sent != pDev->tx_cur) || pDev->tx_all_in_use) { - /* Has this descriptor been sent? */ - ctrl = SPW_READ((volatile void *)&pDev->tx[pDev->tx_sent].ctrl); - if ( ctrl & SPW_TXBD_EN ) { - break; - } - /* Yes, increment status counters & tx_sent so we can use this descriptor to send more packets with */ - pDev->stat.packets_sent++; - - rtems_semaphore_release(pDev->txsp); - - if ( ctrl & SPW_TXBD_LE ) { - pDev->stat.tx_link_err++; - } - - /* step to next descriptor */ - pDev->tx_sent = (pDev->tx_sent + 1) % pDev->txbufcnt; - pDev->tx_all_in_use = 0; /* not all of the descriptors can be in use since we just freed one. */ - } - - /* Check for rx interrupts */ - if (dmactrl & SPW_DMACTRL_PR) { - rtems_semaphore_release(pDev->rxsp); - } -} + GRSPW_DEV *pDev = (GRSPW_DEV *)arg; + int dmactrl; + int status; + int ctrl; + unsigned int timecode; + + status = SPW_STATUS_READ(pDev); + /*SPW_STATUS_WRITE(pDev, SPW_STATUS_CE | SPW_STATUS_ER | SPW_STATUS_DE | SPW_STATUS_PE | SPW_STATUS_WE | SPW_STATUS_IA | SPW_STATUS_EE | SPW_STATUS_TO);*/ + SPW_STATUS_WRITE(pDev, status & (SPW_STATUS_CE | SPW_STATUS_ER | SPW_STATUS_DE | SPW_STATUS_PE | SPW_STATUS_WE | SPW_STATUS_IA | SPW_STATUS_EE)); + + /* Make sure to put the timecode handling first in order to get the smallest + * possible interrupt latency + */ + if ( (status & SPW_STATUS_TO) && (grspw_timecode_callback != NULL) ) { + /* Timecode received. Let custom function handle this */ + SPW_STATUS_WRITE(pDev, SPW_STATUS_TO); + timecode = SPW_READ(&pDev->regs->time); + (grspw_timecode_callback)(pDev,pDev->regs,pDev->minor,timecode); + } -static rtems_device_driver grspw_initialize( - rtems_device_major_number major, - rtems_device_minor_number minor, - void *arg -) -{ - rtems_status_code status; - int i=0; - char c; - GRSPW_DEV *pDev; - char console_name[20]; - amba_apb_device dev; + /* Clear SPW_DMACTRL_PR if set */ + dmactrl = SPW_READ(&pDev->regs->dma0ctrl); + /*SPW_WRITE(&pDev->regs->dma0ctrl, dmactrl | SPW_DMACTRL_PR);*/ + SPW_WRITE(&pDev->regs->dma0ctrl, dmactrl); + + /* If linkinterrupts are enabled check if it was a linkerror irq and then send an event to the + process set in the config */ + if (pDev->config.link_err_irq) { + if (status & (SPW_STATUS_CE | SPW_STATUS_ER | SPW_STATUS_DE | SPW_STATUS_PE | SPW_STATUS_WE)) { + rtems_event_send(pDev->config.event_id, SPW_LINKERR_EVENT); + if (pDev->config.disable_err) { + /* disable link*/ + SPW_CTRL_WRITE(pDev, (SPW_CTRL_READ(pDev) & 0xFFFFFFFC) | SPW_CTRL_LINKDISABLED); + pDev->config.linkdisabled = 1; + pDev->config.linkstart = 0; + pDev->running = 0; + } + } + } + if (status & SPW_STATUS_CE) { + pDev->stat.credit_err++; + } + if (status & SPW_STATUS_ER) { + pDev->stat.escape_err++; + } + if (status & SPW_STATUS_DE) { + pDev->stat.disconnect_err++; + } + if (status & SPW_STATUS_PE) { + pDev->stat.parity_err++; + } + if (status & SPW_STATUS_WE) { + pDev->stat.write_sync_err++; + } + if (status & SPW_STATUS_IA) { + pDev->stat.invalid_address++; + } + if (status & SPW_STATUS_EE) { + pDev->stat.early_ep++; + } - SPACEWIRE_DBG2("spacewire driver initialization\n"); + /* Check for tx interrupts */ + while( (pDev->tx_sent != pDev->tx_cur) || pDev->tx_all_in_use) { + /* Has this descriptor been sent? */ + ctrl = SPW_READ((volatile void *)&pDev->tx[pDev->tx_sent].ctrl); + if ( ctrl & SPW_TXBD_EN ) { + break; + } + /* Yes, increment status counters & tx_sent so we can use this descriptor to send more packets with */ + pDev->stat.packets_sent++; - /* Copy device name */ - strcpy(console_name,GRSPW_DEVNAME); + rtems_semaphore_release(pDev->txsp); - /* Get the number of GRSPW cores */ - i=0; spw_cores = 0; spw_cores2 = 0; + if ( ctrl & SPW_TXBD_LE ) { + pDev->stat.tx_link_err++; + } - /* get number of GRSPW cores */ - spw_cores = amba_get_number_apbslv_devices(amba_bus,VENDOR_GAISLER,GAISLER_SPACEWIRE); - spw_cores2 = amba_get_number_apbslv_devices(amba_bus,VENDOR_GAISLER,GAISLER_GRSPW2); -#if 0 - if ( spw_cores > SPACEWIRE_MAX_CORENR ) - spw_cores = SPACEWIRE_MAX_CORENR; - - while (i < amba_conf.apbslv.devnr) { - conf = amba_get_confword(amba_conf.apbslv, i, 0); - if ((amba_vendor(conf) == VENDOR_GAISLER) && (amba_device(conf) == GAISLER_SPACEWIRE)) - spw_cores++; - i++; - } -#endif + /* step to next descriptor */ + pDev->tx_sent = (pDev->tx_sent + 1) % pDev->txbufcnt; + pDev->tx_all_in_use = 0; /* not all of the descriptors can be in use since we just freed one. */ + } - if ( (spw_cores+spw_cores2) < 1 ){ - /* No GRSPW cores around... */ - return RTEMS_SUCCESSFUL; - } - - /* Allocate memory for all spacewire cores */ - grspw_devs = (GRSPW_DEV *)malloc((spw_cores+spw_cores2) * sizeof(GRSPW_DEV)); - - /* Zero out all memory */ - memset(grspw_devs,0,(spw_cores+spw_cores2) * sizeof(GRSPW_DEV)); - - /* loop all found spacewire cores */ - i = 0; - for(minor=0; minor<(spw_cores+spw_cores2); minor++){ - - pDev = &grspw_devs[minor]; - - /* Get device */ - if ( spw_cores > minor ) { - amba_find_next_apbslv(amba_bus,VENDOR_GAISLER,GAISLER_SPACEWIRE,&dev,minor); - pDev->core_ver = 1; - } else { - amba_find_next_apbslv(amba_bus,VENDOR_GAISLER,GAISLER_GRSPW2,&dev,minor-spw_cores); - pDev->core_ver = 2; - } - - pDev->regs = (LEON3_SPACEWIRE_Regs_Map *)dev.start; - pDev->irq = dev.irq; - pDev->minor = minor; - pDev->open = 0; - - /* register interrupt routine */ - GRSPW_REG_INT(GRSPW_PREFIX(_interrupt_handler), pDev->irq, pDev); - - SPACEWIRE_DBG("spacewire core at [0x%x]\n", (unsigned int) pDev->regs); - - /* initialize the code with some resonable values, - actual initialization is done later using ioctl(fd) - on the opened device */ - pDev->config.rxmaxlen = SPACEWIRE_RXPCK_SIZE; - pDev->txdbufsize = SPACEWIRE_TXD_SIZE; - pDev->txhbufsize = SPACEWIRE_TXH_SIZE; - pDev->rxbufsize = SPACEWIRE_RXPCK_SIZE; - pDev->txbufcnt = SPACEWIRE_TXBUFS_NR; - pDev->rxbufcnt = SPACEWIRE_RXBUFS_NR; - pDev->config.check_rmap_err = 0; - pDev->config.tx_blocking = 0; - pDev->config.tx_block_on_full = 0; - pDev->config.rx_blocking = 0; - pDev->config.disable_err = 0; - pDev->config.link_err_irq = 0; - pDev->config.event_id = 0; - - pDev->ptr_rxbuf0 = 0; - pDev->ptr_txdbuf0 = 0; - pDev->ptr_txhbuf0 = 0; - -#ifdef GRSPW_STATIC_MEM - GRSPW_CALC_MEMOFS(spw_cores,minor,&pDev->membase,&pDev->memend,&pDev->mem_bdtable); -#endif + /* Check for rx interrupts */ + if (dmactrl & SPW_DMACTRL_PR) { + rtems_semaphore_release(pDev->rxsp); + } +} - if (grspw_buffer_alloc(pDev)) - return RTEMS_NO_MEMORY; - - } - - /* Register Device Names, /dev/grspw0, /dev/grspw1 ... */ - for (i = 0; i < spw_cores+spw_cores2; i++) { - GRSPW_DEVNAME_NO(console_name,i); - SPACEWIRE_DBG("registering minor %i as %s\n", i, console_name); - status = rtems_io_register_name( console_name, major, i); - if (status != RTEMS_SUCCESSFUL){ - rtems_fatal_error_occurred(status); - } - } - - /* Initialize Hardware and semaphores*/ - c = 'a'; - for (i = 0; i < spw_cores+spw_cores2; i++) { - pDev = &grspw_devs[i]; - rtems_semaphore_create( - rtems_build_name('T', 'x', 'S', c), - 0, - RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | \ - RTEMS_NO_PRIORITY_CEILING, - 0, - &(pDev->txsp)); - rtems_semaphore_create( - rtems_build_name('R', 'x', 'S', c), - 0, - RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | \ - RTEMS_NO_PRIORITY_CEILING, - 0, - &(pDev->rxsp)); - c++; - grspw_hw_init(pDev); - } - - return RTEMS_SUCCESSFUL; +static rtems_device_driver grspw_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + /* Initialize device-common data structures here */ + return RTEMS_SUCCESSFUL; } static rtems_device_driver grspw_open( rtems_device_major_number major, rtems_device_minor_number minor, - void * arg - ) + void * arg + ) { - GRSPW_DEV *pDev; - SPACEWIRE_DBGC(DBGSPW_IOCALLS, "open [%i,%i]\n", major, minor); - if ( minor >= (spw_cores+spw_cores2) ) { - SPACEWIRE_DBG("minor %i too big\n", minor); - return RTEMS_INVALID_NAME; - } - pDev = &grspw_devs[minor]; - - if ( pDev->open ) - return RTEMS_RESOURCE_IN_USE; - - /* Mark device open */ - pDev->open = 1; - - pDev->stat.tx_link_err = 0; - pDev->stat.rx_rmap_header_crc_err = 0; - pDev->stat.rx_rmap_data_crc_err = 0; - pDev->stat.rx_eep_err = 0; - pDev->stat.rx_truncated = 0; - pDev->stat.parity_err = 0; - pDev->stat.escape_err = 0; - pDev->stat.credit_err = 0; - pDev->stat.write_sync_err = 0; - pDev->stat.disconnect_err = 0; - pDev->stat.early_ep = 0; - pDev->stat.invalid_address = 0; - pDev->stat.packets_sent = 0; - pDev->stat.packets_received = 0; - - pDev->running = 0; - pDev->core_freq_khz = 0; - - /* Reset Core */ - grspw_hw_reset(pDev); - - /* Read default configuration */ - grspw_hw_read_config(pDev); - - return RTEMS_SUCCESSFUL; + GRSPW_DEV *pDev; + struct drvmgr_dev *dev; + SPACEWIRE_DBGC(DBGSPW_IOCALLS, "open [%i,%i]\n", major, minor); + + if ( drvmgr_get_dev(&grspw_drv_info.general, minor, &dev) ) { + SPACEWIRE_DBG("Wrong minor %d\n", minor); + return RTEMS_INVALID_NAME; + } + pDev = (GRSPW_DEV *)dev->priv; + + if ( pDev->open ) + return RTEMS_RESOURCE_IN_USE; + + /* Mark device open */ + pDev->open = 1; + + pDev->stat.tx_link_err = 0; + pDev->stat.rx_rmap_header_crc_err = 0; + pDev->stat.rx_rmap_data_crc_err = 0; + pDev->stat.rx_eep_err = 0; + pDev->stat.rx_truncated = 0; + pDev->stat.parity_err = 0; + pDev->stat.escape_err = 0; + pDev->stat.credit_err = 0; + pDev->stat.write_sync_err = 0; + pDev->stat.disconnect_err = 0; + pDev->stat.early_ep = 0; + pDev->stat.invalid_address = 0; + pDev->stat.packets_sent = 0; + pDev->stat.packets_received = 0; + + pDev->config.rm_prot_id = 0; + pDev->config.keep_source = 0; + pDev->config.check_rmap_err = 0; + pDev->config.tx_blocking = 0; + pDev->config.tx_block_on_full = 0; + pDev->config.rx_blocking = 0; + pDev->config.disable_err = 0; + pDev->config.link_err_irq = 0; + pDev->config.event_id = 0; + pDev->config.rtimeout = 0; + + pDev->running = 0; + pDev->core_freq_khz = 0; + + /* Reset Core */ + grspw_hw_reset(pDev); + + /* Read default configuration */ + grspw_hw_read_config(pDev); + + return RTEMS_SUCCESSFUL; } static rtems_device_driver grspw_close( - rtems_device_major_number major, - rtems_device_minor_number minor, - void * arg - ) -{ - GRSPW_DEV *pDev = &grspw_devs[minor]; + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg + ) +{ + GRSPW_DEV *pDev; + struct drvmgr_dev *dev; + + if ( drvmgr_get_dev(&grspw_drv_info.general, minor, &dev) ) { + return RTEMS_INVALID_NAME; + } + pDev = (GRSPW_DEV *)dev->priv; - SPACEWIRE_DBGC(DBGSPW_IOCALLS, "close [%i,%i]\n", major, minor); - rtems_semaphore_delete(pDev->txsp); - rtems_semaphore_delete(pDev->rxsp); + SPACEWIRE_DBGC(DBGSPW_IOCALLS, "close [%i,%i]\n", major, minor); + rtems_semaphore_delete(pDev->txsp); + rtems_semaphore_delete(pDev->rxsp); - grspw_hw_stop(pDev,1,1); + grspw_hw_stop(pDev,1,1); - grspw_hw_reset(pDev); + grspw_hw_reset(pDev); - /* Mark device closed - not open */ - pDev->open = 0; + /* Mark device closed - not open */ + pDev->open = 0; - return RTEMS_SUCCESSFUL; + return RTEMS_SUCCESSFUL; } static rtems_device_driver grspw_read( - rtems_device_major_number major, - rtems_device_minor_number minor, - void * arg - ) + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg + ) { - GRSPW_DEV *pDev = &grspw_devs[minor]; - rtems_libio_rw_args_t *rw_args; - unsigned int count = 0; - rw_args = (rtems_libio_rw_args_t *) arg; + rtems_libio_rw_args_t *rw_args; + unsigned int count = 0; + GRSPW_DEV *pDev; + struct drvmgr_dev *dev; + int status; + + if ( drvmgr_get_dev(&grspw_drv_info.general, minor, &dev) ) { + return RTEMS_INVALID_NAME; + } + pDev = (GRSPW_DEV *)dev->priv; + + rw_args = (rtems_libio_rw_args_t *) arg; - /* is link up? */ - if ( !pDev->running ) { - return RTEMS_INVALID_NAME; - } + /* is link up? */ + if ( !pDev->running ) { + return RTEMS_INVALID_NAME; + } - if ((rw_args->count < 1) || (rw_args->buffer == NULL)) { - return RTEMS_INVALID_NAME; - } + if ((rw_args->count < 1) || (rw_args->buffer == NULL)) { + return RTEMS_INVALID_NAME; + } - SPACEWIRE_DBGC(DBGSPW_IOCALLS, "read [%i,%i]: buf:0x%x len:%i \n", major, minor, (unsigned int)rw_args->buffer, rw_args->count); + SPACEWIRE_DBGC(DBGSPW_IOCALLS, "read [%i,%i]: buf:0x%x len:%i \n", major, minor, (unsigned int)rw_args->buffer, rw_args->count); - while ( (count = grspw_hw_receive(pDev, rw_args->buffer, rw_args->count)) == 0) { - /* wait a moment for any descriptors to get available - * + while ( (count = grspw_hw_receive(pDev, rw_args->buffer, rw_args->count)) == 0) { + /* wait a moment for any descriptors to get available + * * Semaphore is signaled by interrupt handler */ if (pDev->config.rx_blocking) { SPACEWIRE_DBG2("Rx blocking\n"); - rtems_semaphore_obtain(pDev->rxsp, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + if ( pDev->config.rtimeout ) { + status = rtems_semaphore_obtain(pDev->rxsp, RTEMS_WAIT, pDev->config.rtimeout); + if ( status == RTEMS_TIMEOUT ) + return RTEMS_TIMEOUT; + } else { + rtems_semaphore_obtain(pDev->rxsp, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + } } else { - SPACEWIRE_DBG2("Rx non blocking\n"); + SPACEWIRE_DBG2("Rx non blocking\n"); return RTEMS_RESOURCE_IN_USE; } } -#ifdef DEBUG_SPACEWIRE_ONOFF - if (DEBUG_SPACEWIRE_FLAGS & DBGSPW_DUMP) { - int k; - for (k = 0; k < count; k++){ - if (k % 16 == 0) { - printf ("\n"); - } - printf ("%.2x(%c) ", rw_args->buffer[k] & 0xff, isprint(rw_args->buffer[k] & 0xff) ? rw_args->buffer[k] & 0xff : ' '); - } - printf ("\n"); - } +#ifdef DEBUG_SPACEWIRE_ONOFF + if (DEBUG_SPACEWIRE_FLAGS & DBGSPW_DUMP) { + int k; + for (k = 0; k < count; k++){ + if (k % 16 == 0) { + printf ("\n"); + } + printf ("%.2x(%c) ", rw_args->buffer[k] & 0xff, isprint(rw_args->buffer[k] & 0xff) ? rw_args->buffer[k] & 0xff : ' '); + } + printf ("\n"); + } #endif - rw_args->bytes_moved = count; - return RTEMS_SUCCESSFUL; - + rw_args->bytes_moved = count; + return RTEMS_SUCCESSFUL; } static rtems_device_driver grspw_write( - rtems_device_major_number major, - rtems_device_minor_number minor, - void * arg + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg ) { - GRSPW_DEV *pDev = &grspw_devs[minor]; - rtems_libio_rw_args_t *rw_args; - rw_args = (rtems_libio_rw_args_t *) arg; - SPACEWIRE_DBGC(DBGSPW_IOCALLS, "write [%i,%i]: buf:0x%x len:%i\n", major, minor, (unsigned int)rw_args->buffer, rw_args->count); - - /* is link up? */ - if ( !pDev->running ) { - return RTEMS_INVALID_NAME; - } - - if ((rw_args->count > pDev->txdbufsize) || (rw_args->count < 1) || (rw_args->buffer == NULL)) { - return RTEMS_INVALID_NAME; - } - - while ((rw_args->bytes_moved = grspw_hw_send(pDev, 0, NULL, rw_args->count, rw_args->buffer)) == 0) { - if (pDev->config.tx_block_on_full == 1) { - SPACEWIRE_DBG2("Tx Block on full \n"); - rtems_semaphore_obtain(pDev->txsp, RTEMS_WAIT, RTEMS_NO_TIMEOUT); - } else { - SPACEWIRE_DBG2("Tx non blocking return when full \n"); - return RTEMS_RESOURCE_IN_USE; - } - } - return RTEMS_SUCCESSFUL; + rtems_libio_rw_args_t *rw_args; + GRSPW_DEV *pDev; + struct drvmgr_dev *dev; + + if ( drvmgr_get_dev(&grspw_drv_info.general, minor, &dev) ) { + return RTEMS_INVALID_NAME; + } + pDev = (GRSPW_DEV *)dev->priv; + + rw_args = (rtems_libio_rw_args_t *) arg; + SPACEWIRE_DBGC(DBGSPW_IOCALLS, "write [%i,%i]: buf:0x%x len:%i\n", major, minor, (unsigned int)rw_args->buffer, rw_args->count); + + /* is link up? */ + if ( !pDev->running ) { + return RTEMS_INVALID_NAME; + } + + if ((rw_args->count > pDev->txdbufsize) || (rw_args->count < 1) || (rw_args->buffer == NULL)) { + return RTEMS_INVALID_NAME; + } + + while ((rw_args->bytes_moved = grspw_hw_send(pDev, 0, NULL, rw_args->count, rw_args->buffer, 0)) == 0) { + if (pDev->config.tx_block_on_full == 1) { + SPACEWIRE_DBG2("Tx Block on full \n"); + rtems_semaphore_obtain(pDev->txsp, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + } else { + SPACEWIRE_DBG2("Tx non blocking return when full \n"); + return RTEMS_RESOURCE_IN_USE; + } + } + return RTEMS_SUCCESSFUL; } static rtems_device_driver grspw_control( - rtems_device_major_number major, - rtems_device_minor_number minor, - void * arg - ) + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg + ) { - GRSPW_DEV *pDev = &grspw_devs[minor]; - spw_ioctl_pkt_send *args; - spw_ioctl_packetsize *ps; - int status; - unsigned int tmp,nodeaddr,nodemask; - int timeout; - rtems_device_driver ret; - rtems_libio_ioctl_args_t *ioarg = (rtems_libio_ioctl_args_t *) arg; - SPACEWIRE_DBGC(DBGSPW_IOCALLS, "ctrl [%i,%i]\n", major, minor); - - if (!ioarg) - return RTEMS_INVALID_NAME; - - - ioarg->ioctl_return = 0; - switch(ioarg->command) { - case SPACEWIRE_IOCTRL_SET_NODEADDR: - /*set node address*/ - SPACEWIRE_DBGC(DBGSPW_IOCTRL, "SPACEWIRE_IOCTRL_SET_NODEADDR %i\n",(unsigned int)ioarg->buffer); - if ((unsigned int)ioarg->buffer > 255) { - return RTEMS_INVALID_NAME; - } - nodeaddr = ((unsigned int)ioarg->buffer) & 0xff; - tmp = SPW_READ(&pDev->regs->nodeaddr); - tmp &= 0xffffff00; /* Remove old address */ - tmp |= nodeaddr; - SPW_WRITE(&pDev->regs->nodeaddr, tmp); - if ((SPW_READ(&pDev->regs->nodeaddr)&0xff) != nodeaddr) { - return RTEMS_IO_ERROR; - } - pDev->config.nodeaddr = nodeaddr; - break; - case SPACEWIRE_IOCTRL_SET_NODEMASK: - /*set node address*/ - SPACEWIRE_DBGC(DBGSPW_IOCTRL, "SPACEWIRE_IOCTRL_SET_NODEMASK %i\n",(unsigned int)ioarg->buffer); - if ( pDev->core_ver > 1 ){ - if ((unsigned int)ioarg->buffer > 255) { - return RTEMS_INVALID_NAME; - } - nodemask = ((unsigned int)ioarg->buffer) & 0xff; - tmp = SPW_READ(&pDev->regs->nodeaddr); - tmp &= 0xffff00ff; /* Remove old mask */ - tmp |= nodemask<<8; - SPW_WRITE(&pDev->regs->nodeaddr, tmp); - if (((SPW_READ(&pDev->regs->nodeaddr)>>8)&0xff) != nodemask) { - return RTEMS_IO_ERROR; - } - pDev->config.nodemask = nodemask; - }else{ - SPACEWIRE_DBG("SPACEWIRE_IOCTRL_SET_NODEMASK: not implemented in GRSPW1 HW\n"); - } - break; - case SPACEWIRE_IOCTRL_SET_RXBLOCK: - SPACEWIRE_DBGC(DBGSPW_IOCTRL, "SPACEWIRE_IOCTRL_SET_RXBLOCK %i \n", (unsigned int)ioarg->buffer); - if ((unsigned int)ioarg->buffer > 1) { - return RTEMS_INVALID_NAME; - } - pDev->config.rx_blocking = (unsigned int)ioarg->buffer; - break; - case SPACEWIRE_IOCTRL_SET_DESTKEY: - SPACEWIRE_DBGC(DBGSPW_IOCTRL,"SPACEWIRE_IOCTRL_SET_DESTKEY %i\n", (unsigned int)ioarg->buffer); - if (!pDev->config.is_rmap) { - return RTEMS_NOT_IMPLEMENTED; - } - if ((unsigned int)ioarg->buffer > 255) { - return RTEMS_INVALID_NAME; - } - SPW_WRITE(&pDev->regs->destkey, (unsigned int)ioarg->buffer); - if (SPW_READ(&pDev->regs->destkey) != (unsigned int)ioarg->buffer) { - return RTEMS_IO_ERROR; - } - pDev->config.destkey = (unsigned int)ioarg->buffer; - break; - case SPACEWIRE_IOCTRL_SET_CLKDIV: - SPACEWIRE_DBGC(DBGSPW_IOCTRL,"SPACEWIRE_IOCTRL_SET_CLKDIV %i\n", (unsigned int)ioarg->buffer); - if ((unsigned int)ioarg->buffer > 255) { - return RTEMS_INVALID_NAME; - } - tmp = SPW_READ(&pDev->regs->clkdiv); - tmp &= ~0xff; /* Remove old Clockdiv Setting */ - tmp |= ((unsigned int)ioarg->buffer) & 0xff; /* add new clockdiv setting */ - SPW_WRITE(&pDev->regs->clkdiv, tmp); - if (SPW_READ(&pDev->regs->clkdiv) != tmp) { - return RTEMS_IO_ERROR; - } - pDev->config.clkdiv = tmp; - break; - case SPACEWIRE_IOCTRL_SET_CLKDIVSTART: - SPACEWIRE_DBGC(DBGSPW_IOCTRL,"SPACEWIRE_IOCTRL_SET_CLKDIVSTART %i\n", (unsigned int)ioarg->buffer); - if ((unsigned int)ioarg->buffer > 255) { - return RTEMS_INVALID_NAME; - } - tmp = SPW_READ(&pDev->regs->clkdiv); - tmp &= ~0xff00; /* Remove old Clockdiv Start Setting */ - tmp |= (((unsigned int)ioarg->buffer) & 0xff)<<8; /* add new clockdiv start setting */ - SPW_WRITE(&pDev->regs->clkdiv, tmp); - if (SPW_READ(&pDev->regs->clkdiv) != tmp) { - return RTEMS_IO_ERROR; - } - pDev->config.clkdiv = tmp; - break; - case SPACEWIRE_IOCTRL_SET_TIMER: - SPACEWIRE_DBGC(DBGSPW_IOCTRL,"SPACEWIRE_IOCTRL_SET_TIMER %i\n", (unsigned int)ioarg->buffer); - if ( pDev->core_ver <= 1 ) { - if ((unsigned int)ioarg->buffer > 4095) { - return RTEMS_INVALID_NAME; - } - SPW_WRITE(&pDev->regs->timer, (SPW_READ(&pDev->regs->timer) & 0xFFFFF000) | ((unsigned int)ioarg->buffer & 0xFFF)); - if ((SPW_READ(&pDev->regs->timer) & 0xFFF) != (unsigned int)ioarg->buffer) { - return RTEMS_IO_ERROR; - } - pDev->config.timer = (unsigned int)ioarg->buffer; - }else{ - SPACEWIRE_DBG("SPACEWIRE_IOCTRL_SET_TIMER: removed in GRSPW2 HW\n"); - } - break; - case SPACEWIRE_IOCTRL_SET_DISCONNECT: - SPACEWIRE_DBGC(DBGSPW_IOCTRL,"SPACEWIRE_IOCTRL_SET_DISCONNECT %i\n", (unsigned int)ioarg->buffer); - if ( pDev->core_ver <= 1 ) { - if ((unsigned int)ioarg->buffer > 1023) { - return RTEMS_INVALID_NAME; - } - SPW_WRITE(&pDev->regs->timer, (SPW_READ(&pDev->regs->timer) & 0xFFC00FFF) | (((unsigned int)ioarg->buffer & 0x3FF) << 12)); - if (((SPW_READ(&pDev->regs->timer) >> 12) & 0x3FF) != (unsigned int)ioarg->buffer) { - return RTEMS_IO_ERROR; - } - pDev->config.disconnect = (unsigned int)ioarg->buffer; - }else{ - SPACEWIRE_DBG("SPACEWIRE_IOCTRL_SET_DISCONNECT: not implemented for GRSPW2\n"); - } - break; - case SPACEWIRE_IOCTRL_SET_PROMISCUOUS: - SPACEWIRE_DBGC(DBGSPW_IOCTRL,"SPACEWIRE_IOCTRL_SET_PROMISCUOUS %i \n", (unsigned int)ioarg->buffer); - if ((unsigned int)ioarg->buffer > 1) { - return RTEMS_INVALID_NAME; - } - SPW_CTRL_WRITE(pDev, SPW_CTRL_READ(pDev) | ((unsigned int)ioarg->buffer << 5)); - if (((SPW_CTRL_READ(pDev) >> 5) & 1) != (unsigned int)ioarg->buffer) { - return RTEMS_IO_ERROR; - } - pDev->config.promiscuous = (unsigned int)ioarg->buffer; - break; - case SPACEWIRE_IOCTRL_SET_RMAPEN: - SPACEWIRE_DBGC(DBGSPW_IOCTRL,"SPACEWIRE_IOCTRL_SET_RMAPEN %i \n", (unsigned int)ioarg->buffer); - if ((unsigned int)ioarg->buffer > 1) { - return RTEMS_INVALID_NAME; - } - SPW_CTRL_WRITE(pDev, (SPW_STATUS_READ(pDev) & 0xFFFEFFFF) | ((unsigned int)ioarg->buffer << 16)); - if (((SPW_CTRL_READ(pDev) >> 16) & 1) != (unsigned int)ioarg->buffer) { - return RTEMS_IO_ERROR; - } - pDev->config.rmapen = (unsigned int)ioarg->buffer; - break; - case SPACEWIRE_IOCTRL_SET_RMAPBUFDIS: - SPACEWIRE_DBGC(DBGSPW_IOCTRL,"SPACEWIRE_IOCTRL_SET_RMAPBUFDIS %i \n", (unsigned int)ioarg->buffer); - if ((unsigned int)ioarg->buffer > 1) { - return RTEMS_INVALID_NAME; - } - SPW_CTRL_WRITE(pDev, (SPW_STATUS_READ(pDev) & 0xFFFDFFFF) | ((unsigned int)ioarg->buffer << 17)); - if (((SPW_CTRL_READ(pDev) >> 17) & 1) != (unsigned int)ioarg->buffer) { - return RTEMS_IO_ERROR; - } - pDev->config.rmapbufdis = (unsigned int)ioarg->buffer; - break; - case SPACEWIRE_IOCTRL_SET_CHECK_RMAP: - SPACEWIRE_DBGC(DBGSPW_IOCTRL,"SPACEWIRE_IOCTRL_SET_CHECK_RMAP %i \n", (unsigned int)ioarg->buffer); - if ((unsigned int)ioarg->buffer > 1) { - return RTEMS_INVALID_NAME; - } - pDev->config.check_rmap_err = (unsigned int)ioarg->buffer; - break; - case SPACEWIRE_IOCTRL_SET_RM_PROT_ID: - SPACEWIRE_DBGC(DBGSPW_IOCTRL, "SPACEWIRE_IOCTRL_SET_RM_PROT_ID %i \n", (unsigned int)ioarg->buffer); - if ((unsigned int)ioarg->buffer > 1) { - return RTEMS_INVALID_NAME; - } - pDev->config.rm_prot_id = (unsigned int)ioarg->buffer; - break; - case SPACEWIRE_IOCTRL_SET_TXBLOCK: - SPACEWIRE_DBGC(DBGSPW_IOCTRL, "SPACEWIRE_IOCTRL_SET_TXBLOCK %i \n", (unsigned int)ioarg->buffer); - if ((unsigned int)ioarg->buffer > 1) { - return RTEMS_INVALID_NAME; - } - pDev->config.tx_blocking = (unsigned int)ioarg->buffer; - break; - case SPACEWIRE_IOCTRL_SET_TXBLOCK_ON_FULL: - SPACEWIRE_DBGC(DBGSPW_IOCTRL, "SPACEWIRE_IOCTRL_SET_TXBLOCK_ON_FULL %i \n", (unsigned int)ioarg->buffer); - if ((unsigned int)ioarg->buffer > 1) { - return RTEMS_INVALID_NAME; - } - pDev->config.tx_block_on_full = (unsigned int)ioarg->buffer; - break; - case SPACEWIRE_IOCTRL_SET_DISABLE_ERR: - SPACEWIRE_DBGC(DBGSPW_IOCTRL, "SPACEWIRE_IOCTRL_SET_DISABLE_ERR %i \n", (unsigned int)ioarg->buffer); - if ((unsigned int)ioarg->buffer > 1) { - return RTEMS_INVALID_NAME; - } - pDev->config.disable_err = (unsigned int)ioarg->buffer; - break; - case SPACEWIRE_IOCTRL_SET_LINK_ERR_IRQ: - SPACEWIRE_DBGC(DBGSPW_IOCTRL, "SPACEWIRE_IOCTRL_SET_LINK_ERR_IRQ %i \n", (unsigned int)ioarg->buffer); - SPACEWIRE_DBGC(DBGSPW_IOCTRL, "CTRL REG: %x\n", SPW_CTRL_READ(pDev)); - if ((unsigned int)ioarg->buffer > 1) { - return RTEMS_INVALID_NAME; - } - SPW_CTRL_WRITE(pDev, (SPW_CTRL_READ(pDev) & 0xFFFFFDF7) | ((unsigned int)ioarg->buffer << 9) | (pDev->config.link_err_irq << 3)); - SPACEWIRE_DBGC(DBGSPW_IOCTRL, "CTRL REG: %x\n", SPW_CTRL_READ(pDev)); - if (((SPW_CTRL_READ(pDev) >> 9) & 1) != (unsigned int)ioarg->buffer) { - return RTEMS_IO_ERROR; - } - pDev->config.link_err_irq = (unsigned int)ioarg->buffer; - break; - case SPACEWIRE_IOCTRL_SET_EVENT_ID: - SPACEWIRE_DBGC(DBGSPW_IOCTRL, "SPACEWIRE_IOCTRL_SET_EVENT_ID %i \n", (unsigned int)ioarg->buffer); - pDev->config.event_id = (rtems_id)ioarg->buffer; - SPACEWIRE_DBGC(DBGSPW_IOCTRL, "Event id: %i\n", pDev->config.event_id); - break; - - /* Change MAX Packet size by: - * - stop RX/TX (if on) - * - wait for hw to complete RX DMA (if on) - * - reallocate buffers with new size - * - tell hw about new size & start RX/TX again (if previously on) - */ - case SPACEWIRE_IOCTRL_SET_PACKETSIZE: - if (ioarg->buffer == NULL) - return RTEMS_INVALID_NAME; - ps = (spw_ioctl_packetsize*) ioarg->buffer; - SPACEWIRE_DBGC(DBGSPW_IOCTRL,"SPACEWIRE_IOCTRL_SET_RXPACKETSIZE %i \n", (unsigned int)ioarg->buffer); - - tmp = pDev->running; - - if ( pDev->running ){ - /* Stop RX */ - grspw_hw_stop(pDev,1,1); - - /* If packetsize fails it is good to know if in running mode */ - pDev->running = 0; - - /* Wait for Receiver to finnish pending DMA transfers if any */ - grspw_hw_wait_rx_inactive(pDev); - } - - /* Save new buffer sizes */ - pDev->rxbufsize = ps->rxsize; - pDev->txdbufsize = ps->txdsize; - pDev->txhbufsize = ps->txhsize; - pDev->config.rxmaxlen = pDev->rxbufsize; - - /* Free previous buffers & allocate buffers with new size */ - if (grspw_buffer_alloc(pDev)) - return RTEMS_NO_MEMORY; - - /* if RX was actived before, we reactive it again */ - if ( tmp ) { - if ( (status = grspw_hw_startup(pDev,-1)) != RTEMS_SUCCESSFUL ) { - return status; - } - pDev->running = 1; - } + spw_ioctl_pkt_send *args; + spw_ioctl_packetsize *ps; + int status; + unsigned int tmp,mask,nodeaddr,nodemask; + int timeout; + rtems_device_driver ret; + rtems_libio_ioctl_args_t *ioarg = (rtems_libio_ioctl_args_t *) arg; + GRSPW_DEV *pDev; + struct drvmgr_dev *dev; + + SPACEWIRE_DBGC(DBGSPW_IOCALLS, "ctrl [%i,%i]\n", major, minor); + + if ( drvmgr_get_dev(&grspw_drv_info.general, minor, &dev) ) { + return RTEMS_INVALID_NAME; + } + pDev = (GRSPW_DEV *)dev->priv; + + if (!ioarg) + return RTEMS_INVALID_NAME; + + ioarg->ioctl_return = 0; + switch(ioarg->command) { + case SPACEWIRE_IOCTRL_SET_NODEADDR: + /*set node address*/ + SPACEWIRE_DBGC(DBGSPW_IOCTRL, "SPACEWIRE_IOCTRL_SET_NODEADDR %i\n",(unsigned int)ioarg->buffer); + if ((unsigned int)ioarg->buffer > 255) { + return RTEMS_INVALID_NAME; + } + nodeaddr = ((unsigned int)ioarg->buffer) & 0xff; + tmp = SPW_READ(&pDev->regs->nodeaddr); + tmp &= 0xffffff00; /* Remove old address */ + tmp |= nodeaddr; + SPW_WRITE(&pDev->regs->nodeaddr, tmp); + if ((SPW_READ(&pDev->regs->nodeaddr)&0xff) != nodeaddr) { + return RTEMS_IO_ERROR; + } + pDev->config.nodeaddr = nodeaddr; + break; + case SPACEWIRE_IOCTRL_SET_NODEMASK: + /*set node address*/ + SPACEWIRE_DBGC(DBGSPW_IOCTRL, "SPACEWIRE_IOCTRL_SET_NODEMASK %i\n",(unsigned int)ioarg->buffer); + if ( pDev->core_ver > 1 ){ + if ((unsigned int)ioarg->buffer > 255) { + return RTEMS_INVALID_NAME; + } + nodemask = ((unsigned int)ioarg->buffer) & 0xff; + tmp = SPW_READ(&pDev->regs->nodeaddr); + tmp &= 0xffff00ff; /* Remove old mask */ + tmp |= nodemask<<8; + SPW_WRITE(&pDev->regs->nodeaddr, tmp); + if (((SPW_READ(&pDev->regs->nodeaddr)>>8)&0xff) != nodemask) { + return RTEMS_IO_ERROR; + } + pDev->config.nodemask = nodemask; + }else{ + SPACEWIRE_DBG("SPACEWIRE_IOCTRL_SET_NODEMASK: not implemented in GRSPW1 HW\n"); + } + break; + case SPACEWIRE_IOCTRL_SET_RXBLOCK: + SPACEWIRE_DBGC(DBGSPW_IOCTRL, "SPACEWIRE_IOCTRL_SET_RXBLOCK %i \n", (unsigned int)ioarg->buffer); + if ((unsigned int)ioarg->buffer > 1) { + return RTEMS_INVALID_NAME; + } + pDev->config.rx_blocking = (unsigned int)ioarg->buffer; + break; + case SPACEWIRE_IOCTRL_SET_DESTKEY: + SPACEWIRE_DBGC(DBGSPW_IOCTRL,"SPACEWIRE_IOCTRL_SET_DESTKEY %i\n", (unsigned int)ioarg->buffer); + if (!pDev->config.is_rmap) { + return RTEMS_NOT_IMPLEMENTED; + } + if ((unsigned int)ioarg->buffer > 255) { + return RTEMS_INVALID_NAME; + } + SPW_WRITE(&pDev->regs->destkey, (unsigned int)ioarg->buffer); + if (SPW_READ(&pDev->regs->destkey) != (unsigned int)ioarg->buffer) { + return RTEMS_IO_ERROR; + } + pDev->config.destkey = (unsigned int)ioarg->buffer; + break; + case SPACEWIRE_IOCTRL_SET_CLKDIV: + SPACEWIRE_DBGC(DBGSPW_IOCTRL,"SPACEWIRE_IOCTRL_SET_CLKDIV %i\n", (unsigned int)ioarg->buffer); + if ((unsigned int)ioarg->buffer > 255) { + return RTEMS_INVALID_NAME; + } + if ( pDev->core_ver == 3 ) + break; + tmp = SPW_READ(&pDev->regs->clkdiv); + tmp &= ~0xff; /* Remove old Clockdiv Setting */ + tmp |= ((unsigned int)ioarg->buffer) & 0xff; /* add new clockdiv setting */ + SPW_WRITE(&pDev->regs->clkdiv, tmp); + if (SPW_READ(&pDev->regs->clkdiv) != tmp) { + return RTEMS_IO_ERROR; + } + pDev->config.clkdiv = tmp; + break; + case SPACEWIRE_IOCTRL_SET_CLKDIVSTART: + SPACEWIRE_DBGC(DBGSPW_IOCTRL,"SPACEWIRE_IOCTRL_SET_CLKDIVSTART %i\n", (unsigned int)ioarg->buffer); + if ((unsigned int)ioarg->buffer > 255) { + return RTEMS_INVALID_NAME; + } + if ( pDev->core_ver == 3 ) + break; + tmp = SPW_READ(&pDev->regs->clkdiv); + tmp &= ~0xff00; /* Remove old Clockdiv Start Setting */ + tmp |= (((unsigned int)ioarg->buffer) & 0xff)<<8; /* add new clockdiv start setting */ + SPW_WRITE(&pDev->regs->clkdiv, tmp); + if (SPW_READ(&pDev->regs->clkdiv) != tmp) { + return RTEMS_IO_ERROR; + } + pDev->config.clkdiv = tmp; + break; + case SPACEWIRE_IOCTRL_SET_TIMER: + SPACEWIRE_DBGC(DBGSPW_IOCTRL,"SPACEWIRE_IOCTRL_SET_TIMER %i\n", (unsigned int)ioarg->buffer); + if ( pDev->core_ver <= 1 ) { + if ((unsigned int)ioarg->buffer > 4095) { + return RTEMS_INVALID_NAME; + } + SPW_WRITE(&pDev->regs->timer, (SPW_READ(&pDev->regs->timer) & 0xFFFFF000) | ((unsigned int)ioarg->buffer & 0xFFF)); + if ((SPW_READ(&pDev->regs->timer) & 0xFFF) != (unsigned int)ioarg->buffer) { + return RTEMS_IO_ERROR; + } + pDev->config.timer = (unsigned int)ioarg->buffer; + }else{ + SPACEWIRE_DBG("SPACEWIRE_IOCTRL_SET_TIMER: removed in GRSPW2 HW\n"); + } + break; + case SPACEWIRE_IOCTRL_SET_DISCONNECT: + SPACEWIRE_DBGC(DBGSPW_IOCTRL,"SPACEWIRE_IOCTRL_SET_DISCONNECT %i\n", (unsigned int)ioarg->buffer); + if ( pDev->core_ver <= 1 ) { + if ((unsigned int)ioarg->buffer > 1023) { + return RTEMS_INVALID_NAME; + } + SPW_WRITE(&pDev->regs->timer, (SPW_READ(&pDev->regs->timer) & 0xFFC00FFF) | (((unsigned int)ioarg->buffer & 0x3FF) << 12)); + if (((SPW_READ(&pDev->regs->timer) >> 12) & 0x3FF) != (unsigned int)ioarg->buffer) { + return RTEMS_IO_ERROR; + } + pDev->config.disconnect = (unsigned int)ioarg->buffer; + }else{ + SPACEWIRE_DBG("SPACEWIRE_IOCTRL_SET_DISCONNECT: not implemented for GRSPW2\n"); + } + break; + case SPACEWIRE_IOCTRL_SET_PROMISCUOUS: + SPACEWIRE_DBGC(DBGSPW_IOCTRL,"SPACEWIRE_IOCTRL_SET_PROMISCUOUS %i \n", (unsigned int)ioarg->buffer); + if ((unsigned int)ioarg->buffer > 1) { + return RTEMS_INVALID_NAME; + } + SPW_CTRL_WRITE(pDev, SPW_CTRL_READ(pDev) | ((unsigned int)ioarg->buffer << 5)); + if (((SPW_CTRL_READ(pDev) >> 5) & 1) != (unsigned int)ioarg->buffer) { + return RTEMS_IO_ERROR; + } + pDev->config.promiscuous = (unsigned int)ioarg->buffer; + break; + case SPACEWIRE_IOCTRL_SET_RMAPEN: + SPACEWIRE_DBGC(DBGSPW_IOCTRL,"SPACEWIRE_IOCTRL_SET_RMAPEN %i \n", (unsigned int)ioarg->buffer); + if ((unsigned int)ioarg->buffer > 1) { + return RTEMS_INVALID_NAME; + } + SPW_CTRL_WRITE(pDev, (SPW_CTRL_READ(pDev) & 0xFFFEFFFF) | ((unsigned int)ioarg->buffer << 16)); + if (((SPW_CTRL_READ(pDev) >> 16) & 1) != (unsigned int)ioarg->buffer) { + return RTEMS_IO_ERROR; + } + pDev->config.rmapen = (unsigned int)ioarg->buffer; + break; + case SPACEWIRE_IOCTRL_SET_RMAPBUFDIS: + SPACEWIRE_DBGC(DBGSPW_IOCTRL,"SPACEWIRE_IOCTRL_SET_RMAPBUFDIS %i \n", (unsigned int)ioarg->buffer); + if ((unsigned int)ioarg->buffer > 1) { + return RTEMS_INVALID_NAME; + } + SPW_CTRL_WRITE(pDev, (SPW_CTRL_READ(pDev) & 0xFFFDFFFF) | ((unsigned int)ioarg->buffer << 17)); + if (((SPW_CTRL_READ(pDev) >> 17) & 1) != (unsigned int)ioarg->buffer) { + return RTEMS_IO_ERROR; + } + pDev->config.rmapbufdis = (unsigned int)ioarg->buffer; + break; + case SPACEWIRE_IOCTRL_SET_CHECK_RMAP: + SPACEWIRE_DBGC(DBGSPW_IOCTRL,"SPACEWIRE_IOCTRL_SET_CHECK_RMAP %i \n", (unsigned int)ioarg->buffer); + if ((unsigned int)ioarg->buffer > 1) { + return RTEMS_INVALID_NAME; + } + pDev->config.check_rmap_err = (unsigned int)ioarg->buffer; + break; + case SPACEWIRE_IOCTRL_SET_RM_PROT_ID: + SPACEWIRE_DBGC(DBGSPW_IOCTRL, "SPACEWIRE_IOCTRL_SET_RM_PROT_ID %i \n", (unsigned int)ioarg->buffer); + if ((unsigned int)ioarg->buffer > 1) { + return RTEMS_INVALID_NAME; + } + pDev->config.rm_prot_id = (unsigned int)ioarg->buffer; + break; + case SPACEWIRE_IOCTRL_SET_KEEP_SOURCE: + SPACEWIRE_DBGC(DBGSPW_IOCTRL, "SPACEWIRE_IOCTRL_SET_KEEP_SOURCE %i \n", (unsigned int)ioarg->buffer); + if ((unsigned int)ioarg->buffer > 1) { + return RTEMS_INVALID_NAME; + } + pDev->config.keep_source = (unsigned int)ioarg->buffer; + break; + case SPACEWIRE_IOCTRL_SET_TXBLOCK: + SPACEWIRE_DBGC(DBGSPW_IOCTRL, "SPACEWIRE_IOCTRL_SET_TXBLOCK %i \n", (unsigned int)ioarg->buffer); + if ((unsigned int)ioarg->buffer > 1) { + return RTEMS_INVALID_NAME; + } + pDev->config.tx_blocking = (unsigned int)ioarg->buffer; + break; + case SPACEWIRE_IOCTRL_SET_TXBLOCK_ON_FULL: + SPACEWIRE_DBGC(DBGSPW_IOCTRL, "SPACEWIRE_IOCTRL_SET_TXBLOCK_ON_FULL %i \n", (unsigned int)ioarg->buffer); + if ((unsigned int)ioarg->buffer > 1) { + return RTEMS_INVALID_NAME; + } + pDev->config.tx_block_on_full = (unsigned int)ioarg->buffer; + break; + case SPACEWIRE_IOCTRL_SET_DISABLE_ERR: + SPACEWIRE_DBGC(DBGSPW_IOCTRL, "SPACEWIRE_IOCTRL_SET_DISABLE_ERR %i \n", (unsigned int)ioarg->buffer); + if ((unsigned int)ioarg->buffer > 1) { + return RTEMS_INVALID_NAME; + } + pDev->config.disable_err = (unsigned int)ioarg->buffer; + break; + case SPACEWIRE_IOCTRL_SET_LINK_ERR_IRQ: + SPACEWIRE_DBGC(DBGSPW_IOCTRL, "SPACEWIRE_IOCTRL_SET_LINK_ERR_IRQ %i \n", (unsigned int)ioarg->buffer); + SPACEWIRE_DBGC(DBGSPW_IOCTRL, "CTRL REG: %x\n", SPW_CTRL_READ(pDev)); + if ((unsigned int)ioarg->buffer > 1) { + return RTEMS_INVALID_NAME; + } + tmp = (SPW_CTRL_READ(pDev) & 0xFFFFFDF7) | ((unsigned int)ioarg->buffer << 9); + if (tmp & (SPW_CTRL_LI|SPW_CTRL_TQ)) + tmp |= SPW_CTRL_IE; + SPW_CTRL_WRITE(pDev, tmp); + SPACEWIRE_DBGC(DBGSPW_IOCTRL, "CTRL REG: %x\n", SPW_CTRL_READ(pDev)); + if (((SPW_CTRL_READ(pDev) >> 9) & 1) != (unsigned int)ioarg->buffer) { + return RTEMS_IO_ERROR; + } + pDev->config.link_err_irq = (unsigned int)ioarg->buffer; + break; + case SPACEWIRE_IOCTRL_SET_EVENT_ID: + SPACEWIRE_DBGC(DBGSPW_IOCTRL, "SPACEWIRE_IOCTRL_SET_EVENT_ID %i \n", (unsigned int)ioarg->buffer); + pDev->config.event_id = (rtems_id)ioarg->buffer; + SPACEWIRE_DBGC(DBGSPW_IOCTRL, "Event id: %i\n", pDev->config.event_id); + break; + + /* Change MAX Packet size by: + * - stop RX/TX (if on) + * - wait for hw to complete RX DMA (if on) + * - reallocate buffers with new size + * - tell hw about new size & start RX/TX again (if previously on) + */ + case SPACEWIRE_IOCTRL_SET_PACKETSIZE: + if (ioarg->buffer == NULL) + return RTEMS_INVALID_NAME; + ps = (spw_ioctl_packetsize*) ioarg->buffer; + SPACEWIRE_DBGC(DBGSPW_IOCTRL,"SPACEWIRE_IOCTRL_SET_RXPACKETSIZE %i \n", (unsigned int)ioarg->buffer); + + tmp = pDev->running; + + if ( pDev->running ){ + /* Stop RX */ + grspw_hw_stop(pDev,1,1); + + /* If packetsize fails it is good to know if in running mode */ + pDev->running = 0; + + /* Wait for Receiver to finnish pending DMA transfers if any */ + grspw_hw_wait_rx_inactive(pDev); + } + + /* Save new buffer sizes */ + pDev->rxbufsize = ((ps->rxsize+7)&~7); + pDev->txdbufsize = ps->txdsize; + pDev->txhbufsize = ps->txhsize; + pDev->config.rxmaxlen = pDev->rxbufsize; + + /* Free previous buffers & allocate buffers with new size */ + if (grspw_buffer_alloc(pDev)) + return RTEMS_NO_MEMORY; + + /* if RX was actived before, we reactive it again */ + if ( tmp ) { + if ( (status = grspw_hw_startup(pDev,-1)) != RTEMS_SUCCESSFUL ) { + return status; + } + pDev->running = 1; + } #if 0 - /* Rewrite previous config which was wasted due to reset in hw_startup */ - SPW_WRITE(&pDev->regs->nodeaddr, pDev->config.nodeaddr); - SPW_WRITE(&pDev->regs->destkey, pDev->config.destkey); - SPW_WRITE(&pDev->regs->clkdiv, pDev->config.clkdiv); - SPW_WRITE(&pDev->regs->timer, pDev->config.timer | ( (pDev->config.disconnect & 0x3FF) << 12) ); - SPW_CTRL_WRITE(pDev, (SPW_CTRL_READ(pDev) & !(SPW_CTRL_LINKSTART | SPW_CTRL_PM | SPW_CTRL_RE | SPW_CTRL_RD | SPW_CTRL_TT | SPW_CTRL_TR)) | \ - (pDev->config.promiscuous << 5) | (pDev->config.rmapen << 16) | (pDev->config.rmapbufdis << 17) | \ - (pDev->config.linkdisabled) | (pDev->config.linkstart << 1)); + /* Rewrite previous config which was wasted due to reset in hw_startup */ + SPW_WRITE(&pDev->regs->nodeaddr, pDev->config.nodeaddr); + SPW_WRITE(&pDev->regs->destkey, pDev->config.destkey); + SPW_WRITE(&pDev->regs->clkdiv, pDev->config.clkdiv); + SPW_WRITE(&pDev->regs->timer, pDev->config.timer | ( (pDev->config.disconnect & 0x3FF) << 12) ); + SPW_CTRL_WRITE(pDev, (SPW_CTRL_READ(pDev) & !(SPW_CTRL_LINKSTART | SPW_CTRL_PM | SPW_CTRL_RE | SPW_CTRL_RD | SPW_CTRL_TT | SPW_CTRL_TR)) | \ + (pDev->config.promiscuous << 5) | (pDev->config.rmapen << 16) | (pDev->config.rmapbufdis << 17) | \ + (pDev->config.linkdisabled) | (pDev->config.linkstart << 1)); #endif - break; - case SPACEWIRE_IOCTRL_GET_CONFIG: - if (ioarg->buffer == NULL) - return RTEMS_INVALID_NAME; - SPACEWIRE_DBG2("SPACEWIRE_IOCTRL_GET_CONFIG \n"); - (*(spw_config *)ioarg->buffer).nodeaddr = pDev->config.nodeaddr; - (*(spw_config *)ioarg->buffer).nodemask = pDev->config.nodemask; - (*(spw_config *)ioarg->buffer).destkey = pDev->config.destkey; - (*(spw_config *)ioarg->buffer).clkdiv = pDev->config.clkdiv; - (*(spw_config *)ioarg->buffer).rxmaxlen = pDev->config.rxmaxlen; - (*(spw_config *)ioarg->buffer).timer = pDev->config.timer; - (*(spw_config *)ioarg->buffer).disconnect = pDev->config.disconnect; - (*(spw_config *)ioarg->buffer).promiscuous = pDev->config.promiscuous; - (*(spw_config *)ioarg->buffer).rmapen = pDev->config.rmapen; - (*(spw_config *)ioarg->buffer).rmapbufdis = pDev->config.rmapbufdis; - (*(spw_config *)ioarg->buffer).check_rmap_err = pDev->config.check_rmap_err; - (*(spw_config *)ioarg->buffer).rm_prot_id = pDev->config.rm_prot_id; - (*(spw_config *)ioarg->buffer).tx_blocking = pDev->config.tx_blocking; - (*(spw_config *)ioarg->buffer).disable_err = pDev->config.disable_err; - (*(spw_config *)ioarg->buffer).link_err_irq = pDev->config.link_err_irq; - (*(spw_config *)ioarg->buffer).event_id = pDev->config.event_id; - (*(spw_config *)ioarg->buffer).is_rmap = pDev->config.is_rmap; - (*(spw_config *)ioarg->buffer).is_rmapcrc = pDev->config.is_rmapcrc; - (*(spw_config *)ioarg->buffer).is_rxunaligned = pDev->config.is_rxunaligned; - (*(spw_config *)ioarg->buffer).linkdisabled = pDev->config.linkdisabled; - (*(spw_config *)ioarg->buffer).linkstart = pDev->config.linkstart; - (*(spw_config *)ioarg->buffer).rx_blocking = pDev->config.rx_blocking; - (*(spw_config *)ioarg->buffer).tx_block_on_full = pDev->config.tx_block_on_full; - break; - case SPACEWIRE_IOCTRL_GET_LINK_STATUS: - SPACEWIRE_DBGC(DBGSPW_IOCTRL,"SPACEWIRE_IOCTRL_GET_STATUS=%i \n", (unsigned int)((SPW_STATUS_READ(pDev) >> 21) & 0x7)); - *(unsigned int *)ioarg->buffer = (unsigned int )((SPW_STATUS_READ(pDev) >> 21) & 0x7); - break; - case SPACEWIRE_IOCTRL_GET_STATISTICS: - if (ioarg->buffer == NULL) - return RTEMS_INVALID_NAME; - SPACEWIRE_DBG2("SPACEWIRE_IOCTRL_GET_STATISTICS \n"); - (*(spw_stats *)ioarg->buffer).tx_link_err = pDev->stat.tx_link_err; - (*(spw_stats *)ioarg->buffer).rx_rmap_header_crc_err = pDev->stat.rx_rmap_header_crc_err; - (*(spw_stats *)ioarg->buffer).rx_rmap_data_crc_err = pDev->stat.rx_rmap_data_crc_err; - (*(spw_stats *)ioarg->buffer).rx_eep_err = pDev->stat.rx_eep_err; - (*(spw_stats *)ioarg->buffer).rx_truncated = pDev->stat.rx_truncated; - (*(spw_stats *)ioarg->buffer).parity_err = pDev->stat.parity_err; - (*(spw_stats *)ioarg->buffer).escape_err = pDev->stat.escape_err; - (*(spw_stats *)ioarg->buffer).credit_err = pDev->stat.credit_err; - (*(spw_stats *)ioarg->buffer).write_sync_err = pDev->stat.write_sync_err; - (*(spw_stats *)ioarg->buffer).disconnect_err = pDev->stat.disconnect_err; - (*(spw_stats *)ioarg->buffer).early_ep = pDev->stat.early_ep; - (*(spw_stats *)ioarg->buffer).invalid_address = pDev->stat.invalid_address; - (*(spw_stats *)ioarg->buffer).packets_sent = pDev->stat.packets_sent; - (*(spw_stats *)ioarg->buffer).packets_received = pDev->stat.packets_received; - break; - case SPACEWIRE_IOCTRL_CLR_STATISTICS: - SPACEWIRE_DBG2("SPACEWIRE_IOCTRL_CLR_STATISTICS \n"); - pDev->stat.tx_link_err = 0; - pDev->stat.rx_rmap_header_crc_err = 0; - pDev->stat.rx_rmap_data_crc_err = 0; - pDev->stat.rx_eep_err = 0; - pDev->stat.rx_truncated = 0; - pDev->stat.parity_err = 0; - pDev->stat.escape_err = 0; - pDev->stat.credit_err = 0; - pDev->stat.write_sync_err = 0; - pDev->stat.disconnect_err = 0; - pDev->stat.early_ep = 0; - pDev->stat.invalid_address = 0; - pDev->stat.packets_sent = 0; - pDev->stat.packets_received = 0; - break; - case SPACEWIRE_IOCTRL_SEND: - if (ioarg->buffer == NULL) - return RTEMS_INVALID_NAME; - args = (spw_ioctl_pkt_send *)ioarg->buffer; - args->sent = 0; - - /* is link up? */ - if ( !pDev->running ) { - return RTEMS_INVALID_NAME; - } - - SPACEWIRE_DBGC(DBGSPW_IOCALLS, "write [%i,%i]: hlen: %i hbuf:0x%x dlen:%i dbuf:0x%x\n", major, minor, - (unsigned int)args->hlen, (int)args->hdr,(unsigned int)args->dlen, (int)args->data); - - if ((args->hlen > pDev->txhbufsize) || (args->dlen > pDev->txdbufsize) || - ((args->hlen+args->dlen) < 1) || - ((args->hdr == NULL) && (args->hlen != 0)) || ((args->data == NULL) && (args->dlen != 0))) { - return RTEMS_INVALID_NAME; - } - while ((args->sent = grspw_hw_send(pDev, args->hlen, args->hdr, args->dlen, args->data)) == 0) { - if (pDev->config.tx_block_on_full == 1) { - SPACEWIRE_DBG2("Tx Block on full \n"); - rtems_semaphore_obtain(pDev->txsp, RTEMS_WAIT, RTEMS_NO_TIMEOUT); - } else { - SPACEWIRE_DBG2("Tx non blocking return when full \n"); - return RTEMS_RESOURCE_IN_USE; - } - } - SPACEWIRE_DBGC(DBGSPW_IOCALLS, "Tx ioctl return: %i \n", args->sent); - break; - - case SPACEWIRE_IOCTRL_LINKDISABLE: - pDev->config.linkdisabled = 1; - pDev->config.linkstart = 0; - SPW_CTRL_WRITE(pDev, (SPW_CTRL_READ(pDev) & 0xFFFFFFFC) | 1); - if ((SPW_CTRL_READ(pDev) & 3) != 1) { - return RTEMS_IO_ERROR; - } - break; - - case SPACEWIRE_IOCTRL_LINKSTART: - pDev->config.linkdisabled = 0; - pDev->config.linkstart = 1; - SPW_CTRL_WRITE(pDev, (SPW_CTRL_READ(pDev) & 0xFFFFFFFC) | 2); - if ((SPW_CTRL_READ(pDev) & 3) != 2) { - return RTEMS_IO_ERROR; - } - break; - - /* Calculate timer register from GRSPW Core frequency - * Also possible to set disconnect and timer64 from - * - SPACEWIRE_IOCTRL_SET_DISCONNECT - * - SPACEWIRE_IOCTRL_SET_TIMER - */ - case SPACEWIRE_IOCTRL_SET_COREFREQ: - pDev->core_freq_khz = (unsigned int)ioarg->buffer; - if ( pDev->core_freq_khz == 0 ){ - /* Get GRSPW clock frequency from system clock. - * System clock has been read from timer inited - * by RTEMS loader (mkprom) - */ - pDev->core_freq_khz = sys_freq_khz; - } - - /* Only GRSPW1 needs the Timer64 and Disconnect values - * GRSPW2 and onwards doesn't have this register. - */ - if ( pDev->core_ver <= 1 ){ - /* Calculate Timer64 & Disconnect */ - pDev->config.timer = grspw_calc_timer64(pDev->core_freq_khz); - pDev->config.disconnect = grspw_calc_disconnect(pDev->core_freq_khz); - - /* Set Timer64 & Disconnect Register */ - SPW_WRITE(&pDev->regs->timer, - (SPW_READ(&pDev->regs->timer) & 0xFFC00000) | - ((pDev->config.disconnect & 0x3FF)<<12) | - (pDev->config.timer & 0xFFF)); - - /* Check that the registers were written successfully */ - tmp = SPW_READ(&pDev->regs->timer) & 0x003fffff; - if ( ((tmp & 0xFFF) != pDev->config.timer) || - (((tmp >> 12) & 0x3FF) != pDev->config.disconnect) ) { - return RTEMS_IO_ERROR; - } - } - break; - - case SPACEWIRE_IOCTRL_START: - if ( pDev->running ){ - return RTEMS_INVALID_NAME; - } - - /* Get timeout from userspace - * timeout: - * ¤ -1 = Default timeout - * ¤ less than -1 = forever - * ¤ 0 = no wait, proceed if link is up - * ¤ positive = specifies number of system clock ticks that - * startup will wait for link to enter ready mode. - */ - timeout = (int)ioarg->buffer; - - if ( (ret=grspw_hw_startup(pDev,timeout)) != RTEMS_SUCCESSFUL ) { - return ret; - } - pDev->running = 1; - break; - - case SPACEWIRE_IOCTRL_STOP: - if ( !pDev->running ){ - return RTEMS_INVALID_NAME; - } - pDev->running = 0; - - /* Stop Receiver and transmitter */ - grspw_hw_stop(pDev,1,1); - break; - - default: - return RTEMS_NOT_IMPLEMENTED; - } - - SPACEWIRE_DBGC(DBGSPW_IOCALLS, "SPW_IOCTRL Return\n"); - return RTEMS_SUCCESSFUL; + break; + case SPACEWIRE_IOCTRL_GET_CONFIG: + if (ioarg->buffer == NULL) + return RTEMS_INVALID_NAME; + SPACEWIRE_DBG2("SPACEWIRE_IOCTRL_GET_CONFIG \n"); + (*(spw_config *)ioarg->buffer).nodeaddr = pDev->config.nodeaddr; + (*(spw_config *)ioarg->buffer).nodemask = pDev->config.nodemask; + (*(spw_config *)ioarg->buffer).destkey = pDev->config.destkey; + (*(spw_config *)ioarg->buffer).clkdiv = pDev->config.clkdiv; + (*(spw_config *)ioarg->buffer).rxmaxlen = pDev->config.rxmaxlen; + (*(spw_config *)ioarg->buffer).timer = pDev->config.timer; + (*(spw_config *)ioarg->buffer).disconnect = pDev->config.disconnect; + (*(spw_config *)ioarg->buffer).promiscuous = pDev->config.promiscuous; + (*(spw_config *)ioarg->buffer).rmapen = pDev->config.rmapen; + (*(spw_config *)ioarg->buffer).rmapbufdis = pDev->config.rmapbufdis; + (*(spw_config *)ioarg->buffer).check_rmap_err = pDev->config.check_rmap_err; + (*(spw_config *)ioarg->buffer).rm_prot_id = pDev->config.rm_prot_id; + (*(spw_config *)ioarg->buffer).tx_blocking = pDev->config.tx_blocking; + (*(spw_config *)ioarg->buffer).disable_err = pDev->config.disable_err; + (*(spw_config *)ioarg->buffer).link_err_irq = pDev->config.link_err_irq; + (*(spw_config *)ioarg->buffer).event_id = pDev->config.event_id; + (*(spw_config *)ioarg->buffer).is_rmap = pDev->config.is_rmap; + (*(spw_config *)ioarg->buffer).is_rmapcrc = pDev->config.is_rmapcrc; + (*(spw_config *)ioarg->buffer).is_rxunaligned = pDev->config.is_rxunaligned; + (*(spw_config *)ioarg->buffer).linkdisabled = pDev->config.linkdisabled; + (*(spw_config *)ioarg->buffer).linkstart = pDev->config.linkstart; + (*(spw_config *)ioarg->buffer).rx_blocking = pDev->config.rx_blocking; + (*(spw_config *)ioarg->buffer).tx_block_on_full = pDev->config.tx_block_on_full; + (*(spw_config *)ioarg->buffer).keep_source = pDev->config.keep_source; + (*(spw_config *)ioarg->buffer).rtimeout = pDev->config.rtimeout; + break; + case SPACEWIRE_IOCTRL_GET_LINK_STATUS: + SPACEWIRE_DBGC(DBGSPW_IOCTRL,"SPACEWIRE_IOCTRL_GET_STATUS=%i \n", (unsigned int)((SPW_STATUS_READ(pDev) >> 21) & 0x7)); + *(unsigned int *)ioarg->buffer = (unsigned int )((SPW_STATUS_READ(pDev) >> 21) & 0x7); + break; + case SPACEWIRE_IOCTRL_GET_STATISTICS: + if (ioarg->buffer == NULL) + return RTEMS_INVALID_NAME; + SPACEWIRE_DBG2("SPACEWIRE_IOCTRL_GET_STATISTICS \n"); + (*(spw_stats *)ioarg->buffer).tx_link_err = pDev->stat.tx_link_err; + (*(spw_stats *)ioarg->buffer).rx_rmap_header_crc_err = pDev->stat.rx_rmap_header_crc_err; + (*(spw_stats *)ioarg->buffer).rx_rmap_data_crc_err = pDev->stat.rx_rmap_data_crc_err; + (*(spw_stats *)ioarg->buffer).rx_eep_err = pDev->stat.rx_eep_err; + (*(spw_stats *)ioarg->buffer).rx_truncated = pDev->stat.rx_truncated; + (*(spw_stats *)ioarg->buffer).parity_err = pDev->stat.parity_err; + (*(spw_stats *)ioarg->buffer).escape_err = pDev->stat.escape_err; + (*(spw_stats *)ioarg->buffer).credit_err = pDev->stat.credit_err; + (*(spw_stats *)ioarg->buffer).write_sync_err = pDev->stat.write_sync_err; + (*(spw_stats *)ioarg->buffer).disconnect_err = pDev->stat.disconnect_err; + (*(spw_stats *)ioarg->buffer).early_ep = pDev->stat.early_ep; + (*(spw_stats *)ioarg->buffer).invalid_address = pDev->stat.invalid_address; + (*(spw_stats *)ioarg->buffer).packets_sent = pDev->stat.packets_sent; + (*(spw_stats *)ioarg->buffer).packets_received = pDev->stat.packets_received; + break; + case SPACEWIRE_IOCTRL_CLR_STATISTICS: + SPACEWIRE_DBG2("SPACEWIRE_IOCTRL_CLR_STATISTICS \n"); + pDev->stat.tx_link_err = 0; + pDev->stat.rx_rmap_header_crc_err = 0; + pDev->stat.rx_rmap_data_crc_err = 0; + pDev->stat.rx_eep_err = 0; + pDev->stat.rx_truncated = 0; + pDev->stat.parity_err = 0; + pDev->stat.escape_err = 0; + pDev->stat.credit_err = 0; + pDev->stat.write_sync_err = 0; + pDev->stat.disconnect_err = 0; + pDev->stat.early_ep = 0; + pDev->stat.invalid_address = 0; + pDev->stat.packets_sent = 0; + pDev->stat.packets_received = 0; + break; + case SPACEWIRE_IOCTRL_SEND: + if (ioarg->buffer == NULL) + return RTEMS_INVALID_NAME; + args = (spw_ioctl_pkt_send *)ioarg->buffer; + args->sent = 0; + + /* is link up? */ + if ( !pDev->running ) { + return RTEMS_INVALID_NAME; + } + + SPACEWIRE_DBGC(DBGSPW_IOCALLS, "write [%i,%i]: hlen: %i hbuf:0x%x dlen:%i dbuf:0x%x\n", major, minor, + (unsigned int)args->hlen, (int)args->hdr,(unsigned int)args->dlen, (int)args->data); + + if ((args->hlen > pDev->txhbufsize) || (args->dlen > pDev->txdbufsize) || + ((args->hlen+args->dlen) < 1) || + ((args->hdr == NULL) && (args->hlen != 0)) || ((args->data == NULL) && (args->dlen != 0))) { + return RTEMS_INVALID_NAME; + } + while ((args->sent = grspw_hw_send(pDev, args->hlen, args->hdr, args->dlen, args->data, args->options)) == 0) { + if (pDev->config.tx_block_on_full == 1) { + SPACEWIRE_DBG2("Tx Block on full \n"); + rtems_semaphore_obtain(pDev->txsp, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + } else { + SPACEWIRE_DBG2("Tx non blocking return when full \n"); + return RTEMS_RESOURCE_IN_USE; + } + } + SPACEWIRE_DBGC(DBGSPW_IOCALLS, "Tx ioctl return: %i \n", args->sent); + break; + + case SPACEWIRE_IOCTRL_LINKDISABLE: + pDev->config.linkdisabled = 1; + pDev->config.linkstart = 0; + if ( pDev->core_ver != 3 ) { + SPW_CTRL_WRITE(pDev, (SPW_CTRL_READ(pDev) & 0xFFFFFFFC) | 1); + if ((SPW_CTRL_READ(pDev) & 3) != 1) { + return RTEMS_IO_ERROR; + } + } + break; + + case SPACEWIRE_IOCTRL_LINKSTART: + pDev->config.linkdisabled = 0; + pDev->config.linkstart = 1; + if ( pDev->core_ver != 3 ) { + SPW_CTRL_WRITE(pDev, (SPW_CTRL_READ(pDev) & 0xFFFFFFFC) | 2); + if ((SPW_CTRL_READ(pDev) & 3) != 2) { + return RTEMS_IO_ERROR; + } + } + break; + + /* Calculate timer register from GRSPW Core frequency + * Also possible to set disconnect and timer64 from + * - SPACEWIRE_IOCTRL_SET_DISCONNECT + * - SPACEWIRE_IOCTRL_SET_TIMER + */ + case SPACEWIRE_IOCTRL_SET_COREFREQ: + pDev->core_freq_khz = (unsigned int)ioarg->buffer; + if ( pDev->core_freq_khz == 0 ){ + /* Get GRSPW clock frequency from system clock. + * System clock has been read from timer inited + * by RTEMS loader (mkprom) + */ + drvmgr_freq_get(pDev->dev, DEV_APB_SLV, + &pDev->core_freq_khz); + /* Convert from Hz -> kHz */ + pDev->core_freq_khz = pDev->core_freq_khz / 1000; + } + + /* Only GRSPW1 needs the Timer64 and Disconnect values + * GRSPW2 and onwards doesn't have this register. + */ + if ( pDev->core_ver <= 1 ){ + /* Calculate Timer64 & Disconnect */ + pDev->config.timer = grspw_calc_timer64(pDev->core_freq_khz); + pDev->config.disconnect = grspw_calc_disconnect(pDev->core_freq_khz); + + /* Set Timer64 & Disconnect Register */ + SPW_WRITE(&pDev->regs->timer, + (SPW_READ(&pDev->regs->timer) & 0xFFC00000) | + ((pDev->config.disconnect & 0x3FF)<<12) | + (pDev->config.timer & 0xFFF)); + + /* Check that the registers were written successfully */ + tmp = SPW_READ(&pDev->regs->timer) & 0x003fffff; + if ( ((tmp & 0xFFF) != pDev->config.timer) || + (((tmp >> 12) & 0x3FF) != pDev->config.disconnect) ) { + return RTEMS_IO_ERROR; + } + } + break; + + case SPACEWIRE_IOCTRL_START: + if ( pDev->running ){ + return RTEMS_INVALID_NAME; + } + + /* Get timeout from userspace + * timeout: + * ¤ -1 = Default timeout + * ¤ less than -1 = forever + * ¤ 0 = no wait, proceed if link is up + * ¤ positive = specifies number of system clock ticks that + * startup will wait for link to enter ready mode. + */ + timeout = (int)ioarg->buffer; + + if ( (ret=grspw_hw_startup(pDev,timeout)) != RTEMS_SUCCESSFUL ) { + return ret; + } + pDev->running = 1; + /* Register interrupt routine and unmask IRQ */ + drvmgr_interrupt_register(pDev->dev, 0, "grspw", grspw_interrupt, pDev); + + break; + + case SPACEWIRE_IOCTRL_STOP: + if ( !pDev->running ){ + return RTEMS_INVALID_NAME; + } + /* Disable interrupts */ + drvmgr_interrupt_unregister(dev, 0, grspw_interrupt, pDev); + + pDev->running = 0; + + /* Stop Receiver and transmitter */ + grspw_hw_stop(pDev,1,1); + break; + + /* Set time-code control register bits, and Enables/Disables + * Time code interrupt, make sure to connect the callback + * grspw_timecode_callback if using interrupts. + */ + case SPACEWIRE_IOCTRL_SET_TCODE_CTRL: + tmp = (unsigned int)ioarg->buffer; + mask = tmp & (SPACEWIRE_TCODE_CTRL_IE_MSK|SPACEWIRE_TCODE_CTRL_TT_MSK|SPACEWIRE_TCODE_CTRL_TR_MSK); + mask <<= 8; + tmp &= mask; + tmp = (SPW_CTRL_READ(pDev) & ~(mask | SPW_CTRL_IE)) | tmp; + if (tmp & (SPW_CTRL_LI|SPW_CTRL_TQ)) + tmp |= SPW_CTRL_IE; + SPW_CTRL_WRITE(pDev, tmp); + break; + + /* Set time register and optionaly send a time code */ + case SPACEWIRE_IOCTRL_SET_TCODE: + tmp = (unsigned int)ioarg->buffer; + /* Set timecode register */ + if (tmp & SPACEWIRE_TCODE_SET) { + SPW_WRITE(&pDev->regs->time, + ((SPW_READ(&pDev->regs->time) & ~(0xff)) | + (tmp & SPACEWIRE_TCODE_TCODE))); + } + /* Send timecode directly (tick-in) ? */ + if (tmp & SPACEWIRE_TCODE_TX) { + SPW_CTRL_WRITE(pDev, + ((SPW_CTRL_READ(pDev) & ~(SPW_CTRL_TI)) | SPW_CTRL_TI)); + } + break; + + /* Read time code register and tick-out status bit */ + case SPACEWIRE_IOCTRL_GET_TCODE: + tmp = (unsigned int)ioarg->buffer; + if ( !tmp ){ + return RTEMS_INVALID_NAME; + } + + /* Copy timecode register */ + if (SPW_READ(&pDev->regs->status) & SPW_STATUS_TO) { + *(unsigned int *)tmp = (1 << 8) | SPW_READ(&pDev->regs->time); + } else { + *(unsigned int *)tmp = SPW_READ(&pDev->regs->time); + } + break; + + case SPACEWIRE_IOCTRL_SET_READ_TIMEOUT: + pDev->config.rtimeout = (unsigned int)ioarg->buffer; + break; + + default: + return RTEMS_NOT_IMPLEMENTED; + } + + SPACEWIRE_DBGC(DBGSPW_IOCALLS, "SPW_IOCTRL Return\n"); + return RTEMS_SUCCESSFUL; } /* ============================================================================== */ static int grspw_set_rxmaxlen(GRSPW_DEV *pDev) { - unsigned int rxmax; - SPW_WRITE(&pDev->regs->dma0rxmax,pDev->config.rxmaxlen); /*set rx maxlength*/ - rxmax = SPW_READ(&pDev->regs->dma0rxmax); - if (rxmax != pDev->config.rxmaxlen) { - return 0; - } - return 1; + unsigned int rxmax; + SPW_WRITE(&pDev->regs->dma0rxmax,pDev->config.rxmaxlen); /*set rx maxlength*/ + rxmax = SPW_READ(&pDev->regs->dma0rxmax); + if (rxmax != pDev->config.rxmaxlen) { + return 0; + } + return 1; } static int grspw_hw_init(GRSPW_DEV *pDev) { - unsigned int ctrl; + unsigned int ctrl; - ctrl = SPW_CTRL_READ(pDev); + ctrl = SPW_CTRL_READ(pDev); -#ifdef GRSPW_STATIC_MEM - pDev->rx = (SPACEWIRE_RXBD *) pDev->mem_bdtable; - pDev->tx = (SPACEWIRE_RXBD *) pDev->mem_bdtable + SPACEWIRE_BDTABLE_SIZE; -#else - pDev->rx = (SPACEWIRE_RXBD *) SPW_ALIGN(&pDev->_rxtable, SPACEWIRE_BDTABLE_SIZE); - pDev->tx = (SPACEWIRE_TXBD *) SPW_ALIGN(&pDev->_txtable, SPACEWIRE_BDTABLE_SIZE); -#endif - SPACEWIRE_DBG("hw_init [minor %i]\n", pDev->minor); + pDev->rx = (SPACEWIRE_RXBD *) SPW_ALIGN(pDev->ptr_bd0, SPACEWIRE_BDTABLE_SIZE); + pDev->tx = (SPACEWIRE_TXBD *) &pDev->rx[SPACEWIRE_RXBUFS_NR]; + + /* Translate into remote address */ + drvmgr_translate(pDev->dev, 0, 0, (void *)pDev->rx, (void **)&pDev->rx_remote); + drvmgr_translate(pDev->dev, 0, 0, (void *)pDev->tx, (void **)&pDev->tx_remote); - pDev->config.is_rmap = ctrl & SPW_CTRL_RA; - pDev->config.is_rxunaligned = ctrl & SPW_CTRL_RX; - pDev->config.is_rmapcrc = ctrl & SPW_CTRL_RC; - return 0; + SPACEWIRE_DBG("hw_init [minor %i]\n", pDev->minor); + + pDev->config.is_rmap = ctrl & SPW_CTRL_RA; + pDev->config.is_rxunaligned = ctrl & SPW_CTRL_RX; + pDev->config.is_rmapcrc = ctrl & SPW_CTRL_RC; + return 0; } -static int grspw_hw_waitlink (GRSPW_DEV *pDev, int timeout) +static int grspw_hw_waitlink (GRSPW_DEV *pDev, int timeout) { - int j; - - if ( timeout == -1 ){ - /* Wait default timeout */ - timeout = SPACEWIRE_INIT_TIMEOUT; - } - - j=0; - while (SPW_LINKSTATE(SPW_STATUS_READ(pDev)) != 5) { - if ( timeout < -1 ) { - /* wait forever */ - }else if ( j >= timeout ){ - /* timeout reached, return fail */ - return 1; - } - - /* Sleep for 10 ticks */ - rtems_task_wake_after(10); - j+=10; - } - return 0; + int j; + + /* No actual link interface on a DMA-only GRSPW2 connected to the + * SPW router + */ + if (pDev->core_ver == 3) + return 0; + + if ( timeout == -1 ){ + /* Wait default timeout */ + timeout = SPACEWIRE_INIT_TIMEOUT; + } + + j=0; + while (SPW_LINKSTATE(SPW_STATUS_READ(pDev)) != 5) { + if ( timeout < -1 ) { + /* wait forever */ + }else if ( j >= timeout ){ + /* timeout reached, return fail */ + return 1; + } + + /* Sleep for 10 ticks */ + rtems_task_wake_after(10); + j+=10; + } + return 0; } static void grspw_hw_reset(GRSPW_DEV *pDev) { - SPW_CTRL_WRITE(pDev, SPW_CTRL_RESET); /*reset core*/ - SPW_STATUS_WRITE(pDev, SPW_STATUS_TO | SPW_STATUS_CE | SPW_STATUS_ER | SPW_STATUS_DE | SPW_STATUS_PE | - SPW_STATUS_WE | SPW_STATUS_IA | SPW_STATUS_EE); /*clear status*/ - SPW_CTRL_WRITE(pDev, SPW_CTRL_LINKSTART); /*start link core*/ + SPW_CTRL_WRITE(pDev, SPW_CTRL_RESET); /*reset core*/ + SPW_STATUS_WRITE(pDev, SPW_STATUS_TO | SPW_STATUS_CE | SPW_STATUS_ER | SPW_STATUS_DE | SPW_STATUS_PE | + SPW_STATUS_WE | SPW_STATUS_IA | SPW_STATUS_EE); /*clear status*/ + SPW_CTRL_WRITE(pDev, SPW_CTRL_LINKSTART); /*start link core*/ } static void grspw_hw_read_config(GRSPW_DEV *pDev) { - unsigned int tmp; + unsigned int tmp; + + tmp = SPW_READ(&pDev->regs->nodeaddr); + pDev->config.nodeaddr = 0xFF & tmp; + pDev->config.nodemask = 0xFF & (tmp>>8); + pDev->config.destkey = 0xFF & SPW_READ(&pDev->regs->destkey); + pDev->config.clkdiv = 0xFFFF & SPW_READ(&pDev->regs->clkdiv); + + tmp = SPW_CTRL_READ(pDev); + pDev->config.promiscuous = 1 & (tmp >> 5); + pDev->config.rmapen = 1 & (tmp >> 16); + pDev->config.rmapbufdis = 1 & (tmp >> 17); + pDev->config.is_rmap = 1 & (tmp >> 31); + pDev->config.is_rxunaligned = 1 & (tmp >> 30); + pDev->config.is_rmapcrc = 1 & (tmp >> 29); + pDev->config.linkdisabled = 1 & tmp; + pDev->config.linkstart = 1 & (tmp >> 1); + + if ( pDev->core_ver <= 1 ){ + tmp = SPW_READ(&pDev->regs->timer); + pDev->config.timer = 0xFFF & tmp; + pDev->config.disconnect = 0x3FF & (tmp >> 12); + }else{ + pDev->config.timer = 0; + pDev->config.disconnect = 0; + } - tmp = SPW_READ(&pDev->regs->nodeaddr); - pDev->config.nodeaddr = 0xFF & tmp; - pDev->config.nodemask = 0xFF & (tmp>>8); - pDev->config.destkey = 0xFF & SPW_READ(&pDev->regs->destkey); - pDev->config.clkdiv = 0xFFFF & SPW_READ(&pDev->regs->clkdiv); - - tmp = SPW_CTRL_READ(pDev); - pDev->config.promiscuous = 1 & (tmp >> 5); - pDev->config.rmapen = 1 & (tmp >> 16); - pDev->config.rmapbufdis = 1 & (tmp >> 17); - pDev->config.is_rmap = 1 & (tmp >> 31); - pDev->config.is_rxunaligned = 1 & (tmp >> 30); - pDev->config.is_rmapcrc = 1 & (tmp >> 29); - pDev->config.linkdisabled = 1 & tmp; - pDev->config.linkstart = 1 & (tmp >> 1); - - if ( pDev->core_ver <= 1 ){ - tmp = SPW_READ(&pDev->regs->timer); - pDev->config.timer = 0xFFF & tmp; - pDev->config.disconnect = 0x3FF & (tmp >> 12); - }else{ - pDev->config.timer = 0; - pDev->config.disconnect = 0; - } - - return; + return; } /* timeout is given in ticks */ static int grspw_hw_startup (GRSPW_DEV *pDev, int timeout) { - int i; - unsigned int dmactrl; - - SPW_WRITE(&pDev->regs->status, (SPW_STATUS_TO|SPW_STATUS_CE|SPW_STATUS_ER|SPW_STATUS_DE|SPW_STATUS_PE|SPW_STATUS_WE|SPW_STATUS_IA|SPW_STATUS_EE)); /*clear status*/ - - if (grspw_hw_waitlink(pDev,timeout)) { - SPACEWIRE_DBG2("Device open. Link is not up\n"); - return RTEMS_TIMEOUT; - } - - SPW_WRITE(&pDev->regs->dma0ctrl, SPW_DMACTRL_PS | SPW_DMACTRL_PR | SPW_DMACTRL_TA | SPW_DMACTRL_RA); /*clear status, set ctrl*/ - - - if ((dmactrl = SPW_READ(&pDev->regs->dma0ctrl)) != 0) { - SPACEWIRE_DBG2("DMACtrl is not cleared\n"); - return RTEMS_IO_ERROR; - } - - /* prepare transmit buffers */ - for (i = 0; i < pDev->txbufcnt; i++) { - pDev->tx[i].ctrl = 0; - pDev->tx[i].addr_header = memarea_to_hw(((unsigned int)&pDev->ptr_txhbuf0[0]) + (i * pDev->txhbufsize)); - pDev->tx[i].addr_data = memarea_to_hw(((unsigned int)&pDev->ptr_txdbuf0[0]) + (i * pDev->txdbufsize)); - } - pDev->tx_cur = 0; - pDev->tx_sent = 0; - pDev->tx_all_in_use = 0; - - /* prepare receive buffers */ - for (i = 0; i < pDev->rxbufcnt; i++) { - if (i+1 == pDev->rxbufcnt) { - pDev->rx[i].ctrl = SPW_RXBD_IE | SPW_RXBD_EN | SPW_RXBD_WR; - } else { - pDev->rx[i].ctrl = SPW_RXBD_IE | SPW_RXBD_EN; - } - pDev->rx[i].addr = memarea_to_hw(((unsigned int)&pDev->ptr_rxbuf0[0]) + (i * pDev->rxbufsize)); - } - pDev->rxcur = 0; - pDev->rxbufcur = -1; - grspw_set_rxmaxlen(pDev); - - SPW_WRITE(&pDev->regs->dma0txdesc, memarea_to_hw((unsigned int) pDev->tx)); - SPW_WRITE(&pDev->regs->dma0rxdesc, memarea_to_hw((unsigned int) pDev->rx)); - - /* start RX */ - dmactrl = SPW_READ(&pDev->regs->dma0ctrl); - SPW_WRITE(&pDev->regs->dma0ctrl, (dmactrl & SPW_PREPAREMASK_RX) | SPW_DMACTRL_RD | SPW_DMACTRL_RXEN | SPW_DMACTRL_NS | SPW_DMACTRL_TXIE | SPW_DMACTRL_RXIE); - - SPACEWIRE_DBGC(DBGSPW_TX,"0x%x: setup complete\n", (unsigned int)pDev->regs); - return RTEMS_SUCCESSFUL; + int i; + unsigned int dmactrl; + + SPW_WRITE(&pDev->regs->status, (SPW_STATUS_TO|SPW_STATUS_CE|SPW_STATUS_ER|SPW_STATUS_DE|SPW_STATUS_PE|SPW_STATUS_WE|SPW_STATUS_IA|SPW_STATUS_EE)); /*clear status*/ + + if (grspw_hw_waitlink(pDev,timeout)) { + SPACEWIRE_DBG2("Device open. Link is not up\n"); + return RTEMS_TIMEOUT; + } + + SPW_WRITE(&pDev->regs->dma0ctrl, SPW_DMACTRL_PS | SPW_DMACTRL_PR | SPW_DMACTRL_TA | SPW_DMACTRL_RA); /*clear status, set ctrl*/ + + if ((dmactrl = SPW_READ(&pDev->regs->dma0ctrl)) != 0) { + SPACEWIRE_DBG2("DMACtrl is not cleared\n"); + return RTEMS_IO_ERROR; + } + + /* prepare transmit buffers */ + for (i = 0; i < pDev->txbufcnt; i++) { + pDev->tx[i].ctrl = 0; + pDev->tx[i].addr_header = ((unsigned int)&pDev->ptr_txhbuf0_remote[0]) + (i * pDev->txhbufsize); + pDev->tx[i].addr_data = ((unsigned int)&pDev->ptr_txdbuf0_remote[0]) + (i * pDev->txdbufsize); + } + pDev->tx_cur = 0; + pDev->tx_sent = 0; + pDev->tx_all_in_use = 0; + + /* prepare receive buffers */ + for (i = 0; i < pDev->rxbufcnt; i++) { + if (i+1 == pDev->rxbufcnt) { + pDev->rx[i].ctrl = SPW_RXBD_IE | SPW_RXBD_EN | SPW_RXBD_WR; + } else { + pDev->rx[i].ctrl = SPW_RXBD_IE | SPW_RXBD_EN; + } + pDev->rx[i].addr = ((unsigned int)&pDev->ptr_rxbuf0_remote[0]) + (i * pDev->rxbufsize); + } + pDev->rxcur = 0; + pDev->rxbufcur = -1; + grspw_set_rxmaxlen(pDev); + + SPW_WRITE(&pDev->regs->dma0txdesc, pDev->tx_remote); + SPW_WRITE(&pDev->regs->dma0rxdesc, pDev->rx_remote); + + /* start RX */ + dmactrl = SPW_READ(&pDev->regs->dma0ctrl); + SPW_WRITE(&pDev->regs->dma0ctrl, (dmactrl & SPW_PREPAREMASK_RX) | SPW_DMACTRL_RD | SPW_DMACTRL_RXEN | SPW_DMACTRL_NS | SPW_DMACTRL_TXIE | SPW_DMACTRL_RXIE); + + SPACEWIRE_DBGC(DBGSPW_TX,"0x%x: setup complete\n", (unsigned int)pDev->regs); + return RTEMS_SUCCESSFUL; } /* Wait until the receiver is inactive */ static void grspw_hw_wait_rx_inactive(GRSPW_DEV *pDev) { - while( SPW_READ(&pDev->regs->dma0ctrl) & SPW_DMACTRL_RX ){ - /* switching may be needed: - * - low frequency GRSPW - * - mega packet incoming - */ - rtems_task_wake_after(1); - } + while( SPW_READ(&pDev->regs->dma0ctrl) & SPW_DMACTRL_RX ){ + /* switching may be needed: + * - low frequency GRSPW + * - mega packet incoming + */ + rtems_task_wake_after(1); + } } /* Stop the rx or/and tx by disabling the receiver/transmitter */ -static int grspw_hw_stop (GRSPW_DEV *pDev, int rx, int tx) -{ - unsigned int dmactrl; - - /* stop rx and/or tx */ - dmactrl = SPW_READ(&pDev->regs->dma0ctrl); - if ( rx ) { - dmactrl &= ~(SPW_DMACTRL_RXEN|SPW_DMACTRL_RXIE|SPW_DMACTRL_RD); - } - if ( tx ) { - dmactrl &= ~(SPW_DMACTRL_TXEN|SPW_DMACTRL_TXIE); - } - /*SPW_WRITE(&pDev->regs->dma0ctrl, (dmactrl & SPW_PREPAREMASK_RX) & ~(SPW_DMACTRL_RD | SPW_DMACTRL_RXEN) & ~(SPW_DMACTRL_TXEN));*/ - - /* don't clear status flags */ - dmactrl &= ~(SPW_DMACTRL_RA|SPW_DMACTRL_PR|SPW_DMACTRL_AI); - SPW_WRITE(&pDev->regs->dma0ctrl, dmactrl); - return RTEMS_SUCCESSFUL; -} - -int grspw_hw_send(GRSPW_DEV *pDev, unsigned int hlen, char *hdr, unsigned int dlen, char *data) +static int grspw_hw_stop (GRSPW_DEV *pDev, int rx, int tx) { + unsigned int dmactrl; - unsigned int dmactrl, ctrl; -#ifdef DEBUG_SPACEWIRE_ONOFF - unsigned int k; -#endif - rtems_interrupt_level level; - unsigned int cur = pDev->tx_cur; - char *txh = pDev->ptr_txhbuf0 + (cur * pDev->txhbufsize); - char *txd = pDev->ptr_txdbuf0 + (cur * pDev->txdbufsize); + /* stop rx and/or tx */ + dmactrl = SPW_READ(&pDev->regs->dma0ctrl); + if ( rx ) { + dmactrl &= ~(SPW_DMACTRL_RXEN|SPW_DMACTRL_RXIE|SPW_DMACTRL_RD); + } + if ( tx ) { + dmactrl &= ~(SPW_DMACTRL_TXEN|SPW_DMACTRL_TXIE); + } + /*SPW_WRITE(&pDev->regs->dma0ctrl, (dmactrl & SPW_PREPAREMASK_RX) & ~(SPW_DMACTRL_RD | SPW_DMACTRL_RXEN) & ~(SPW_DMACTRL_TXEN));*/ - ctrl = SPW_READ((volatile void *)&pDev->tx[cur].ctrl); + /* don't clear status flags */ + dmactrl &= ~(SPW_DMACTRL_RA|SPW_DMACTRL_PR|SPW_DMACTRL_AI); + SPW_WRITE(&pDev->regs->dma0ctrl, dmactrl); + return RTEMS_SUCCESSFUL; +} - if (ctrl & SPW_TXBD_EN) { - return 0; - } - memcpy(&txd[0], data, dlen); - memcpy(&txh[0], hdr, hlen); +int grspw_hw_send(GRSPW_DEV *pDev, unsigned int hlen, char *hdr, unsigned int dlen, char *data, unsigned int options) +{ + unsigned int dmactrl, ctrl; #ifdef DEBUG_SPACEWIRE_ONOFF - if (DEBUG_SPACEWIRE_FLAGS & DBGSPW_DUMP) { - for (k = 0; k < hlen; k++){ - if (k % 16 == 0) { - printf ("\n"); - } - printf ("%.2x(%c) ",txh[k] & 0xff,isprint(txh[k] & 0xff) ? txh[k] & 0xff : ' '); - } - printf ("\n"); - } - if (DEBUG_SPACEWIRE_FLAGS & DBGSPW_DUMP) { - for (k = 0; k < dlen; k++){ - if (k % 16 == 0) { - printf ("\n"); - } - printf ("%.2x(%c) ",txd[k] & 0xff,isprint(txd[k] & 0xff) ? txd[k] & 0xff : ' '); - } - printf ("\n"); - } + unsigned int k; #endif + rtems_interrupt_level level; + unsigned int cur = pDev->tx_cur, bdctrl; + char *txh = pDev->ptr_txhbuf0 + (cur * pDev->txhbufsize); + char *txd = pDev->ptr_txdbuf0 + (cur * pDev->txdbufsize); + char *txh_remote = pDev->ptr_txhbuf0_remote + (cur * pDev->txhbufsize); + char *txd_remote = pDev->ptr_txdbuf0_remote + (cur * pDev->txdbufsize); + unsigned int tmp, tmp2; + + ctrl = SPW_READ((volatile void *)&pDev->tx[cur].ctrl); + + if (ctrl & SPW_TXBD_EN) { + return 0; + } - pDev->tx[cur].addr_header = memarea_to_hw((unsigned int)txh); - pDev->tx[cur].len = dlen; - pDev->tx[cur].addr_data = memarea_to_hw((unsigned int)txd); - if (pDev->tx_cur == (pDev->txbufcnt - 1) ) { - pDev->tx[cur].ctrl = SPW_TXBD_IE | SPW_TXBD_WR | SPW_TXBD_EN | hlen; - } else { - pDev->tx[cur].ctrl = SPW_TXBD_IE | SPW_TXBD_EN | hlen; - } + memcpy(&txd[0], data, dlen); + memcpy(&txh[0], hdr, hlen); - dmactrl = SPW_READ(&pDev->regs->dma0ctrl); - SPW_WRITE(&pDev->regs->dma0ctrl, (dmactrl & SPW_PREPAREMASK_TX) | SPW_DMACTRL_TXEN | SPW_DMACTRL_TXIE); +#ifdef DEBUG_SPACEWIRE_ONOFF + if (DEBUG_SPACEWIRE_FLAGS & DBGSPW_DUMP) { + for (k = 0; k < hlen; k++){ + if (k % 16 == 0) { + printf ("\n"); + } + printf ("%.2x(%c) ",txh[k] & 0xff,isprint(txh[k] & 0xff) ? txh[k] & 0xff : ' '); + } + printf ("\n"); + } + if (DEBUG_SPACEWIRE_FLAGS & DBGSPW_DUMP) { + for (k = 0; k < dlen; k++){ + if (k % 16 == 0) { + printf ("\n"); + } + printf ("%.2x(%c) ",txd[k] & 0xff,isprint(txd[k] & 0xff) ? txd[k] & 0xff : ' '); + } + printf ("\n"); + } +#endif + + pDev->tx[cur].addr_header = (unsigned int)txh_remote; + pDev->tx[cur].len = dlen; + pDev->tx[cur].addr_data = (unsigned int)txd_remote; + + bdctrl = SPW_TXBD_IE | SPW_TXBD_EN | hlen; + if ( options & GRSPW_PKTSEND_OPTION_HDR_CRC ) + bdctrl |= SPW_TXBD_HC; + if ( options & GRSPW_PKTSEND_OPTION_DATA_CRC ) + bdctrl |= SPW_TXBD_DC; + bdctrl |= options & GRSPW_PKTSEND_OPTION_NOCRCLEN_MASK; + + /* Update counters */ + rtems_interrupt_disable(level); + if (pDev->tx_cur == (pDev->txbufcnt - 1) ) { + bdctrl |= SPW_TXBD_WR; + } + pDev->tx[cur].ctrl = bdctrl; - /* Update counters */ - rtems_interrupt_disable(level); + dmactrl = SPW_READ(&pDev->regs->dma0ctrl); + SPW_WRITE(&pDev->regs->dma0ctrl, (dmactrl & SPW_PREPAREMASK_TX) | SPW_DMACTRL_TXEN | SPW_DMACTRL_TXIE); - pDev->tx_cur = (pDev->tx_cur + 1) % pDev->txbufcnt; - if (pDev->tx_cur == pDev->tx_sent) { - pDev->tx_all_in_use = 1; - } - rtems_interrupt_enable(level); + pDev->tx_cur = (pDev->tx_cur + 1) % pDev->txbufcnt; + if (pDev->tx_cur == pDev->tx_sent) { + pDev->tx_all_in_use = 1; + } + rtems_interrupt_enable(level); - /* In blocking mode wait until message is sent */ + /* In blocking mode wait until message is sent */ if (pDev->config.tx_blocking) { while ( SPW_READ(&pDev->regs->dma0ctrl) & SPW_DMACTRL_TXEN) { /* if changed to blocking mode */ - SPACEWIRE_DBGC(DBGSPW_TX, "Tx blocking\n"); - rtems_semaphore_obtain(pDev->txsp, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + SPACEWIRE_DBGC(DBGSPW_TX, "Tx blocking\n"); + rtems_semaphore_obtain(pDev->txsp, RTEMS_WAIT, RTEMS_NO_TIMEOUT); } } - SPACEWIRE_DBGC(DBGSPW_TX, "0x%x: transmitted <%i> bytes\n", (unsigned int) pDev->regs, dlen+hlen); - return hlen + dlen; + SPACEWIRE_DBGC(DBGSPW_TX, "0x%x: transmitted <%i> bytes\n", (unsigned int) pDev->regs, dlen+hlen); + return hlen + dlen; } static int grspw_hw_receive(GRSPW_DEV *pDev, char *b, int c) { - unsigned int len, rxlen, ctrl; - unsigned int cur; - unsigned int tmp; - unsigned int dump_start_len; - int i; - char *rxb; - - if ( pDev->config.promiscuous ) { + unsigned int len, rxlen, ctrl; + unsigned int cur; + unsigned int tmp; + unsigned int dump_start_len; + int i; + char *rxb; + + if ( pDev->config.promiscuous || pDev->config.keep_source ) { dump_start_len = 0; /* make sure address and prot can be read in promiscuous mode */ } else if (pDev->config.rm_prot_id) { dump_start_len = 2; /* skip source address and protocol id */ @@ -1637,92 +1866,153 @@ static int grspw_hw_receive(GRSPW_DEV *pDev, char *b, int c) { dump_start_len = 1; /* default: skip only source address */ } - rxlen = 0; - cur = pDev->rxcur; - rxb = pDev->ptr_rxbuf0 + (cur * pDev->rxbufsize); - - SPACEWIRE_DBGC(DBGSPW_RX, "0x%x: waitin packet at pos %i\n", (unsigned int) pDev->regs, cur); - - ctrl = SPW_READ((volatile void *)&pDev->rx[cur].ctrl); - if (ctrl & SPW_RXBD_EN) { - return rxlen; - } - SPACEWIRE_DBGC(DBGSPW_RX, "checking packet\n"); - - len = SPW_RXBD_LENGTH & ctrl; - if (!((ctrl & SPW_RXBD_ERROR) || (pDev->config.check_rmap_err && (ctrl & SPW_RXBD_RMAPERROR)))) { - if (pDev->rxbufcur == -1) { - SPACEWIRE_DBGC(DBGSPW_RX, "incoming packet len %i\n", len); - pDev->stat.packets_received++; - pDev->rxbufcur = dump_start_len; - } - rxlen = tmp = len - pDev->rxbufcur; - SPACEWIRE_DBGC(DBGSPW_RX, "C %i\n", c); - SPACEWIRE_DBGC(DBGSPW_RX, "Dump %i\n", dump_start_len); - SPACEWIRE_DBGC(DBGSPW_RX, "Bufcur %i\n", pDev->rxbufcur); - SPACEWIRE_DBGC(DBGSPW_RX, "Rxlen %i\n", rxlen ); - if (rxlen > c) { - rxlen = c; - } - if (CPU_SPARC_HAS_SNOOPING) { - memcpy(b, rxb+pDev->rxbufcur, rxlen); - } else { - for(i = 0; i < rxlen; i++) { - b[i] = MEM_READ(rxb+pDev->rxbufcur); - } - } - - pDev->rxbufcur += rxlen; - if (c >= tmp) { - SPACEWIRE_DBGC(DBGSPW_RX, "Next descriptor\n"); - grspw_rxnext(pDev); - } - } else { - check_rx_errors(pDev, ctrl); - } - return rxlen; + rxlen = 0; + cur = pDev->rxcur; + rxb = pDev->ptr_rxbuf0 + (cur * pDev->rxbufsize); + + SPACEWIRE_DBGC(DBGSPW_RX, "0x%x: waitin packet at pos %i\n", (unsigned int) pDev->regs, cur); + + ctrl = SPW_READ((volatile void *)&pDev->rx[cur].ctrl); + if (ctrl & SPW_RXBD_EN) { + return rxlen; + } + SPACEWIRE_DBGC(DBGSPW_RX, "checking packet\n"); + + len = SPW_RXBD_LENGTH & ctrl; + if (!((ctrl & SPW_RXBD_ERROR) || (pDev->config.check_rmap_err && (ctrl & SPW_RXBD_RMAPERROR)))) { + if (pDev->rxbufcur == -1) { + SPACEWIRE_DBGC(DBGSPW_RX, "incoming packet len %i\n", len); + pDev->stat.packets_received++; + pDev->rxbufcur = dump_start_len; + } + rxlen = tmp = len - pDev->rxbufcur; + SPACEWIRE_DBGC(DBGSPW_RX, "C %i\n", c); + SPACEWIRE_DBGC(DBGSPW_RX, "Dump %i\n", dump_start_len); + SPACEWIRE_DBGC(DBGSPW_RX, "Bufcur %i\n", pDev->rxbufcur); + SPACEWIRE_DBGC(DBGSPW_RX, "Rxlen %i\n", rxlen ); + if (rxlen > c) { + rxlen = c; + } + if (CPU_SPARC_HAS_SNOOPING) { +/* if ( 1 ) {*/ + /*printf("RX_MEMCPY(0x%x, 0x%x, 0x%x)\n", (unsigned int)b, (unsigned int)(rxb+pDev->rxbufcur), (unsigned int)rxlen);*/ + memcpy(b, rxb+pDev->rxbufcur, rxlen); + } else { + int left = rxlen; + /* Copy word wise if Aligned */ + if ( (((int)b & 3) == 0) && (((int)(rxb+pDev->rxbufcur) & 3) == 0) ){ + while(left>=32){ + *(int *)(b+0) = MEM_READ32(rxb+pDev->rxbufcur+0); + *(int *)(b+4) = MEM_READ32(rxb+pDev->rxbufcur+4); + *(int *)(b+8) = MEM_READ32(rxb+pDev->rxbufcur+8); + *(int *)(b+12) = MEM_READ32(rxb+pDev->rxbufcur+12); + *(int *)(b+16) = MEM_READ32(rxb+pDev->rxbufcur+16); + *(int *)(b+20) = MEM_READ32(rxb+pDev->rxbufcur+20); + *(int *)(b+24) = MEM_READ32(rxb+pDev->rxbufcur+24); + *(int *)(b+28) = MEM_READ32(rxb+pDev->rxbufcur+28); + rxb+=32; + b+=32; + left-=32; + } + while(left>=4){ + *(int *)b = MEM_READ32(rxb+pDev->rxbufcur); + rxb+=4; + b+=4; + left-=4; + } + } + for(i = 0; i < left; i++) { + b[i] = MEM_READ8(rxb+pDev->rxbufcur+i); + } + } + + pDev->rxbufcur += rxlen; + if (c >= tmp) { + SPACEWIRE_DBGC(DBGSPW_RX, "Next descriptor\n"); + grspw_rxnext(pDev); + } + } else { + check_rx_errors(pDev, ctrl); + grspw_rxnext(pDev); + } + return rxlen; } -static void grspw_rxnext(GRSPW_DEV *pDev) +static void grspw_rxnext(GRSPW_DEV *pDev) { - unsigned int dmactrl; - unsigned int cur = pDev->rxcur; - unsigned int ctrl = 0; - if (cur == (pDev->rxbufcnt - 1)) { - pDev->rx[cur].ctrl = ctrl | SPW_RXBD_EN | SPW_RXBD_IE | SPW_RXBD_WR; - cur = 0; - } else { - pDev->rx[cur].ctrl = ctrl | SPW_RXBD_EN | SPW_RXBD_IE; - cur++; - } - - pDev->rxcur = cur; - pDev->rxbufcur = -1; - - /* start RX */ - dmactrl = SPW_READ(&pDev->regs->dma0ctrl); - SPW_WRITE(&pDev->regs->dma0ctrl, (dmactrl & SPW_PREPAREMASK_RX) | SPW_DMACTRL_RD | SPW_DMACTRL_RXEN | SPW_DMACTRL_RXIE | SPW_DMACTRL_NS); + unsigned int dmactrl; + unsigned int cur = pDev->rxcur; + unsigned int ctrl = 0; + rtems_interrupt_level level; + + rtems_interrupt_disable(level); + + if (cur == (pDev->rxbufcnt - 1)) { + pDev->rx[cur].ctrl = ctrl | SPW_RXBD_EN | SPW_RXBD_IE | SPW_RXBD_WR; + cur = 0; + } else { + pDev->rx[cur].ctrl = ctrl | SPW_RXBD_EN | SPW_RXBD_IE; + cur++; + } + + pDev->rxcur = cur; + pDev->rxbufcur = -1; + + /* start RX */ + dmactrl = SPW_READ(&pDev->regs->dma0ctrl); + SPW_WRITE(&pDev->regs->dma0ctrl, (dmactrl & SPW_PREPAREMASK_RX) | SPW_DMACTRL_RD | SPW_DMACTRL_RXEN | SPW_DMACTRL_RXIE | SPW_DMACTRL_NS); + rtems_interrupt_enable(level); +} + +static void check_rx_errors(GRSPW_DEV *pDev, int ctrl) +{ + if (ctrl & SPW_RXBD_EEP) { + pDev->stat.rx_eep_err++; + } + if (ctrl & SPW_RXBD_EHC) { + if (pDev->config.check_rmap_err) { + pDev->stat.rx_rmap_header_crc_err++; + } + } + if (ctrl & SPW_RXBD_EDC) { + if (pDev->config.check_rmap_err) { + pDev->stat.rx_rmap_data_crc_err++; + } + } + if (ctrl & SPW_RXBD_ETR) { + pDev->stat.rx_truncated++; + } } -static void check_rx_errors(GRSPW_DEV *pDev, int ctrl) + +void grspw_print_dev(struct drvmgr_dev *dev, int options) { - if (ctrl & SPW_RXBD_EEP) { - pDev->stat.rx_eep_err++; - } - if (ctrl & SPW_RXBD_EHC) { - if (pDev->config.check_rmap_err) { - pDev->stat.rx_rmap_header_crc_err++; - } - } - if (ctrl & SPW_RXBD_EDC) { - if (pDev->config.check_rmap_err) { - pDev->stat.rx_rmap_data_crc_err++; - } - } - if (ctrl & SPW_RXBD_ETR) { - pDev->stat.rx_truncated++; - } + GRSPW_DEV *pDev = dev->priv; + struct amba_dev_info *devinfo; + + devinfo = (struct amba_dev_info *)pDev->dev->businfo; + + /* Print */ + printf("--- GRSPW %s ---\n", pDev->devName); + printf(" REGS: 0x%x\n", (unsigned int)pDev->regs); + printf(" IRQ: %d\n", pDev->irq); + printf(" CORE VERSION: %d\n", pDev->core_ver); + printf(" CTRL: 0x%x\n", pDev->regs->ctrl); + printf(" STATUS: 0x%x\n", pDev->regs->status); + printf(" DMA0CTRL: 0x%x\n", pDev->regs->dma0ctrl); + printf(" TXBD: 0x%x\n", (unsigned int)pDev->tx); + printf(" RXBD: 0x%x\n", (unsigned int)pDev->rx); } +void grspw_print(int options) +{ + struct amba_drv_info *drv = &grspw_drv_info; + struct drvmgr_dev *dev; + dev = drv->general.dev; + while(dev) { + grspw_print_dev(dev, options); + dev = dev->next_in_drv; + } +} diff --git a/c/src/lib/libbsp/sparc/shared/spw/grspw_pci.c b/c/src/lib/libbsp/sparc/shared/spw/grspw_pci.c deleted file mode 100644 index a8003be8f9..0000000000 --- a/c/src/lib/libbsp/sparc/shared/spw/grspw_pci.c +++ /dev/null @@ -1,127 +0,0 @@ -/* Select PCI driver */ -#define GRSPW_PCI - -#undef GRSPW_MAXDEVS -#undef DEBUG_SPACEWIRE_ONOFF - -/* Only Malloced memory supported - */ -#undef GRSPW_LOCAL_MEM - -/* memory must be aligned to a 128k boundary */ -unsigned int grspwpci_memarea_address; -#define GRSPW_LOCAL_MEM_ADR grspwpci_memarea_address - -/* We have custom address tranlation for HW addresses */ -#define GRSPW_ADR_TO - -/* MEMAREA=>CPU used when reading descriptor buffer pointers, - * they need to be translated from adresses used by GRSPW HW - * into CPU readable addresses. - * - * NOT NEEDED AS GRSPW DRIVER USES INDEXES TO GET DESCRIPTOR - * DATA POINTER ADDRESSES. - */ -#undef GRSPW_ADR_FROM - -/* Set registered device name */ -#define GRSPW_DEVNAME "/dev/grspwpci0" -#define GRSPW_DEVNAME_NO(devstr,no) ((devstr)[13]='0'+(no)) - -/* Any non-static function will begin with */ -#define GRSPW_PREFIX(name) grspwpci##name - -/* do nothing, assume that the interrupt handler is called - * setup externally calling b1553_interrupt_handler. - */ -#define GRSPW_REG_INT(handler,irq,arg) \ - if ( grspw_pci_int_reg ) \ - grspw_pci_int_reg(handler,irq,arg); - -void (*grspw_pci_int_reg)(void *handler, int irq, void *arg) = 0; - - -#ifdef GRSPW_ADR_TO -/* Translate an address within the Memory Region (memarea) into an Hardware - * device address. This address is put into hardware registers or descriptors - * so that the hardware can access the Memory Region. - * Example: - * A local AMBA access at 0xe0000000 will translate into PCI address 0x40000000, - * the PCI address 0x40000000 will translate into LEON-AMBA address 0x40000000. - */ -unsigned int grspwpci_hw_address; -static inline unsigned int memarea_to_hw(unsigned int addr) { - /* don't translate? */ - if ( grspwpci_hw_address == 0xffffffff ) - return addr; - return ((addr & 0x0fffffff) | grspwpci_hw_address); -} -#endif - -/* not used since BRM Core work with offsets */ -#ifdef GRSPW_ADR_FROM -unsigned int grspwpci_cpu_access_address; -static inline unsigned int hw_to_cpu(unsigned int addr) { - /* don't translate? */ - if ( grspwpci_cpu_address == 0xffffffff ) - return addr; - return ((addr & 0x0fffffff) | grspwpci_cpu_address); -} -#endif - -int grspwpci_interrupt_handler(int irq, void *arg); -#include "grspw.c" - -/* - * - * memarea = preallocated memory somewhere, pointer to start of memory. - * hw_address = how to translate a memarea address into an HW device AMBA address. - */ - -int grspw_pci_register( - amba_confarea_type *bus, - unsigned int memarea, - unsigned int hw_address - ) -{ - /* Setup configuration */ - - /* if zero the malloc will be used */ - grspwpci_memarea_address = memarea; - - grspwpci_hw_address = hw_address; - -#ifdef GRSPW_ADR_FROM - grspwpci_cpu_address = memarea & 0xf0000000; -#endif - - /* Register the driver */ - return GRSPW_PREFIX(_register)(bus); -} - -/* Call this from PCI interrupt handler - * irq = the irq number of the HW device local to that IRQMP controller - * - */ -int grspwpci_interrupt_handler(int irq, void *arg){ - grspw_interrupt( (GRSPW_DEV *)arg ); - return 0; -} - -#if 0 -int grspw_pci_interrupt_handler(int irqmask){ - int i; - unsigned int mask=0; - /* find minor */ - for(i=0; i<spw_cores; i++){ - if ( (1<<SPW_PARAM(i).irq) & irqmask ){ - mask |= 1<<SPW_PARAM(i).irq; - grspw_interrupt(i); - /* more interrupts to scan for? */ - if ( irqmask & ~mask ) - return mask; /* handled */ - } - } - return mask; -} -#endif diff --git a/c/src/lib/libbsp/sparc/shared/spw/grspw_pkt.c b/c/src/lib/libbsp/sparc/shared/spw/grspw_pkt.c new file mode 100644 index 0000000000..10a869748d --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/spw/grspw_pkt.c @@ -0,0 +1,2634 @@ +/* + * Aeroflex Gaisler GRSPW2 SpaceWire Kernel Library Interface for RTEMS. + * + * This driver can be used to implement a standard I/O system "char"-driver + * or used directly. NOTE SMP support has not been tested. + * + * COPYRIGHT (c) 2011 + * 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. + */ + +#include <rtems.h> +#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 <grspw_pkt.h> + +/* This driver has been prepared for SMP operation however never tested + * on a SMP system - use on your own risk. + */ +#ifdef RTEMS_HAS_SMP + +#include <rtems/score/smplock.h> /* spin-lock */ + +/* SPIN_LOCK() and SPIN_UNLOCK() NOT_IMPLEMENTED_BY_RTEMS. Use _IRQ version + * to implement. + */ +#define SPIN_DECLARE(name) SMP_lock_spinlock_simple_Control name +#define SPIN_INIT(lock) _SMP_lock_spinlock_simple_Initialize(lock) +#define SPIN_LOCK(lock, level) SPIN_LOCK_IRQ(lock, level) +#define SPIN_LOCK_IRQ(lock, level) (level) = _SMP_lock_spinlock_simple_Obtain(lock) +#define SPIN_UNLOCK(lock, level) SPIN_UNLOCK_IRQ(lock, level) +#define SPIN_UNLOCK_IRQ(lock, level) _SMP_lock_spinlock_simple_Release(lock, level) +#define IRQFLAGS_TYPE ISR_Level + +#else + +#define SPIN_DECLARE(name) +#define SPIN_INIT(lock) +#define SPIN_LOCK(lock, level) +#define SPIN_LOCK_IRQ(lock, level) rtems_interrupt_disable(level) +#define SPIN_UNLOCK(lock, level) +#define SPIN_UNLOCK_IRQ(lock, level) rtems_interrupt_enable(level) +#define IRQFLAGS_TYPE rtems_interrupt_level + +#endif + +/*#define STATIC*/ +#define STATIC static + +/*#define GRSPW_DBG(args...) printk(args)*/ +#define GRSPW_DBG(args...) + +struct grspw_dma_regs { + volatile unsigned int ctrl; /* DMA Channel Control */ + volatile unsigned int rxmax; /* RX Max Packet Length */ + volatile unsigned int txdesc; /* TX Descriptor Base/Current */ + volatile unsigned int rxdesc; /* RX Descriptor Base/Current */ + volatile unsigned int addr; /* Address Register */ + volatile unsigned int resv[3]; +}; + +struct grspw_regs { + volatile unsigned int ctrl; + volatile unsigned int status; + volatile unsigned int nodeaddr; + volatile unsigned int clkdiv; + volatile unsigned int destkey; + volatile unsigned int time; + volatile unsigned int timer; /* Used only in GRSPW1 */ + volatile unsigned int resv1; + + /* DMA Registers, ctrl.NCH determines number of ports, + * up to 4 channels are supported + */ + struct grspw_dma_regs dma[4]; +}; + +/* GRSPW - Control Register - 0x00 */ +#define GRSPW_CTRL_RA_BIT 31 +#define GRSPW_CTRL_RX_BIT 30 +#define GRSPW_CTRL_RC_BIT 29 +#define GRSPW_CTRL_NCH_BIT 27 +#define GRSPW_CTRL_PO_BIT 26 +#define GRSPW_CTRL_PS_BIT 21 +#define GRSPW_CTRL_NP_BIT 20 +#define GRSPW_CTRL_RD_BIT 17 +#define GRSPW_CTRL_RE_BIT 16 +#define GRSPW_CTRL_TR_BIT 11 +#define GRSPW_CTRL_TT_BIT 10 +#define GRSPW_CTRL_LI_BIT 9 +#define GRSPW_CTRL_TQ_BIT 8 +#define GRSPW_CTRL_RS_BIT 6 +#define GRSPW_CTRL_PM_BIT 5 +#define GRSPW_CTRL_TI_BIT 4 +#define GRSPW_CTRL_IE_BIT 3 +#define GRSPW_CTRL_AS_BIT 2 +#define GRSPW_CTRL_LS_BIT 1 +#define GRSPW_CTRL_LD_BIT 0 + +#define GRSPW_CTRL_RA (1<<GRSPW_CTRL_RA_BIT) +#define GRSPW_CTRL_RX (1<<GRSPW_CTRL_RX_BIT) +#define GRSPW_CTRL_RC (1<<GRSPW_CTRL_RC_BIT) +#define GRSPW_CTRL_NCH (0x3<<GRSPW_CTRL_NCH_BIT) +#define GRSPW_CTRL_PO (1<<GRSPW_CTRL_PO_BIT) +#define GRSPW_CTRL_PS (1<<GRSPW_CTRL_PS_BIT) +#define GRSPW_CTRL_NP (1<<GRSPW_CTRL_NP_BIT) +#define GRSPW_CTRL_RD (1<<GRSPW_CTRL_RD_BIT) +#define GRSPW_CTRL_RE (1<<GRSPW_CTRL_RE_BIT) +#define GRSPW_CTRL_TR (1<<GRSPW_CTRL_TR_BIT) +#define GRSPW_CTRL_TT (1<<GRSPW_CTRL_TT_BIT) +#define GRSPW_CTRL_LI (1<<GRSPW_CTRL_LI_BIT) +#define GRSPW_CTRL_TQ (1<<GRSPW_CTRL_TQ_BIT) +#define GRSPW_CTRL_RS (1<<GRSPW_CTRL_RS_BIT) +#define GRSPW_CTRL_PM (1<<GRSPW_CTRL_PM_BIT) +#define GRSPW_CTRL_TI (1<<GRSPW_CTRL_TI_BIT) +#define GRSPW_CTRL_IE (1<<GRSPW_CTRL_IE_BIT) +#define GRSPW_CTRL_AS (1<<GRSPW_CTRL_AS_BIT) +#define GRSPW_CTRL_LS (1<<GRSPW_CTRL_LS_BIT) +#define GRSPW_CTRL_LD (1<<GRSPW_CTRL_LD_BIT) + +/* GRSPW - Status Register - 0x04 */ +#define GRSPW_STS_LS_BIT 21 +#define GRSPW_STS_AP_BIT 9 +#define GRSPW_STS_EE_BIT 8 +#define GRSPW_STS_IA_BIT 7 +#define GRSPW_STS_WE_BIT 6 +#define GRSPW_STS_PE_BIT 4 +#define GRSPW_STS_DE_BIT 3 +#define GRSPW_STS_ER_BIT 2 +#define GRSPW_STS_CE_BIT 1 +#define GRSPW_STS_TO_BIT 0 + +#define GRSPW_STS_LS (0x7<<GRSPW_STS_LS_BIT) +#define GRSPW_STS_AP (1<<GRSPW_STS_AP_BIT) +#define GRSPW_STS_EE (1<<GRSPW_STS_EE_BIT) +#define GRSPW_STS_IA (1<<GRSPW_STS_IA_BIT) +#define GRSPW_STS_WE (1<<GRSPW_STS_WE_BIT) +#define GRSPW_STS_PE (1<<GRSPW_STS_PE_BIT) +#define GRSPW_STS_DE (1<<GRSPW_STS_DE_BIT) +#define GRSPW_STS_ER (1<<GRSPW_STS_ER_BIT) +#define GRSPW_STS_CE (1<<GRSPW_STS_CE_BIT) +#define GRSPW_STS_TO (1<<GRSPW_STS_TO_BIT) + +/* GRSPW - Default Address Register - 0x08 */ +#define GRSPW_DEF_ADDR_BIT 0 +#define GRSPW_DEF_MASK_BIT 8 +#define GRSPW_DEF_ADDR (0xff<<GRSPW_DEF_ADDR_BIT) +#define GRSPW_DEF_MASK (0xff<<GRSPW_DEF_MASK_BIT) + +/* GRSPW - Clock Divisor Register - 0x0C */ +#define GRSPW_CLKDIV_START_BIT 8 +#define GRSPW_CLKDIV_RUN_BIT 0 +#define GRSPW_CLKDIV_START (0xff<<GRSPW_CLKDIV_START_BIT) +#define GRSPW_CLKDIV_RUN (0xff<<GRSPW_CLKDIV_RUN_BIT) +#define GRSPW_CLKDIV_MASK (GRSPW_CLKDIV_START|GRSPW_CLKDIV_RUN) + +/* GRSPW - Destination key Register - 0x10 */ +#define GRSPW_DK_DESTKEY_BIT 0 +#define GRSPW_DK_DESTKEY (0xff<<GRSPW_DK_DESTKEY_BIT) + +/* GRSPW - Time Register - 0x14 */ +#define GRSPW_TIME_CTRL_BIT 0 +#define GRSPW_TIME_CNT_BIT 6 +#define GRSPW_TIME_CTRL (0x3f<<GRSPW_TIME_CTRL_BIT) +#define GRSPW_TIME_TCNT (0x3<<GRSPW_TIME_CNT_BIT) + +/* GRSPW - DMA Control Register - 0x20*N */ +#define GRSPW_DMACTRL_LE_BIT 16 +#define GRSPW_DMACTRL_SP_BIT 15 +#define GRSPW_DMACTRL_SA_BIT 14 +#define GRSPW_DMACTRL_EN_BIT 13 +#define GRSPW_DMACTRL_NS_BIT 12 +#define GRSPW_DMACTRL_RD_BIT 11 +#define GRSPW_DMACTRL_RX_BIT 10 +#define GRSPW_DMACTRL_AT_BIT 9 +#define GRSPW_DMACTRL_RA_BIT 8 +#define GRSPW_DMACTRL_TA_BIT 7 +#define GRSPW_DMACTRL_PR_BIT 6 +#define GRSPW_DMACTRL_PS_BIT 5 +#define GRSPW_DMACTRL_AI_BIT 4 +#define GRSPW_DMACTRL_RI_BIT 3 +#define GRSPW_DMACTRL_TI_BIT 2 +#define GRSPW_DMACTRL_RE_BIT 1 +#define GRSPW_DMACTRL_TE_BIT 0 + +#define GRSPW_DMACTRL_LE (1<<GRSPW_DMACTRL_LE_BIT) +#define GRSPW_DMACTRL_SP (1<<GRSPW_DMACTRL_SP_BIT) +#define GRSPW_DMACTRL_SA (1<<GRSPW_DMACTRL_SA_BIT) +#define GRSPW_DMACTRL_EN (1<<GRSPW_DMACTRL_EN_BIT) +#define GRSPW_DMACTRL_NS (1<<GRSPW_DMACTRL_NS_BIT) +#define GRSPW_DMACTRL_RD (1<<GRSPW_DMACTRL_RD_BIT) +#define GRSPW_DMACTRL_RX (1<<GRSPW_DMACTRL_RX_BIT) +#define GRSPW_DMACTRL_AT (1<<GRSPW_DMACTRL_AT_BIT) +#define GRSPW_DMACTRL_RA (1<<GRSPW_DMACTRL_RA_BIT) +#define GRSPW_DMACTRL_TA (1<<GRSPW_DMACTRL_TA_BIT) +#define GRSPW_DMACTRL_PR (1<<GRSPW_DMACTRL_PR_BIT) +#define GRSPW_DMACTRL_PS (1<<GRSPW_DMACTRL_PS_BIT) +#define GRSPW_DMACTRL_AI (1<<GRSPW_DMACTRL_AI_BIT) +#define GRSPW_DMACTRL_RI (1<<GRSPW_DMACTRL_RI_BIT) +#define GRSPW_DMACTRL_TI (1<<GRSPW_DMACTRL_TI_BIT) +#define GRSPW_DMACTRL_RE (1<<GRSPW_DMACTRL_RE_BIT) +#define GRSPW_DMACTRL_TE (1<<GRSPW_DMACTRL_TE_BIT) + +/* GRSPW - DMA Channel Max Packet Length Register - (0x20*N + 0x04) */ +#define GRSPW_DMARXLEN_MAX_BIT 0 +#define GRSPW_DMARXLEN_MAX (0xffffff<<GRSPW_DMARXLEN_MAX_BIT) + +/* GRSPW - DMA Channel Address Register - (0x20*N + 0x10) */ +#define GRSPW_DMAADR_ADDR_BIT 0 +#define GRSPW_DMAADR_MASK_BIT 8 +#define GRSPW_DMAADR_ADDR (0xff<<GRSPW_DMAADR_ADDR_BIT) +#define GRSPW_DMAADR_MASK (0xff<<GRSPW_DMAADR_MASK_BIT) + +/* RX Buffer Descriptor */ +struct grspw_rxbd { + volatile unsigned int ctrl; + volatile unsigned int addr; +}; + +/* TX Buffer Descriptor */ +struct grspw_txbd { + volatile unsigned int ctrl; + volatile unsigned int haddr; + volatile unsigned int dlen; + volatile unsigned int daddr; +}; + +/* GRSPW - DMA RXBD Ctrl */ +#define GRSPW_RXBD_LEN_BIT 0 +#define GRSPW_RXBD_LEN (0x1ffffff<<GRSPW_RXBD_LEN_BIT) +#define GRSPW_RXBD_EN (1<<25) +#define GRSPW_RXBD_WR (1<<26) +#define GRSPW_RXBD_IE (1<<27) +#define GRSPW_RXBD_EP (1<<28) +#define GRSPW_RXBD_HC (1<<29) +#define GRSPW_RXBD_DC (1<<30) +#define GRSPW_RXBD_TR (1<<31) + +#define GRSPW_TXBD_HLEN (0xff<<0) +#define GRSPW_TXBD_NCL (0xf<<8) +#define GRSPW_TXBD_EN (1<<12) +#define GRSPW_TXBD_WR (1<<13) +#define GRSPW_TXBD_IE (1<<14) +#define GRSPW_TXBD_LE (1<<15) +#define GRSPW_TXBD_HC (1<<16) +#define GRSPW_TXBD_DC (1<<17) + +#define GRSPW_DMAADR_MASK_BIT 8 +#define GRSPW_DMAADR_ADDR (0xff<<GRSPW_DMAADR_ADDR_BIT) +#define GRSPW_DMAADR_MASK (0xff<<GRSPW_DMAADR_MASK_BIT) + + +/* GRSPW Error Condition */ +#define GRSPW_STAT_ERROR (GRSPW_STS_EE | GRSPW_STS_IA | GRSPW_STS_WE | GRSPW_STS_PE | GRSPW_STS_DE | GRSPW_STS_ER | GRSPW_STS_CE) +#define GRSPW_DMA_STATUS_ERROR (GRSPW_DMACTRL_RA | GRSPW_DMACTRL_TA) +/* GRSPW Link configuration options */ +#define GRSPW_LINK_CFG (GRSPW_CTRL_LI | GRSPW_CTRL_LD | GRSPW_CTRL_LS | GRSPW_CTRL_AS) +#define GRSPW_LINKSTATE(status) ((status & GRSPW_CTRL_LS) >> GRSPW_CTRL_LS_BIT) + +/* Software Defaults */ +#define DEFAULT_RXMAX 1024 /* 1 KBytes Max RX Packet Size */ + +/* GRSPW Constants */ +#define GRSPW_TXBD_NR 64 /* Maximum number of TX Descriptors */ +#define GRSPW_RXBD_NR 128 /* Maximum number of RX Descriptors */ +#define BDTAB_SIZE 0x400 /* BD Table Size (RX or TX) */ +#define BDTAB_ALIGN 0x400 /* BD Table Alignment Requirement */ + +/* Memory and HW Registers Access routines. All 32-bit access routines */ +#define BD_WRITE(addr, val) (*(volatile unsigned int *)(addr) = (unsigned int)(val)) +/*#define BD_READ(addr) (*(volatile unsigned int *)(addr))*/ +#define BD_READ(addr) leon_r32_no_cache((unsigned long)(addr)) +#define REG_WRITE(addr, val) (*(volatile unsigned int *)(addr) = (unsigned int)(val)) +#define REG_READ(addr) (*(volatile unsigned int *)(addr)) + +struct grspw_ring { + struct grspw_ring *next; /* Next Descriptor */ + union { + struct grspw_txbd *tx; /* Descriptor Address */ + struct grspw_rxbd *rx; /* Descriptor Address */ + } bd; + struct grspw_pkt *pkt; /* Packet description associated.NULL if none*/ +}; + +/* An entry in the TX descriptor Ring */ +struct grspw_txring { + struct grspw_txring *next; /* Next Descriptor */ + struct grspw_txbd *bd; /* Descriptor Address */ + struct grspw_pkt *pkt; /* Packet description associated.NULL if none*/ +}; + +/* An entry in the RX descriptor Ring */ +struct grspw_rxring { + struct grspw_rxring *next; /* Next Descriptor */ + struct grspw_rxbd *bd; /* Descriptor Address */ + struct grspw_pkt *pkt; /* Packet description associated.NULL if none*/ +}; + + +struct grspw_dma_priv { + struct grspw_priv *core; /* GRSPW Core */ + struct grspw_dma_regs *regs; /* DMA Channel Registers */ + int index; /* DMA Channel Index @ GRSPW core */ + int open; /* DMA Channel opened by user */ + int started; /* DMA Channel activity (start|stop) */ + rtems_id sem_dma; /* DMA Channel Semaphore */ + struct grspw_dma_stats stats; /* DMA Channel Statistics */ + struct grspw_dma_config cfg; /* DMA Channel Configuration */ + + /*** RX ***/ + + /* RX Descriptor Ring */ + struct grspw_rxbd *rx_bds; /* Descriptor Address */ + struct grspw_rxbd *rx_bds_hwa; /* Descriptor HW Address */ + struct grspw_rxring *rx_ring_base; + struct grspw_rxring *rx_ring_head; /* Next descriptor to enable */ + struct grspw_rxring *rx_ring_tail; /* Oldest enabled Descriptor */ + int rx_irq_en_cnt_curr; + struct { + int waiting; + int ready_cnt; + int op; + int recv_cnt; + rtems_id sem_wait; /* RX Semaphore used to implement RX blocking */ + } rx_wait; + + /* Queue of Packets READY to be scheduled */ + struct grspw_list ready; + int ready_cnt; + + /* Scheduled RX Packets Queue */ + struct grspw_list rx_sched; + int rx_sched_cnt; + + /* Queue of Packets that has been RECIEVED */ + struct grspw_list recv; + int recv_cnt; + + + /*** TX ***/ + + /* TX Descriptor Ring */ + struct grspw_txbd *tx_bds; /* Descriptor Address */ + struct grspw_txbd *tx_bds_hwa; /* Descriptor HW Address */ + struct grspw_txring *tx_ring_base; + struct grspw_txring *tx_ring_head; + struct grspw_txring *tx_ring_tail; + int tx_irq_en_cnt_curr; + struct { + int waiting; + int send_cnt; + int op; + int sent_cnt; + rtems_id sem_wait; /* TX Semaphore used to implement TX blocking */ + } tx_wait; + + /* Queue of Packets ready to be scheduled for transmission */ + struct grspw_list send; + int send_cnt; + + /* Scheduled TX Packets Queue */ + struct grspw_list tx_sched; + int tx_sched_cnt; + + /* Queue of Packets that has been SENT */ + struct grspw_list sent; + int sent_cnt; +}; + +struct grspw_priv { + char devname[8]; /* Device name "grspw%d" */ + struct drvmgr_dev *dev; /* Device */ + struct grspw_regs *regs; /* Virtual Address of APB Registers */ + int irq; /* AMBA IRQ number of core */ + int index; /* Index in order it was probed */ + int core_index; /* Core Bus Index */ + int open; /* If Device is alrady opened (=1) or not (=0) */ + void *data; /* User private Data for this device instance, set by grspw_initialize_user */ + + /* Features supported by Hardware */ + struct grspw_hw_sup hwsup; + + /* Pointer to an array of Maximally 4 DMA Channels */ + struct grspw_dma_priv *dma; + + /* Spin-lock ISR protection */ + SPIN_DECLARE(devlock); + + /* Descriptor Memory Area for TX & RX and all DMA channels */ + unsigned int bd_mem; + unsigned int bd_mem_alloced; + + /*** Time Code Handling ***/ + void (*tcisr)(void *data, int timecode); + void *tcisr_arg; + + /* Disable Link on SpW Link error */ + int dis_link_on_err; + + /* "Core Global" Statistics gathered, not dependent on DMA channel */ + struct grspw_core_stats stats; +}; + +int grspw_initialized = 0; +int grspw_count = 0; +struct workqueue_struct *grspw_workq = NULL; +rtems_id grspw_sem; +static struct grspw_priv *priv_tab[GRSPW_MAX]; + +/* callback to upper layer when devices are discovered/removed */ +void *(*grspw_dev_add)(int) = NULL; +void (*grspw_dev_del)(int,void*) = NULL; + +/* USER OVERRIDABLE - The work task priority. Set to -1 to disable creating + * the work-task and work-queue to save space. + */ +int grspw_work_task_priority __attribute__((weak)) = 100; +int grspw_task_stop = 0; +rtems_id grspw_work_task; +rtems_id grspw_work_queue = 0; +#define WORK_NONE 0 +#define WORK_SHUTDOWN 0x100 +#define WORK_DMA(channel) (0x1 << (channel)) +#define WORK_DMA_MASK 0xf /* max 4 channels */ +#define WORK_CORE_BIT 16 +#define WORK_CORE_MASK 0xffff +#define WORK_CORE(device) ((device) << WORK_CORE_BIT) + +STATIC void grspw_hw_stop(struct grspw_priv *priv); +STATIC void grspw_hw_dma_stop(struct grspw_dma_priv *dma); +STATIC void grspw_dma_reset(struct grspw_dma_priv *dma); +STATIC void grspw_dma_stop_locked(struct grspw_dma_priv *dma); +STATIC void grspw_isr(void *data); + +void *grspw_open(int dev_no) +{ + struct grspw_priv *priv; + unsigned int bdtabsize, hwa; + int i; + union drvmgr_key_value *value; + + if (grspw_initialized != 1 || (dev_no >= grspw_count)) + return NULL; + + priv = priv_tab[dev_no]; + + /* Take GRSPW lock - Wait until we get semaphore */ + if (rtems_semaphore_obtain(grspw_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT) + != RTEMS_SUCCESSFUL) + return NULL; + + if (priv->open) { + priv = NULL; + goto out; + } + + /* Initialize Spin-lock for GRSPW Device. This is to protect + * CTRL and DMACTRL registers from ISR. + */ + SPIN_INIT(&priv->devlock); + + priv->tcisr = NULL; + priv->tcisr_arg = NULL; + + grspw_stats_clr(priv); + + /* Allocate TX & RX Descriptor memory area for all DMA + * channels. Max-size descriptor area is allocated (or user assigned): + * - 128 RX descriptors per DMA Channel + * - 64 TX descriptors per DMA Channel + */ + value = drvmgr_dev_key_get(priv->dev, "bdDmaArea", KEY_TYPE_INT); + if (value) { + priv->bd_mem = value->i; + priv->bd_mem_alloced = 0; + if (priv->bd_mem & (BDTAB_ALIGN-1)) { + GRSPW_DBG("GRSPW[%d]: user-def DMA-area not aligned", + priv->index); + priv = NULL; + goto out; + } + } else { + bdtabsize = 2 * BDTAB_SIZE * priv->hwsup.ndma_chans; + priv->bd_mem_alloced = (unsigned int)malloc(bdtabsize + BDTAB_ALIGN - 1); + if (priv->bd_mem_alloced == 0) { + priv = NULL; + goto out; + } + /* Align memory */ + priv->bd_mem = (priv->bd_mem_alloced + (BDTAB_ALIGN - 1)) & + ~(BDTAB_ALIGN-1); + } + + /* Translate into DMA address that HW can use to access DMA + * descriptors + */ + drvmgr_translate(priv->dev, 0, 0, (void *)priv->bd_mem, (void **)&hwa); + + GRSPW_DBG("GRSPW%d DMA descriptor table setup: (alloced:%p, bd_mem:%p, size: %d)\n", + priv->index, priv->bd_mem_alloced, priv->bd_mem, bdtabsize + BDTAB_ALIGN - 1); + for (i=0; i<priv->hwsup.ndma_chans; i++) { + /* Do DMA Channel Init, other variables etc. are inited + * when respective DMA channel is opened. + * + * index & core are initialized by probe function. + */ + priv->dma[i].open = 0; + priv->dma[i].rx_bds = (struct grspw_rxbd *) + (priv->bd_mem + i*BDTAB_SIZE*2); + priv->dma[i].rx_bds_hwa = (struct grspw_rxbd *) + (hwa + BDTAB_SIZE*(2*i)); + priv->dma[i].tx_bds = (struct grspw_txbd *) + (priv->bd_mem + BDTAB_SIZE*(2*i+1)); + priv->dma[i].tx_bds_hwa = (struct grspw_txbd *) + (hwa + BDTAB_SIZE*(2*i+1)); + GRSPW_DBG(" DMA[%i]: RX %p - %p (%p - %p) TX %p - %p (%p - %p)\n", + i, + priv->dma[i].rx_bds, (void *)priv->dma[i].rx_bds + BDTAB_SIZE - 1, + priv->dma[i].rx_bds_hwa, (void *)priv->dma[i].rx_bds_hwa + BDTAB_SIZE - 1, + priv->dma[i].tx_bds, (void *)priv->dma[i].tx_bds + BDTAB_SIZE - 1, + priv->dma[i].tx_bds_hwa, (void *)priv->dma[i].tx_bds_hwa + BDTAB_SIZE - 1); + } + + /* Basic initialization of hardware, clear some registers but + * keep Link/RMAP/Node-Address registers intact. + */ + grspw_hw_stop(priv); + + /* Register Interrupt handler and enable IRQ at IRQ ctrl */ + drvmgr_interrupt_register(priv->dev, 0, priv->devname, grspw_isr, priv); + + /* Take the device */ + priv->open = 1; +out: + rtems_semaphore_release(grspw_sem); + return priv; +} + +void grspw_close(void *d) +{ + struct grspw_priv *priv = d; + int i; + + /* Take GRSPW lock - Wait until we get semaphore */ + if (rtems_semaphore_obtain(grspw_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT) + != RTEMS_SUCCESSFUL) + return; + + /* Stop Hardware from doing DMA, put HW into "startup-state", + * Stop hardware from generating IRQ. + */ + for (i=0; i<priv->hwsup.ndma_chans; i++) + grspw_dma_close(&priv->dma[i]); + grspw_hw_stop(priv); + + /* Mark not open */ + priv->open = 0; + + rtems_semaphore_release(grspw_sem); + + /* Check that all threads are out? */ +} + +void grspw_hw_support(void *d, struct grspw_hw_sup *hw) +{ + struct grspw_priv *priv = d; + + *hw = priv->hwsup; +} + +void grspw_addr_ctrl(void *d, struct grspw_addr_config *cfg) +{ + struct grspw_priv *priv = d; + struct grspw_regs *regs = priv->regs; + unsigned int ctrl, nodeaddr; + IRQFLAGS_TYPE irqflags; + int i; + + if (!priv || !cfg) + return; + + SPIN_LOCK_IRQ(&priv->devlock, irqflags); + + if (cfg->promiscuous != -1) { + /* Set Configuration */ + ctrl = REG_READ(®s->ctrl); + if (cfg->promiscuous) + ctrl |= GRSPW_CTRL_PM; + else + ctrl &= ~GRSPW_CTRL_PM; + REG_WRITE(®s->ctrl, ctrl); + REG_WRITE(®s->nodeaddr, (cfg->def_mask<<8) | cfg->def_addr); + + for (i=0; i<priv->hwsup.ndma_chans; i++) { + ctrl = REG_READ(®s->dma[i].ctrl); + ctrl &= ~(GRSPW_DMACTRL_PS|GRSPW_DMACTRL_PR|GRSPW_DMA_STATUS_ERROR); + if (cfg->dma_nacfg[i].node_en) { + ctrl |= GRSPW_DMACTRL_EN; + REG_WRITE(®s->dma[i].addr, + (cfg->dma_nacfg[i].node_addr & 0xff) | + ((cfg->dma_nacfg[i].node_mask & 0xff)<<8)); + } else { + ctrl &= ~GRSPW_DMACTRL_EN; + } + REG_WRITE(®s->dma[i].ctrl, ctrl); + } + } + + /* Read Current Configuration */ + cfg->promiscuous = REG_READ(®s->ctrl) & GRSPW_CTRL_PM; + nodeaddr = REG_READ(®s->nodeaddr); + cfg->def_addr = (nodeaddr & GRSPW_DEF_ADDR) >> GRSPW_DEF_ADDR_BIT; + cfg->def_mask = (nodeaddr & GRSPW_DEF_MASK) >> GRSPW_DEF_MASK_BIT; + for (i=0; i<priv->hwsup.ndma_chans; i++) { + cfg->dma_nacfg[i].node_en = REG_READ(®s->dma[i].ctrl) & + GRSPW_DMACTRL_EN; + ctrl = REG_READ(®s->dma[i].addr); + cfg->dma_nacfg[i].node_addr = (ctrl & GRSPW_DMAADR_ADDR) >> + GRSPW_DMAADR_ADDR_BIT; + cfg->dma_nacfg[i].node_mask = (ctrl & GRSPW_DMAADR_MASK) >> + GRSPW_DMAADR_MASK_BIT; + } + SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); + for (; i<4; i++) { + cfg->dma_nacfg[i].node_en = 0; + cfg->dma_nacfg[i].node_addr = 0; + cfg->dma_nacfg[i].node_mask = 0; + } +} + +/* Return Current Status Register */ +unsigned int grspw_link_status(void *d) +{ + struct grspw_priv *priv = d; + + return REG_READ(&priv->regs->status); +} + +/* Return Current Link State */ +spw_link_state_t grspw_link_state(void *d) +{ + struct grspw_priv *priv = d; + unsigned int status = REG_READ(&priv->regs->status); + + return (status & GRSPW_STS_LS) >> GRSPW_STS_LS_BIT; +} + +/* options and clkdiv [in/out]: set to -1 to only read current config */ +void grspw_link_ctrl(void *d, int *options, int *clkdiv) +{ + struct grspw_priv *priv = d; + struct grspw_regs *regs = priv->regs; + unsigned int ctrl; + IRQFLAGS_TYPE irqflags; + + /* Write? */ + if (clkdiv) { + if (*clkdiv != -1) + REG_WRITE(®s->clkdiv, *clkdiv & GRSPW_CLKDIV_MASK); + *clkdiv = REG_READ(®s->clkdiv) & GRSPW_CLKDIV_MASK; + } + if (options) { + SPIN_LOCK_IRQ(&priv->devlock, irqflags); + ctrl = REG_READ(®s->ctrl); + if (*options != -1) { + ctrl = (ctrl & ~GRSPW_LINK_CFG) | + (*options & GRSPW_LINK_CFG); + + /* Enable Global IRQ only of LI or TQ is set */ + if (ctrl & (GRSPW_CTRL_LI|GRSPW_CTRL_TQ)) + ctrl |= GRSPW_CTRL_IE; + else + ctrl &= ~GRSPW_CTRL_IE; + + REG_WRITE(®s->ctrl, ctrl); + priv->dis_link_on_err = (*options & LINKOPTS_DIS_ONERR) >> 3; + } + SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); + *options = (ctrl & GRSPW_LINK_CFG)|(priv->dis_link_on_err << 3); + } +} + +/* Generate Tick-In (increment Time Counter, Send Time Code) */ +void grspw_tc_tx(void *d) +{ + struct grspw_priv *priv = d; + struct grspw_regs *regs = priv->regs; + IRQFLAGS_TYPE irqflags; + + SPIN_LOCK_IRQ(&priv->devlock, irqflags); + REG_WRITE(®s->ctrl, REG_READ(®s->ctrl) | GRSPW_CTRL_TI); + SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); +} + +void grspw_tc_ctrl(void *d, int *options) +{ + struct grspw_priv *priv = d; + struct grspw_regs *regs = priv->regs; + unsigned int ctrl; + IRQFLAGS_TYPE irqflags; + + if (options == NULL) + return; + + /* Write? */ + if (*options != -1) { + SPIN_LOCK_IRQ(&priv->devlock, irqflags); + ctrl = REG_READ(®s->ctrl); + ctrl &= ~(GRSPW_CTRL_TR|GRSPW_CTRL_TT|GRSPW_CTRL_TQ); + ctrl |= (*options & 0xd) << GRSPW_CTRL_TQ_BIT; + + /* Enable Global IRQ only of LI or TQ is set */ + if (ctrl & (GRSPW_CTRL_LI|GRSPW_CTRL_TQ)) + ctrl |= GRSPW_CTRL_IE; + else + ctrl &= ~GRSPW_CTRL_IE; + + REG_WRITE(®s->ctrl, ctrl); + SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); + } else + ctrl = REG_READ(®s->ctrl); + *options = (ctrl >> GRSPW_CTRL_TQ_BIT) & 0xd; +} + +/* Assign ISR Function to TimeCode RX IRQ */ +void grspw_tc_isr(void *d, void (*tcisr)(void *data, int tc), void *data) +{ + struct grspw_priv *priv = d; + + priv->tcisr_arg = data; + priv->tcisr = tcisr; +} + +/* Read/Write TCTRL and TIMECNT. Write if not -1, always read current value + * TCTRL = bits 7 and 6 + * TIMECNT = bits 5 to 0 + */ +void grspw_tc_time(void *d, int *time) +{ + struct grspw_priv *priv = d; + struct grspw_regs *regs = priv->regs; + + if (time == NULL) + return; + if (*time != -1) + REG_WRITE(®s->time, *time & (GRSPW_TIME_TCNT | GRSPW_TIME_CTRL)); + *time = REG_READ(®s->time) & (GRSPW_TIME_TCNT | GRSPW_TIME_CTRL); +} + +/* Set (not -1) and/or read RMAP options. */ +int grspw_rmap_ctrl(void *d, int *options, int *dstkey) +{ + struct grspw_priv *priv = d; + struct grspw_regs *regs = priv->regs; + unsigned int ctrl; + IRQFLAGS_TYPE irqflags; + + if (dstkey) { + if (*dstkey != -1) + REG_WRITE(®s->destkey, *dstkey & GRSPW_DK_DESTKEY); + *dstkey = REG_READ(®s->destkey) & GRSPW_DK_DESTKEY; + } + if (options) { + if (*options != -1) { + if ((*options & RMAPOPTS_EN_RMAP) && !priv->hwsup.rmap) + return -1; + + + SPIN_LOCK_IRQ(&priv->devlock, irqflags); + ctrl = REG_READ(®s->ctrl); + ctrl &= ~(GRSPW_CTRL_RE|GRSPW_CTRL_RD); + ctrl |= (*options & 0x3) << GRSPW_CTRL_RE_BIT; + REG_WRITE(®s->ctrl, ctrl); + SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); + } + *options = (REG_READ(®s->ctrl) >> GRSPW_CTRL_RE_BIT) & 0x3; + } + + return 0; +} + +void grspw_rmap_support(void *d, char *rmap, char *rmap_crc) +{ + struct grspw_priv *priv = d; + + if (rmap) + *rmap = priv->hwsup.rmap; + if (rmap_crc) + *rmap_crc = priv->hwsup.rmap_crc; +} + +/* Select port, if + * -1=The current selected port is returned + * 0=Port 0 + * 1=Port 1 + * Others=Both Port0 and Port1 + */ +int grspw_port_ctrl(void *d, int *port) +{ + struct grspw_priv *priv = d; + struct grspw_regs *regs = priv->regs; + unsigned int ctrl; + IRQFLAGS_TYPE irqflags; + + if (port == NULL) + return -1; + + if ((*port == 1) || (*port == 0)) { + /* Select port user selected */ + if ((*port == 1) && (priv->hwsup.nports < 2)) + return -1; /* Changing to Port 1, but only one port available */ + SPIN_LOCK_IRQ(&priv->devlock, irqflags); + ctrl = REG_READ(®s->ctrl); + ctrl &= ~(GRSPW_CTRL_NP | GRSPW_CTRL_PS); + ctrl |= (*port & 1) << GRSPW_CTRL_PS_BIT; + REG_WRITE(®s->ctrl, ctrl); + SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); + } else if (*port > 1) { + /* Select both ports */ + SPIN_LOCK_IRQ(&priv->devlock, irqflags); + REG_WRITE(®s->ctrl, REG_READ(®s->ctrl) | GRSPW_CTRL_NP); + SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); + } + + /* Get current settings */ + ctrl = REG_READ(®s->ctrl); + if (ctrl & GRSPW_CTRL_NP) { + /* Any port, selected by hardware */ + if (priv->hwsup.nports > 1) + *port = 3; + else + *port = 0; /* Port0 the only port available */ + } else { + *port = (ctrl & GRSPW_CTRL_PS) >> GRSPW_CTRL_PS_BIT; + } + + return 0; +} + +/* Returns Number ports available in hardware */ +int grspw_port_count(void *d) +{ + struct grspw_priv *priv = d; + + return priv->hwsup.nports; +} + +/* Current active port: 0 or 1 */ +int grspw_port_active(void *d) +{ + struct grspw_priv *priv = d; + unsigned int status; + + status = REG_READ(&priv->regs->status); + + return (status & GRSPW_STS_AP) >> GRSPW_STS_AP_BIT; +} + +void grspw_stats_read(void *d, struct grspw_core_stats *sts) +{ + struct grspw_priv *priv = d; + + if (sts == NULL) + return; + memcpy(sts, &priv->stats, sizeof(priv->stats)); +} + +void grspw_stats_clr(void *d) +{ + struct grspw_priv *priv = d; + + /* Clear most of the statistics */ + memset(&priv->stats, 0, sizeof(priv->stats)); +} + +/*** DMA Interface ***/ + +/* Initialize the RX and TX Descriptor Ring, empty of packets */ +STATIC void grspw_bdrings_init(struct grspw_dma_priv *dma) +{ + struct grspw_ring *r; + int i; + + /* Empty BD rings */ + dma->rx_ring_head = dma->rx_ring_base; + dma->rx_ring_tail = dma->rx_ring_base; + dma->tx_ring_head = dma->tx_ring_base; + dma->tx_ring_tail = dma->tx_ring_base; + + /* Init RX Descriptors */ + r = (struct grspw_ring *)dma->rx_ring_base; + for (i=0; i<GRSPW_RXBD_NR; i++) { + + /* Init Ring Entry */ + r[i].next = &r[i+1]; + r[i].bd.rx = &dma->rx_bds[i]; + r[i].pkt = NULL; + + /* Init HW Descriptor */ + BD_WRITE(&r[i].bd.rx->ctrl, 0); + BD_WRITE(&r[i].bd.rx->addr, 0); + } + r[GRSPW_RXBD_NR-1].next = &r[0]; + + /* Init TX Descriptors */ + r = (struct grspw_ring *)dma->tx_ring_base; + for (i=0; i<GRSPW_TXBD_NR; i++) { + + /* Init Ring Entry */ + r[i].next = &r[i+1]; + r[i].bd.tx = &dma->tx_bds[i]; + r[i].pkt = NULL; + + /* Init HW Descriptor */ + BD_WRITE(&r[i].bd.tx->ctrl, 0); + BD_WRITE(&r[i].bd.tx->haddr, 0); + BD_WRITE(&r[i].bd.tx->dlen, 0); + BD_WRITE(&r[i].bd.tx->daddr, 0); + } + r[GRSPW_TXBD_NR-1].next = &r[0]; +} + +/* Try to populate descriptor ring with as many as possible READY unused packet + * buffers. The packets assigned with to a descriptor are put in the end of + * the scheduled list. + * + * The number of Packets scheduled is returned. + * + * - READY List -> RX-SCHED List + * - Descriptors are initialized and enabled for reception + */ +STATIC int grspw_rx_schedule_ready(struct grspw_dma_priv *dma) +{ + int cnt; + unsigned int ctrl, dmactrl; + void *hwaddr; + struct grspw_rxring *curr_bd; + struct grspw_pkt *curr_pkt, *last_pkt; + struct grspw_list lst; + IRQFLAGS_TYPE irqflags; + + /* Is Ready Q empty? */ + if (grspw_list_is_empty(&dma->ready)) + return 0; + + cnt = 0; + lst.head = curr_pkt = dma->ready.head; + curr_bd = dma->rx_ring_head; + while (!curr_bd->pkt) { + + /* Assign Packet to descriptor */ + curr_bd->pkt = curr_pkt; + + /* Prepare descriptor address. */ + hwaddr = curr_pkt->data; + if (curr_pkt->flags & PKT_FLAG_TR_DATA) { + drvmgr_translate(dma->core->dev, 0, 0, hwaddr, &hwaddr); + if (curr_pkt->data == hwaddr) /* translation needed? */ + curr_pkt->flags &= ~PKT_FLAG_TR_DATA; + } + BD_WRITE(&curr_bd->bd->addr, hwaddr); + + ctrl = GRSPW_RXBD_EN; + if (curr_bd->next == dma->rx_ring_base) { + /* Wrap around (only needed when smaller descriptor + * table) + */ + ctrl |= GRSPW_RXBD_WR; + } + + /* Is this Packet going to be an interrupt Packet? */ + if ((--dma->rx_irq_en_cnt_curr) <= 0) { + if (dma->cfg.rx_irq_en_cnt == 0) { + /* IRQ is disabled. A big number to avoid + * equal to zero too often + */ + dma->rx_irq_en_cnt_curr = 0x3fffffff; + } else { + dma->rx_irq_en_cnt_curr = dma->cfg.rx_irq_en_cnt; + ctrl |= GRSPW_RXBD_IE; + } + } + + if (curr_pkt->flags & RXPKT_FLAG_IE) + ctrl |= GRSPW_RXBD_IE; + + /* Enable descriptor */ + BD_WRITE(&curr_bd->bd->ctrl, ctrl); + + last_pkt = curr_pkt; + curr_bd = curr_bd->next; + cnt++; + + /* Get Next Packet from Ready Queue */ + if (curr_pkt == dma->ready.tail) { + /* Handled all in ready queue. */ + curr_pkt = NULL; + break; + } + curr_pkt = curr_pkt->next; + } + + /* Has Packets been scheduled? */ + if (cnt > 0) { + /* Prepare list for insertion/deleation */ + lst.tail = last_pkt; + + /* Remove scheduled packets from ready queue */ + grspw_list_remove_head_list(&dma->ready, &lst); + dma->ready_cnt -= cnt; + if (dma->stats.ready_cnt_min > dma->ready_cnt) + dma->stats.ready_cnt_min = dma->ready_cnt; + + /* Insert scheduled packets into scheduled queue */ + grspw_list_append_list(&dma->rx_sched, &lst); + dma->rx_sched_cnt += cnt; + if (dma->stats.rx_sched_cnt_max < dma->rx_sched_cnt) + dma->stats.rx_sched_cnt_max = dma->rx_sched_cnt; + + /* Update TX ring posistion */ + dma->rx_ring_head = curr_bd; + + /* Make hardware aware of the newly enabled descriptors + * We must protect from ISR which writes RI|TI + */ + SPIN_LOCK_IRQ(&dma->core->devlock, irqflags); + dmactrl = REG_READ(&dma->regs->ctrl); + dmactrl &= ~(GRSPW_DMACTRL_PS|GRSPW_DMACTRL_PR|GRSPW_DMA_STATUS_ERROR); + dmactrl |= GRSPW_DMACTRL_RE | GRSPW_DMACTRL_RD; + REG_WRITE(&dma->regs->ctrl, dmactrl); + SPIN_UNLOCK_IRQ(&dma->core->devlock, irqflags); + } + + return cnt; +} + +/* Scans the RX desciptor table for scheduled Packet that has been received, + * and moves these Packet from the head of the scheduled queue to the + * tail of the recv queue. + * + * Also, for all packets the status is updated. + * + * - SCHED List -> SENT List + * + * Return Value + * Number of packets moved + */ +STATIC int grspw_rx_process_scheduled(struct grspw_dma_priv *dma) +{ + struct grspw_rxring *curr; + struct grspw_pkt *last_pkt; + int recv_pkt_cnt = 0; + unsigned int ctrl; + struct grspw_list lst; + + curr = dma->rx_ring_tail; + + /* Step into RX ring to find if packets have been scheduled for + * reception. + */ + if (!curr->pkt) + return 0; /* No scheduled packets, thus no received, abort */ + + /* There has been Packets scheduled ==> scheduled Packets may have been + * received and needs to be collected into RECV List. + * + * A temporary list "lst" with all received packets is created. + */ + lst.head = curr->pkt; + + /* Loop until first enabled "unrecveived" SpW Packet is found. + * An unused descriptor is indicated by an unassigned pkt field. + */ + while (curr->pkt && !((ctrl=BD_READ(&curr->bd->ctrl)) & GRSPW_RXBD_EN)) { + /* Handle one received Packet */ + + /* Remember last handled Packet so that insertion/removal from + * Packet lists go fast. + */ + last_pkt = curr->pkt; + + /* Get Length of Packet in bytes, and reception options */ + last_pkt->dlen = (ctrl & GRSPW_RXBD_LEN) >> GRSPW_RXBD_LEN_BIT; + + /* Set flags to indicate error(s) and CRC information, + * and Mark Received. + */ + last_pkt->flags = (last_pkt->flags & ~RXPKT_FLAG_OUTPUT_MASK) | + ((ctrl >> 20) & RXPKT_FLAG_OUTPUT_MASK) | + RXPKT_FLAG_RX; + + /* Packet was Truncated? */ + if (ctrl & GRSPW_RXBD_TR) + dma->stats.rx_err_trunk++; + + /* Error End-Of-Packet? */ + if (ctrl & GRSPW_RXBD_EP) + dma->stats.rx_err_endpkt++; + curr->pkt = NULL; /* Mark descriptor unused */ + + /* Increment */ + curr = curr->next; + recv_pkt_cnt++; + } + + /* 1. Remove all handled packets from scheduled queue + * 2. Put all handled packets into recv queue + */ + if (recv_pkt_cnt > 0) { + + /* Update Stats, Number of Received Packets */ + dma->stats.rx_pkts += recv_pkt_cnt; + + /* Save RX ring posistion */ + dma->rx_ring_tail = curr; + + /* Prepare list for insertion/deleation */ + lst.tail = last_pkt; + + /* Remove received Packets from RX-SCHED queue */ + grspw_list_remove_head_list(&dma->rx_sched, &lst); + dma->rx_sched_cnt -= recv_pkt_cnt; + if (dma->stats.rx_sched_cnt_min > dma->rx_sched_cnt) + dma->stats.rx_sched_cnt_min = dma->rx_sched_cnt; + + /* Insert received Packets into RECV queue */ + grspw_list_append_list(&dma->recv, &lst); + dma->recv_cnt += recv_pkt_cnt; + if (dma->stats.recv_cnt_max < dma->recv_cnt) + dma->stats.recv_cnt_max = dma->recv_cnt; + } + + return recv_pkt_cnt; +} + +/* Try to populate descriptor ring with as many SEND packets as possible. The + * packets assigned with to a descriptor are put in the end of + * the scheduled list. + * + * The number of Packets scheduled is returned. + * + * - SEND List -> TX-SCHED List + * - Descriptors are initialized and enabled for transmission + */ +STATIC int grspw_tx_schedule_send(struct grspw_dma_priv *dma) +{ + int cnt; + unsigned int ctrl, dmactrl; + void *hwaddr; + struct grspw_txring *curr_bd; + struct grspw_pkt *curr_pkt, *last_pkt; + struct grspw_list lst; + IRQFLAGS_TYPE irqflags; + + /* Is Ready Q empty? */ + if (grspw_list_is_empty(&dma->send)) + return 0; + + cnt = 0; + lst.head = curr_pkt = dma->send.head; + curr_bd = dma->tx_ring_head; + while (!curr_bd->pkt) { + + /* Assign Packet to descriptor */ + curr_bd->pkt = curr_pkt; + + /* Set up header transmission */ + if (curr_pkt->hdr && curr_pkt->hlen) { + hwaddr = curr_pkt->hdr; + if (curr_pkt->flags & PKT_FLAG_TR_HDR) { + drvmgr_translate(dma->core->dev, 0, 0, hwaddr, &hwaddr); + /* translation needed? */ + if (curr_pkt->hdr == hwaddr) + curr_pkt->flags &= ~PKT_FLAG_TR_HDR; + } + BD_WRITE(&curr_bd->bd->haddr, hwaddr); + ctrl = GRSPW_TXBD_EN | curr_pkt->hlen; + } else { + ctrl = GRSPW_TXBD_EN; + } + /* Enable IRQ generation and CRC options as specified + * by user. + */ + ctrl |= (curr_pkt->flags & TXPKT_FLAG_INPUT_MASK) << 8; + + if (curr_bd->next == dma->tx_ring_base) { + /* Wrap around (only needed when smaller descriptor table) */ + ctrl |= GRSPW_TXBD_WR; + } + + /* Is this Packet going to be an interrupt Packet? */ + if ((--dma->tx_irq_en_cnt_curr) <= 0) { + if (dma->cfg.tx_irq_en_cnt == 0) { + /* IRQ is disabled. + * A big number to avoid equal to zero too often + */ + dma->tx_irq_en_cnt_curr = 0x3fffffff; + } else { + dma->tx_irq_en_cnt_curr = dma->cfg.tx_irq_en_cnt; + ctrl |= GRSPW_TXBD_IE; + } + } + + /* Prepare descriptor address. Parts of CTRL is written to + * DLEN for debug-only (CTRL is cleared by HW). + */ + if (curr_pkt->data && curr_pkt->dlen) { + hwaddr = curr_pkt->data; + if (curr_pkt->flags & PKT_FLAG_TR_DATA) { + drvmgr_translate(dma->core->dev, 0, 0, hwaddr, &hwaddr); + /* translation needed? */ + if (curr_pkt->data == hwaddr) + curr_pkt->flags &= ~PKT_FLAG_TR_DATA; + } + BD_WRITE(&curr_bd->bd->daddr, hwaddr); + BD_WRITE(&curr_bd->bd->dlen, curr_pkt->dlen | + ((ctrl & 0x3f000) << 12)); + } else { + BD_WRITE(&curr_bd->bd->daddr, 0); + BD_WRITE(&curr_bd->bd->dlen, ((ctrl & 0x3f000) << 12)); + } + + /* Enable descriptor */ + BD_WRITE(&curr_bd->bd->ctrl, ctrl); + + last_pkt = curr_pkt; + curr_bd = curr_bd->next; + cnt++; + + /* Get Next Packet from Ready Queue */ + if (curr_pkt == dma->send.tail) { + /* Handled all in ready queue. */ + curr_pkt = NULL; + break; + } + curr_pkt = curr_pkt->next; + } + + /* Have Packets been scheduled? */ + if (cnt > 0) { + /* Prepare list for insertion/deleation */ + lst.tail = last_pkt; + + /* Remove scheduled packets from ready queue */ + grspw_list_remove_head_list(&dma->send, &lst); + dma->send_cnt -= cnt; + if (dma->stats.send_cnt_min > dma->send_cnt) + dma->stats.send_cnt_min = dma->send_cnt; + + /* Insert scheduled packets into scheduled queue */ + grspw_list_append_list(&dma->tx_sched, &lst); + dma->tx_sched_cnt += cnt; + if (dma->stats.tx_sched_cnt_max < dma->tx_sched_cnt) + dma->stats.tx_sched_cnt_max = dma->tx_sched_cnt; + + /* Update TX ring posistion */ + dma->tx_ring_head = curr_bd; + + /* Make hardware aware of the newly enabled descriptors */ + SPIN_LOCK_IRQ(&dma->core->devlock, irqflags); + dmactrl = REG_READ(&dma->regs->ctrl); + dmactrl &= ~(GRSPW_DMACTRL_PS|GRSPW_DMACTRL_PR|GRSPW_DMA_STATUS_ERROR); + dmactrl |= GRSPW_DMACTRL_TE; + REG_WRITE(&dma->regs->ctrl, dmactrl); + SPIN_UNLOCK_IRQ(&dma->core->devlock, irqflags); + } + return cnt; +} + +/* Scans the TX desciptor table for transmitted packets, and moves these + * packets from the head of the scheduled queue to the tail of the sent queue. + * + * Also, for all packets the status is updated. + * + * - SCHED List -> SENT List + * + * Return Value + * Number of packet moved + */ +STATIC int grspw_tx_process_scheduled(struct grspw_dma_priv *dma) +{ + struct grspw_txring *curr; + struct grspw_pkt *last_pkt; + int sent_pkt_cnt = 0; + unsigned int ctrl; + struct grspw_list lst; + + curr = dma->tx_ring_tail; + + /* Step into TX ring to find if packets have been scheduled for + * transmission. + */ + if (!curr->pkt) + return 0; /* No scheduled packets, thus no sent, abort */ + + /* There has been Packets scheduled ==> scheduled Packets may have been + * transmitted and needs to be collected into SENT List. + * + * A temporary list "lst" with all sent packets is created. + */ + lst.head = curr->pkt; + + /* Loop until first enabled "un-transmitted" SpW Packet is found. + * An unused descriptor is indicated by an unassigned pkt field. + */ + while (curr->pkt && !((ctrl=BD_READ(&curr->bd->ctrl)) & GRSPW_TXBD_EN)) { + /* Handle one sent Packet */ + + /* Remember last handled Packet so that insertion/removal from + * packet lists go fast. + */ + last_pkt = curr->pkt; + + /* Set flags to indicate error(s) and Mark Sent. + */ + last_pkt->flags = (last_pkt->flags & ~TXPKT_FLAG_OUTPUT_MASK) | + (ctrl & TXPKT_FLAG_LINKERR) | + TXPKT_FLAG_TX; + + /* Sent packet experienced link error? */ + if (ctrl & GRSPW_TXBD_LE) + dma->stats.tx_err_link++; + + curr->pkt = NULL; /* Mark descriptor unused */ + + /* Increment */ + curr = curr->next; + sent_pkt_cnt++; + } + + /* 1. Remove all handled packets from TX-SCHED queue + * 2. Put all handled packets into SENT queue + */ + if (sent_pkt_cnt > 0) { + /* Update Stats, Number of Transmitted Packets */ + dma->stats.tx_pkts += sent_pkt_cnt; + + /* Save TX ring posistion */ + dma->tx_ring_tail = curr; + + /* Prepare list for insertion/deleation */ + lst.tail = last_pkt; + + /* Remove sent packets from TX-SCHED queue */ + grspw_list_remove_head_list(&dma->tx_sched, &lst); + dma->tx_sched_cnt -= sent_pkt_cnt; + if (dma->stats.tx_sched_cnt_min > dma->tx_sched_cnt) + dma->stats.tx_sched_cnt_min = dma->tx_sched_cnt; + + /* Insert received packets into SENT queue */ + grspw_list_append_list(&dma->sent, &lst); + dma->sent_cnt += sent_pkt_cnt; + if (dma->stats.sent_cnt_max < dma->sent_cnt) + dma->stats.sent_cnt_max = dma->sent_cnt; + } + + return sent_pkt_cnt; +} + +void *grspw_dma_open(void *d, int chan_no) +{ + struct grspw_priv *priv = d; + struct grspw_dma_priv *dma; + int size; + + if ((chan_no < 0) && (priv->hwsup.ndma_chans <= chan_no)) + return NULL; + + dma = &priv->dma[chan_no]; + + /* Take GRSPW lock */ + if (rtems_semaphore_obtain(grspw_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT) + != RTEMS_SUCCESSFUL) + return NULL; + + if (dma->open) { + dma = NULL; + goto out; + } + + dma->started = 0; + + /* Set Default Configuration: + * + * - MAX RX Packet Length = + * - Disable IRQ generation + * - + */ + dma->cfg.rxmaxlen = DEFAULT_RXMAX; + dma->cfg.rx_irq_en_cnt = 0; + dma->cfg.tx_irq_en_cnt = 0; + dma->cfg.flags = DMAFLAG_NO_SPILL; + + /* DMA Channel Semaphore created with count = 1 */ + if (rtems_semaphore_create( + rtems_build_name('S', 'D', '0' + priv->index, '0' + chan_no), 1, + RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | \ + RTEMS_NO_INHERIT_PRIORITY | RTEMS_LOCAL | \ + RTEMS_NO_PRIORITY_CEILING, 0, &dma->sem_dma) != RTEMS_SUCCESSFUL) { + dma = NULL; + goto out; + } + + /* Allocate memory for the two descriptor rings */ + size = sizeof(struct grspw_ring) * (GRSPW_RXBD_NR + GRSPW_TXBD_NR); + dma->rx_ring_base = (struct grspw_rxring *)malloc(size); + dma->tx_ring_base = (struct grspw_txring *)&dma->rx_ring_base[GRSPW_RXBD_NR]; + if (dma->rx_ring_base == NULL) { + dma = NULL; + goto out; + } + + /* Create DMA RX and TX Channel sempahore with count = 0 */ + if (rtems_semaphore_create( + rtems_build_name('S', 'R', '0' + priv->index, '0' + chan_no), 0, + RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | \ + RTEMS_NO_INHERIT_PRIORITY | RTEMS_LOCAL | \ + RTEMS_NO_PRIORITY_CEILING, 0, &dma->rx_wait.sem_wait) != RTEMS_SUCCESSFUL) { + dma = NULL; + goto out; + } + if (rtems_semaphore_create( + rtems_build_name('S', 'T', '0' + priv->index, '0' + chan_no), 0, + RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | \ + RTEMS_NO_INHERIT_PRIORITY | RTEMS_LOCAL | \ + RTEMS_NO_PRIORITY_CEILING, 0, &dma->tx_wait.sem_wait) != RTEMS_SUCCESSFUL) { + dma = NULL; + goto out; + } + + /* Reset software structures */ + grspw_dma_reset(dma); + + /* Take the device */ + dma->open = 1; +out: + /* Return GRSPW Lock */ + rtems_semaphore_release(grspw_sem); + + return dma; +} + +/* Initialize Software Structures: + * - Clear all Queues + * - init BD ring + * - init IRQ counter + * - clear statistics counters + * - init wait structures and semaphores + */ +STATIC void grspw_dma_reset(struct grspw_dma_priv *dma) +{ + /* Empty RX and TX queues */ + grspw_list_clr(&dma->ready); + grspw_list_clr(&dma->rx_sched); + grspw_list_clr(&dma->recv); + grspw_list_clr(&dma->send); + grspw_list_clr(&dma->tx_sched); + grspw_list_clr(&dma->sent); + dma->ready_cnt = 0; + dma->rx_sched_cnt = 0; + dma->recv_cnt = 0; + dma->send_cnt = 0; + dma->tx_sched_cnt = 0; + dma->sent_cnt = 0; + + dma->rx_irq_en_cnt_curr = 0; + dma->tx_irq_en_cnt_curr = 0; + + grspw_bdrings_init(dma); + + dma->rx_wait.waiting = 0; + dma->tx_wait.waiting = 0; + + grspw_dma_stats_clr(dma); +} + +void grspw_dma_close(void *c) +{ + struct grspw_dma_priv *dma = c; + + if (!dma->open) + return; + + /* Take device lock - Wait until we get semaphore */ + if (rtems_semaphore_obtain(dma->sem_dma, RTEMS_WAIT, RTEMS_NO_TIMEOUT) + != RTEMS_SUCCESSFUL) + return; + + grspw_dma_stop_locked(dma); + + /* Free resources */ + rtems_semaphore_delete(dma->rx_wait.sem_wait); + rtems_semaphore_delete(dma->tx_wait.sem_wait); + rtems_semaphore_delete(dma->sem_dma); /* Release and delete lock */ + + /* Free memory */ + if (dma->rx_ring_base) + free(dma->rx_ring_base); + dma->rx_ring_base = NULL; + dma->tx_ring_base = NULL; + + dma->open = 0; +} + +/* Schedule List of packets for transmission at some point in + * future. + * + * 1. Move transmitted packets to SENT List (SCHED->SENT) + * 2. Add the requested packets to the SEND List (USER->SEND) + * 3. Schedule as many packets as possible (SEND->SCHED) + */ +int grspw_dma_tx_send(void *c, int opts, struct grspw_list *pkts, int count) +{ + struct grspw_dma_priv *dma = c; + int ret; + + /* Take DMA channel lock */ + if (rtems_semaphore_obtain(dma->sem_dma, RTEMS_WAIT, RTEMS_NO_TIMEOUT) + != RTEMS_SUCCESSFUL) + return -1; + + if (dma->started == 0) { + ret = 1; /* signal DMA has been stopped */ + goto out; + } + ret = 0; + + /* 1. Move transmitted packets to SENT List (SCHED->SENT) */ + if ((opts & 1) == 0) + grspw_tx_process_scheduled(dma); + + /* 2. Add the requested packets to the SEND List (USER->SEND) */ + if (pkts) { + grspw_list_append_list(&dma->send, pkts); + dma->send_cnt += count; + if (dma->stats.send_cnt_max < dma->send_cnt) + dma->stats.send_cnt_max = dma->send_cnt; + } + + /* 3. Schedule as many packets as possible (SEND->SCHED) */ + if ((opts & 2) == 0) + grspw_tx_schedule_send(dma); + +out: + /* Unlock DMA channel */ + rtems_semaphore_release(dma->sem_dma); + + return ret; +} + +int grspw_dma_tx_reclaim(void *c, int opts, struct grspw_list *pkts, int *count) +{ + struct grspw_dma_priv *dma = c; + struct grspw_pkt *pkt, *lastpkt; + int cnt, started; + + /* Take DMA channel lock */ + if (rtems_semaphore_obtain(dma->sem_dma, RTEMS_WAIT, RTEMS_NO_TIMEOUT) + != RTEMS_SUCCESSFUL) + return -1; + + /* 1. Move transmitted packets to SENT List (SCHED->SENT) */ + started = dma->started; + if ((started > 0) && ((opts & 1) == 0)) + grspw_tx_process_scheduled(dma); + + /* Move all/count SENT packet to the callers list (SENT->USER) */ + if (pkts) { + if ((count == NULL) || (*count == -1) || + (*count >= dma->sent_cnt)) { + /* Move all SENT Packets */ + *pkts = dma->sent; + grspw_list_clr(&dma->sent); + if (count) + *count = dma->sent_cnt; + dma->sent_cnt = 0; + } else { + /* Move a number of SENT Packets */ + pkts->head = pkt = lastpkt = dma->sent.head; + cnt = 0; + while (cnt < *count) { + lastpkt = pkt; + pkt = pkt->next; + cnt++; + } + if (cnt > 0) { + pkts->tail = lastpkt; + grspw_list_remove_head_list(&dma->sent, pkts); + dma->sent_cnt -= cnt; + } else { + grspw_list_clr(pkts); + } + } + } else if (count) { + *count = 0; + } + + /* 3. Schedule as many packets as possible (SEND->SCHED) */ + if ((started > 0 ) && ((opts & 2) == 0)) + grspw_tx_schedule_send(dma); + + /* Unlock DMA channel */ + rtems_semaphore_release(dma->sem_dma); + + return (~started) & 1; /* signal DMA has been stopped */ +} + +void grspw_dma_tx_count(void *c, int *send, int *sched, int *sent) +{ + struct grspw_dma_priv *dma = c; + + if (send) + *send = dma->send_cnt; + if (sched) + *sched = dma->tx_sched_cnt; + if (sent) + *sent = dma->sent_cnt; +} + +static inline int grspw_tx_wait_eval(struct grspw_dma_priv *dma) +{ + int send_val, sent_val; + + if (dma->tx_wait.send_cnt >= (dma->send_cnt + dma->tx_sched_cnt)) + send_val = 1; + else + send_val = 0; + + if (dma->tx_wait.sent_cnt <= dma->sent_cnt) + sent_val = 1; + else + sent_val = 0; + + /* AND or OR ? */ + if (dma->tx_wait.op == 0) + return send_val & sent_val; /* AND */ + else + return send_val | sent_val; /* OR */ +} + +/* Block until send_cnt or fewer packets are Queued in "Send and Scheduled" Q, + * op (AND or OR), sent_cnt or more packet "have been sent" (Sent Q) condition + * is met. + * If a link error occurs and the Stop on Link error is defined, this function + * will also return to caller. + */ +int grspw_dma_tx_wait(void *c, int send_cnt, int op, int sent_cnt, int timeout) +{ + struct grspw_dma_priv *dma = c; + int ret, rc; + + if (timeout == 0) + timeout = RTEMS_NO_TIMEOUT; + +check_condition: + + /* Take DMA channel lock */ + if (rtems_semaphore_obtain(dma->sem_dma, RTEMS_WAIT, RTEMS_NO_TIMEOUT) + != RTEMS_SUCCESSFUL) + return -1; + + /* Check so that no other thread is waiting, this driver only supports + * one waiter at a time. + */ + if (dma->tx_wait.waiting) { + ret = -1; + goto out; + } + + /* Stop if link error or similar, abort */ + if (dma->started == 0) { + ret = 1; + goto out; + } + + /* Set up Condition */ + dma->tx_wait.send_cnt = send_cnt; + dma->tx_wait.op = op; + dma->tx_wait.sent_cnt = sent_cnt; + + if (grspw_tx_wait_eval(dma) == 0) { + /* Prepare Wait */ + dma->tx_wait.waiting = 1; + + /* Release DMA channel lock */ + rtems_semaphore_release(dma->sem_dma); + + /* Try to take Wait lock, if this fail link may have gone down + * or user stopped this DMA channel + */ + rc = rtems_semaphore_obtain(dma->tx_wait.sem_wait, RTEMS_WAIT, + timeout); + if (rc == RTEMS_TIMEOUT) { + dma->tx_wait.waiting = 0; + return 2; + } else if (rc == RTEMS_UNSATISFIED || + rc == RTEMS_OBJECT_WAS_DELETED) { + dma->tx_wait.waiting = 0; + return 1; /* sem was flushed/deleted, means DMA stop */ + } else if (rc != RTEMS_SUCCESSFUL) + return -1; + + /* Check condition once more */ + goto check_condition; + } else { + /* No Wait needed */ + dma->tx_wait.waiting = 0; + } + + ret = 0; +out: + /* Unlock DMA channel */ + rtems_semaphore_release(dma->sem_dma); + + return ret; +} + +int grspw_dma_rx_recv(void *c, int opts, struct grspw_list *pkts, int *count) +{ + struct grspw_dma_priv *dma = c; + struct grspw_pkt *pkt, *lastpkt; + int cnt, started; + + /* Take DMA channel lock */ + if (rtems_semaphore_obtain(dma->sem_dma, RTEMS_WAIT, RTEMS_NO_TIMEOUT) + != RTEMS_SUCCESSFUL) + return -1; + + /* 1. Move Scheduled packets to RECV List (SCHED->RECV) */ + started = dma->started; + if (((opts & 1) == 0) && (started > 0)) + grspw_rx_process_scheduled(dma); + + /* Move all RECV packet to the callers list */ + if (pkts) { + if ((count == NULL) || (*count == -1) || + (*count >= dma->recv_cnt)) { + /* Move all Received packets */ + *pkts = dma->recv; + grspw_list_clr(&dma->recv); + if ( count ) + *count = dma->recv_cnt; + dma->recv_cnt = 0; + } else { + /* Move a number of RECV Packets */ + pkts->head = pkt = lastpkt = dma->recv.head; + cnt = 0; + while (cnt < *count) { + lastpkt = pkt; + pkt = pkt->next; + cnt++; + } + if (cnt > 0) { + pkts->tail = lastpkt; + grspw_list_remove_head_list(&dma->recv, pkts); + dma->recv_cnt -= cnt; + } else { + grspw_list_clr(pkts); + } + } + } else if (count) { + *count = 0; + } + + /* 3. Schedule as many free packet buffers as possible (READY->SCHED) */ + if (((opts & 2) == 0) && (started > 0)) + grspw_rx_schedule_ready(dma); + + /* Unlock DMA channel */ + rtems_semaphore_release(dma->sem_dma); + + return (~started) & 1; +} + +int grspw_dma_rx_prepare(void *c, int opts, struct grspw_list *pkts, int count) +{ + struct grspw_dma_priv *dma = c; + int ret; + + /* Take DMA channel lock */ + if (rtems_semaphore_obtain(dma->sem_dma, RTEMS_WAIT, RTEMS_NO_TIMEOUT) + != RTEMS_SUCCESSFUL) + return -1; + + if (dma->started == 0) { + ret = 1; + goto out; + } + + /* 1. Move Received packets to RECV List (SCHED->RECV) */ + if ((opts & 1) == 0) + grspw_rx_process_scheduled(dma); + + /* 2. Add the "free/ready" packet buffers to the READY List (USER->READY) */ + if (pkts && (count > 0)) { + grspw_list_append_list(&dma->ready, pkts); + dma->ready_cnt += count; + if (dma->stats.ready_cnt_max < dma->ready_cnt) + dma->stats.ready_cnt_max = dma->ready_cnt; + } + + /* 3. Schedule as many packets as possible (READY->SCHED) */ + if ((opts & 2) == 0) + grspw_rx_schedule_ready(dma); + + ret = 0; +out: + /* Unlock DMA channel */ + rtems_semaphore_release(dma->sem_dma); + + return ret; +} + +void grspw_dma_rx_count(void *c, int *ready, int *sched, int *recv) +{ + struct grspw_dma_priv *dma = c; + + if (ready) + *ready = dma->ready_cnt; + if (sched) + *sched = dma->rx_sched_cnt; + if (recv) + *recv = dma->recv_cnt; +} + +static inline int grspw_rx_wait_eval(struct grspw_dma_priv *dma) +{ + int ready_val, recv_val; + + if (dma->rx_wait.ready_cnt >= (dma->ready_cnt + dma->rx_sched_cnt)) + ready_val = 1; + else + ready_val = 0; + + if (dma->rx_wait.recv_cnt <= dma->recv_cnt) + recv_val = 1; + else + recv_val = 0; + + /* AND or OR ? */ + if (dma->rx_wait.op == 0) + return ready_val & recv_val; /* AND */ + else + return ready_val | recv_val; /* OR */ +} + +/* Block until recv_cnt or more packets are Queued in RECV Q, op (AND or OR), + * ready_cnt or fewer packet buffers are available in the "READY and Scheduled" Q, + * condition is met. + * If a link error occurs and the Stop on Link error is defined, this function + * will also return to caller, however with an error. + */ +int grspw_dma_rx_wait(void *c, int recv_cnt, int op, int ready_cnt, int timeout) +{ + struct grspw_dma_priv *dma = c; + int ret, rc; + + if (timeout == 0) + timeout = RTEMS_NO_TIMEOUT; + +check_condition: + + /* Take DMA channel lock */ + if (rtems_semaphore_obtain(dma->sem_dma, RTEMS_WAIT, RTEMS_NO_TIMEOUT) + != RTEMS_SUCCESSFUL) + return -1; + + /* Check so that no other thread is waiting, this driver only supports + * one waiter at a time. + */ + if (dma->rx_wait.waiting) { + ret = -1; + goto out; + } + + /* Stop if link error or similar (MDA stopped) */ + if (dma->started == 0) { + ret = 1; + goto out; + } + + /* Set up Condition */ + dma->rx_wait.recv_cnt = recv_cnt; + dma->rx_wait.op = op; + dma->rx_wait.ready_cnt = ready_cnt; + + if (grspw_rx_wait_eval(dma) == 0) { + /* Prepare Wait */ + dma->rx_wait.waiting = 1; + + /* Release channel lock */ + rtems_semaphore_release(dma->sem_dma); + + /* Try to take Wait lock, if this fail link may have gone down + * or user stopped this DMA channel + */ + rc = rtems_semaphore_obtain(dma->rx_wait.sem_wait, RTEMS_WAIT, + timeout); + if (rc == RTEMS_TIMEOUT) { + dma->rx_wait.waiting = 0; + return 2; + } else if (rc == RTEMS_UNSATISFIED || + rc == RTEMS_OBJECT_WAS_DELETED) { + dma->rx_wait.waiting = 0; + return 1; /* sem was flushed/deleted, means DMA stop */ + } else if (rc != RTEMS_SUCCESSFUL) + return -1; + + /* Check condition once more */ + goto check_condition; + } else { + /* No Wait needed */ + dma->rx_wait.waiting = 0; + } + ret = 0; + +out: + /* Unlock DMA channel */ + rtems_semaphore_release(dma->sem_dma); + + return ret; +} + +int grspw_dma_config(void *c, struct grspw_dma_config *cfg) +{ + struct grspw_dma_priv *dma = c; + + if (dma->started || !cfg) + return -1; + + if (cfg->flags & ~DMAFLAG_MASK) + return -1; + + /* Update Configuration */ + memcpy(&dma->cfg, cfg, sizeof(*cfg)); + + return 0; +} + +void grspw_dma_config_read(void *c, struct grspw_dma_config *cfg) +{ + struct grspw_dma_priv *dma = c; + + /* Copy Current Configuration */ + memcpy(cfg, &dma->cfg, sizeof(*cfg)); +} + +void grspw_dma_stats_read(void *c, struct grspw_dma_stats *sts) +{ + struct grspw_dma_priv *dma = c; + + memcpy(sts, &dma->stats, sizeof(dma->stats)); +} + +void grspw_dma_stats_clr(void *c) +{ + struct grspw_dma_priv *dma = c; + + /* Clear most of the statistics */ + memset(&dma->stats, 0, sizeof(dma->stats)); + + /* Init proper default values so that comparisons will work the + * first time. + */ + dma->stats.send_cnt_min = 0x3fffffff; + dma->stats.tx_sched_cnt_min = 0x3fffffff; + dma->stats.ready_cnt_min = 0x3fffffff; + dma->stats.rx_sched_cnt_min = 0x3fffffff; +} + +int grspw_dma_start(void *c) +{ + struct grspw_dma_priv *dma = c; + struct grspw_dma_regs *dregs = dma->regs; + unsigned int ctrl; + + if (dma->started) + return 0; + + /* Initialize Software Structures: + * - Clear all Queues + * - init BD ring + * - init IRQ counter + * - clear statistics counters + * - init wait structures and semaphores + */ + grspw_dma_reset(dma); + + /* RX&RD and TX is not enabled until user fills SEND and READY Queue + * with SpaceWire Packet buffers. So we do not have to worry about + * IRQs for this channel just yet. However other DMA channels + * may be active. + * + * Some functionality that is not changed during started mode is set up + * once and for all here: + * + * - RX MAX Packet length + * - TX Descriptor base address to first BD in TX ring (not enabled) + * - RX Descriptor base address to first BD in RX ring (not enabled) + * - IRQs (TX DMA, RX DMA, DMA ERROR) + * - Strip PID + * - Strip Address + * - No Spill + * - Receiver Enable + * - disable on link error (LE) + * + * Note that the address register and the address enable bit in DMACTRL + * register must be left untouched, they are configured on a GRSPW + * core level. + * + * Note that the receiver is enabled here, but since descriptors are + * not enabled the GRSPW core may stop/pause RX (if NS bit set) until + * descriptors are enabled or it may ignore RX packets (NS=0) until + * descriptors are enabled (writing RD bit). + */ + REG_WRITE(&dregs->txdesc, dma->tx_bds_hwa); + REG_WRITE(&dregs->rxdesc, dma->rx_bds_hwa); + + /* MAX Packet length */ + REG_WRITE(&dma->regs->rxmax, dma->cfg.rxmaxlen); + + ctrl = GRSPW_DMACTRL_AI | GRSPW_DMACTRL_PS | GRSPW_DMACTRL_PR | + GRSPW_DMACTRL_TA | GRSPW_DMACTRL_RA | GRSPW_DMACTRL_RE | + (dma->cfg.flags & DMAFLAG_MASK) << GRSPW_DMACTRL_NS_BIT; + if (dma->core->dis_link_on_err) + ctrl |= GRSPW_DMACTRL_LE; + if (dma->cfg.rx_irq_en_cnt != 0) + ctrl |= GRSPW_DMACTRL_RI; + if (dma->cfg.tx_irq_en_cnt != 0) + ctrl |= GRSPW_DMACTRL_TI; + REG_WRITE(&dregs->ctrl, ctrl); + + dma->started = 1; /* open up other DMA interfaces */ + + return 0; +} + +STATIC void grspw_dma_stop_locked(struct grspw_dma_priv *dma) +{ + IRQFLAGS_TYPE irqflags; + + if (dma->started == 0) + return; + dma->started = 0; + + SPIN_LOCK_IRQ(&priv->devlock, irqflags); + grspw_hw_dma_stop(dma); + SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); + + /* From here no more packets will be sent, however + * there may still exist scheduled packets that has been + * sent, and packets in the SEND Queue waiting for free + * descriptors. All packets are moved to the SENT Queue + * so that the user may get its buffers back, the user + * must look at the TXPKT_FLAG_TX in order to determine + * if the packet was sent or not. + */ + + /* Retreive scheduled all sent packets */ + grspw_tx_process_scheduled(dma); + + /* Move un-sent packets in SEND and SCHED queue to the + * SENT Queue. (never marked sent) + */ + if (!grspw_list_is_empty(&dma->tx_sched)) { + grspw_list_append_list(&dma->sent, &dma->tx_sched); + grspw_list_clr(&dma->tx_sched); + dma->sent_cnt += dma->tx_sched_cnt; + dma->tx_sched_cnt = 0; + } + if (!grspw_list_is_empty(&dma->send)) { + grspw_list_append_list(&dma->sent, &dma->send); + grspw_list_clr(&dma->send); + dma->sent_cnt += dma->send_cnt; + dma->send_cnt = 0; + } + + /* Similar for RX */ + grspw_rx_process_scheduled(dma); + if (!grspw_list_is_empty(&dma->rx_sched)) { + grspw_list_append_list(&dma->recv, &dma->rx_sched); + grspw_list_clr(&dma->rx_sched); + dma->recv_cnt += dma->rx_sched_cnt; + dma->rx_sched_cnt = 0; + } + if (!grspw_list_is_empty(&dma->ready)) { + grspw_list_append_list(&dma->recv, &dma->ready); + grspw_list_clr(&dma->ready); + dma->recv_cnt += dma->ready_cnt; + dma->ready_cnt = 0; + } + + /* Throw out blocked threads */ + rtems_semaphore_flush(dma->rx_wait.sem_wait); + rtems_semaphore_flush(dma->tx_wait.sem_wait); +} + +void grspw_dma_stop(void *c) +{ + struct grspw_dma_priv *dma = c; + + /* Take DMA Channel lock */ + if (rtems_semaphore_obtain(dma->sem_dma, RTEMS_WAIT, RTEMS_NO_TIMEOUT) + != RTEMS_SUCCESSFUL) + return; + + grspw_dma_stop_locked(dma); + + rtems_semaphore_release(dma->sem_dma); +} + +/* Do general work, invoked indirectly from ISR */ +static void grspw_work_shutdown_func(struct grspw_priv *priv) +{ + int i; + + /* Link is down for some reason, and the user has configured + * that we stop all DMA channels and throw out all blocked + * threads. + */ + for (i=0; i<priv->hwsup.ndma_chans; i++) + grspw_dma_stop(&priv->dma[i]); + grspw_hw_stop(priv); +} + +/* Do DMA work on one channel, invoked indirectly from ISR */ +static void grspw_work_dma_func(struct grspw_dma_priv *dma) +{ + int tx_cond_true, rx_cond_true; + unsigned int ctrl; + IRQFLAGS_TYPE irqflags; + + rx_cond_true = 0; + tx_cond_true = 0; + dma->stats.irq_cnt++; + + /* Take DMA channel lock */ + if (rtems_semaphore_obtain(dma->sem_dma, RTEMS_WAIT, RTEMS_NO_TIMEOUT) + != RTEMS_SUCCESSFUL) + return; + + /* Look at cause we were woken up and clear source */ + SPIN_LOCK_IRQ(&priv->devlock, irqflags); + ctrl = REG_READ(&dma->regs->ctrl); + + /* Read/Write DMA error ? */ + if (ctrl & GRSPW_DMA_STATUS_ERROR) { + /* DMA error -> Stop DMA channel (both RX and TX) */ + SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); + grspw_dma_stop_locked(dma); + } else if (ctrl & (GRSPW_DMACTRL_PR | GRSPW_DMACTRL_PS)) { + /* DMA has finished a TX/RX packet */ + ctrl &= ~GRSPW_DMACTRL_AT; + if (dma->cfg.rx_irq_en_cnt != 0) + ctrl |= GRSPW_DMACTRL_RI; + if (dma->cfg.tx_irq_en_cnt != 0) + ctrl |= GRSPW_DMACTRL_TI; + REG_WRITE(&dma->regs->ctrl, ctrl); + SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); + if (ctrl & GRSPW_DMACTRL_PR) { + /* Do RX Work */ + dma->stats.rx_work_cnt++; + grspw_rx_process_scheduled(dma); + dma->stats.rx_work_enabled += grspw_rx_schedule_ready(dma); + /* Check to see if condition for waking blocked USER + * task is fullfilled. + */ + if (dma->rx_wait.waiting) { + rx_cond_true = grspw_rx_wait_eval(dma); + if (rx_cond_true) + dma->rx_wait.waiting = 0; + } + } + if (ctrl & GRSPW_DMACTRL_PS) { + /* Do TX Work */ + dma->stats.tx_work_cnt++; + grspw_tx_process_scheduled(dma); + dma->stats.tx_work_enabled += grspw_tx_schedule_send(dma); + if (dma->tx_wait.waiting) { + tx_cond_true = grspw_tx_wait_eval(dma); + if (tx_cond_true) + dma->tx_wait.waiting = 0; + } + } + } else + SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); + + /* Release lock */ + rtems_semaphore_release(dma->sem_dma); + + if (rx_cond_true) + rtems_semaphore_release(dma->rx_wait.sem_wait); + + if (tx_cond_true) + rtems_semaphore_release(dma->tx_wait.sem_wait); +} + +/* Work task is receiving work for the work message queue posted from + * the ISR. + */ +void grspw_work_func(rtems_task_argument unused) +{ + rtems_status_code status; + unsigned int message; + size_t size; + struct grspw_priv *priv; + int i; + + while (grspw_task_stop == 0) { + /* Wait for ISR to schedule work */ + status = rtems_message_queue_receive( + grspw_work_queue, &message, + &size, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + if (status != RTEMS_SUCCESSFUL) + break; + + /* Handle work */ + priv = priv_tab[message >> WORK_CORE_BIT]; + if (message & WORK_SHUTDOWN) + grspw_work_shutdown_func(priv); + else if (message & WORK_DMA_MASK) { + for (i = 0; i < 4; i++) { + if (message & WORK_DMA(i)) + grspw_work_dma_func(&priv->dma[i]); + } + } + } + rtems_task_delete(RTEMS_SELF); +} + +STATIC void grspw_isr(void *data) +{ + struct grspw_priv *priv = data; + unsigned int dma_stat, stat, stat_clrmsk, ctrl, timecode; + int i, handled = 0, message = WORK_NONE; +#ifdef RTEMS_HAS_SMP + IRQFLAGS_TYPE irqflags; +#endif + + /* Get Status from Hardware */ + stat = REG_READ(&priv->regs->status); + stat_clrmsk = stat & (GRSPW_STS_TO | GRSPW_STAT_ERROR); + + /* Make sure to put the timecode handling first in order to get the + * smallest possible interrupt latency + */ + if ((stat & GRSPW_STS_TO) && (priv->tcisr != NULL)) { + /* Timecode received. Let custom function handle this */ + timecode = priv->regs->time; + (priv->tcisr)(priv->tcisr_arg, timecode); + } + + /* An Error occured? */ + if (stat & GRSPW_STAT_ERROR) { + /* Wake Global WorkQ */ + handled = 1; + + if (stat & GRSPW_STS_EE) + priv->stats.err_eeop++; + + if (stat & GRSPW_STS_IA) + priv->stats.err_addr++; + + if (stat & GRSPW_STS_PE) + priv->stats.err_parity++; + + if (stat & GRSPW_STS_ER) + priv->stats.err_escape++; + + if (stat & GRSPW_STS_CE) + priv->stats.err_credit++; + + if (stat & GRSPW_STS_WE) + priv->stats.err_wsync++; + + if (priv->dis_link_on_err) { + /* Disable the link, no more transfers are expected + * on any DMA channel. + */ + SPIN_LOCK(&priv->devlock, irqflags); + ctrl = REG_READ(&priv->regs->ctrl); + REG_WRITE(&priv->regs->ctrl, GRSPW_CTRL_LD | + (ctrl & ~(GRSPW_CTRL_IE|GRSPW_CTRL_LS))); + SPIN_UNLOCK(&priv->devlock, irqflags); + /* Signal to work-thread to stop DMA and clean up */ + message = WORK_SHUTDOWN; + } + } + + /* Clear Status Flags */ + if (stat_clrmsk) { + handled = 1; + REG_WRITE(&priv->regs->status, stat_clrmsk); + } + + /* A DMA transfer or Error occured? In that case disable more IRQs + * from the DMA channel, then invoke the workQ. + * + * Also the GI interrupt flag may not be available for older + * designs where (was added together with mutiple DMA channels). + */ + SPIN_LOCK(&priv->devlock, irqflags); + for (i=0; i<priv->hwsup.ndma_chans; i++) { + dma_stat = REG_READ(&priv->regs->dma[i].ctrl); + /* Check for Errors and if Packets been sent or received if + * respective IRQ are enabled + */ +#ifdef HW_WITH_GI + if ( dma_stat & (GRSPW_DMA_STATUS_ERROR | GRSPW_DMACTRL_GI) ) { +#else + if ( (((dma_stat << 3) & (GRSPW_DMACTRL_PR | GRSPW_DMACTRL_PS)) + | GRSPW_DMA_STATUS_ERROR) & dma_stat ) { +#endif + /* Disable Further IRQs (until enabled again) + * from this DMA channel. Let the status + * bit remain so that they can be handled by + * work function. + */ + REG_WRITE(&priv->regs->dma[i].ctrl, dma_stat & + ~(GRSPW_DMACTRL_RI|GRSPW_DMACTRL_TI| + GRSPW_DMACTRL_PR|GRSPW_DMACTRL_PS| + GRSPW_DMACTRL_RA|GRSPW_DMACTRL_TA| + GRSPW_DMACTRL_AT)); + message |= WORK_DMA(i); + handled = 1; + } + } + SPIN_UNLOCK(&priv->devlock, irqflags); + + if (handled != 0) + priv->stats.irq_cnt++; + + /* Schedule work by sending message to work thread */ + if ((message != WORK_NONE) && grspw_work_queue) { + message |= WORK_CORE(priv->index); + stat = rtems_message_queue_send(grspw_work_queue, &message, 4); + if (stat != RTEMS_SUCCESSFUL) + printk("grspw_isr(%d): message fail %d (0x%x)\n", + priv->index, stat, message); + } +} + +STATIC void grspw_hw_dma_stop(struct grspw_dma_priv *dma) +{ + unsigned int ctrl; + struct grspw_dma_regs *dregs = dma->regs; + + ctrl = REG_READ(&dregs->ctrl) & (GRSPW_DMACTRL_LE | GRSPW_DMACTRL_EN | + GRSPW_DMACTRL_SP | GRSPW_DMACTRL_SA | GRSPW_DMACTRL_NS); + ctrl |= GRSPW_DMACTRL_AT; + REG_WRITE(&dregs->ctrl, ctrl); +} + +STATIC void grspw_hw_dma_softreset(struct grspw_dma_priv *dma) +{ + unsigned int ctrl; + struct grspw_dma_regs *dregs = dma->regs; + + ctrl = REG_READ(&dregs->ctrl) & (GRSPW_DMACTRL_LE | GRSPW_DMACTRL_EN); + REG_WRITE(&dregs->ctrl, ctrl); + + REG_WRITE(&dregs->rxmax, DEFAULT_RXMAX); + REG_WRITE(&dregs->txdesc, 0); + REG_WRITE(&dregs->rxdesc, 0); +} + +/* Hardware Action: + * - stop DMA + * - do not bring down the link (RMAP may be active) + * - RMAP settings untouched (RMAP may be active) + * - port select untouched (RMAP may be active) + * - timecodes are disabled + * - IRQ generation disabled + * - status not cleared (let user analyze it if requested later on) + * - Node address / First DMA channels Node address + * is untouched (RMAP may be active) + */ +STATIC void grspw_hw_stop(struct grspw_priv *priv) +{ + int i; + unsigned int ctrl; + IRQFLAGS_TYPE irqflags; + + SPIN_LOCK_IRQ(&priv->devlock, irqflags); + + for (i=0; i<priv->hwsup.ndma_chans; i++) + grspw_hw_dma_stop(&priv->dma[i]); + + ctrl = REG_READ(&priv->regs->ctrl); + REG_WRITE(&priv->regs->ctrl, ctrl & ( + GRSPW_CTRL_LD | GRSPW_CTRL_LS | GRSPW_CTRL_AS | + GRSPW_CTRL_RE | GRSPW_CTRL_RD | + GRSPW_CTRL_NP | GRSPW_CTRL_PS)); + + SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); +} + +/* Soft reset of GRSPW core registers */ +STATIC void grspw_hw_softreset(struct grspw_priv *priv) +{ + int i; + + for (i=0; i<priv->hwsup.ndma_chans; i++) + grspw_hw_dma_softreset(&priv->dma[i]); + + REG_WRITE(&priv->regs->status, 0xffffffff); + REG_WRITE(&priv->regs->time, 0); +} + +int grspw_dev_count(void) +{ + return grspw_count; +} + +void grspw_initialize_user(void *(*devfound)(int), void (*devremove)(int,void*)) +{ + int i; + struct grspw_priv *priv; + + /* Set new Device Found Handler */ + grspw_dev_add = devfound; + grspw_dev_del = devremove; + + if (grspw_initialized == 1 && grspw_dev_add) { + /* Call callback for every previously found device */ + for (i=0; i<grspw_count; i++) { + priv = priv_tab[i]; + if (priv) + priv->data = grspw_dev_add(i); + } + } +} + +/******************* Driver manager interface ***********************/ + +/* Driver prototypes */ +static int grspw_common_init(void); +static int grspw2_init3(struct drvmgr_dev *dev); + +static struct drvmgr_drv_ops grspw2_ops = +{ + .init = {NULL, NULL, grspw2_init3, NULL}, + .remove = NULL, + .info = NULL +}; + +static struct amba_dev_id grspw2_ids[] = +{ + {VENDOR_GAISLER, GAISLER_SPW}, /* not yet supported */ + {VENDOR_GAISLER, GAISLER_SPW2}, + {VENDOR_GAISLER, GAISLER_SPW2_DMA}, + {0, 0} /* Mark end of table */ +}; + +static struct amba_drv_info grspw2_drv_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_GAISLER_GRSPW2_ID,/* Driver ID */ + "GRSPW_PKT_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &grspw2_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + sizeof(struct grspw_priv), /* Let DrvMgr alloc priv */ + }, + &grspw2_ids[0] +}; + +void grspw2_register_drv (void) +{ + GRSPW_DBG("Registering GRSPW2 packet driver\n"); + drvmgr_drv_register(&grspw2_drv_info.general); +} + +static int grspw2_init3(struct drvmgr_dev *dev) +{ + struct grspw_priv *priv; + struct amba_dev_info *ambadev; + struct ambapp_core *pnpinfo; + int i, size; + unsigned int ctrl; + union drvmgr_key_value *value; + + GRSPW_DBG("GRSPW[%d] on bus %s\n", dev->minor_drv, + dev->parent->dev->name); + + if (grspw_count > GRSPW_MAX) + return DRVMGR_ENORES; + + priv = dev->priv; + if (priv == NULL) + return DRVMGR_NOMEM; + priv->dev = dev; + + /* If first device init common part of driver */ + if (grspw_common_init()) + return DRVMGR_FAIL; + + /*** Now we take care of device initialization ***/ + + /* Get device information from AMBA PnP information */ + ambadev = (struct amba_dev_info *)dev->businfo; + if (ambadev == NULL) + return -1; + pnpinfo = &ambadev->info; + priv->irq = pnpinfo->irq; + priv->regs = (struct grspw_regs *)pnpinfo->apb_slv->start; + + /* Read Hardware Support from Control Register */ + ctrl = REG_READ(&priv->regs->ctrl); + priv->hwsup.rmap = (ctrl & GRSPW_CTRL_RA) >> GRSPW_CTRL_RA_BIT; + priv->hwsup.rmap_crc = (ctrl & GRSPW_CTRL_RC) >> GRSPW_CTRL_RC_BIT; + priv->hwsup.rx_unalign = (ctrl & GRSPW_CTRL_RX) >> GRSPW_CTRL_RX_BIT; + priv->hwsup.nports = 1 + ((ctrl & GRSPW_CTRL_PO) >> GRSPW_CTRL_PO_BIT); + priv->hwsup.ndma_chans = 1 + ((ctrl & GRSPW_CTRL_NCH) >> GRSPW_CTRL_NCH_BIT); + + /* Construct hardware version identification */ + priv->hwsup.hw_version = pnpinfo->device << 16 | pnpinfo->apb_slv->ver; + + if ((pnpinfo->device == GAISLER_SPW2) || + (pnpinfo->device == GAISLER_SPW2_DMA)) { + priv->hwsup.strip_adr = 1; /* All GRSPW2 can strip Address */ + priv->hwsup.strip_pid = 1; /* All GRSPW2 can strip PID */ + } else { + /* Autodetect GRSPW1 features? */ + priv->hwsup.strip_adr = 0; + priv->hwsup.strip_pid = 0; + } + + /* Let user limit the number of DMA channels on this core to save + * space. Only the first nDMA channels will be available. + */ + value = drvmgr_dev_key_get(priv->dev, "nDMA", KEY_TYPE_INT); + if (value && (value->i < priv->hwsup.ndma_chans)) + priv->hwsup.ndma_chans = value->i; + + /* Allocate and init Memory for all DMA channels */ + size = sizeof(struct grspw_dma_priv) * priv->hwsup.ndma_chans; + priv->dma = (struct grspw_dma_priv *) malloc(size); + if (priv->dma == NULL) + return DRVMGR_NOMEM; + memset(priv->dma, 0, size); + for (i=0; i<priv->hwsup.ndma_chans; i++) { + priv->dma[i].core = priv; + priv->dma[i].index = i; + priv->dma[i].regs = &priv->regs->dma[i]; + } + + /* Startup Action: + * - stop DMA + * - do not bring down the link (RMAP may be active) + * - RMAP settings untouched (RMAP may be active) + * - port select untouched (RMAP may be active) + * - timecodes are diabled + * - IRQ generation disabled + * - status cleared + * - Node address / First DMA channels Node address + * is untouched (RMAP may be active) + */ + grspw_hw_stop(priv); + grspw_hw_softreset(priv); + + /* Register character device in registered region */ + priv->index = grspw_count; + priv_tab[priv->index] = priv; + grspw_count++; + + /* Device name */ + sprintf(priv->devname, "grspw%d", priv->index); + + /* Tell above layer about new device */ + if (grspw_dev_add) + priv->data = grspw_dev_add(priv->index); + + return DRVMGR_OK; +} + +/******************* Driver Implementation ***********************/ + +static int grspw_common_init(void) +{ + if (grspw_initialized == 1) + return 0; + if (grspw_initialized == -1) + return -1; + grspw_initialized = -1; + + /* Device Semaphore created with count = 1 */ + if (rtems_semaphore_create(rtems_build_name('S', 'G', 'L', 'S'), 1, + RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | \ + RTEMS_NO_INHERIT_PRIORITY | RTEMS_LOCAL | \ + RTEMS_NO_PRIORITY_CEILING, 0, &grspw_sem) != RTEMS_SUCCESSFUL) + return -1; + + /* Work queue, Work thread. Not created if user disables it. + * user can disable it when interrupt is not used to save resources + */ + if (grspw_work_task_priority != -1) { + if (rtems_message_queue_create( + rtems_build_name('S', 'G', 'L', 'Q'), 32, 4, RTEMS_FIFO, + &grspw_work_queue) != RTEMS_SUCCESSFUL) + return -1; + + if (rtems_task_create(rtems_build_name('S', 'G', 'L', 'T'), + grspw_work_task_priority, RTEMS_MINIMUM_STACK_SIZE, + RTEMS_PREEMPT | RTEMS_NO_ASR, RTEMS_NO_FLOATING_POINT, + &grspw_work_task) != RTEMS_SUCCESSFUL) + return -1; + + if (rtems_task_start(grspw_work_task, grspw_work_func, 0) != + RTEMS_SUCCESSFUL) + return -1; + } + + grspw_initialized = 1; + return 0; +} diff --git a/c/src/lib/libbsp/sparc/shared/spw/grspw_rasta.c b/c/src/lib/libbsp/sparc/shared/spw/grspw_rasta.c deleted file mode 100644 index 6cd5698f44..0000000000 --- a/c/src/lib/libbsp/sparc/shared/spw/grspw_rasta.c +++ /dev/null @@ -1,155 +0,0 @@ -/* Select PCI driver */ -#define GRSPW_PCI - -#undef GRSPW_MAXDEVS -#undef DEBUG_SPACEWIRE_ONOFF -/*#define DEBUG_SPACEWIRE_ONOFF*/ -/* - * If USE_AT697_RAM is defined the RAM on the AT697 board will be used for DMA buffers (but rx message queue is always in AT697 ram). - * USE_AT697_DMA specifies whether the messages will be fetched using DMA or PIO. - * - * RASTA_PCI_BASE is the base address of the GRPCI AHB slave - * - */ - -#define USE_AT697_RAM 1 -#define USE_AT697_DMA 0 -#define RASTA_PCI_BASE 0xe0000000 -#define GRSPW_RASTA_MEM_OFF 0x21000 - -/* Make GRSPW driver use malloced or static memory - */ -#ifdef USE_AT697_RAM -#undef GRSPW_STATIC_MEM -#else -#define GRSPW_STATIC_MEM -#define GRSPW_CALC_MEMOFS(maxcores,corenum,ptr_mem_base,ptr_mem_end,ptr_bdtable_base) \ - grspw_rasta_calc_memoffs((maxcores),(corenum),(ptr_mem_base),(ptr_mem_end),(ptr_bdtable_base)) -#endif - -/* We have custom address tranlation for HW addresses */ -#define GRSPW_ADR_TO - -/* MEMAREA=>CPU used when reading descriptor buffer pointers, - * they need to be translated from adresses used by GRSPW HW - * into CPU readable addresses. - * - * NOT NEEDED AS GRSPW DRIVER USES INDEXES TO GET DESCRIPTOR - * DATA POINTER ADDRESSES. - */ -#undef GRSPW_ADR_FROM - -/* Set registered device name */ -#define GRSPW_DEVNAME "/dev/grspwrasta0" -#define GRSPW_DEVNAME_NO(devstr,no) ((devstr)[15]='0'+(no)) - -/* Any non-static function will begin with */ -#define GRSPW_PREFIX(name) grspwrasta##name - -/* do nothing, assume that the interrupt handler is called - * setup externally calling grspw_interrupt_handler. - */ -#define GRSPW_REG_INT(handler,irq,arg) \ - if ( grspw_rasta_int_reg ) \ - grspw_rasta_int_reg(handler,irq,arg); - -#define GRSPW_DONT_BYPASS_CACHE - -#ifdef GRSPW_ADR_TO -/* Translate a address within the Memory Region (memarea) into an Hardware - * device address. This address is put into hardware registers or descriptors - * so that the hardware can access the Memory Region. - * Example: - * An local AMBA access at 0xe0000000 will translate into PCI address 0x40000000, - * the PCI address 0x40000000 will translate into CPU-AMBA address 0x40000000. - */ -static inline unsigned int memarea_to_hw(unsigned int addr) { - return ((addr & 0x0fffffff) | RASTA_PCI_BASE); -} -#endif - -void (*grspw_rasta_int_reg)(void *handler, int irq, void *arg) = 0; - -#ifdef GRSPW_STATIC_MEM -static int grspw_rasta_calc_memoffs(int maxcores, int corenum, unsigned int *mem_base, unsigned int *mem_end, unsigned int *bdtable_base); -#endif - -int grspw_rasta_interrupt_handler(unsigned int status); - -void grspwrasta_interrupt_handler(int irq, void *pDev); - -#include "grspw.c" - -unsigned int grspw_rasta_memarea_address; - -/* Register RASTA GRSPW driver. - * - * memarea = preallocated memory somewhere, pointer to start of memory. - */ - -int grspw_rasta_register( - amba_confarea_type *bus, - unsigned int ram_base - ) -{ - /* Setup configuration */ - - /* if zero the malloc will be used */ - grspw_rasta_memarea_address = ram_base + GRSPW_RASTA_MEM_OFF; - - /* Register the driver */ - return GRSPW_PREFIX(_register)(bus); -} - -#if 0 -/* Call this from PCI interrupt handler, simply figures out - * which GRSPW core was responsible for the IRQ (may be multiple). - * v = status of the PCI/AMBA MCPU IRQ CTRL - */ -int grspw_rasta_interrupt_handler(unsigned int status) -{ - int minor; - - for(minor = 0; minor < spw_cores; minor++) { - if (status & (1<<grspw_devs[minor].irq) ) { - grspw_interrupt(&grspw_devs[minor]); - } - } -} -#endif - -void GRSPW_PREFIX(_interrupt_handler)(int irq, void *pDev) -{ - grspw_interrupt(pDev); -} - - -#ifdef GRSPW_STATIC_MEM -/* - * --------------------------------------- <- - * | Core1: BD TABLE 1 and 2 | - * | Core2: BD TABLE 1 and 2 | - * | Core3: BD TABLE 1 and 2 | - * |-------------------------------------| - * | Core1: rx data buf + rx header buf | - * | Core2: rx data buf + rx header buf | - * | Core3: rx data buf + rx header buf | - * --------------------------------------- - */ -static int grspw_rasta_calc_memoffs(int maxcores, int corenum, unsigned int *mem_base, unsigned int *mem_end, unsigned int *bdtable_base) -{ - if ( maxcores > 3 ) - return -1; - - if ( bdtable_base ) - *bdtable_base = grspw_rasta_memarea_address + corenum*2*SPACEWIRE_BDTABLE_SIZE; - - if ( mem_base ) - *mem_base = grspw_rasta_memarea_address + coremax*2*SPACEWIRE_BDTABLE_SIZE + corenum*BUFMEM_PER_LINK; - - if ( mem_end ) - *mem_end = grspw_rasta_memarea_address + coremax*2*SPACEWIRE_BDTABLE_SIZE + (corenum+1)*BUFMEM_PER_LINK; - - return 0; -} -#endif 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..e649596058 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/spw/grspw_router.c @@ -0,0 +1,547 @@ +/* GRSPW ROUTER APB-Register Driver. + * + * COPYRIGHT (c) 2010. + * Aeroflex Gaisler AB + * + * Author: Daniel Hellstrom + */ + +#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/spw/rmap.c b/c/src/lib/libbsp/sparc/shared/spw/rmap.c new file mode 100644 index 0000000000..a3fe60f8bc --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/spw/rmap.c @@ -0,0 +1,645 @@ +/* RMAP stack and RMAP driver interface implementation + * + * COPYRIGHT (c) 2009 + * Aeroflex Gaisler. + * + * 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. + * + * 2009-11-17, Daniel Hellstrom <daniel@gaisler.com> + * Created + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <netinet/in.h> +#include "rmap.h" + +#ifdef DEBUG + #define DBG(args...) printf(args); +#else + #define DBG(args...) +#endif + +static int rmap_stack_count = 0; + +static unsigned char RMAP_CRCTable[256] = { + 0x00, 0x91, 0xe3, 0x72, 0x07, 0x96, 0xe4, 0x75, + 0x0e, 0x9f, 0xed, 0x7c, 0x09, 0x98, 0xea, 0x7b, + 0x1c, 0x8d, 0xff, 0x6e, 0x1b, 0x8a, 0xf8, 0x69, + 0x12, 0x83, 0xf1, 0x60, 0x15, 0x84, 0xf6, 0x67, + 0x38, 0xa9, 0xdb, 0x4a, 0x3f, 0xae, 0xdc, 0x4d, + 0x36, 0xa7, 0xd5, 0x44, 0x31, 0xa0, 0xd2, 0x43, + 0x24, 0xb5, 0xc7, 0x56, 0x23, 0xb2, 0xc0, 0x51, + 0x2a, 0xbb, 0xc9, 0x58, 0x2d, 0xbc, 0xce, 0x5f, + 0x70, 0xe1, 0x93, 0x02, 0x77, 0xe6, 0x94, 0x05, + 0x7e, 0xef, 0x9d, 0x0c, 0x79, 0xe8, 0x9a, 0x0b, + 0x6c, 0xfd, 0x8f, 0x1e, 0x6b, 0xfa, 0x88, 0x19, + 0x62, 0xf3, 0x81, 0x10, 0x65, 0xf4, 0x86, 0x17, + 0x48, 0xd9, 0xab, 0x3a, 0x4f, 0xde, 0xac, 0x3d, + 0x46, 0xd7, 0xa5, 0x34, 0x41, 0xd0, 0xa2, 0x33, + 0x54, 0xc5, 0xb7, 0x26, 0x53, 0xc2, 0xb0, 0x21, + 0x5a, 0xcb, 0xb9, 0x28, 0x5d, 0xcc, 0xbe, 0x2f, + 0xe0, 0x71, 0x03, 0x92, 0xe7, 0x76, 0x04, 0x95, + 0xee, 0x7f, 0x0d, 0x9c, 0xe9, 0x78, 0x0a, 0x9b, + 0xfc, 0x6d, 0x1f, 0x8e, 0xfb, 0x6a, 0x18, 0x89, + 0xf2, 0x63, 0x11, 0x80, 0xf5, 0x64, 0x16, 0x87, + 0xd8, 0x49, 0x3b, 0xaa, 0xdf, 0x4e, 0x3c, 0xad, + 0xd6, 0x47, 0x35, 0xa4, 0xd1, 0x40, 0x32, 0xa3, + 0xc4, 0x55, 0x27, 0xb6, 0xc3, 0x52, 0x20, 0xb1, + 0xca, 0x5b, 0x29, 0xb8, 0xcd, 0x5c, 0x2e, 0xbf, + 0x90, 0x01, 0x73, 0xe2, 0x97, 0x06, 0x74, 0xe5, + 0x9e, 0x0f, 0x7d, 0xec, 0x99, 0x08, 0x7a, 0xeb, + 0x8c, 0x1d, 0x6f, 0xfe, 0x8b, 0x1a, 0x68, 0xf9, + 0x82, 0x13, 0x61, 0xf0, 0x85, 0x14, 0x66, 0xf7, + 0xa8, 0x39, 0x4b, 0xda, 0xaf, 0x3e, 0x4c, 0xdd, + 0xa6, 0x37, 0x45, 0xd4, 0xa1, 0x30, 0x42, 0xd3, + 0xb4, 0x25, 0x57, 0xc6, 0xb3, 0x22, 0x50, 0xc1, + 0xba, 0x2b, 0x59, 0xc8, 0xbd, 0x2c, 0x5e, 0xcf, +}; + +unsigned char rmap_crc_calc(unsigned char *data, unsigned int len) +{ + unsigned char crc = 0; + unsigned int i; + for (i = 0; i < len; i++) { + crc = RMAP_CRCTable[(crc ^ data[i]) & 0xff]; + } + return crc; +} + +struct rmap_priv { + struct rmap_config *config; /* Configuration */ + void *drv_cookie; /* Driver private structure */ + struct rmap_drv_timeout timeout; /* Driver timeout */ + unsigned short tid; /* Current TID */ + char drv_cap; /* Driver capabiliteis */ + char running; /* 1 is started, 0 if stopped */ + unsigned char blocking; /* Blocking mode */ + unsigned char tx_pkt_hdr[256+9]; /* Packet header used for transmission, last 9 bytes is for RMW commands */ + unsigned int _rx_pkt_buf; /* RX packet Buffer */ + unsigned int *rx_pkt_buf; /* RX packet Buffer aligned to 64b*/ + unsigned int rx_pkt_buf_len; /* RX packet Buffer length */ + struct rmap_spw_pkt rxpkt; /* RX packet */ + struct rmap_spw_pkt txpkt; /* TX packet */ + rtems_id lock; /* Optional Semaphore protection against multiple threads (thread-safe) */ +}; + +void *rmap_init(struct rmap_config *config) +{ + struct rmap_priv *priv; + int status; + + priv = (struct rmap_priv *)malloc(sizeof(struct rmap_priv)); + if ( !priv ) + return NULL; + + memset(priv, 0, sizeof(struct rmap_priv)); + priv->config = config; + if ( config->tid_msb >= 0 ) { + priv->tid = config->tid_msb << 8; + } + priv->drv_cookie = priv->config->drv->cookie; + + /* Create Semaphore used to make RMAP layer Thread-safe */ + if ( config->thread_safe ) { + status = rtems_semaphore_create( + rtems_build_name('R', 'M', 'A', 'P' + rmap_stack_count++), + 1, + RTEMS_FIFO | RTEMS_COUNTING_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | \ + RTEMS_LOCAL | RTEMS_NO_PRIORITY_CEILING, + 0, + &priv->lock); + if ( status != RTEMS_SUCCESSFUL ) { + printf("RMAP_INIT: Failed to create semaphore: %d\n", status); + free(priv); + return NULL; + } + DBG("RMAP_INIT: semaphore ID: 0x%x (%p)\n", priv->lock, &priv->lock); + } + + return priv; +} + +void rmap_init_rxpkt(struct rmap_priv *priv, struct rmap_spw_pkt *pkt) +{ + pkt->data = (unsigned char *)priv->rx_pkt_buf; + pkt->dlen = priv->rx_pkt_buf_len; + pkt->hlen = 0; + pkt->hdr = 0; + pkt->options = 0; +} + + +int rmap_start(struct rmap_priv *priv) +{ + if ( priv->running == 0 ) { + if ( !priv->_rx_pkt_buf ) { + /* Header length + Data CRC + 4 extra */ + priv->rx_pkt_buf_len = priv->config->max_rx_len + 16 + 1 + 4; + priv->_rx_pkt_buf = (unsigned int)malloc(priv->rx_pkt_buf_len+4); + priv->rx_pkt_buf = (unsigned int*)((priv->_rx_pkt_buf+7)&~7); + if ( priv->rx_pkt_buf == NULL ) + return -1; + + } + + /* Set blocking mode */ + if ( priv->config->drv->ops.ioctl(priv->drv_cookie, RMAP_DRV_IOCTL_BLOCK, (void *)(int)priv->blocking) ){ + /* Failed to set blocking mode */ + return -1; + } + + /* Set timeout, defualt is zero = Wait forever */ + if ( priv->config->drv->ops.ioctl(priv->drv_cookie, RMAP_DRV_IOCTL_TIMEOUT, (void *)&priv->timeout) ){ + /* Failed to set timeout */ + return -1; + } + + if ( priv->drv_cap == 0 ) { + unsigned int capabilities; + if ( priv->config->drv->ops.ioctl(priv->drv_cookie, RMAP_DRV_IOCTL_GET_CAP, &capabilities) ){ + /* Failed to get capabilities, assume none */ + priv->drv_cap = 0; + } else { + priv->drv_cap = capabilities & 0xff; + } + } + + if ( priv->config->drv->ops.ioctl(priv->drv_cookie, RMAP_DRV_IOCTL_START, 0) ){ + /* Failed to start */ + return -1; + } + + } + priv->running = 1; + + return 0; +} + + +/* Disables the RMAP stack to send/receive commands */ +int rmap_stop(struct rmap_priv *priv) +{ + if ( priv->running == 0 ) + return 0; + + priv->running = 0; + if ( priv->config->drv->ops.ioctl(priv->drv_cookie, RMAP_DRV_IOCTL_STOP, 0) ){ + /* Failed to start */ + return -1; + } + + return 0; +} + +/* Set operating mode */ +int rmap_set_blocking(struct rmap_priv *priv, unsigned int mode) +{ + if ( priv->running ) + return -1; + + if ( mode != RMAP_IOCTRL_BLOCK_ALL ) { + return -1; + } + + priv->blocking = mode; + + return 0; +} + +/* Set Timeout */ +int rmap_set_timeout(struct rmap_priv *priv, struct rmap_drv_timeout *timeout) +{ + priv->timeout = *timeout; + if ( priv->config->drv->ops.ioctl(priv->drv_cookie, RMAP_DRV_IOCTL_TIMEOUT, timeout) ){ + /* Failed to set timeout */ + return -1; + } + return 0; +} + +int rmap_get_config(struct rmap_priv *priv, struct rmap_config *result) +{ + if ( !result || !priv ) + return -1; + *result = *priv->config; + return 0; +} + +/* Configure stack (blocking etc)*/ +int rmap_ioctl(void *cookie, int command, void *arg) +{ + switch (command) { + case RMAP_IOCTL_START: + return rmap_start((struct rmap_priv *)cookie); + case RMAP_IOCTL_STOP: + return rmap_stop((struct rmap_priv *)cookie); + case RMAP_IOCTL_BLOCK: + return rmap_set_blocking((struct rmap_priv *)cookie, (unsigned int)arg); + case RMAP_IOCTL_TIMEOUT: + return rmap_set_timeout((struct rmap_priv *)cookie, (struct rmap_drv_timeout *)arg); + case RMAP_IOCTL_GET_CONFIG: + return rmap_get_config((struct rmap_priv *)cookie, (struct rmap_config *)arg); + default: + return -1; + } +} + +int rmap_build(struct rmap_priv *priv, struct rmap_command *cmd, struct rmap_spw_pkt *txpkt) +{ + int len1, len2; + unsigned char *pos, *pkt = (unsigned char *)txpkt->hdr; + unsigned int length, src_path_len; + int options; + + /* Begin with address translation */ + if ( priv->config->route_func ) { + /* Custom translation */ + + /* Generate path addressing to destination */ + len1 = 120; + priv->config->route_func(priv, 0, priv->config->spw_adr, cmd->dstadr, pkt, &len1); + + /* Generate path addressing from destination */ + len2 = 13; + priv->config->route_func(priv, 1, priv->config->spw_adr, cmd->dstadr, &pkt[len1+3], &len2); + + } else { + pkt[0] = cmd->dstadr & 0xff; + pkt[4] = priv->config->spw_adr; + len1 = 1; + len2 = 1; + } + /* Protocol Identifier */ + if ( len2 == 1 ) { + src_path_len = 0; + } else { + src_path_len = ((len2-1)+3) / 4; + } + pos = &pkt[len1]; + *pos++ = 0x01; + *pos++ = 0x40 | (cmd->type << 2) | src_path_len; + *pos++ = cmd->dstkey; + pos += len2; /* Jump over address, len1 + 3 + len2 */ + *pos++ = (priv->tid >> 8); + *pos++ = priv->tid & 0xff; + cmd->tid = priv->tid; /* Remember TID */ + /* Increment TID */ + if ( priv->config->tid_msb < 0 ) { + priv->tid++; + } else { + if ( (priv->tid & 0xff) == 0xff) { + priv->tid = priv->tid & 0xff00; + } else { + priv->tid++; + } + } + + /* Address */ + *pos++ = (cmd->address >> 32) & 0xff; /* Extended Address */ + *pos++ = (cmd->address >> 24) & 0xff; + *pos++ = (cmd->address >> 16) & 0xff; + *pos++ = (cmd->address >> 8) & 0xff; + *pos++ = cmd->address & 0xff; + /* pos = pkt[len1+3+len2+7] */ + if ( cmd->type & RMAP_CMD_WRITE ) { /* WRITE */ + length = cmd->data.write.length; + } else if ( cmd->type == RMAP_CMD_RMWI ) { /* READ-MODIFY_WRITE */ + length = cmd->data.read_m_write.length * 2; + } else { /* READ */ + length = cmd->data.read.length; + } + *pos++ = (length >> 16) & 0xff; + *pos++ = (length >> 8) & 0xff; + *pos++ = length & 0xff; + txpkt->hlen = pos - pkt; /* HDR CRC is fixed later */ + + if ( cmd->type & RMAP_CMD_WRITE ) { /* WRITE */ + txpkt->dlen = length; + if ( cmd->data.write.data > 0 ) { + txpkt->data = cmd->data.write.data; /* CRC will be added later */ + } else { + txpkt->data = ((char *)&txpkt->dlen + 1); /* Temporary storage of CRC when no data */ + } + } else if ( cmd->type == RMAP_CMD_RMWI ) { /* READ-MODIFY_WRITE */ + txpkt->dlen = length; /* CRC will be added later */ + txpkt->data = &txpkt->hdr[256]; + if ( cmd->data.read_m_write.length == 4 ) { + /* 4byte */ + txpkt->hdr[256] = (cmd->data.read_m_write.data >> 24) & 0xff; + txpkt->hdr[257] = (cmd->data.read_m_write.data >> 16) & 0xff; + txpkt->hdr[258] = (cmd->data.read_m_write.data >> 8) & 0xff; + txpkt->hdr[259] = cmd->data.read_m_write.data & 0xff; + + txpkt->hdr[260] = (cmd->data.read_m_write.mask >> 24) & 0xff; + txpkt->hdr[261] = (cmd->data.read_m_write.mask >> 16) & 0xff; + txpkt->hdr[262] = (cmd->data.read_m_write.mask >> 8) & 0xff; + txpkt->hdr[263] = cmd->data.read_m_write.mask & 0xff; + } else if ( cmd->data.read_m_write.length == 1 ) { + /* 1byte */ + txpkt->hdr[256] = cmd->data.read_m_write.data & 0xff; + txpkt->hdr[257] = cmd->data.read_m_write.mask & 0xff; + } else if ( cmd->data.read_m_write.length == 2 ) { + /* 2byte */ + txpkt->hdr[256] = (cmd->data.read_m_write.data >> 8) & 0xff; + txpkt->hdr[257] = cmd->data.read_m_write.data & 0xff; + + txpkt->hdr[258] = (cmd->data.read_m_write.mask >> 8) & 0xff; + txpkt->hdr[259] = cmd->data.read_m_write.mask & 0xff; + } else { + /* Wrong input */ + return -1; + } + } else { /* READ */ + txpkt->dlen = 0; + txpkt->data = NULL; + } + + /*** CRC ***/ + if ( len1 > 1 ) /* PATH target address not part of Header CRC */ + options = PKT_OPTION_HDR_CRC_SKIPLEN(len1-1); + else + options = 0; + + /* HEADER CRC */ + if ( priv->drv_cap & DRV_CAP_HDR_CRC ) { + options |= PKT_OPTION_HDR_CRC; + } else { + /* Generate CRC and put it at the end of data buffer, Don't calculate + * CRC on Destination Path address. + */ + unsigned char crc; + crc = rmap_crc_calc(&txpkt->hdr[len1-1], txpkt->hlen - (len1-1)); + ((unsigned char *)txpkt->hdr)[txpkt->hlen] = crc; + txpkt->hlen++; + } + /* DATA CRC */ + if ( txpkt->data ) { + if ( txpkt->dlen == 0 ) { + /* Need to put CRC even when no data available, + * CRC will always be 0x00 + */ + ((unsigned char *)txpkt->data)[0] = 0x00; + txpkt->dlen = 1; + } else if ( priv->drv_cap & DRV_CAP_DATA_CRC ) { + options |= PKT_OPTION_DATA_CRC; + } else { + /* Generate CRC and put it at the end of data buffer */ + unsigned char crc; + crc = rmap_crc_calc(txpkt->data, txpkt->dlen); + ((unsigned char *)txpkt->data)[txpkt->dlen] = crc; + txpkt->dlen++; + } + } + + txpkt->options = options; + + return 0; +} + +int rmap_parse(struct rmap_priv *priv, struct rmap_command *cmd, struct rmap_spw_pkt *rxpkt) +{ + unsigned char *pkt; + unsigned int length; + + pkt = rxpkt->data; + + /* Check that the protocol ID, Replay and TID matches before proceeding */ + if ( (pkt[1] != 0x01) || ((pkt[2] & 0x40) != 0) || + (pkt[5] != (cmd->tid>>8)) || (pkt[6] != (cmd->tid & 0xff))) { + return -1; + } + + cmd->status = pkt[3]; + if ( cmd->type & RMAP_CMD_WRITE ){ + return 0; + } + + if ( cmd->type == RMAP_CMD_RMWI ) { + /* Copy data content */ + cmd->data.read_m_write.oldlength = length = pkt[10]; + if ( length == 4 ) { + cmd->data.read_m_write.olddata = ntohl(*(unsigned int *)&pkt[12]); + } else if ( length == 1 ){ + cmd->data.read_m_write.olddata = pkt[12]; + } else if ( length == 2 ) { + cmd->data.read_m_write.olddata = ntohs(*(unsigned short *)&pkt[12]); + } else { + cmd->data.read_m_write.olddata = 0; + } + return 0; + } + + /* READ COMMAND RESPONSE - copy data */ + if ( cmd->status != 0 ) { + /* Error status, skip other bytes */ + return 0; + } + + /* Length may be equal or less than request nembuer of bytes */ + cmd->data.read.datalength = length = (pkt[8]<<16) | (pkt[9]<<8) | pkt[10]; + if ( length > cmd->data.read.length ) { + printf("Response DATA Length != Command Data Length [%p, %p]\n", + cmd, pkt); + return -1; + } + if ( length > 0 ) { + memcpy(cmd->data.read.data, &pkt[12], length); + } + + return 0; +} + +int rmap_send(void *cookie, struct rmap_command *cmd) +{ + struct rmap_priv *priv = cookie; + struct rmap_drv *drv; + int wait_response; + int ret=0, status; + struct rmap_spw_pkt *txpkt, *rxpkt; + + if ( !priv || !cmd || !priv->running ) + return -1; + + /*** Check invalid command type ***/ + if ( (cmd->type > RMAP_CMD_WIVA) || + ((cmd->type < RMAP_CMD_RMWI) && (cmd->type != RMAP_CMD_RS) && (cmd->type != RMAP_CMD_RI)) + ) + return -1; + if ( (cmd->type == RMAP_CMD_RMWI) && (cmd->data.read_m_write.length != 1) && + (cmd->data.read_m_write.length != 2) && (cmd->data.read_m_write.length != 4) + ) + return -1; + + /*** Determine if stack must wait for response to arrive after transmission ***/ + wait_response = 0; + if ( ((cmd->type & RMAP_CMD_WRITE) == 0) || /* READ ALWAYS RETURNS DATA */ + (cmd->type & RMAP_CMD_ACKNOWLEDGE) /* WAIT FOR ACK */ + ) + wait_response = 1; + + /*** Check that READ/WRITE length is within boundaries ***/ + if ( (cmd->type & RMAP_CMD_WRITE) && (cmd->data.write.length > (priv->config->max_tx_len-4)) ) { + return -1; + } + if ( ((cmd->type & RMAP_CMD_WRITE) == 0) && (cmd->data.read.length > (priv->config->max_rx_len)) ) { + return -1; + } + + if ( priv->lock ) { + status = rtems_semaphore_obtain(priv->lock, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + if ( status != RTEMS_SUCCESSFUL ) { + printf("RMAP-STACK: FAILED TO OBTAIN LOCK: %d (%p)\n", + status, priv->lock); + return -1; + } + } + + /*** Alloc RX/TX packet ***/ + txpkt = &priv->txpkt; + rxpkt = &priv->rxpkt; + + /*** Build command ***/ + txpkt->hdr = &priv->tx_pkt_hdr[0]; + txpkt->hlen = 0; + txpkt->data = NULL; + txpkt->dlen = 0; + txpkt->options = 0; + if ( rmap_build(priv, cmd, txpkt) ){ + ret = -1; + goto out_sem; + } + + /*** Send command ***/ + drv = priv->config->drv; + if ( !drv->ops.send ){ + ret = -1; + goto out_sem; + } + + /* Schedule packet for transmission */ + status = drv->ops.send(priv->drv_cookie, txpkt); + if ( status ) { + /* Failed to schedule packet */ + ret = -1; + goto out_sem; + } + cmd->status = 0; + + /*** Wait for response ***/ + if ( wait_response ) { +retry: + rmap_init_rxpkt(priv, rxpkt); + status = drv->ops.recv(priv->drv_cookie, rxpkt); + if ( status < 1 ) { + /* Failed to receive packet, for example timeout driver error + * or driver not in blocking mode. + */ + ret = -2; + goto out_sem; + } + /*** ONE RECEIVED PACKET ***/ + + /* Parse input packet */ + if ( rmap_parse(priv, cmd, rxpkt) == -1 ) { + /* Got wrong packet, for example wrong TID */ + DBG("RMAP: wrong packet\n"); + goto retry; + } + } + +out_sem: + if ( priv->lock ) { + rtems_semaphore_release(priv->lock); + } + return ret; +} + +int rmap_write(void *cookie, void *dst, void *buf, int length, int dstadr, int dstkey) +{ + struct rmap_command_write writecmd; + int status; + + /* Build RMAP Write Command. Do not use Verify Write */ + writecmd.type = RMAP_CMD_WI; + /*writecmd.type = RMAP_CMD_WIV;*/ + writecmd.dstadr = dstadr; + writecmd.dstkey = dstkey; + writecmd.address = (unsigned int)dst; + writecmd.length = length; + writecmd.data = (unsigned char *)buf; + +#ifdef DEBUG + if ( length == 4 ) { + printf("RMAP_WRITE(4): 0x%08x <== 0x%08x\n", dst, *(unsigned int *)buf); + } else { + printf("RMAP_WRITE: 0x%08x - 0x%08x\n", dst, ((unsigned int)dst)+(length-1)); + } +#endif + + /* Send Command */ + status = rmap_send(cookie, (struct rmap_command *)&writecmd); + if ( status ) { + printf("GRTMPAHB WRITE: Failed to send/receive command %d\n", status); + return -1; + } + + /* Read Data */ + if ( writecmd.status != 0 ) { + printf("GRTMPAHB WRITE: Status non-zero 0x%x, address: 0x%llx\n", + writecmd.status, writecmd.address); + return -1; + } + + /* Return sucessful */ + return 0; +} + +int rmap_read(void *cookie, void *src, void *buf, int length, int dstadr, int dstkey) +{ + /* Send it, and wait for result */ + struct rmap_command_read readcmd; + int status; + + /* Build RMAP READ Command */ + readcmd.type = RMAP_CMD_RI; + readcmd.dstadr = dstadr; + readcmd.dstkey = dstkey; + readcmd.address = (unsigned int)src; + readcmd.length = length; + readcmd.datalength = 0; + readcmd.data = buf; + + DBG("RMAP READ1: 0x%08x - 0x%08x\n", (unsigned int)src, ((unsigned int)src + (readcmd.length - 1))); + + /* Send Command */ + status = rmap_send(cookie, (struct rmap_command *)&readcmd); + if ( status ) { + printf("RMAP READ: Failed to send/receive command %d\n", status); + memset(buf, 0, length); + /* Should we remove device? */ + return -1; + } + + /* Read Data */ + if ( readcmd.status != 0 ) { + printf("RMAP READ: Status non-zero 0x%x, dlen: 0x%x\n", readcmd.status, readcmd.datalength); + memset(buf, 0, length); + /* Should we remove device? */ + return -1; + } + +#ifdef DEBUG + if ( length == 4 ) { + printf("RMAP READ2: 0x%08x(4): 0x%08x\n", (unsigned int)src, *(unsigned int *)buf); + } else { + printf("RMAP READ2: 0x%08x - 0x%08x\n", (unsigned int)src, ((unsigned int)src)+(length-1)); + } +#endif + + /* Return sucessful */ + return 0; +} diff --git a/c/src/lib/libbsp/sparc/shared/spw/rmap_drv_grspw.c b/c/src/lib/libbsp/sparc/shared/spw/rmap_drv_grspw.c new file mode 100644 index 0000000000..83a0b04266 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/spw/rmap_drv_grspw.c @@ -0,0 +1,161 @@ +/* GRSPW RMAP Driver it use the standard GRSPW RTEMS DRIVER + * + * COPYRIGHT (c) 2009 + * Aeroflex Gaisler. + * + * 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. + * + * 2009-11-17, Daniel Hellstrom <daniel@gaisler.com> + * Created + */ + +#include <rtems.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include <grspw.h> +#include "rmap.h" +#include "rmap_drv_grspw.h" + +struct rmap_drv_grspw_priv { + struct rmap_drv_grspw_config *config; +}; + +void *rmap_drv_grspw_init(struct rmap_drv_grspw_config *config) +{ + struct rmap_drv_grspw_priv *priv; + + priv = (struct rmap_drv_grspw_priv *)malloc(sizeof(struct rmap_drv_grspw_priv)); + if ( !priv ) + return NULL; + memset(priv, 0, sizeof(struct rmap_drv_grspw_priv)); + + priv->config = config; + + return priv; +} + +int rmap_drv_grspw_ops_init(void *cookie) +{ + /*struct rmap_drv_grspw_priv *priv = cookie;*/ + return 0; +} + +int rmap_drv_grspw_get_cap(struct rmap_drv_grspw_priv *priv, unsigned int *result) +{ + spw_config grspw_config; + int status; + unsigned int options; + + status = ioctl(priv->config->fd, SPACEWIRE_IOCTRL_GET_CONFIG, &grspw_config); + if ( status != 0 ) { + *result = 0; + return -1; + } + + /* Get capabilities from GRSPW driver */ + options = 0; + if ( grspw_config.is_rmap || grspw_config.is_rmapcrc ) + options |= (PKT_OPTION_HDR_CRC | PKT_OPTION_DATA_CRC); + + *result = options; + + return 0; +} + +int rmap_drv_grspw_set_rtimeout(struct rmap_drv_grspw_priv *priv, unsigned int timeout) +{ + int status; + + status = ioctl(priv->config->fd, SPACEWIRE_IOCTRL_SET_READ_TIMEOUT, timeout); + if ( status != 0 ) { + return -1; + } + + return 0; +} + + +int rmap_drv_grspw_ops_ioctl(void *cookie, int command, void *arg) +{ + struct rmap_drv_grspw_priv *priv = cookie; + struct rmap_drv_timeout *timeout; + + switch ( command ) { + case RMAP_DRV_IOCTL_START: + case RMAP_DRV_IOCTL_STOP: + case RMAP_DRV_IOCTL_BLOCK: /* Only blocking mode supported */ + break; + case RMAP_DRV_IOCTL_TIMEOUT: /* Only Read timeout support */ + timeout = arg; + if ( timeout->options & RMAP_TIMEOUT_SET_RTIME ) { + /* Set read timeout */ + rmap_drv_grspw_set_rtimeout(priv, timeout->rtimeout); + } + break; + case RMAP_DRV_IOCTL_GET_CAP: /* No DATA/HEADER CRC capabilities */ + return rmap_drv_grspw_get_cap(cookie, (unsigned int *)arg); + default: + return -1; + } + return 0; +} + +int rmap_drv_grspw_ops_send(void *cookie, struct rmap_spw_pkt *pkt) +{ + struct rmap_drv_grspw_priv *priv = cookie; + spw_ioctl_pkt_send *grspw_pkt; + int status; + + grspw_pkt = (spw_ioctl_pkt_send *)&pkt->hlen; + grspw_pkt->options = pkt->options & (PKT_OPTION_HDR_CRC_SKIPLEN_MASK| + PKT_OPTION_DATA_CRC|PKT_OPTION_HDR_CRC); + grspw_pkt->sent = 0; + + /* Send packet by using the IOCTL SEND method, it takes separate header and data arguments */ + status = ioctl(priv->config->fd, SPACEWIRE_IOCTRL_SEND, grspw_pkt); + if ( status < 0 ) { + return -1; + } + return 0; +} + +int rmap_drv_grspw_ops_recv(void *cookie, struct rmap_spw_pkt *pkt) +{ + struct rmap_drv_grspw_priv *priv = cookie; + int len; + + if ( !pkt->data ) { + return -1; + } + + len = read(priv->config->fd, pkt->data, pkt->dlen); + if ( len < 0 ) { + printf("RMAP_DRV_GRSPW: Error reading: %s (%d, %d)", strerror(errno), errno, len); + return -1; + } else if ( len == 0 ) { + if ( errno == ETIMEDOUT ) { + /* Read Timeout */ + return -2; + } + return -1; + } + + /* Update packet size */ + pkt->dlen = len; + + return 1; /* Received one packet */ +} + +struct rmap_drv_ops rmap_grspw_ops = +{ + .init = rmap_drv_grspw_ops_init, + .ioctl = rmap_drv_grspw_ops_ioctl, + .send = rmap_drv_grspw_ops_send, + .recv = rmap_drv_grspw_ops_recv +}; diff --git a/c/src/lib/libbsp/sparc/shared/startup/early_malloc.c b/c/src/lib/libbsp/sparc/shared/startup/early_malloc.c new file mode 100644 index 0000000000..c6abaca2b1 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/startup/early_malloc.c @@ -0,0 +1,44 @@ +/* + * Early dynamic memory allocation (not freeable) for BSP + * boot routines. Minimum alignment 8 bytes. Memory is + * allocated after _end, it will shrink the workspace. + * + * COPYRIGHT (c) 2011. + * 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. + * + * $Id: early_malloc.c + */ + +#include <bsp.h> +#include <stdlib.h> + +/* Tells us where to put the workspace in case remote debugger is present. */ +extern uint32_t rdb_start; + +/* Must be aligned to 8 */ +extern unsigned int early_mem; + +/* must be identical to STACK_SIZE in start.S */ +#define STACK_SIZE (16 * 1024) + +void *bsp_early_malloc(int size) +{ + void *start; + + /* Not early anymore? */ + if (early_mem == ~0) + return malloc(size); + + size = (size + 7) & ~0x7; + if (rdb_start - STACK_SIZE - early_mem < size) + return NULL; + + start = (void *)early_mem; + early_mem += size; + + return start; +} 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..96b1ec4e63 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/time/grctm.c @@ -0,0 +1,410 @@ +/* GRCTM - CCSDS Time Manager - register 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. + * + */ + +#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..ae8c875179 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/time/spwcuc.c @@ -0,0 +1,370 @@ +/* SPWCUC - SpaceWire - CCSDS unsegmented Code Transfer Protocol GRLIB core + * register 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. + * + */ + +#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/timer/gptimer.c b/c/src/lib/libbsp/sparc/shared/timer/gptimer.c new file mode 100644 index 0000000000..3f3877f66d --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/timer/gptimer.c @@ -0,0 +1,533 @@ +/* This file contains the driver for the GRLIB GPTIMER timers port. The driver + * is implemented by using the tlib.c simple timer layer and the Driver + * Manager. + * + * The Driver can be configured using driver resources: + * + * - timerStart Timer Index if first Timer, this parameters is typically used + * in AMP systems for resource allocation. The Timers before + * timerStart will not be accessed. + * - timerCnt Number of timers that the driver will use, this parameters is + * typically used in AMP systems for resource allocation between + * OS instances. + * - prescaler Base prescaler, normally set by bootloader but can be + * overridden. The default scaler reload value set by bootloader + * is so that Timers operate in 1MHz. Setting the prescaler to a + * lower value increase the accuracy of the timers but shortens + * the time until underflow happens. + * - clockTimer Used to select a particular timer to be the system clock + * timer. This is useful when multiple GPTIMERs cores are + * available, or in AMP systems. By default the TLIB selects the + * first timer registered as system clock timer. + * + * The BSP define APBUART_INFO_AVAIL in order to add the info routine + * used for debugging. + * + * COPYRIGHT (c) 2010. + * Aeroflex Gaisler. + * + * 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. + * + * 2010-09-27, Daniel Hellstrom <daniel@gaisler.com> + * created + */ + +#include <rtems.h> +#include <bsp.h> +#include <stdlib.h> +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> +#include <grlib.h> +#include "tlib.h" + +#if defined(LEON3) && defined(RTEMS_DRVMGR_STARTUP) +#include <leon.h> +volatile struct gptimer_regs *LEON3_Timer_Regs = 0; +#endif + +#ifdef GPTIMER_INFO_AVAIL +#include <stdio.h> +#endif + +/* GPTIMER Core Configuration Register (READ-ONLY) */ +#define GPTIMER_CFG_TIMERS_BIT 0 +#define GPTIMER_CFG_IRQ_BIT 3 +#define GPTIMER_CFG_SI_BIT 8 +#define GPTIMER_CFG_DF_BIT 9 + +#define GPTIMER_CFG_TIMERS (0x7<<GPTIMER_CFG_TIMERS_BIT) +#define GPTIMER_CFG_IRQ (0x1f<<GPTIMER_CFG_IRQ_BIT) +#define GPTIMER_CFG_SI (1<<GPTIMER_CFG_SI_BIT) +#define GPTIMER_CFG_DF (1<<GPTIMER_CFG_DF_BIT) + +/* GPTIMER Timer Control Register */ +#define GPTIMER_CTRL_EN_BIT 0 +#define GPTIMER_CTRL_RS_BIT 1 +#define GPTIMER_CTRL_LD_BIT 2 +#define GPTIMER_CTRL_IE_BIT 3 +#define GPTIMER_CTRL_IP_BIT 4 +#define GPTIMER_CTRL_CH_BIT 5 +#define GPTIMER_CTRL_DH_BIT 6 + +#define GPTIMER_CTRL_EN (1<<GPTIMER_CTRL_EN_BIT) +#define GPTIMER_CTRL_RS (1<<GPTIMER_CTRL_RS_BIT) +#define GPTIMER_CTRL_LD (1<<GPTIMER_CTRL_LD_BIT) +#define GPTIMER_CTRL_IE (1<<GPTIMER_CTRL_IE_BIT) +#define GPTIMER_CTRL_IP (1<<GPTIMER_CTRL_IP_BIT) +#define GPTIMER_CTRL_CH (1<<GPTIMER_CTRL_CH_BIT) +#define GPTIMER_CTRL_DH (1<<GPTIMER_CTRL_DH_BIT) + +#define DBG(x...) + +/* GPTIMER timer private */ +struct gptimer_timer { + struct tlib_dev tdev; /* Must be first in struct */ + struct gptimer_timer_regs *tregs; + char index; /* Timer Index in this driver */ + char tindex; /* Timer Index In Hardware */ + unsigned char irq_ack_mask; +}; + +/* GPTIMER Core private */ +struct gptimer_priv { + struct drvmgr_dev *dev; + struct gptimer_regs *regs; + unsigned int base_clk; + unsigned int base_freq; + int separate_interrupt; + + /* Structure per Timer unit, the core supports up to 8 timers */ + int timer_cnt; + struct gptimer_timer timers[0]; +}; + +void gptimer_isr(void *data); + +#if 0 +void gptimer_tlib_irq_register(struct tlib_drv *tdrv, tlib_isr_t func, void *data) +{ + struct gptimer_priv *priv = (struct gptimer_priv *)tdrv; + + if ( SHARED ...) + + + drvmgr_interrupt_register(); +} +#endif + +/******************* Driver manager interface ***********************/ + +/* Driver prototypes */ +struct tlib_drv gptimer_tlib_drv; +int gptimer_device_init(struct gptimer_priv *priv); + +int gptimer_init1(struct drvmgr_dev *dev); +#ifdef GPTIMER_INFO_AVAIL +static int gptimer_info( + struct drvmgr_dev *dev, + void (*print_line)(void *p, char *str), + void *p, int, char *argv[]); +#define GTIMER_INFO_FUNC gptimer_info +#else +#define GTIMER_INFO_FUNC NULL +#endif + +struct drvmgr_drv_ops gptimer_ops = +{ + .init = {gptimer_init1, NULL, NULL, NULL}, + .remove = NULL, + .info = GTIMER_INFO_FUNC, +}; + +struct amba_dev_id gptimer_ids[] = +{ + {VENDOR_GAISLER, GAISLER_GPTIMER}, + {0, 0} /* Mark end of table */ +}; + +struct amba_drv_info gptimer_drv_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_GAISLER_GPTIMER_ID,/* Driver ID */ + "GPTIMER_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &gptimer_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + 0, + }, + &gptimer_ids[0] +}; + +void gptimer_register_drv (void) +{ + DBG("Registering GPTIMER driver\n"); + drvmgr_drv_register(&gptimer_drv_info.general); +} + +int gptimer_init1(struct drvmgr_dev *dev) +{ + struct gptimer_priv *priv; + struct gptimer_regs *regs; + struct amba_dev_info *ambadev; + struct ambapp_core *pnpinfo; + int timer_hw_cnt, timer_cnt, timer_start; + int i, size; + struct gptimer_timer *timer; + union drvmgr_key_value *value; + unsigned char irq_ack_mask; +#if defined(LEON3) && defined(RTEMS_DRVMGR_STARTUP) + char timer_index[7]; +#endif + + /* Get device information from AMBA PnP information */ + ambadev = (struct amba_dev_info *)dev->businfo; + if ( ambadev == NULL ) { + return -1; + } + pnpinfo = &ambadev->info; + regs = (struct gptimer_regs *)pnpinfo->apb_slv->start; + + DBG("GPTIMER[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name); + + /* Get number of Timers */ + timer_hw_cnt = regs->cfg & GPTIMER_CFG_TIMERS; + + /* Let user spelect a range of timers to be used. In AMP systems + * it is sometimes neccessary to leave timers for other CPU instances. + * + * The default operation in AMP is to shared the timers within the + * first GPTIMER core as below. This can of course be overrided by + * driver resources. + */ + timer_cnt = timer_hw_cnt; + timer_start = 0; +#if defined(RTEMS_MULTIPROCESSING) && defined(LEON3) + if ((dev->minor_drv == 0) && drvmgr_on_rootbus(dev)) { + timer_cnt = 1; + timer_start = LEON3_Cpu_Index; + } +#endif + value = drvmgr_dev_key_get(dev, "timerStart", KEY_TYPE_INT); + if ( value) { + timer_start = value->i; + timer_cnt = timer_hw_cnt - timer_start; + } + value = drvmgr_dev_key_get(dev, "timerCnt", KEY_TYPE_INT); + if ( value && (value->i < timer_cnt) ) { + timer_cnt = value->i; + } + + /* Allocate Common Timer Description, size depends on how many timers + * are present. + */ + size = sizeof(struct gptimer_priv) + + timer_cnt*sizeof(struct gptimer_timer); + priv = dev->priv = (struct gptimer_priv *)malloc(size); + if ( !priv ) + return DRVMGR_NOMEM; + memset(priv, 0, size); + priv->dev = dev; + priv->regs = regs; + +#if defined(LEON3) && defined(RTEMS_DRVMGR_STARTUP) + if ( drvmgr_on_rootbus(priv->dev) && !LEON3_Timer_Regs) { + /* Bootloader has initialized the Timer prescaler to 1MHz, + * this means that the AMBA Frequency is 1MHz * PRESCALER. + */ + priv->base_clk = (regs->scaler_reload + 1) * 1000000; + ambapp_bus_freq_register(priv->dev,DEV_APB_SLV,priv->base_clk); + LEON3_Timer_Regs = (void *)regs; + } else +#endif + { + /* The Base Frequency of the GPTIMER core is the same as the + * frequency of the AMBA bus it is situated on. + */ + drvmgr_freq_get(dev, DEV_APB_SLV, &priv->base_clk); + } + + /* This core will may provide important Timer functionality + * to other drivers and the RTEMS kernel, the Clock driver + * may for example use this device. So the Timer driver must be + * initialized in the first iiitialization stage. + */ + + /*** Initialize Hardware ***/ + + /* If user request to set prescaler, we will do that. However, note + * that doing so for the Root-Bus GPTIMER may affect the RTEMS Clock + * so that Clock frequency is wrong. + */ + value = drvmgr_dev_key_get(priv->dev, "prescaler", KEY_TYPE_INT); + if ( value ) + regs->scaler_reload = value->i; + + /* Get Frequency that the timers are operating in (after prescaler) */ + priv->base_freq = priv->base_clk / (priv->regs->scaler_reload + 1); + + /* Stop Timer and probe Pending bit. In newer hardware the + * timer has pending bit is cleared by writing a one to it, + * whereas older versions it is cleared with a zero. + */ + priv->regs->timer[0].ctrl = GPTIMER_CTRL_IP; + if ((priv->regs->timer[0].ctrl & GPTIMER_CTRL_IP) != 0) + irq_ack_mask = ~GPTIMER_CTRL_IP; + else + irq_ack_mask = ~0; + + priv->timer_cnt = timer_cnt; + for (i=0; i<timer_cnt; i++) { + timer = &priv->timers[i]; + timer->index = i; + timer->tindex = i + timer_start; + timer->tregs = ®s->timer[(int)timer->tindex]; + timer->tdev.drv = &gptimer_tlib_drv; + timer->irq_ack_mask = irq_ack_mask; + + /* Register Timer at Timer Library */ +#if defined(LEON3) && defined(RTEMS_DRVMGR_STARTUP) + timer_index[i] = +#endif + tlib_dev_reg(&timer->tdev); + } + + /* Check Interrupt support implementation, two cases: + * A. All Timers share one IRQ + * B. Each Timer have an individual IRQ. The number is: + * BASE_IRQ + timer_index + */ + priv->separate_interrupt = regs->cfg & GPTIMER_CFG_SI; + + if ( priv->separate_interrupt == 0 ) { + /* Shared IRQ handler */ + drvmgr_interrupt_register( + priv->dev, + 0, + "gptimer_shared", + gptimer_isr, + priv); + } + + /* Older HW */ + + + + /* If the user request a certain Timer to be the RTEMS Clock Timer, + * the timer must be registered at the Clock Driver. + */ +#if defined(LEON3) && defined(RTEMS_DRVMGR_STARTUP) + value = drvmgr_dev_key_get(priv->dev, "clockTimer", KEY_TYPE_INT); + if ( value && (value->i < timer_cnt) ) { + LEON3_Timer_Regs = (void *)regs; + Clock_timer_register(timer_index[value->i]); + } +#endif + + return DRVMGR_OK; +} + +#ifdef GPTIMER_INFO_AVAIL +static int gptimer_info( + struct drvmgr_dev *dev, + void (*print_line)(void *p, char *str), + void *p, int argc, char *argv[]) +{ + struct gptimer_priv *priv = dev->priv; + struct gptimer_timer *timer; + char buf[64]; + int i; + + if (priv == NULL || argc != 0) + return -DRVMGR_EINVAL; + + sprintf(buf, "Timer Count: %d", priv->timer_cnt); + print_line(p, buf); + sprintf(buf, "REGS: 0x%08x", (unsigned int)priv->regs); + print_line(p, buf); + sprintf(buf, "BASE SCALER: %d", priv->regs->scaler_reload); + print_line(p, buf); + sprintf(buf, "BASE FREQ: %dkHz", priv->base_freq / 1000); + print_line(p, buf); + sprintf(buf, "SeparateIRQ: %s", priv->separate_interrupt ? "YES":"NO"); + print_line(p, buf); + + for (i=0; i<priv->timer_cnt; i++) { + timer = &priv->timers[i]; + sprintf(buf, " - TIMER HW Index %d -", timer->tindex); + print_line(p, buf); + sprintf(buf, " TLIB Index: %d", timer->index); + print_line(p, buf); + sprintf(buf, " RELOAD REG: %d", timer->tregs->reload); + print_line(p, buf); + sprintf(buf, " CTRL REG: %d", timer->tregs->ctrl); + print_line(p, buf); + } + + return DRVMGR_OK; +} +#endif + +static inline struct gptimer_priv *priv_from_timer(struct gptimer_timer *t) +{ + return (struct gptimer_priv *) + ((unsigned int)t - + sizeof(struct gptimer_priv) - + t->index * sizeof(struct gptimer_timer)); +} + +int gptimer_tlib_int_pend(struct tlib_dev *hand, int ack) +{ + struct gptimer_timer *timer = (struct gptimer_timer *)hand; + unsigned int ctrl = timer->tregs->ctrl; + + if ((ctrl & (GPTIMER_CTRL_IP | GPTIMER_CTRL_IE)) == + (GPTIMER_CTRL_IP | GPTIMER_CTRL_IE)) { + /* clear Pending IRQ ? */ + if (ack) + timer->tregs->ctrl = ctrl & timer->irq_ack_mask; + return 1; /* timer generated IRQ */ + } else + return 0; /* was not timer causing IRQ */ +} + +void gptimer_isr(void *data) +{ + struct gptimer_priv *priv = data; + int i; + + /* Check all timers for IRQ */ + for (i=0;i<priv->timer_cnt; i++) { + if (gptimer_tlib_int_pend((void *)&priv->timers[i], 0)) { + /* IRQ Was generated by Timer and Pending flag has *not* + * yet been cleared, this is to allow ISR to look at + * pending bit. Call ISR registered. Clear pending bit. + */ + if (priv->timers[i].tdev.isr_func) { + priv->timers[i].tdev.isr_func( + priv->timers[i].tdev.isr_data); + } + gptimer_tlib_int_pend((void *)&priv->timers[i], 1); + } + } +} + +void gptimer_tlib_reset(struct tlib_dev *hand) +{ + struct gptimer_timer *timer = (struct gptimer_timer *)hand; + + timer->tregs->ctrl = 0; + timer->tregs->reload = 0xffffffff; + timer->tregs->ctrl = GPTIMER_CTRL_LD; +} + +void gptimer_tlib_get_freq( + struct tlib_dev *hand, + unsigned int *basefreq, + unsigned int *tickrate) +{ + struct gptimer_timer *timer = (struct gptimer_timer *)hand; + struct gptimer_priv *priv = priv_from_timer(timer); + + /* Calculate base frequency from Timer Clock and Prescaler */ + if ( basefreq ) + *basefreq = priv->base_freq; + if ( tickrate ) + *tickrate = timer->tregs->reload + 1; +} + +int gptimer_tlib_set_freq(struct tlib_dev *hand, unsigned int tickrate) +{ + struct gptimer_timer *timer = (struct gptimer_timer *)hand; + + timer->tregs->reload = tickrate - 1; + + /*Check that value was allowed (Timer may not be as wide as expected)*/ + if ( timer->tregs->reload != (tickrate - 1) ) + return -1; + else + return 0; +} + +void gptimer_tlib_irq_reg(struct tlib_dev *hand, tlib_isr_t func, void *data) +{ + struct gptimer_timer *timer = (struct gptimer_timer *)hand; + struct gptimer_priv *priv = priv_from_timer(timer); + + if ( priv->separate_interrupt ) { + drvmgr_interrupt_register(priv->dev, timer->tindex, + "gptimer", func, data); + } + + timer->tregs->ctrl |= GPTIMER_CTRL_IE; +} + +void gptimer_tlib_irq_unreg(struct tlib_dev *hand, tlib_isr_t func, void *data) +{ + struct gptimer_timer *timer = (struct gptimer_timer *)hand; + struct gptimer_priv *priv = priv_from_timer(timer); + + /* Turn off IRQ at source, unregister IRQ handler */ + timer->tregs->ctrl &= ~GPTIMER_CTRL_IE; + + if ( priv->separate_interrupt ) { + drvmgr_interrupt_unregister(priv->dev, timer->tindex, + func, data); + } else { + timer->tdev.isr_func = NULL; + } +} + +void gptimer_tlib_start(struct tlib_dev *hand, int once) +{ + struct gptimer_timer *timer = (struct gptimer_timer *)hand; + unsigned int ctrl; + + /* Load the selected frequency before starting Frequency */ + ctrl = GPTIMER_CTRL_LD | GPTIMER_CTRL_EN; + if ( once == 0 ) + ctrl |= GPTIMER_CTRL_RS; /* Restart Timer */ + timer->tregs->ctrl |= ctrl; +} + +void gptimer_tlib_stop(struct tlib_dev *hand) +{ + struct gptimer_timer *timer = (struct gptimer_timer *)hand; + + /* Load the selected Frequency */ + timer->tregs->ctrl &= ~(GPTIMER_CTRL_EN|GPTIMER_CTRL_IP); +} + +void gptimer_tlib_restart(struct tlib_dev *hand) +{ + struct gptimer_timer *timer = (struct gptimer_timer *)hand; + + timer->tregs->ctrl |= GPTIMER_CTRL_LD | GPTIMER_CTRL_EN; +} + +void gptimer_tlib_get_counter(struct tlib_dev *hand, unsigned int *counter) +{ + struct gptimer_timer *timer = (struct gptimer_timer *)hand; + + *counter = timer->tregs->value; +} + +struct tlib_drv gptimer_tlib_drv = +{ + .reset = gptimer_tlib_reset, + .get_freq = gptimer_tlib_get_freq, + .set_freq = gptimer_tlib_set_freq, + .irq_reg = gptimer_tlib_irq_reg, + .irq_unreg = gptimer_tlib_irq_unreg, + .start = gptimer_tlib_start, + .stop = gptimer_tlib_stop, + .restart = gptimer_tlib_restart, + .get_counter = gptimer_tlib_get_counter, + .custom = NULL, + .int_pend = gptimer_tlib_int_pend, +}; diff --git a/c/src/lib/libbsp/sparc/shared/timer/tlib.c b/c/src/lib/libbsp/sparc/shared/timer/tlib.c new file mode 100644 index 0000000000..3cfb7e4f6c --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/timer/tlib.c @@ -0,0 +1,66 @@ +#include <rtems.h> +#include <tlib.h> + +struct tlib_dev *tlib_dev_head = NULL; +struct tlib_dev *tlib_dev_tail = NULL; +static int tlib_dev_cnt = 0; + +/* Register Timer device to Timer Library */ +int tlib_dev_reg(struct tlib_dev *newdev) +{ + /* Reset device */ + newdev->status = 0; + newdev->isr_func = NULL; + newdev->index = tlib_dev_cnt; + + /* Insert last in queue */ + newdev->next = NULL; + if ( tlib_dev_tail == NULL ) { + tlib_dev_head = newdev; + } else { + tlib_dev_tail->next = newdev; + } + tlib_dev_tail = newdev; + + /* Return Index of Registered Timer */ + return tlib_dev_cnt++; +} + +void *tlib_open(int timer_no) +{ + struct tlib_dev *dev; + + if ( timer_no < 0 ) + return NULL; + + dev = tlib_dev_head; + while ( (timer_no > 0) && dev ) { + timer_no--; + dev = dev->next; + } + if ( dev ) { + if ( dev->status ) + return NULL; + dev->status = 1; + /* Reset Timer to initial state */ + tlib_reset(dev); + } + return dev; +} + +void tlib_close(void *hand) +{ + struct tlib_dev *dev = hand; + + /* Stop any ongoing timer operation and unregister IRQ if registered */ + tlib_stop(dev); + tlib_irq_unregister(dev); + + /* Mark not open */ + dev->status = 0; +} + +int tlib_ntimer(void) +{ + return tlib_dev_cnt; +} diff --git a/c/src/lib/libbsp/sparc/shared/timer/tlib_ckinit.c b/c/src/lib/libbsp/sparc/shared/timer/tlib_ckinit.c new file mode 100644 index 0000000000..a67d31c5ba --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/timer/tlib_ckinit.c @@ -0,0 +1,280 @@ +/* + * Clock Tick Device Driver using Timer Library implemented + * by the GRLIB GPTIMER / LEON2 Timer drivers. + * + * COPYRIGHT (c) 2010. + * 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. + * + */ + +#include <stdlib.h> +#include <bsp.h> +#include <tlib.h> + +/* Undefine (default) this to save space in standard LEON configurations, + * it will assume that Prescaler is running at 1MHz. + */ +/*#undef CLOCK_DRIVER_DONT_ASSUME_PRESCALER_1MHZ*/ + +/* Set the below defines from bsp.h if function needed. +#undef CLOCK_DRIVER_ISRS_PER_TICK +#undef CLOCK_DRIVER_USE_FAST_IDLE +*/ +#define Clock_driver_support_at_tick() + +/* + * Number of Clock ticks since initialization + */ +volatile uint32_t Clock_driver_ticks; + +/* + * Timer Number in Timer Library. Defaults to the first Timer in + * the System. + */ +int Clock_timer = 0; + +/* + * Timer Handle in Timer Library + */ +void *Clock_handle = NULL; + +#ifdef CLOCK_DRIVER_DONT_ASSUME_PRESCALER_1MHZ +unsigned int Clock_basefreq; +#endif + +void Clock_exit(void); + +/* + * Major and minor number. + */ + +rtems_device_major_number rtems_clock_major = UINT32_MAX; +rtems_device_minor_number rtems_clock_minor; + +/* + * Clock_isr + * + * This is the clock tick interrupt handler. + * + * Input parameters: + * vector - vector number + * + * Output parameters: NONE + * + * Return values: NONE + * + */ + +void Clock_isr(void *arg_unused) +{ + /* + * Support for shared interrupts. Ack IRQ at source, only handle + * interrupts generated from the tick-timer. Clearing pending bit + * is also needed for Clock_nanoseconds_since_last_tick() to work. + */ + if ( tlib_interrupt_pending(Clock_handle, 1) == 0 ) + return; + + /* + * Accurate count of ISRs + */ + + Clock_driver_ticks += 1; + +#ifdef CLOCK_DRIVER_USE_FAST_IDLE + do { + rtems_clock_tick(); + } while ( _Thread_Executing == _Thread_Idle && + _Thread_Heir == _Thread_Executing); + + Clock_driver_support_at_tick(); + return; + +#else + + /* + * Add custom handling at every tick from bsp.h + */ + Clock_driver_support_at_tick(); + +#ifdef CLOCK_DRIVER_ISRS_PER_TICK + /* + * The driver is multiple ISRs per clock tick. + */ + + if ( !Clock_driver_isrs ) { + + rtems_clock_tick(); + + Clock_driver_isrs = CLOCK_DRIVER_ISRS_PER_TICK; + } + Clock_driver_isrs--; +#else + + /* + * The driver is one ISR per clock tick. + */ + rtems_clock_tick(); +#endif +#endif +} + +/* + * Clock_exit + * + * This routine allows the clock driver to exit by masking the interrupt and + * disabling the clock's counter. + * + * Input parameters: NONE + * + * Output parameters: NONE + * + * Return values: NONE + * + */ + +void Clock_exit( void ) +{ + /* Stop all activity of the Timer, no more ISRs. + * We could be using tlib_close(), however tlib_stop() is quicker + * and independent of IRQ unregister code. + */ + if ( Clock_handle ) { + tlib_stop(Clock_handle); + Clock_handle = NULL; + } +} + +uint32_t Clock_nanoseconds_since_last_tick(void) +{ + uint32_t clicks; + int ip; + + if ( !Clock_handle ) + return 0; + + tlib_get_counter(Clock_handle, (unsigned int *)&clicks); + /* protect against timer counter underflow/overflow */ + ip = tlib_interrupt_pending(Clock_handle, 0); + if (ip) + tlib_get_counter(Clock_handle, (unsigned int *)&clicks); + +#ifdef CLOCK_DRIVER_DONT_ASSUME_PRESCALER_1MHZ + { + /* Down counter. Calc from BaseFreq. */ + uint64_t tmp; + if (ip) + clicks += Clock_basefreq; + tmp = ((uint64_t)clicks * 1000000000) / ((uint64_t)Clock_basefreq); + return (uint32_t)tmp; + } +#else + /* Down counter. Timer base frequency is initialized to 1 MHz */ + return (uint32_t) + ((rtems_configuration_get_microseconds_per_tick() << ip) - clicks) * 1000; +#endif +} + +/* + * Clock_initialize + * + * This routine initializes the clock driver and starts the Clock. + * + * Input parameters: + * major - clock device major number + * minor - clock device minor number + * parg - pointer to optional device driver arguments + * + * Output parameters: NONE + * + * Return values: + * rtems_device_driver status code + */ + +rtems_device_driver Clock_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *pargp +) +{ + unsigned int tick_hz; + + /* + * Take Timer that should be used as system timer. + * + */ + Clock_handle = tlib_open(Clock_timer); + if ( Clock_handle == NULL ) { + /* System Clock Timer not found */ + return RTEMS_NOT_DEFINED; + } + + /* + * Install Clock ISR before starting timer + */ + tlib_irq_register(Clock_handle, Clock_isr, NULL); + + /* Set Timer Frequency to tick at Configured value. The Timer + * Frequency is set in multiples of the timer base frequency. + * + * In standard LEON3 designs the base frequency is is 1MHz, to + * save instructions define CLOCK_DRIVER_ASSUME_PRESCALER_1MHZ + * to avoid 64-bit calculation. + */ +#ifdef CLOCK_DRIVER_DONT_ASSUME_PRESCALER_1MHZ + { + uint64_t tmp; + + tlib_get_freq(Clock_handle, &Clock_basefreq, NULL); + + tmp = (uint64_t)Clock_basefreq; + tmp = tmp * (unint64_t)rtems_configuration_get_microseconds_per_tick(); + tick_hz = tmp / 1000000; + } +#else + tick_hz = rtems_configuration_get_microseconds_per_tick(); +#endif + + tlib_set_freq(Clock_handle, tick_hz); + + rtems_clock_set_nanoseconds_extension(Clock_nanoseconds_since_last_tick); + + /* + * IRQ and Frequency is setup, now we start the Timer. IRQ is still + * disabled globally during startup, so IRQ will hold for a while. + */ + tlib_start(Clock_handle, 0); + + /* + * Register function called at system shutdown + */ + atexit( Clock_exit ); + + /* + * make major/minor avail to others such as shared memory driver + */ + + rtems_clock_major = major; + rtems_clock_minor = minor; + + /* + * If we are counting ISRs per tick, then initialize the counter. + */ + +#ifdef CLOCK_DRIVER_ISRS_PER_TICK + Clock_driver_isrs = CLOCK_DRIVER_ISRS_PER_TICK; +#endif + + return RTEMS_SUCCESSFUL; +} + +/*** Timer Driver Interface ***/ + +void Clock_timer_register(int timer_number) +{ + Clock_timer = timer_number; +} 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..470c5c7eb4 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/tmtc/grtc.c @@ -0,0 +1,1960 @@ +/* GRTC Telecommand decoder driver + * + * -------------------------------------------------------------------------- + * -- This file is a part of GAISLER RESEARCH source code. + * -- Copyright (C) 2009, Aeroflex Gaisler AB - all rights reserved. + * -- + * -- ANY USE OR REDISTRIBUTION IN PART OR IN WHOLE MUST BE HANDLED IN + * -- ACCORDANCE WITH THE GAISLER LICENSE AGREEMENT AND MUST BE APPROVED + * -- IN ADVANCE IN WRITING. + * -- + * -- BY DEFAULT, DISTRIBUTION OR DISCLOSURE IS NOT PERMITTED. + * -------------------------------------------------------------------------- + * + * 2008-12-11, Daniel Hellstrom <daniel@gaisler.com> + * Converted to support driver manager + * + * 2007-04-01, Daniel Hellstrom <daniel@gaisler.com> + * New driver in sparc shared directory. + * + */ + +#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 ){ + if ( buf_arg->custom_buffer ){ + if ( (unsigned int)buf_arg->custom_buffer & 1 ) { + /* Remote address given, the address is as the GRTC core looks at it */ + + /* Translate the base address into an address that the the CPU can understand */ + mem = ((unsigned int)buf_arg->custom_buffer & ~1); + drvmgr_translate(pDev->dev, 1, 1, (void *)mem, (void **)&pDev->buf); + } else { + pDev->buf = buf_arg->custom_buffer; + } + pDev->buf_custom = 1; + }else{ + 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; + } + } + } + /* 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(pDev->dev, 0, 0, (void *)pDev->buf, (void **)&pDev->buf_remote); + 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/grtc_rmap.c b/c/src/lib/libbsp/sparc/shared/tmtc/grtc_rmap.c new file mode 100644 index 0000000000..ac4586797f --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/tmtc/grtc_rmap.c @@ -0,0 +1,2233 @@ +/* GRTC Telecommand decoder driver + * + * -------------------------------------------------------------------------- + * -- This file is a part of GAISLER RESEARCH source code. + * -- Copyright (C) 2009, Aeroflex Gaisler AB - all rights reserved. + * -- + * -- ANY USE OR REDISTRIBUTION IN PART OR IN WHOLE MUST BE HANDLED IN + * -- ACCORDANCE WITH THE GAISLER LICENSE AGREEMENT AND MUST BE APPROVED + * -- IN ADVANCE IN WRITING. + * -- + * -- BY DEFAULT, DISTRIBUTION OR DISCLOSURE IS NOT PERMITTED. + * -------------------------------------------------------------------------- + * + * The BSP define GRTC_RMAP_INFO_AVAIL in order to add the info routine + * used for debugging. + * + * 2009-11-21, Daniel Hellstrom <daniel@gaisler.com> + * Created from on-chip GRTC driver + * + */ + +#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 <drvmgr/ambapp_bus_rmap.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 + +/**** START: RMAP STUFF ****/ +#define DESCRIPTOR_MAX 128 +#define WRITE_REG(pDev, adr, value) pDev->rw_w32((uint32_t *)adr, (uint32_t)value, &pDev->rw_arg) +#define READ_REG(pDev, adr) pDev->rw_r32((uint32_t *)adr, &pDev->rw_arg) +#define MEMGET(pDev, dst, src, len) pDev->rw_rmem(dst, (const void *)src, len, &pDev->rw_arg) + +/**** END: RMAP STUFF ****/ + +/* +#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) */ +}; + +/* Holds the cached data and the cached READ/WRITE pointers. + * + * IMPORTANT: The READ/WRITE pointers must always reflect the cached data available, the real + * hardware increment it's WRITE pointer all the time. We update the hw READ pointer + * each time we copy from the DMA area into our "cached" data area. + * The software update the cached READ pointer, however this is ignored by the "sync" + * routine, since the hw READ pointer has already been updated when coping the data + * to the cached area. + * + * buf: Set to CPU local SRAM copy (cached copy) + * buf_remote: Set to remote buffer start (original) + */ +struct cached_regs { + unsigned int asr; + unsigned int rp; + unsigned int wp; +}; + +#define CACHED_READ_REG(priv, name) ((priv)->cache.name) +#define CACHED_WRITE_REG(priv, name, val) ((priv)->cache.name = (val)) + +struct grtc_priv { + struct drvmgr_dev *dev; /* Driver manager device */ + char devName[64]; /* 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; + int alloc_part_dma; + +/* 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; + + struct cached_regs cache; + struct drvmgr_rw_arg rw_arg; + ambapp_rmap_w32 rw_w32; + ambapp_rmap_r32 rw_r32; + ambapp_rmap_rmem rw_rmem; + int irq_support; + +#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); + +#ifdef GRTC_RMAP_INFO_AVAIL +static int grtc_info( + struct drvmgr_dev *dev, + void (*print_line)(void *p, char *str), + void *p, int, char *argv[]); +#define GRTC_INFO_FUNC grtc_info +#else +#define GRTC_INFO_FUNC NULL +#endif + +static struct drvmgr_drv_ops grtc_ops = +{ + .init = {NULL, grtc_init2, grtc_init3, NULL}, + .remove = NULL, + .info = GRTC_INFO_FUNC, +}; + +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_RMAP, /* Bus Type */ + &grtc_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + sizeof(struct grtc_priv), + }, + &grtc_ids[0] +}; + +void grtc_rmap_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; + + /* Get Read/Write operations for bus */ + priv->rw_arg.dev = dev; + priv->rw_arg.arg = (void *)drvmgr_func_call(dev->parent, AMBAPP_RMAP_RW_ARG, dev, NULL, NULL, NULL); + drvmgr_func_get(dev->parent, AMBAPP_RMAP_R32, (void *)&priv->rw_r32); + drvmgr_func_get(dev->parent, AMBAPP_RMAP_W32, (void *)&priv->rw_w32); + drvmgr_func_get(dev->parent, AMBAPP_RMAP_RMEM, (void *)&priv->rw_rmem); + + /* 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; + 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 grtc_regs *)pnpinfo->ahb_slv->start[0]; + pDev->minor = pDev->dev->minor_drv; + pDev->open = 0; + pDev->running = 0; + + pDev->alloc_part_dma = 0; + value = drvmgr_dev_key_get(pDev->dev, "dmaAllocPartition", KEY_TYPE_INT); + if ( value ) + pDev->alloc_part_dma = value->i; + + /* 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 */ + WRITE_REG(priv, &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, &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); +} + +#if NOT_IMPLEMENTED +/* Reads as much as possible 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; +} + +/* Calculates how many bytes are available in the TC DMA buffer area, both in upper + * and lower parts of the TC DMA area. + */ +static int grtc_data_avail(struct grtc_priv *pDev) +{ + unsigned int rp, wp, asr, bufmax, rrp, rwp; + struct grtc_regs *regs = pDev->regs; +#warning NOT CONVERTED + + 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)); +} +#endif + +static int grtc_start(struct grtc_priv *pDev) +{ + struct grtc_regs *regs = pDev->regs; + unsigned int tmp; + + if ( !pDev->buf || + (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 */ + WRITE_REG(pDev, ®s->cor, (GRTC_SEB | GRTC_COR_CRST)); + if ( READ_REG(pDev, ®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; + WRITE_REG(pDev, ®s->gctrl, (GRTC_SEB | tmp)); + + /* Clear any pending interrupt */ + tmp = READ_REG(pDev, ®s->pir); + WRITE_REG(pDev, ®s->picr, GRTC_INT_ALL); + + /* Unmask only the Overrun interrupt */ + WRITE_REG(pDev, ®s->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. + */ + WRITE_REG(pDev, ®s->asr, ((unsigned int)pDev->buf_remote | ((pDev->len>>10)-1))); + WRITE_REG(pDev, ®s->rp, (unsigned int)pDev->buf_remote); + + /* Update Cache (ASR and READ/WRITE POINTERS) */ + MEMGET(pDev, &pDev->cache, &pDev->regs->asr, sizeof(struct cached_regs)); + + /* 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 */ + WRITE_REG(pDev, ®s->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 */ + WRITE_REG(pDev, ®s->cor, GRTC_SEB); + + /* disable all interrupts and clear them */ + WRITE_REG(pDev, ®s->imr, 0); + tmp = READ_REG(pDev, ®s->pir); + WRITE_REG(pDev, ®s->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); +} + +#if NOT_IMPLEMENTED +/* 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; +} +#endif + +/* Update cached area */ +static int grtc_sync_dma_area(struct grtc_priv *pDev, struct cached_regs *cache) +{ + struct cached_regs hw; + unsigned int cache_space, cache_data_avail; + unsigned int count; + unsigned int new_wp; + unsigned int cache_bufmax, cache_rrp, cache_rwp, cache_upper, cache_lower; + + unsigned int hw_data_avail; + unsigned int hw_bufmax, hw_rrp, hw_rwp, hw_upper, hw_lower; + + int left, cnt; + + /* 1. Calculate how much data that can be copied into our cache */ + cache_bufmax = (cache->asr & GRTC_ASR_RXLEN) >> GRTC_ASR_RXLEN_BIT; + cache_bufmax = (cache_bufmax+1) << 10; /* Convert from 1kbyte blocks into bytes */ + /* Relative rp and wp */ + cache_rrp = cache->rp - (cache->asr & GRTC_ASR_BUFST); + cache_rwp = cache->wp - (cache->asr & GRTC_ASR_BUFST); + cache_upper = grtc_hw_data_avail_upper(cache_rrp, cache_rwp, cache_bufmax); + cache_lower = grtc_hw_data_avail_lower(cache_rrp, cache_rwp, cache_bufmax); + cache_data_avail = cache_upper + cache_lower; + cache_space = pDev->len - cache_data_avail; + if ( cache_space < (128+8) ) { + /* Never copy less than 128Bytes when so cache is so full */ + return 0; + } + cache_space -= 8; /* Never fill cache, to avoid special cases. */ + + /* 2. Read ASR and READ/WRITE POINTERS over SpW */ + MEMGET(pDev, &hw, &pDev->regs->asr, sizeof(struct cached_regs)); + + /* 3. Calculate how much data is available in the non-cached DMA data area + * on the remote target. + */ + hw_bufmax = (hw.asr & GRTC_ASR_RXLEN) >> GRTC_ASR_RXLEN_BIT; + hw_bufmax = (hw_bufmax+1) << 10; /* Convert from 1kbyte blocks into bytes */ + /* Relative rp and wp */ + hw_rrp = hw.rp - (hw.asr & GRTC_ASR_BUFST); + hw_rwp = hw.wp - (hw.asr & GRTC_ASR_BUFST); + hw_upper = grtc_hw_data_avail_upper(hw_rrp, hw_rwp, hw_bufmax); + hw_lower = grtc_hw_data_avail_lower(hw_rrp, hw_rwp, hw_bufmax); + hw_data_avail = hw_upper + hw_lower; + + /* 4. Decide how much to copy */ + if ( hw_data_avail > cache_space ) { + count = cache_space; + } else { + count = hw_data_avail; + } + /* A frame header is always at least 5 bytes + 5 control bytes = 10bytes */ + if ( count < 10 ) { + return 0; + } + /* Avoid copying less than 128 unless we have nothing to process. Say nothing less than + * 128 bytes is ever copied, we may miss a couple of small frames if no further TC frames + * are received. + */ + if ( (count < 128) && (cache_data_avail > 10 ) ) { + /* Never copy less than 128Bytes, unless we have nothing to process */ + return 0; + } + left = count; + + /* 5. Copy from Upper */ + if ( hw_upper > 0) { + if ( left < hw_upper ) { + cnt = left; + } else { + cnt = hw_upper; + } + MEMGET(pDev, ((char *)pDev->buf + hw_rrp), (void *)((char *)pDev->buf_remote + hw_rrp), cnt); + left -= cnt; + + new_wp = cache->wp + cnt; + } + + /* 6. Copy from Lower */ + if ( left > 0 ) { + if ( left < hw_lower ){ + cnt = left; + } else { + cnt = hw_lower; /* Read all lower data available */ + } + MEMGET(pDev, (void *)pDev->buf, (void *)pDev->buf_remote, cnt); + left -= cnt; + + new_wp = (unsigned int)((char *)pDev->buf_remote + cnt); + } + + /* 7. Update Hardware registers, READ pointer to tell hardware about new space available */ + if ( (hw.rp+count) >= ((hw.asr&GRTC_ASR_BUFST)+hw_bufmax) ){ + WRITE_REG(pDev, &pDev->regs->rp, (hw.rp+count-hw_bufmax)); + } else { + WRITE_REG(pDev, &pDev->regs->rp, (hw.rp+count)); + } + + /* 8. Update Cached registers, WRITE pointer */ + cache->wp = new_wp; + + return 0; +} + +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)); + + memset(&pDev->cache,0,sizeof(pDev->cache)); + + /* 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) +{ +#if NOT_IMPLEMENTED + 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; + } +#endif + 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) +{ + unsigned int rp, wp, asr, bufmax, rrp, rwp; + unsigned int upper, lower; + unsigned int count, cnt; + int found; + + FUNCDBG(); + + rp = CACHED_READ_REG(pDev, rp); + asr = CACHED_READ_REG(pDev, asr); + wp = CACHED_READ_REG(pDev, 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) ){ + CACHED_WRITE_REG(pDev, rp, (rp+count-bufmax)); + } else { + CACHED_WRITE_REG(pDev, 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) +{ + 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 = CACHED_READ_REG(pDev, rp); + asr = CACHED_READ_REG(pDev, asr); + bufmax = (asr & GRTC_ASR_RXLEN) >> GRTC_ASR_RXLEN_BIT; + bufmax = (bufmax+1) << 10; /* Convert from 1kbyte blocks into bytes */ + wp = CACHED_READ_REG(pDev, 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) ){ + CACHED_WRITE_REG(pDev, rp, (rp+count-bufmax)); + } else { + CACHED_WRITE_REG(pDev, 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) +{ + 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 = CACHED_READ_REG(pDev, rp); + asr = CACHED_READ_REG(pDev, asr); + bufmax = (asr & GRTC_ASR_RXLEN) >> GRTC_ASR_RXLEN_BIT; + bufmax = (bufmax+1) << 10; /* Convert from 1kbyte blocks into bytes */ + wp = CACHED_READ_REG(pDev, 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) ){ + CACHED_WRITE_REG(pDev, rp, (rp+count-bufmax)); + } else { + CACHED_WRITE_REG(pDev, 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 tmpdata[8]; + +#if NOT_IMPLEMENTED + IRQ_GLOBAL_PREPARE(oldLevel); +#endif + + 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 & Unmask interrupt */ + if (drvmgr_interrupt_register(pDev->dev, 0, "grtc_rmap", grtc_interrupt, pDev) != DRVMGR_OK) + pDev->irq_support = 0; + else + pDev->irq_support = 1; + + /* Read and write are now open... */ + break; + + case GRTC_IOC_STOP: + if ( !pDev->running ) { + return RTEMS_RESOURCE_IN_USE; + } + if ( pDev->irq_support ) + 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 ) { + free(pDev->buf); + if ( !pDev->buf_custom ) + ; /* Return buf_remote to AMBAPP RMAP bus */ + pDev->_buf = NULL; + } + pDev->buf = NULL; + pDev->buf_custom = 0; + pDev->len = buf_arg->length*1024; + + if ( pDev->len > 0 ) { + pDev->buf = malloc(pDev->len); + + if ( buf_arg->custom_buffer ) { + /* No translation from CPU to RMAP target + * possible since not mapped in memory space. + */ + if (((unsigned int)buf_arg->custom_buffer & 0x1) == 0) + return RTEMS_INVALID_NAME; + pDev->buf_remote = (unsigned int)buf_arg->custom_buffer & ~0x1; + pDev->buf_custom = 1; + } else if ( pDev->buf ) { + pDev->buf_remote = (void *)ambapp_rmap_partition_memalign(pDev->dev, pDev->alloc_part_dma, (~GRTC_ASR_BUFST + 1), pDev->len); + } + + if ( !pDev->buf || !pDev->buf_remote ) { + if ( pDev->buf ) + free(pDev->buf); + pDev->len = 0; + pDev->_buf = NULL; + pDev->buf_remote = 0; + DBG("GRTC: Failed to allocate memory\n"); + return RTEMS_NO_MEMORY; + } + } + 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; + } +#if NOT_IMPLEMENTED + /* 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); +#endif + /* Read over SpaceWire */ + MEMGET(pDev, &tmpdata[0], &pDev->regs->sir, 7*sizeof(unsigned int)); + hwregs->sir = tmpdata[0]; + hwregs->far = tmpdata[1]; + hwregs->clcw1 = tmpdata[2]; + hwregs->clcw2 = tmpdata[3]; + hwregs->phir = tmpdata[4]; + hwregs->str = tmpdata[6]; /* 5=cor */ + 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; + } + + /* Update cache by copying from remote DMA area over SpW */ + grtc_sync_dma_area(pDev, &pDev->cache); + + 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 = 0; + return RTEMS_NOT_IMPLEMENTED; +/* This not implemented because Register is no directly mapped + *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(pDev, ®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 ){ +#warning NOT IMPLMEENTED +#if NOT_IMPLEMENTED + 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; +#endif + /* 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 ) + WRITE_REG(pDev, ®s->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; +} + +#ifdef GRTC_RMAP_INFO_AVAIL +static int grtc_info( + struct drvmgr_dev *dev, + void (*print_line)(void *p, char *str), + void *p, int argc, char *argv[]) +{ + struct grtc_priv *priv = dev->priv; + char buf[64]; + struct grtc_ioc_hw_status hwsts, *hwregs; + unsigned int tmpdata[8]; + + if (priv == NULL || argc != 0) + return -DRVMGR_EINVAL; + + sprintf(buf, "REGS: 0x%08x", (unsigned int)priv->regs); + print_line(p, buf); + sprintf(buf, "Device Name: %s", priv->devName); + print_line(p, buf); + sprintf(buf, "IRQ: %ssupported", priv->irq_support ? "" : "not "); + print_line(p, buf); + sprintf(buf, "STARTED: %s", priv->running ? "YES" : "NO"); + print_line(p, buf); + sprintf(buf, "MODE: %s", priv->mode ? "FRAME" : "RAW"); + print_line(p, buf); + sprintf(buf, "BLOCKING: %s", priv->blocking ? "BLOCKING" : "POLL"); + print_line(p, buf); + sprintf(buf, "DMA SIZE: %dBytes", priv->len); + print_line(p, buf); + sprintf(buf, "DMA AREA: %p - %p", + priv->buf_remote, priv->buf_remote+(priv->len-1)); + print_line(p, buf); + sprintf(buf, "NO. Pools: %d", priv->pool_cnt); + print_line(p, buf); + + /* Statistics */ + sprintf(buf, "Received OK: %llu frames", priv->stats.frames_recv); + print_line(p, buf); + sprintf(buf, "Errors: %u frames", priv->stats.err); + print_line(p, buf); + sprintf(buf, "Header err: %u frames", priv->stats.err_hdr); + print_line(p, buf); + sprintf(buf, "Dropped: %u frames", priv->stats.dropped); + print_line(p, buf); + sprintf(buf, "Ready: %u frames", priv->ready.cnt); + print_line(p, buf); + + /* Read over SpaceWire */ + MEMGET(priv, &tmpdata[0], &priv->regs->sir, 7*sizeof(unsigned int)); + hwregs = &hwsts; + hwregs->sir = tmpdata[0]; + hwregs->far = tmpdata[1]; + hwregs->clcw1 = tmpdata[2]; + hwregs->clcw2 = tmpdata[3]; + hwregs->phir = tmpdata[4]; + hwregs->str = tmpdata[6]; /* 5=cor */ + + print_line(p, "HW REGS:"); + sprintf(buf, " SIR: 0x%08x", hwregs->sir); + print_line(p, buf); + sprintf(buf, " FAR: 0x%08x", hwregs->far); + print_line(p, buf); + sprintf(buf, " CLCW1: 0x%08x", hwregs->clcw1); + print_line(p, buf); + sprintf(buf, " CLCW2: 0x%08x", hwregs->clcw2); + print_line(p, buf); + sprintf(buf, " PHIR: 0x%08x", hwregs->phir); + print_line(p, buf); + sprintf(buf, " STR: 0x%08x", hwregs->str); + print_line(p, buf); + + return DRVMGR_OK; +} +#endif 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..8c5bc95b93 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/tmtc/grtm.c @@ -0,0 +1,1599 @@ +/* GRTM CCSDS Telemetry Encoder driver + * + * -------------------------------------------------------------------------- + * -- This file is a part of GAISLER RESEARCH source code. + * -- Copyright (C) 2009, Aeroflex Gaisler AB - all rights reserved. + * -- + * -- ANY USE OR REDISTRIBUTION IN PART OR IN WHOLE MUST BE HANDLED IN + * -- ACCORDANCE WITH THE GAISLER LICENSE AGREEMENT AND MUST BE APPROVED + * -- IN ADVANCE IN WRITING. + * -- + * -- BY DEFAULT, DISTRIBUTION OR DISCLOSURE IS NOT PERMITTED. + * -------------------------------------------------------------------------- + * + * 2008-12-11, Daniel Hellstrom <daniel@gaisler.com> + * Converted to support driver manager + * + * 2007-04-17, Daniel Hellstrom <daniel@gaisler.com> + * New driver in sparc shared directory. + * + */ + +#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; + unsigned int txrdy; + + /* 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(pDev->dev, 0, 0, (void *)pDev->bds, (void **)®s->dma_bd); + /*regs->dma_bd = (unsigned int)pDev->bds;*/ + + /* 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--; + } + + /* Check transmitter startup OK */ + i = 1000000; + do { + /* Location of TXRDY Bit is different for different revisions */ + if ( pDev->subrev == 0 ) { + txrdy = READ_REG(®s->dma_ctrl) & + GRTM_REV0_DMA_CTRL_TXRDY; + } else { + txrdy = READ_REG(®s->dma_status) & + GRTM_REV1_DMA_STS_TXRDY; + } + if (txrdy != 0) + break; + + asm volatile ("nop"::); + } while ( --i > 0 ); + if ( i == 0 ) { + /* 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, 0, 0, (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; +} diff --git a/c/src/lib/libbsp/sparc/shared/tmtc/grtm_rmap.c b/c/src/lib/libbsp/sparc/shared/tmtc/grtm_rmap.c new file mode 100644 index 0000000000..ecce1cdbde --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/tmtc/grtm_rmap.c @@ -0,0 +1,1699 @@ +/* GRTM CCSDS Telemetry Encoder driver + * + * -------------------------------------------------------------------------- + * -- This file is a part of GAISLER RESEARCH source code. + * -- Copyright (C) 2009, Aeroflex Gaisler AB - all rights reserved. + * -- + * -- ANY USE OR REDISTRIBUTION IN PART OR IN WHOLE MUST BE HANDLED IN + * -- ACCORDANCE WITH THE GAISLER LICENSE AGREEMENT AND MUST BE APPROVED + * -- IN ADVANCE IN WRITING. + * -- + * -- BY DEFAULT, DISTRIBUTION OR DISCLOSURE IS NOT PERMITTED. + * -------------------------------------------------------------------------- + * + * 2009-11-23, Daniel Hellstrom <daniel@gaisler.com> + * Created from on-chip GRTM driver + * + */ + +#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 <drvmgr/ambapp_bus_rmap.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 + +/**** START: RMAP STUFF ****/ +#define DESCRIPTOR_MAX 128 +#define WRITE_REG(pDev, adr, value) pDev->rw_w32((uint32_t *)adr, (uint32_t)value, &pDev->rw_arg) +#define READ_REG(pDev, adr) pDev->rw_r32((uint32_t *)adr, &pDev->rw_arg) +#define TRANSFER_FRM(pDev, dstadr, srcadr, length) pDev->rw_wmem(dstadr, srcadr, length, &pDev->rw_arg) + +/* This call will take 128 bytes of buffer at stack */ +#define MEMSET(pDev, adr, c, length) pDev->rw_memset(adr, c, length, &pDev->rw_arg) + +/**** END: RMAP STUFF ****/ + +/* +#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 unused00[2]; + + volatile unsigned int ext_ctrl; /* External Control Register (0x20) */ + + int unused01[2]; + + volatile unsigned int ext_bd; /* External Descriptor Register (0x20) */ + + int unused0[(0x80-0x30)/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; + void *buf; /* Frame buffer in remote address */ +}; + +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; + + int alloc_part_bd; /* Partition to allocate descriptors from */ + int alloc_part_frm; /* Partition to allocate Frame buffers from */ + + struct grtm_bd *bds; + void *_bds; + unsigned char *_frame_buffers; + int frame_length_max; + + /* Interrupt generation */ + int enable_cnt_curr;/* Down counter, when 0 the interrupt bit is set for next descriptor */ + rtems_id handling_transmission; /* SEMAPHORE: 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; + + /* Read/Write access operations */ + struct drvmgr_rw_arg rw_arg; + ambapp_rmap_w32 rw_w32; + ambapp_rmap_r32 rw_r32; + ambapp_rmap_wmem rw_wmem; + ambapp_rmap_memset rw_memset; + + rtems_id sem_tx; +}; + +/* Prototypes */ +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_rmap_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_RMAP, /* Bus Type */ + &grtm_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + 0, + }, + &grtm_ids[0] +}; + +void grtm_rmap_register_drv (void) +{ + DBG("Registering RMAP-GRTM driver\n"); + drvmgr_drv_register(&grtm_rmap_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; + + /* Get Read/Write operations for bus */ + priv->rw_arg.dev = dev; + priv->rw_arg.arg = drvmgr_func_call(dev->parent, AMBAPP_RMAP_RW_ARG, dev, NULL, NULL, NULL); + drvmgr_func_get(dev->parent, AMBAPP_RMAP_R32, (void **)&priv->rw_r32); + drvmgr_func_get(dev->parent, AMBAPP_RMAP_W32, (void **)&priv->rw_w32); + drvmgr_func_get(dev->parent, AMBAPP_RMAP_WMEM, (void **)&priv->rw_wmem); + drvmgr_func_get(dev->parent, AMBAPP_RMAP_MEMSET, + (void **)&priv->rw_memset); + + /* 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; + } + + /* Create ISR protection Counting Semaphore with count = 1 */ + if ( rtems_semaphore_create(rtems_build_name('G', 'T', 'M', '0' + pDev->minor), + 1, + RTEMS_FIFO | RTEMS_COUNTING_SEMAPHORE | RTEMS_PRIORITY | \ + RTEMS_LOCAL | RTEMS_NO_PRIORITY_CEILING, + 0, + &pDev->handling_transmission) != RTEMS_SUCCESSFUL ) { + return -1; + } + + /* Override default 2k MAX FRAME length if available from bus resource */ + pDev->frame_length_max = 2*1024; + value = drvmgr_dev_key_get(pDev->dev, "maxFrameLength", KEY_TYPE_INT); + if ( value ) + pDev->frame_length_max = value->i; + + /* Default to allocate from partition 0 */ + pDev->alloc_part_bd = 0; + pDev->alloc_part_frm = 0; + value = drvmgr_dev_key_get(pDev->dev, "bdAllocPartition", KEY_TYPE_INT); + if ( value ) + pDev->alloc_part_bd = value->i; + value = drvmgr_dev_key_get(pDev->dev, "frameAllocPartition", KEY_TYPE_INT); + if ( value ) + pDev->alloc_part_frm = value->i; + + /* Allocate Memory for Descriptors */ +#ifdef REMOTE_DESCRIPTORS + pDev->bds = 0xA0000000; + pDev->_bds = 0xA0000000; +#else + pDev->_bds = pDev->bds = (struct grtm_bd *) + ambapp_rmap_partition_memalign(pDev->dev, pDev->alloc_part_bd, 0x400, 0x400); +#endif + if ( !pDev->bds ) { + DBG("GRTM: Failed to allocate descriptor table\n"); + return -1; + } + MEMSET(pDev, pDev->bds, 0, 0x400); + + pDev->_ring = malloc(sizeof(struct grtm_ring) * 128); + if ( !pDev->_ring ) { + return -1; + } + pDev->_frame_buffers = (unsigned char *) ambapp_rmap_partition_memalign( + pDev->dev, pDev->alloc_part_frm, 0x4, DESCRIPTOR_MAX * pDev->frame_length_max); + if ( !pDev->_frame_buffers ) { + return -1; + } + + /* Reset Hardware before attaching IRQ handler */ + grtm_hw_reset(pDev); + + /* Read SUB revision number, ignore */ + pDev->subrev = (READ_REG(pDev, &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 */ + WRITE_REG(pDev, &pDev->regs->dma_ctrl, GRTM_DMA_CTRL_RST); +} + +static void grtm_hw_get_implementation(struct grtm_priv *pDev, struct grtm_ioc_hw *hwcfg) +{ + struct grtm_regs *regs = pDev->regs; + unsigned int cfg = READ_REG(pDev, ®s->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, ®s->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; /* Enable interrupt handler from doing descriptor processing, this + * is not efficient since a semaphore must be used to lock the free_sent() + * and grtm_schedule_ready() functions, because they are locking when + * depending on the RMAP stack */ + cfg->timeout = RTEMS_NO_TIMEOUT; + +} + +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; + + /* Check that the buffers that we have allocated allows the new frame length */ + if ( cfg->frame_length > pDev->frame_length_max ) + return -1; + + 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 */ + tmp = (((limit-1) << GRTM_DMA_LEN_LIM_BIT) & GRTM_DMA_LEN_LIM)| + (((cfg->frame_length-1) << GRTM_DMA_LEN_LEN_BIT) & GRTM_DMA_LEN_LEN); + WRITE_REG(pDev, ®s->dma_len, tmp); + + /* 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); + WRITE_REG(pDev, ®s->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); + WRITE_REG(pDev, ®s->code, tmp); + + /* Attached synchronization marker register */ + WRITE_REG(pDev, ®s->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); + WRITE_REG(pDev, ®s->all_frm, tmp); + + /* Master Frame Generation */ + WRITE_REG(pDev, ®s->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); + WRITE_REG(pDev, ®s->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; + unsigned int frame_buffer_ofs; + + /* Clear Descriptors */ + MEMSET(pDev, 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)*DESCRIPTOR_MAX); + frame_buffer_ofs = 0; + for(i=0; i<DESCRIPTOR_MAX-1; i++){ + pDev->_ring[i].next = &pDev->_ring[i+1]; + pDev->_ring[i].bd = &pDev->bds[i]; + pDev->_ring[i].frm = NULL; + pDev->_ring[i].buf = &pDev->_frame_buffers[frame_buffer_ofs]; + WRITE_REG(pDev, &pDev->_ring[i].bd->address, pDev->_ring[i].buf); /* INIT BD ADDRESS */ + frame_buffer_ofs += pDev->frame_length_max; + } + pDev->_ring[DESCRIPTOR_MAX-1].next = &pDev->_ring[0]; + pDev->_ring[DESCRIPTOR_MAX-1].bd = &pDev->bds[DESCRIPTOR_MAX-1]; + pDev->_ring[DESCRIPTOR_MAX-1].frm = NULL; + pDev->_ring[DESCRIPTOR_MAX-1].buf = &pDev->_frame_buffers[frame_buffer_ofs]; + WRITE_REG(pDev, &pDev->_ring[DESCRIPTOR_MAX-1].bd->address, pDev->_ring[DESCRIPTOR_MAX-1].buf); /* INIT BD ADDRESS */ + + 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 */ + WRITE_REG(pDev, ®s->dma_ctrl, GRTM_DMA_CTRL_TXRST); + WRITE_REG(pDev, ®s->dma_ctrl, 0); /* Leave Reset */ + + /* Clear old interrupts */ + WRITE_REG(pDev, ®s->dma_status, GRTM_DMA_STS_ALL); + + /* Set Descriptor Pointer Base register to point to first descriptor */ + WRITE_REG(pDev, ®s->dma_bd, pDev->bds); + /*drvmgr_mmap_translate(pDev->dev, 0, (void *)pDev->bds, (void **)®s->dma_bd);*/ + /*regs->dma_bd = (unsigned int)pDev->bds;*/ + + /* Set hardware options as defined by config */ + if ( grtm_hw_set_config(pDev, cfg, &pDev->hw_avail) ) { + return RTEMS_IO_ERROR; + } + + /* Enable TM Transmitter */ + WRITE_REG(pDev, ®s->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(pDev, txrdy_reg) & txrdy_mask) && (i<1000) ){ + i++; + } + if ( !(READ_REG(pDev, 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) */ + WRITE_REG(pDev, ®s->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 */ + WRITE_REG(pDev, ®s->dma_ctrl, 0); + + /* Clear any pending interrupt */ + WRITE_REG(pDev, ®s->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_rmap_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_rmap_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, 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(pDev, &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, oldLevel; + struct grtm_ring *curr_bd; + struct grtm_frame *curr_frm, *last_frm; + + 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; + +#warning REMOVE TRANSLATE FLAG? + /* Is frame located at SpaceWire target or at the local RAM? */ + if ( curr_frm->flags & GRTM_FLAGS_COPY_DATA ) { + /* Transfer frame to the target via SpW, we make sure that is aligned to a 4byte + * boundary, this is good practice and does not cost anything. + */ + TRANSFER_FRM(pDev, curr_bd->buf, curr_frm->payload, (pDev->config.frame_length + 3) & ~0x3); + + /* That the BD->address is not constant */ + WRITE_REG(pDev, &curr_bd->bd->address, curr_bd->buf); + } else { + /* The Frame has already been copied to the SpaceWire target, we simply + * write the address of the frame. + */ + WRITE_REG(pDev, &curr_bd->bd->address, curr_frm->payload); + } +#if 0 + /* 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_mmap_translate(pDev->dev, 0, (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; + } +#endif + + 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 */ + WRITE_REG(pDev, &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, &pDev->regs->dma_ctrl); + dmactrl &= ~(GRTM_DMA_CTRL_TXRST | GRTM_DMA_CTRL_RST); + dmactrl |= GRTM_DMA_CTRL_EN; + WRITE_REG(pDev, &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_rmap_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_rmap", 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; + } +#warning THIS IOCTL COPY THE REMOTE ADDRESS + *(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 HERE + 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. + */ + rtems_semaphore_obtain(pDev->handling_transmission, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + 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++; + + rtems_semaphore_obtain(pDev->handling_transmission, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + /* 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; + + rtems_semaphore_release(pDev->handling_transmission); + 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 */ + rtems_semaphore_obtain(pDev->handling_transmission, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + + 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 ) { + /* do not lock out interrupt handler any more */ + rtems_semaphore_release(pDev->handling_transmission); + return RTEMS_TIMEOUT; + } else if ( ret == RTEMS_SUCCESSFUL ) { + /* There might be frames available, go check */ + continue; + } else { + /* any error (driver closed, internal error etc.) */ + rtems_semaphore_release(pDev->handling_transmission); + return RTEMS_UNSATISFIED; + } + + }else{ + /* non-blocking mode, we quit */ + chain->head = NULL; + chain->tail = NULL; + /* do not lock out interrupt handler any more */ + rtems_semaphore_release(pDev->handling_transmission); + 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); + + 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 */ + rtems_semaphore_release(pDev->handling_transmission); + 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(pDev, ®s->dma_status); + + /* Spurious Interrupt? */ + if ( !pDev->running ) + return; + + if ( status ) + WRITE_REG(pDev, ®s->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 && + (rtems_semaphore_obtain(pDev->handling_transmission, RTEMS_NO_WAIT, 0) == RTEMS_SUCCESSFUL) ) { + /* 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; + + rtems_semaphore_release(pDev->handling_transmission); + +#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(pDev, ®s->dma_ctrl) & ~GRTM_DMA_CTRL_IE; +#endif + + /* Signal Semaphore to wake waiting thread in ioctl(SEND|RECLAIM) */ + rtems_semaphore_release(pDev->sem_tx); + } + + } +#if 0 +procceed_processing_interrupts: + ; +#endif +} + +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; +} diff --git a/c/src/lib/libbsp/sparc/shared/uart/apbuart.c b/c/src/lib/libbsp/sparc/shared/uart/apbuart.c index 58d0eab4dc..403c81a6bd 100644 --- a/c/src/lib/libbsp/sparc/shared/uart/apbuart.c +++ b/c/src/lib/libbsp/sparc/shared/uart/apbuart.c @@ -1,485 +1,413 @@ -/* - * This file contains the driver for the APBUART serial port. +/* Unused driver at the moment since we can not have two drivers for + * the same hardware... + */ +#if 0 + +/* This file contains the driver for the GRLIB APBUART serial port. * No console driver, only char driver. * - * COPYRIGHT (c) 2007. - * Gaisler Research. + * COPYRIGHT (c) 2008. + * Aeroflex Gaisler. * * 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. * - * + * 2008-12-03, Daniel Hellstrom <daniel@gaisler.com> + * Converted to support driver manager. + * * 2007-07-11, Daniel Hellstrom <daniel@gaisler.com> * Added ioctl command APBUART_CLR_STATS */ +/******************* Driver manager interface ***********************/ #include <bsp.h> #include <rtems/libio.h> #include <stdlib.h> #include <assert.h> #include <rtems/bspIo.h> #include <string.h> +#include <stdio.h> -#include <ambapp.h> +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> #include <apbuart.h> +#include <ambapp.h> +#include <grlib.h> -#ifndef DEFAULT_TXBUF_SIZE - #define DEFAULT_TXBUF_SIZE 32 -#endif -#ifndef DEFAULT_RXBUF_SIZE - #define DEFAULT_RXBUF_SIZE 32 -#endif - -#ifndef APBUART_PREFIX - #define APBUART_PREFIX(name) apbuart##name -#endif - -#if !defined(APBUART_DEVNAME) || !defined(APBUART_DEVNAME_NO) - #undef APBUART_DEVNAME - #undef APBUART_DEVNAME_NO - #define APBUART_DEVNAME "/dev/apbuart0" - #define APBUART_DEVNAME_NO(devstr,no) ((devstr)[12]='0'+(no)) -#endif +/*#define DEBUG 1 */ -#ifndef APBUART_REG_INT - #define APBUART_REG_INT(handler,irq,arg) set_vector(handler,irq+0x10,1) - #undef APBUART_DEFINE_INTHANDLER - #define APBUART_DEFINE_INTHANDLER +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) #endif -/* Default to 40MHz system clock */ -/*#ifndef SYS_FREQ_HZ - #define SYS_FREQ_HZ 40000000 -#endif*/ - typedef struct { - int size; - unsigned char *buf, - *tail, - *head, - *max; - int full; /* no more place in fifo */ + int size; + unsigned char *buf, + *tail, + *head, + *max; + int full; /* no more place in fifo */ } apbuart_fifo; -static apbuart_fifo *apbuart_fifo_create(int size); -static void apbuart_fifo_free(apbuart_fifo *fifo); -static inline int apbuart_fifo_isFull(apbuart_fifo *fifo); -static inline int apbuart_fifo_isEmpty(apbuart_fifo *fifo); -static int apbuart_fifo_put(apbuart_fifo *fifo, unsigned char c); -static int apbuart_fifo_get(apbuart_fifo *fifo, unsigned char *c); -static int inline apbuart_fifo_peek(apbuart_fifo *fifo, unsigned char **c); -static void inline apbuart_fifo_skip(apbuart_fifo *fifo); - -static rtems_device_driver apbuart_initialize(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); -static rtems_device_driver apbuart_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); -static rtems_device_driver apbuart_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); -static rtems_device_driver apbuart_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); -static rtems_device_driver apbuart_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); -static rtems_device_driver apbuart_control(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); - -typedef struct { - ambapp_apb_uart *regs; - int irq; - int minor; - int scaler; - unsigned int baud; - - int txblk; /* Make write block until at least 1 char has - * been put into software send fifo - */ - int tx_flush; /* Set this to block until all data has - * placed into the hardware send fifo - */ - int rxblk; /* Make read block until at least 1 char has - * been received (or taken from software fifo). - */ - int started; /* Set to 1 when in running mode */ - - int ascii_mode; /* Set to 1 to make \n be printed as \r\n */ +struct apbuart_priv { + struct drvmgr_dev *dev; + struct apbuart_regs *regs; + int irq; + int minor; + int scaler; + unsigned int baud; + unsigned int freq_hz; /* UART Core Frequency */ + + int txblk; /* Make write block until at least 1 char has + * been put into software send fifo + */ + int tx_flush; /* Set this to block until all data has + * placed into the hardware send fifo + */ + int rxblk; /* Make read block until at least 1 char has + * been received (or taken from software fifo). + */ + int started; /* Set to 1 when in running mode */ + + int ascii_mode; /* Set to 1 to make \n be printed as \r\n */ /* TX/RX software FIFO Buffers */ - apbuart_fifo *txfifo; - apbuart_fifo *rxfifo; + apbuart_fifo *txfifo; + apbuart_fifo *rxfifo; - apbuart_stats stats; + apbuart_stats stats; - rtems_id dev_sem; - rtems_id rx_sem; - rtems_id tx_sem; -} apbuart_priv; + rtems_id dev_sem; + rtems_id rx_sem; + rtems_id tx_sem; +}; -static int dev_cnt; -static apbuart_priv *apbuarts; -static unsigned int sys_freq_hz; +/* Driver prototypes */ +int apbuart_register_io(rtems_device_major_number *m); +int apbuart_device_init(struct apbuart_priv *priv); -#define APBUART_DRIVER_TABLE_ENTRY { apbuart_initialize, apbuart_open, apbuart_close, apbuart_read, apbuart_write, apbuart_control } - -static rtems_driver_address_table apbuart_driver = APBUART_DRIVER_TABLE_ENTRY; -static amba_confarea_type *amba_bus; - -static void apbuart_interrupt(apbuart_priv *uart); -#ifdef APBUART_DEFINE_INTHANDLER -static void apbuart_interrupt_handler(rtems_vector_number v); -#endif -static void apbuart_hw_close(apbuart_priv *uart); -static void apbuart_hw_open(apbuart_priv *uart); - -/* Uncomment for debug output */ -/* #define DEBUG 1 - #define FUNCDEBUG 1 */ - -#ifdef DEBUG -#define DBG(x...) printk(x) -#else -#define DBG(x...) -#endif -#ifdef FUNCDEBUG -#define FUNCDBG(x...) printk(x) -#else -#define FUNCDBG(x...) -#endif +int apbuart_init2(struct drvmgr_dev *dev); +int apbuart_init3(struct drvmgr_dev *dev); -#ifndef READ_REG - #define READ_REG(address) _APBUART_READ_REG((unsigned int)(address)) - static __inline__ unsigned int _APBUART_READ_REG(unsigned int addr) { - unsigned int tmp; - asm(" lda [%1]1, %0 " - : "=r"(tmp) - : "r"(addr) - ); - return tmp; - } -#endif +struct drvmgr_drv_ops apbuart_ops = +{ + .init = {NULL, apbuart_init2, apbuart_init3, NULL}, + .remove = NULL, + .info = NULL +}; -#if 0 -static int apbuart_outbyte_try(ambapp_apb_uart *regs, unsigned char ch) +struct amba_dev_id apbuart_ids[] = { - if ( (READ_REG(®s->status) & LEON_REG_UART_STATUS_THE) == 0 ) - return -1; /* Failed */ + {VENDOR_GAISLER, GAISLER_APBUART}, + {0, 0} /* Mark end of table */ +}; - /* There is room in fifo, put ch in it */ - regs->data = (unsigned int) ch; - return 0; +struct amba_drv_info apbuart_drv_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_GAISLER_APBUART_ID, /* Driver ID */ + "APBUART_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &apbuart_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + 0, + }, + &apbuart_ids[0] +}; + +static int apbuart_driver_io_registered = 0; +static rtems_device_major_number apbuart_driver_io_major = 0; + +void apbuart_register_drv (void) +{ + DBG("Registering APBUART driver\n"); + drvmgr_drv_register(&apbuart_drv_info.general); } - -static int apbuart_inbyte_try(ambapp_apb_uart *regs) +int apbuart_init2(struct drvmgr_dev *dev) { - unsigned int status; - /* Clear errors if any */ - if ( (status=READ_REG(®s->status)) & LEON_REG_UART_STATUS_ERR) { - regs->status = status & ~LEON_REG_UART_STATUS_ERR; + struct apbuart_priv *priv; + + DBG("APBUART[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name); + if ( strcmp(dev->parent->dev->drv->name, "AMBAPP_GRLIB_DRV") == 0 ) { + /* Let standard console driver take care of APBUART + * for CPU-local APBUART cores. + */ + dev->priv = NULL; + DBG("-- SKIPPING APBUART 1 --\n"); + return DRVMGR_FAIL; } + priv = dev->priv = malloc(sizeof(struct apbuart_priv)); + if ( !priv ) + return DRVMGR_NOMEM; + memset(priv, 0, sizeof(*priv)); + priv->dev = dev; - /* Is Data available? */ - if ( (READ_REG(®s->status) & LEON_REG_UART_STATUS_DR) == 0 ) - return -1; /* No data avail */ + /* This core will not find other cores, so we wait for init2() */ - /* Return Data */ - return (int)READ_REG(®s->data); + return DRVMGR_OK; } -static int apbuart_write_support(apbuart_priv *uart, const char *buf, int len) +int apbuart_init3(struct drvmgr_dev *dev) { - int nwrite = 0; + struct apbuart_priv *priv; + char prefix[32]; + char devName[32]; + rtems_status_code status; - while (nwrite < len) { - if ( apbuart_outbyte_try(minor, *buf++) ){ - /* TX Fifo full */ + priv = dev->priv; - } - nwrite++; - } - return nwrite; -} -#endif + /* Do initialization */ -static void apbuart_hw_open(apbuart_priv *uart){ - unsigned int scaler; + if ( apbuart_driver_io_registered == 0) { + /* Register the I/O driver only once for all cores */ + if ( apbuart_register_io(&apbuart_driver_io_major) ) { + /* Failed to register I/O driver */ + dev->priv = NULL; + return DRVMGR_FAIL; + } - /* Calculate Baudrate */ - if ( uart->scaler > 0 ) { - scaler = uart->scaler; - }else{ - scaler = (((sys_freq_hz*10)/(uart->baud*8))-5)/10; + apbuart_driver_io_registered = 1; } - /* Set new baud rate */ - uart->regs->scaler = scaler; + /* I/O system registered and initialized + * Now we take care of device initialization. + */ - /* Enable receiver & Transmitter */ - uart->regs->ctrl = APBUART_CTRL_RE | APBUART_CTRL_RF | APBUART_CTRL_RI | APBUART_CTRL_TI; -} + /* Get frequency */ + if ( drvmgr_freq_get(dev, DEV_APB_SLV, &priv->freq_hz) ) { + return DRVMGR_FAIL; + } -static void apbuart_hw_close(apbuart_priv *uart){ - /* disable receiver & transmitter & all IRQs */ - uart->regs->ctrl = 0; -} + if ( apbuart_device_init(priv) ) { + return DRVMGR_FAIL; + } -#ifdef APBUART_DEFINE_INTHANDLER -/* interrupt handler */ -static void apbuart_interrupt_handler(rtems_vector_number v){ - int minor; + /* 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/apbuart%d", dev->minor_drv); + } else { + /* Got special prefix, this means we have a bus prefix + * And we should use our "bus minor" + */ + sprintf(devName, "/dev/%sapbuart%d", prefix, dev->minor_bus); + } - /* convert to */ - for(minor = 0; minor < dev_cnt; minor++) { - if ( v == (apbuarts[minor].irq+0x10) ) { - apbuart_interrupt(&apbuarts[minor]); - return; - } + /* Register Device */ + status = rtems_io_register_name(devName, apbuart_driver_io_major, dev->minor_drv); + if (status != RTEMS_SUCCESSFUL) { + return DRVMGR_FAIL; } + + return DRVMGR_OK; } -#endif -/* The interrupt handler, taking care of the - * APBUART hardware - */ -static void apbuart_interrupt(apbuart_priv *uart){ - unsigned int status; - int empty; - unsigned char c, *next_char = NULL; - int signal; +/******************* Driver Implementation ***********************/ - /* Clear & record any error */ - status = READ_REG(&uart->regs->status); - if ( status & (APBUART_STATUS_OV|APBUART_STATUS_PE|APBUART_STATUS_FE) ){ - /* Data overrun */ - if ( status & APBUART_STATUS_OV ){ - uart->stats.hw_dovr++; - } - /* Parity error */ - if ( status & APBUART_STATUS_PE ){ - uart->stats.hw_parity++; - } - /* Framing error */ - if ( status & APBUART_STATUS_FE ){ - uart->stats.hw_frame++; - } - uart->regs->status = status & ~(APBUART_STATUS_OV|APBUART_STATUS_PE|APBUART_STATUS_FE); - } +#ifndef DEFAULT_TXBUF_SIZE + #define DEFAULT_TXBUF_SIZE 32 +#endif +#ifndef DEFAULT_RXBUF_SIZE + #define DEFAULT_RXBUF_SIZE 32 +#endif - /* Empty RX fifo into software fifo */ - signal = 0; - while ( (status=READ_REG(&uart->regs->status)) & APBUART_STATUS_DR ){ - c = READ_REG(&uart->regs->data); - if ( apbuart_fifo_isFull(uart->rxfifo) ){ - uart->stats.sw_dovr++; - DBG("]"); - break; - } - /* put into fifo */ - apbuart_fifo_put(uart->rxfifo,c); +/* Uncomment for debug output */ +/* #define DEBUG 1 + #define FUNCDEBUG 1 */ - /* bump RX counter */ - uart->stats.rx_cnt++; +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif +#ifdef FUNCDEBUG +#define FUNCDBG(x...) printk(x) +#else +#define FUNCDBG(x...) +#endif - signal = 1; +#ifndef READ_REG + #define READ_REG(address) _APBUART_READ_REG((unsigned int)(address)) + static __inline__ unsigned int _APBUART_READ_REG(unsigned int addr) { + unsigned int tmp; + asm(" lda [%1]1, %0 " + : "=r"(tmp) + : "r"(addr) + ); + return tmp; } +#endif - /* Wake RX thread if any */ - if ( signal ) - rtems_semaphore_release(uart->rx_sem); - - /* If room in HW fifo and we got more chars to be sent */ - if ( !(status & APBUART_STATUS_TF) ){ - - if ( apbuart_fifo_isEmpty(uart->txfifo) ){ - /* Turn off TX interrupt when no data is to be sent */ - if ( status & APBUART_STATUS_TE ){ - uart->regs->ctrl = READ_REG(&uart->regs->ctrl) & ~APBUART_CTRL_TF; - DBG("?"); - } - return; - } +static apbuart_fifo *apbuart_fifo_create(int size); +static void apbuart_fifo_free(apbuart_fifo *fifo); +static inline int apbuart_fifo_isFull(apbuart_fifo *fifo); +static inline int apbuart_fifo_isEmpty(apbuart_fifo *fifo); +static int apbuart_fifo_put(apbuart_fifo *fifo, unsigned char c); +static int apbuart_fifo_get(apbuart_fifo *fifo, unsigned char *c); +static int inline apbuart_fifo_peek(apbuart_fifo *fifo, unsigned char **c); +static void inline apbuart_fifo_skip(apbuart_fifo *fifo); - /* signal when there will be more room in SW fifo */ - if ( apbuart_fifo_isFull(uart->txfifo) ) - signal = 1; +static rtems_device_driver apbuart_initialize(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver apbuart_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver apbuart_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver apbuart_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver apbuart_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver apbuart_control(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); - do{ - /* Put data into HW TX fifo */ - apbuart_fifo_peek(uart->txfifo,&next_char); - c = *next_char; - if ( uart->ascii_mode && ( c == '\n') ){ - uart->regs->data = '\n'; - *next_char = '\r'; /* avoid sending mutiple '\n' or '\r' */ - }else{ - uart->regs->data = c; - apbuart_fifo_skip(uart->txfifo); /* remove sent char from fifo */ - } - uart->regs->ctrl = READ_REG(&uart->regs->ctrl) | APBUART_CTRL_TE | APBUART_CTRL_TF; - DBG("!"); - }while(!(empty=apbuart_fifo_isEmpty(uart->txfifo)) && - !((status=READ_REG(&uart->regs->status))&APBUART_STATUS_TF) ); +static void apbuart_interrupt(void *arg); - /* Wake userspace thread, on empty or full fifo - * This makes tx_flush and block work. - */ - if ( signal || empty ){ - rtems_semaphore_release(uart->tx_sem); - } - } -} +#define APBUART_DRIVER_TABLE_ENTRY { apbuart_initialize, apbuart_open, apbuart_close, apbuart_read, apbuart_write, apbuart_control } +static rtems_driver_address_table apbuart_driver = APBUART_DRIVER_TABLE_ENTRY; -int APBUART_PREFIX(_register)(amba_confarea_type *bus) { +int apbuart_register_io(rtems_device_major_number *m) +{ rtems_status_code r; - rtems_device_major_number m; - amba_bus = bus; - - FUNCDBG("apbuart_register:\n"); - - if ((r = rtems_io_register_driver(0, &apbuart_driver, &m)) == RTEMS_SUCCESSFUL) { - DBG("APBUART driver successfully registered, major: %d\n", m); + if ((r = rtems_io_register_driver(0, &apbuart_driver, m)) == RTEMS_SUCCESSFUL) { + DBG("APBUART driver successfully registered, major: %d\n", *m); } else { switch(r) { case RTEMS_TOO_MANY: - printk("APBUART rtems_io_register_driver failed: RTEMS_TOO_MANY\n"); return -1; - case RTEMS_INVALID_NUMBER: - printk("APBUART rtems_io_register_driver failed: RTEMS_INVALID_NUMBER\n"); return -1; + printk("APBUART rtems_io_register_driver failed: RTEMS_TOO_MANY\n"); + return -1; + case RTEMS_INVALID_NUMBER: + printk("APBUART rtems_io_register_driver failed: RTEMS_INVALID_NUMBER\n"); + return -1; case RTEMS_RESOURCE_IN_USE: - printk("APBUART rtems_io_register_driver failed: RTEMS_RESOURCE_IN_USE\n"); return -1; + printk("APBUART rtems_io_register_driver failed: RTEMS_RESOURCE_IN_USE\n"); + return -1; default: - printk("APBUART rtems_io_register_driver failed\n"); - return -1; + printk("APBUART rtems_io_register_driver failed\n"); + return -1; } } return 0; } -static rtems_device_driver apbuart_initialize(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +int apbuart_device_init(struct apbuart_priv *priv) { + struct amba_dev_info *ambadev; + struct ambapp_core *pnpinfo; + int minor = priv->dev->minor_drv; + int rxFifoLen, txFifoLen; + 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 apbuart_regs *)pnpinfo->apb_slv->start; - rtems_status_code status; - int i; - amba_apb_device dev; - char fs_name[20]; + /* Clear HW regs */ + priv->regs->status = 0; + priv->regs->ctrl = 0; - FUNCDBG("apbuart_initialize\n"); + /* Get Configuration from Bus resources (Let user override defaults) */ + rxFifoLen = DEFAULT_RXBUF_SIZE; + txFifoLen = DEFAULT_TXBUF_SIZE; - /* Find all APB UART devices */ - dev_cnt = amba_get_number_apbslv_devices(amba_bus,VENDOR_GAISLER,GAISLER_APBUART); - if ( dev_cnt < 1 ){ - /* Failed to find any CAN cores! */ - printk("APBUART: Failed to find any APBUART cores\n\r"); - return -1; - } + value = drvmgr_dev_key_get(priv->dev, "rxFifoLen", KEY_TYPE_INT); + if ( value ) + rxFifoLen = value->i; - strcpy(fs_name,APBUART_DEVNAME); + value = drvmgr_dev_key_get(priv->dev, "txFifoLen", KEY_TYPE_INT); + if ( value ) + txFifoLen = value->i; - DBG("Found %d APBUART(s)\n\r",dev_cnt); + /* Allocate default software buffers */ + priv->txfifo = apbuart_fifo_create(txFifoLen); + priv->rxfifo = apbuart_fifo_create(rxFifoLen); + if ( !priv->txfifo || !priv->rxfifo ) + return -1; - /* Allocate memory for device structures */ - apbuarts = malloc(sizeof(apbuart_priv) * dev_cnt); - if ( !apbuarts ){ - printk("APBUART: Failed to allocate SW memory\n\r"); + /* Device A Semaphore created with count = 1 */ + if ( rtems_semaphore_create(rtems_build_name('A', 'U', 'D', '0'+minor), + 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; } - memset(apbuarts,0,sizeof(sizeof(apbuart_priv) * dev_cnt)); + if ( rtems_semaphore_create(rtems_build_name('A', 'U', 'T', '0'+minor), + 1, + RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE|RTEMS_NO_INHERIT_PRIORITY|RTEMS_LOCAL|RTEMS_NO_PRIORITY_CEILING, + 0, + &priv->tx_sem) != RTEMS_SUCCESSFUL ) { + return -1; + } - /* Detect System Frequency from initialized timer */ -#ifndef SYS_FREQ_HZ -#if defined(LEON3) - /* LEON3: find timer address via AMBA Plug&Play info */ - { - amba_apb_device gptimer; - LEON3_Timer_Regs_Map *tregs; + if ( rtems_semaphore_create(rtems_build_name('A', 'U', 'R', '0'+minor), + 1, + RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE|RTEMS_NO_INHERIT_PRIORITY|RTEMS_LOCAL|RTEMS_NO_PRIORITY_CEILING, + 0, + &priv->rx_sem) != RTEMS_SUCCESSFUL ) { + return -1; + } - if ( amba_find_apbslv(&amba_conf,VENDOR_GAISLER,GAISLER_GPTIMER,&gptimer) == 1 ){ - tregs = (LEON3_Timer_Regs_Map *)gptimer.start; - sys_freq_hz = (tregs->scaler_reload+1)*1000*1000; - DBG("APBUART: detected %dHZ system frequency\n\r",sys_freq_hz); - }else{ - sys_freq_hz = 40000000; /* Default to 40MHz */ - printk("APBUART: Failed to detect system frequency\n\r"); - } + return 0; +} +/******************* I/O driver implementation ***********************/ + +static void apbuart_hw_open(struct apbuart_priv *uart) +{ + unsigned int scaler; + + /* Calculate Baudrate */ + if ( uart->scaler > 0 ) { + scaler = uart->scaler; + } else { + scaler = (((uart->freq_hz*10)/(uart->baud*8))-5)/10; + uart->scaler = scaler; } -#elif defined(LEON2) - /* LEON2: use hardcoded address to get to timer */ - { - LEON_Register_Map *regs = (LEON_Register_Map *)0x80000000; - sys_freq_hz = (regs->Scaler_Reload+1)*1000*1000; - } -#else - #error CPU not supported for OC_CAN driver -#endif -#else - /* Use hardcoded frequency */ - sys_freq_hz = SYS_FREQ_HZ; -#endif + + /* Set new baud rate */ + uart->regs->scaler = scaler; + + /* Enable receiver & Transmitter */ + uart->regs->ctrl = APBUART_CTRL_RE | APBUART_CTRL_RF | APBUART_CTRL_RI | APBUART_CTRL_TI; +} + +static void apbuart_hw_close(struct apbuart_priv *uart) +{ + /* disable receiver & transmitter & all IRQs */ + uart->regs->ctrl = 0; +} - for(i=0; i<dev_cnt; i++){ - /* Get AMBA AHB device info from Plug&Play */ - amba_find_next_apbslv(amba_bus,VENDOR_GAISLER,GAISLER_APBUART,&dev,i); - - printk("APBUART[%d]: at 0x%x irq %d (0x%x)\n\r",i,dev.start,dev.irq,(unsigned int)&apbuarts[i]); - - apbuarts[i].regs = (ambapp_apb_uart *)dev.start; - apbuarts[i].irq = dev.irq; - apbuarts[i].minor = i; - - /* Clear HW regs */ - apbuarts[i].regs->status = 0; - apbuarts[i].regs->ctrl = 0; - - /* Allocate default software buffers */ - apbuarts[i].txfifo = apbuart_fifo_create(DEFAULT_TXBUF_SIZE); - apbuarts[i].rxfifo = apbuart_fifo_create(DEFAULT_RXBUF_SIZE); - if ( !apbuarts[i].txfifo || !apbuarts[i].rxfifo ) - rtems_fatal_error_occurred(RTEMS_NO_MEMORY); - - APBUART_DEVNAME_NO(fs_name,i); - - /* Bind name to device */ - DBG("APBUART[%d]: binding to name %s\n\r",i,fs_name); - status = rtems_io_register_name(fs_name, major, i); - if (status != RTEMS_SUCCESSFUL) - rtems_fatal_error_occurred(status); - - /* Setup interrupt handler for each channel */ - APBUART_REG_INT(APBUART_PREFIX(_interrupt_handler), apbuarts[i].irq, &apbuarts[i]); - - /* Device A Semaphore created with count = 1 */ - if ( rtems_semaphore_create(rtems_build_name('A', 'U', 'D', '0'+i), - 1, - RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE|RTEMS_NO_INHERIT_PRIORITY|RTEMS_LOCAL|RTEMS_NO_PRIORITY_CEILING, - 0, - &apbuarts[i].dev_sem) != RTEMS_SUCCESSFUL ) - return RTEMS_INTERNAL_ERROR; - - if ( rtems_semaphore_create(rtems_build_name('A', 'U', 'T', '0'+i), - 1, - RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE|RTEMS_NO_INHERIT_PRIORITY|RTEMS_LOCAL|RTEMS_NO_PRIORITY_CEILING, - 0, - &apbuarts[i].tx_sem) != RTEMS_SUCCESSFUL ) - return RTEMS_INTERNAL_ERROR; - - if ( rtems_semaphore_create(rtems_build_name('A', 'U', 'R', '0'+i), - 1, - RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE|RTEMS_NO_INHERIT_PRIORITY|RTEMS_LOCAL|RTEMS_NO_PRIORITY_CEILING, - 0, - &apbuarts[i].rx_sem) != RTEMS_SUCCESSFUL ) - return RTEMS_INTERNAL_ERROR; +static rtems_device_driver apbuart_initialize(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + /* Initialize common data structures, for example common semaphores... */ - } return RTEMS_SUCCESSFUL; } static rtems_device_driver apbuart_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) -{ - apbuart_priv *uart; +{ + struct apbuart_priv *uart; + struct drvmgr_dev *dev; - FUNCDBG("apbuart_open: major %d, minor %d\n", major, minor); - - if ( (minor < 0) || (minor >= dev_cnt) ) { + if ( drvmgr_get_dev(&apbuart_drv_info.general, minor, &dev) ) { DBG("Wrong minor %d\n", minor); return RTEMS_INVALID_NAME; } + uart = (struct apbuart_priv *)dev->priv; - uart = &apbuarts[minor]; + FUNCDBG("apbuart_open: major %d, minor %d\n", major, minor); if (rtems_semaphore_obtain(uart->dev_sem, RTEMS_NO_WAIT, RTEMS_NO_TIMEOUT) != RTEMS_SUCCESSFUL) { DBG("apbuart_open: resource in use\n"); @@ -506,8 +434,8 @@ static rtems_device_driver apbuart_open(rtems_device_major_number major, rtems_d /* non-ascii mode */ uart->ascii_mode = 0; - /* not started */ - uart->started = 0; + /* not started */ + uart->started = 0; if ( !uart->txfifo || (uart->txfifo->size!=DEFAULT_TXBUF_SIZE) ){ apbuart_fifo_free(uart->txfifo); @@ -525,39 +453,53 @@ static rtems_device_driver apbuart_open(rtems_device_major_number major, rtems_d } /* Now user must call ioctl(START,0) to begin */ - + return RTEMS_SUCCESSFUL; } static rtems_device_driver apbuart_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) { - apbuart_priv *uart = &apbuarts[minor]; + struct apbuart_priv *uart; + struct drvmgr_dev *dev; - FUNCDBG("apbuart_close[%d]:\n",minor); + if ( drvmgr_get_dev(&apbuart_drv_info.general, minor, &dev) ) { + DBG("Wrong minor %d\n", minor); + return RTEMS_INVALID_NAME; + } + uart = (struct apbuart_priv *)dev->priv; + FUNCDBG("apbuart_close[%d]:\n",minor); + apbuart_hw_close(uart); /* Software state will be set when open is called again */ rtems_semaphore_release(uart->rx_sem); rtems_semaphore_release(uart->tx_sem); uart->started = 0; - + rtems_semaphore_release(uart->dev_sem); - + return RTEMS_SUCCESSFUL; } - + static rtems_device_driver apbuart_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) { rtems_libio_rw_args_t *rw_args; unsigned int count = 0, oldLevel; unsigned char *buf; - apbuart_priv *uart = &apbuarts[minor]; + struct apbuart_priv *uart; + struct drvmgr_dev *dev; + if ( drvmgr_get_dev(&apbuart_drv_info.general, minor, &dev) ) { + DBG("Wrong minor %d\n", minor); + return RTEMS_INVALID_NAME; + } + uart = (struct apbuart_priv *)dev->priv; + rw_args = (rtems_libio_rw_args_t *) arg; FUNCDBG("apbuart_read\n"); - + buf = (unsigned char *)rw_args->buffer; if ( (rw_args->count < 1) || !buf ) return RTEMS_INVALID_NAME; /* EINVAL */ @@ -565,65 +507,71 @@ static rtems_device_driver apbuart_read(rtems_device_major_number major, rtems_d rtems_interrupt_disable(oldLevel); do { if ( (unsigned int)uart < 0x40000000 ) { - printk("UART %x is screwed\n",uart); - } + printk("UART %x is screwed\n",uart); + } /* Read from SW fifo */ - if ( apbuart_fifo_get(uart->rxfifo,&buf[count]) != 0 ){ + if ( apbuart_fifo_get(uart->rxfifo,&buf[count]) != 0 ){ /* non blocking or read at least 1 byte */ if ( (count > 0) || (!uart->rxblk) ) break; /* Return */ - + rtems_interrupt_enable(oldLevel); /* Block thread until a char is received */ rtems_semaphore_obtain(uart->rx_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT); - + rtems_interrupt_disable(oldLevel); continue; } - + /* Got char from SW FIFO */ count++; - + } while (count < rw_args->count ); - + rtems_interrupt_enable(oldLevel); rw_args->bytes_moved = count; - + if (count == 0) return RTEMS_TIMEOUT; /* ETIMEDOUT should be EAGAIN/EWOULDBLOCK */ return RTEMS_SUCCESSFUL; } - + static rtems_device_driver apbuart_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) { rtems_libio_rw_args_t *rw_args; unsigned int count, oldLevel, ctrl; char *buf; - apbuart_priv *uart = &apbuarts[minor]; int direct=0; + struct apbuart_priv *uart; + struct drvmgr_dev *dev; - + if ( drvmgr_get_dev(&apbuart_drv_info.general, minor, &dev) ) { + DBG("Wrong minor %d\n", minor); + return RTEMS_INVALID_NAME; + } + uart = (struct apbuart_priv *)dev->priv; + rw_args = (rtems_libio_rw_args_t *) arg; - + FUNCDBG("apbuart_write\n"); - + buf = rw_args->buffer; - + if ( rw_args->count < 1 || !buf ) - return RTEMS_INVALID_NAME; /* EINVAL */ - + return RTEMS_INVALID_NAME; /* EINVAL */ + count = 0; rtems_interrupt_disable(oldLevel); /* Do we need to start to send first char direct via HW * to get IRQ going. */ - + ctrl = READ_REG(&uart->regs->ctrl); if ( (ctrl & APBUART_CTRL_TF) == 0 ){ - /* TX interrupt is disabled ==> + /* TX interrupt is disabled ==> * SW FIFO is empty and, * HW FIFO empty */ @@ -637,7 +585,7 @@ static rtems_device_driver apbuart_write(rtems_device_major_number major, rtems_ uart->regs->ctrl = ctrl | APBUART_CTRL_TE | APBUART_CTRL_TF; direct = 1; } - + while( count < rw_args->count ) { /* write to HW FIFO direct skipping SW FIFO */ if ( direct && ((READ_REG(&uart->regs->status) & APBUART_STATUS_TF) == 0) ){ @@ -647,23 +595,23 @@ static rtems_device_driver apbuart_write(rtems_device_major_number major, rtems_ else if ( apbuart_fifo_put(uart->txfifo,buf[count]) ){ direct = 0; DBG("APBUART[%d]: write: SW FIFO Full\n\r",minor); - + /* is full, block? */ if ( ((count < 1) && uart->txblk) || uart->tx_flush ){ - + rtems_interrupt_enable(oldLevel); - + rtems_semaphore_obtain(uart->tx_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT); - + rtems_interrupt_disable(oldLevel); - + /* Do we need to start to send first char direct via HW * to get IRQ going. */ - + ctrl = READ_REG(&uart->regs->ctrl); if ( (ctrl & APBUART_CTRL_TF) == 0 ){ - /* TX interrupt is disabled ==> + /* TX interrupt is disabled ==> * SW FIFO is empty and, * HW FIFO empty */ @@ -677,7 +625,7 @@ static rtems_device_driver apbuart_write(rtems_device_major_number major, rtems_ uart->regs->ctrl = ctrl | APBUART_CTRL_TF | APBUART_CTRL_TE; direct = 1; } - + continue; } /* don't block, return current status */ @@ -685,18 +633,18 @@ static rtems_device_driver apbuart_write(rtems_device_major_number major, rtems_ }else{ direct = 0; } - + count++; - + } rtems_interrupt_enable(oldLevel); - + rw_args->bytes_moved = count; - + if (count == 0) return RTEMS_TIMEOUT; /* ETIMEDOUT should be EAGAIN/EWOULDBLOCK */ - + return RTEMS_SUCCESSFUL; } @@ -704,17 +652,24 @@ static rtems_device_driver apbuart_control(rtems_device_major_number major, rtem { rtems_libio_ioctl_args_t *ioarg = (rtems_libio_ioctl_args_t *)arg; unsigned int *data = ioarg->buffer; - apbuart_priv *uart = &apbuarts[minor]; int size; unsigned int baudrate, blocking; apbuart_stats *stats; + struct apbuart_priv *uart; + struct drvmgr_dev *dev; - FUNCDBG("apbuart_control [%i,%i]\n",major, minor); + if ( drvmgr_get_dev(&apbuart_drv_info.general, minor, &dev) ) { + DBG("Wrong minor %d\n", minor); + return RTEMS_INVALID_NAME; + } + uart = (struct apbuart_priv *)dev->priv; + FUNCDBG("apbuart_control [%i,%i]\n",major, minor); + if (!ioarg) return RTEMS_INVALID_NAME; - ioarg->ioctl_return = 0; + ioarg->ioctl_return = 0; switch(ioarg->command) { /* Enable Receiver & transmitter */ @@ -723,58 +678,62 @@ static rtems_device_driver apbuart_control(rtems_device_major_number major, rtem return RTEMS_INVALID_NAME; apbuart_hw_open(uart); uart->started = 1; + /* Setup interrupt handler & Enable IRQ */ + drvmgr_interrupt_register(dev, 0, "apbuart", + apbuart_interrupt, uart); break; /* Close Receiver & transmitter */ case APBUART_STOP: if ( !uart->started ) return RTEMS_INVALID_NAME; + drvmgr_interrupt_unregister(dev, 0, apbuart_interrupt, uart); apbuart_hw_close(uart); uart->started = 0; break; - /* Set RX FIFO Software buffer length + /* Set RX FIFO Software buffer length * It is only possible to change buffer size in * non-running mode. */ case APBUART_SET_RXFIFO_LEN: if ( uart->started ) return RTEMS_RESOURCE_IN_USE; /* EBUSY */ - + size = (int)ioarg->buffer; - if ( size < 1 ) + if ( size < 1 ) return RTEMS_INVALID_NAME; /* EINVAL */ - + /* Free old buffer */ apbuart_fifo_free(uart->rxfifo); - + /* Allocate new buffer & init it */ uart->rxfifo = apbuart_fifo_create(size); if ( !uart->rxfifo ) return RTEMS_NO_MEMORY; break; - /* Set TX FIFO Software buffer length - * It is only possible to change buffer size + /* Set TX FIFO Software buffer length + * It is only possible to change buffer size * while in non-running mode. */ case APBUART_SET_TXFIFO_LEN: if ( uart->started ) return RTEMS_RESOURCE_IN_USE; /* EBUSY */ - + size = (int)ioarg->buffer; - if ( size < 1 ) + if ( size < 1 ) return RTEMS_INVALID_NAME; /* EINVAL */ - + /* Free old buffer */ apbuart_fifo_free(uart->txfifo); - + /* Allocate new buffer & init it */ uart->txfifo = apbuart_fifo_create(size); if ( !uart->txfifo ) return RTEMS_NO_MEMORY; break; - + case APBUART_SET_BAUDRATE: /* Set baud rate of */ baudrate = (int)ioarg->buffer; @@ -784,47 +743,139 @@ static rtems_device_driver apbuart_control(rtems_device_major_number major, rtem uart->scaler = 0; /* use uart->baud */ uart->baud = baudrate; break; - + case APBUART_SET_SCALER: /* use uart->scaler not uart->baud */ uart->scaler = data[0]; break; - + case APBUART_SET_BLOCKING: blocking = (unsigned int)ioarg->buffer; uart->rxblk = ( blocking & APBUART_BLK_RX ); uart->txblk = ( blocking & APBUART_BLK_TX ); uart->tx_flush = ( blocking & APBUART_BLK_FLUSH ); break; - + case APBUART_GET_STATS: stats = (void *)ioarg->buffer; if ( !stats ) return RTEMS_INVALID_NAME; - + /* Copy Stats */ *stats = uart->stats; break; - - case APBUART_CLR_STATS: + + case APBUART_CLR_STATS: /* Clear/reset Stats */ - memset(&uart->stats,0,sizeof(uart->stats)); + memset(&uart->stats,0,sizeof(uart->stats)); break; - + case APBUART_SET_ASCII_MODE: uart->ascii_mode = (int)ioarg->buffer; break; - + default: return RTEMS_NOT_DEFINED; } return RTEMS_SUCCESSFUL; } +/* The interrupt handler, taking care of the + * APBUART hardware + */ +static void apbuart_interrupt(void *arg) +{ + struct apbuart_priv *uart = (struct apbuart_priv *)arg; + unsigned int status; + int empty; + unsigned char c, *next_char = NULL; + int signal; + + /* Clear & record any error */ + status = READ_REG(&uart->regs->status); + if ( status & (APBUART_STATUS_OV|APBUART_STATUS_PE|APBUART_STATUS_FE) ){ + /* Data overrun */ + if ( status & APBUART_STATUS_OV ){ + uart->stats.hw_dovr++; + } + /* Parity error */ + if ( status & APBUART_STATUS_PE ){ + uart->stats.hw_parity++; + } + /* Framing error */ + if ( status & APBUART_STATUS_FE ){ + uart->stats.hw_frame++; + } + uart->regs->status = status & ~(APBUART_STATUS_OV|APBUART_STATUS_PE|APBUART_STATUS_FE); + } + + /* Empty RX fifo into software fifo */ + signal = 0; + while ( (status=READ_REG(&uart->regs->status)) & APBUART_STATUS_DR ){ + c = READ_REG(&uart->regs->data); + if ( apbuart_fifo_isFull(uart->rxfifo) ){ + uart->stats.sw_dovr++; + DBG("]"); + break; + } + /* put into fifo */ + apbuart_fifo_put(uart->rxfifo,c); + + /* bump RX counter */ + uart->stats.rx_cnt++; + + signal = 1; + } + + /* Wake RX thread if any */ + if ( signal ) + rtems_semaphore_release(uart->rx_sem); + + /* If room in HW fifo and we got more chars to be sent */ + if ( !(status & APBUART_STATUS_TF) ){ + + if ( apbuart_fifo_isEmpty(uart->txfifo) ){ + /* Turn off TX interrupt when no data is to be sent */ + if ( status & APBUART_STATUS_TE ){ + uart->regs->ctrl = READ_REG(&uart->regs->ctrl) & ~APBUART_CTRL_TF; + DBG("?"); + } + return; + } + + /* signal when there will be more room in SW fifo */ + if ( apbuart_fifo_isFull(uart->txfifo) ) + signal = 1; + + do{ + /* Put data into HW TX fifo */ + apbuart_fifo_peek(uart->txfifo,&next_char); + c = *next_char; + if ( uart->ascii_mode && ( c == '\n') ){ + uart->regs->data = '\n'; + *next_char = '\r'; /* avoid sending mutiple '\n' or '\r' */ + }else{ + uart->regs->data = c; + apbuart_fifo_skip(uart->txfifo); /* remove sent char from fifo */ + } + uart->regs->ctrl = READ_REG(&uart->regs->ctrl) | APBUART_CTRL_TE | APBUART_CTRL_TF; + DBG("!"); + }while(!(empty=apbuart_fifo_isEmpty(uart->txfifo)) && + !((status=READ_REG(&uart->regs->status))&APBUART_STATUS_TF) ); + + /* Wake userspace thread, on empty or full fifo + * This makes tx_flush and block work. + */ + if ( signal || empty ){ + rtems_semaphore_release(uart->tx_sem); + } + } +} /******************* APBUART FIFO implementation ***********************/ -static apbuart_fifo *apbuart_fifo_create(int size){ +static apbuart_fifo *apbuart_fifo_create(int size) +{ apbuart_fifo *fifo; fifo = (apbuart_fifo *) malloc(size + sizeof(apbuart_fifo)); if ( fifo ) { @@ -839,22 +890,26 @@ static apbuart_fifo *apbuart_fifo_create(int size){ return fifo; } -static void apbuart_fifo_free(apbuart_fifo *fifo){ +static void apbuart_fifo_free(apbuart_fifo *fifo) +{ if ( fifo ) free(fifo); } -static inline int apbuart_fifo_isFull(apbuart_fifo *fifo){ +static inline int apbuart_fifo_isFull(apbuart_fifo *fifo) +{ return fifo->full; } -static inline int apbuart_fifo_isEmpty(apbuart_fifo *fifo){ +static inline int apbuart_fifo_isEmpty(apbuart_fifo *fifo) +{ if ( (fifo->head == fifo->tail) && !fifo->full ) return -1; return 0; } -static int apbuart_fifo_put(apbuart_fifo *fifo, unsigned char c){ +static int apbuart_fifo_put(apbuart_fifo *fifo, unsigned char c) +{ if ( !fifo->full ){ *fifo->head = c; fifo->head = (fifo->head >= fifo->max ) ? fifo->buf : fifo->head+1; @@ -865,7 +920,8 @@ static int apbuart_fifo_put(apbuart_fifo *fifo, unsigned char c){ return -1; } -static int apbuart_fifo_get(apbuart_fifo *fifo, unsigned char *c){ +static int apbuart_fifo_get(apbuart_fifo *fifo, unsigned char *c) +{ if ( apbuart_fifo_isEmpty(fifo) ) return -1; if ( c ) @@ -875,7 +931,8 @@ static int apbuart_fifo_get(apbuart_fifo *fifo, unsigned char *c){ return 0; } -static int inline apbuart_fifo_peek(apbuart_fifo *fifo, unsigned char **c){ +static int inline apbuart_fifo_peek(apbuart_fifo *fifo, unsigned char **c) +{ if ( apbuart_fifo_isEmpty(fifo) ) return -1; if ( c ) @@ -883,9 +940,11 @@ static int inline apbuart_fifo_peek(apbuart_fifo *fifo, unsigned char **c){ return 0; } -static void inline apbuart_fifo_skip(apbuart_fifo *fifo){ +static void inline apbuart_fifo_skip(apbuart_fifo *fifo) +{ if ( !apbuart_fifo_isEmpty(fifo) ){ fifo->tail = (fifo->tail >= fifo->max ) ? fifo->buf : fifo->tail+1; fifo->full = 0; } } +#endif diff --git a/c/src/lib/libbsp/sparc/shared/uart/apbuart_cons.c b/c/src/lib/libbsp/sparc/shared/uart/apbuart_cons.c new file mode 100644 index 0000000000..4e1304b227 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/uart/apbuart_cons.c @@ -0,0 +1,692 @@ +/* This file contains the driver for the GRLIB APBUART serial port. The driver + * is implemented by using the cons.c console layer. Interrupt/Polling/Task + * driven mode can be configured using driver resources: + * + * - mode (0=Polling, 1=Interrupt, 2=Task-Driven-Interrupt Mode) + * - syscon (0=Force not Ssystem Console, 1=Suggest System Console) + * + * The BSP define APBUART_INFO_AVAIL in order to add the info routine + * used for debugging. + * + * COPYRIGHT (c) 2010. + * Aeroflex Gaisler. + * + * 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. + * + * 2010-09-27, Daniel Hellstrom <daniel@gaisler.com> + * created + */ + +/******************* Driver manager interface ***********************/ +#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 <apbuart.h> +#include <ambapp.h> +#include <grlib.h> +#include <cons.h> +#include <rtems/termiostypes.h> + +/*#define DEBUG 1 */ + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +/* LEON3 Low level transmit/receive functions provided by debug-uart code */ +extern void apbuart_outbyte_polled( + struct apbuart_regs *regs, + unsigned char ch, + int do_cr_on_newline, + int wait_sent); +extern int apbuart_inbyte_nonblocking(struct apbuart_regs *regs); +extern struct apbuart_regs *dbg_uart; /* The debug UART */ + +struct apbuart_priv { + struct console_dev condev; + struct drvmgr_dev *dev; + struct apbuart_regs *regs; + char devName[32]; + void *cookie; + int sending; + int mode; +}; + +/* TERMIOS Layer Callback functions */ +void apbuart_get_attributes(struct console_dev *condev, struct termios *t); +int apbuart_set_attributes(int minor, const struct termios *t); +ssize_t apbuart_write_polled(int minor, const char *buf, size_t len); +int apbuart_pollRead(int minor); +ssize_t apbuart_write_intr(int minor, const char *buf, size_t len); +int apbuart_pollRead_task(int minor); +int apbuart_firstOpen(int major, int minor, void *arg); +int apbuart_lastClose(int major, int minor, void *arg); + +void apbuart_isr(void *arg); +int apbuart_get_baud(struct apbuart_priv *uart); + +int apbuart_init1(struct drvmgr_dev *dev); +#ifdef APBUART_INFO_AVAIL +static int apbuart_info( + struct drvmgr_dev *dev, + void (*print_line)(void *p, char *str), + void *p, int, char *argv[]); +#define APBUART_INFO_FUNC apbuart_info +#else +#define APBUART_INFO_FUNC NULL +#endif + +struct drvmgr_drv_ops apbuart_ops = +{ + .init = {apbuart_init1, NULL, NULL, NULL}, + .remove = NULL, + .info = APBUART_INFO_FUNC +}; + +static struct amba_dev_id apbuart_ids[] = +{ + {VENDOR_GAISLER, GAISLER_APBUART}, + {0, 0} /* Mark end of table */ +}; + +static struct amba_drv_info apbuart_drv_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_GAISLER_APBUART_ID, /* Driver ID */ + "APBUART_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &apbuart_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + sizeof(struct apbuart_priv), /*DrvMgr alloc private*/ + }, + &apbuart_ids[0] +}; + +void apbuart_cons_register_drv (void) +{ + DBG("Registering APBUART Console driver\n"); + drvmgr_drv_register(&apbuart_drv_info.general); +} + +/* Interrupt mode routines */ +static const rtems_termios_callbacks Callbacks_intr = { + apbuart_firstOpen, /* firstOpen */ + apbuart_lastClose, /* lastClose */ + NULL, /* pollRead */ + apbuart_write_intr, /* write */ + apbuart_set_attributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + TERMIOS_IRQ_DRIVEN /* outputUsesInterrupts */ +}; + +/* Polling mode routines */ +static const rtems_termios_callbacks Callbacks_task = { + apbuart_firstOpen, /* firstOpen */ + apbuart_lastClose, /* lastClose */ + apbuart_pollRead_task, /* pollRead */ + apbuart_write_intr, /* write */ + apbuart_set_attributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + TERMIOS_TASK_DRIVEN /* outputUsesInterrupts */ +}; + +/* Polling mode routines */ +static const rtems_termios_callbacks Callbacks_poll = { + apbuart_firstOpen, /* firstOpen */ + apbuart_lastClose, /* lastClose */ + apbuart_pollRead, /* pollRead */ + apbuart_write_polled, /* write */ + apbuart_set_attributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + TERMIOS_POLLED /* outputUsesInterrupts */ +}; + +int apbuart_init1(struct drvmgr_dev *dev) +{ + struct apbuart_priv *priv; + struct amba_dev_info *ambadev; + struct ambapp_core *pnpinfo; + union drvmgr_key_value *value; + char prefix[32]; + unsigned int db; + static int first_uart = 1; + + /* The default operation in AMP is to use APBUART[0] for CPU[0], + * APBUART[1] for CPU[1] and so on. The remaining UARTs is not used + * since we don't know how many CPU-cores there are. Note this only + * affects the on-chip amba bus (the root bus). The user can override + * the default resource sharing by defining driver resources for the + * APBUART devices on each AMP OS instance. + */ +#if defined(RTEMS_MULTIPROCESSING) && defined(LEON3) + if (drvmgr_on_rootbus(dev) && dev->minor_drv != LEON3_Cpu_Index && + drvmgr_keys_get(dev, NULL) != 0) { + /* User hasn't configured on-chip APBUART, leave it untouched */ + return DRVMGR_EBUSY; + } +#endif + + DBG("APBUART[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name); + /* Private data was allocated and zeroed by driver manager */ + priv = dev->priv; + if (!priv) + return DRVMGR_NOMEM; + priv->dev = dev; + + /* Get device information from AMBA PnP information */ + ambadev = (struct amba_dev_info *)priv->dev->businfo; + if (ambadev == NULL) + return -1; + pnpinfo = &ambadev->info; + priv->regs = (struct apbuart_regs *)pnpinfo->apb_slv->start; + + /* Clear HW regs, leave baudrate register as it is */ + priv->regs->status = 0; + /* leave debug bit, and Transmitter/receiver if this is the debug UART. + * With old APBUARTs debug is enabled by setting LB and FL, since LB is + * not reset we can not trust is, however since FL is reset we guess + * that we are debugging old UART if both FL and LB is already set. + */ +#ifdef LEON3 + if (priv->regs == dbg_uart) { + db = priv->regs->ctrl & (LEON_REG_UART_CTRL_DB | + LEON_REG_UART_CTRL_RE | + LEON_REG_UART_CTRL_TE | + LEON_REG_UART_CTRL_FL | + LEON_REG_UART_CTRL_LB | + LEON_REG_UART_CTRL_PE | + LEON_REG_UART_CTRL_PS); + } else +#endif + { + if (priv->regs->ctrl & (LEON_REG_UART_CTRL_FL | + LEON_REG_UART_CTRL_LB)) + db = priv->regs->ctrl & (LEON_REG_UART_CTRL_FL | + LEON_REG_UART_CTRL_LB); + else + db = priv->regs->ctrl & LEON_REG_UART_CTRL_DB; + } + + priv->regs->ctrl = db; + + /* The system console and Debug console may depend on this device, so + * initialize it straight away. + * + * We default to have System Console on first APBUART, user may override + * this behaviour by setting the syscon option to 0. + */ + if (drvmgr_on_rootbus(dev) && first_uart) { + priv->condev.flags = CONSOLE_FLAG_SYSCON; + first_uart = 0; + } else { + priv->condev.flags = 0; + } + + value = drvmgr_dev_key_get(priv->dev, "syscon", KEY_TYPE_INT); + if (value) { + if (value->i) + priv->condev.flags |= CONSOLE_FLAG_SYSCON; + else + priv->condev.flags &= ~CONSOLE_FLAG_SYSCON; + } + + priv->condev.fsname = NULL; + priv->condev.ops.get_uart_attrs = apbuart_get_attributes; + + /* Select 0=Polled, 1=IRQ, 2=Task-Driven UART Mode */ + value = drvmgr_dev_key_get(priv->dev, "mode", KEY_TYPE_INT); + if (value) + priv->mode = value->i; + else + priv->mode = TERMIOS_POLLED; + if (priv->mode == TERMIOS_IRQ_DRIVEN) { + priv->condev.callbacks = &Callbacks_intr; + } else if (priv->mode == TERMIOS_TASK_DRIVEN) { + priv->condev.callbacks = &Callbacks_task; + } else { + priv->condev.callbacks = &Callbacks_poll; + } + + /* Get Filesystem name prefix */ + prefix[0] = '\0'; + if (drvmgr_get_dev_prefix(dev, prefix)) { + /* Got special prefix, this means we have a bus prefix + * And we should use our "bus minor" + */ + sprintf(priv->devName, "/dev/%sapbuart%d", prefix, dev->minor_bus); + priv->condev.fsname = priv->devName; + } + + /* Register it as a console device, the console driver will register + * a termios device as well + */ + console_dev_register(&priv->condev); + + return DRVMGR_OK; +} + +#ifdef APBUART_INFO_AVAIL +static int apbuart_info( + struct drvmgr_dev *dev, + void (*print_line)(void *p, char *str), + void *p, int argc, char *argv[]) +{ + struct apbuart_priv *priv = dev->priv; + char *str1; + char buf[64]; + + if (dev->priv == NULL) + return -DRVMGR_EINVAL; + + if (priv->mode == TERMIOS_POLLED) + str1 = "TERMIOS_POLLED"; + else if (priv->mode == TERMIOS_TASK_DRIVEN) + str1 = "TERMIOS_TASK_DRIVEN"; + else if (priv->mode == TERMIOS_TASK_DRIVEN) + str1 = "TERMIOS_TASK_DRIVEN"; + else + str1 = "BAD MODE"; + + sprintf(buf, "UART Mode: %s", str1); + print_line(p, buf); + if (priv->condev.fsname) { + sprintf(buf, "FS Name: %s", priv->condev.fsname); + print_line(p, buf); + } + sprintf(buf, "STATUS REG: 0x%x", priv->regs->status); + print_line(p, buf); + sprintf(buf, "CTRL REG: 0x%x", priv->regs->ctrl); + print_line(p, buf); + sprintf(buf, "SCALER REG: 0x%x baud rate %d", + priv->regs->scaler, apbuart_get_baud(priv)); + print_line(p, buf); + + return DRVMGR_OK; +} +#endif + +#ifndef LEON3 +/* This routine transmits a character, it will busy-wait until on character + * fits in the APBUART Transmit FIFO + */ +void apbuart_outbyte_polled( + struct apbuart_regs *regs, + unsigned char ch, + int do_cr_on_newline, + int wait_sent) +{ +send: + while ((regs->status & LEON_REG_UART_STATUS_THE) == 0) { + /* Lower bus utilization while waiting for UART */ + asm volatile ("nop"::); asm volatile ("nop"::); + asm volatile ("nop"::); asm volatile ("nop"::); + asm volatile ("nop"::); asm volatile ("nop"::); + asm volatile ("nop"::); asm volatile ("nop"::); + } + regs->data = (unsigned int) ch; + + if ((ch == '\n') && do_cr_on_newline) { + ch = '\r'; + goto send; + } + + /* Wait until the character has been sent? */ + if (wait_sent) { + while ((regs->status & LEON_REG_UART_STATUS_THE) == 0) + ; + } +} + +/* This routine polls for one character, return EOF if no character is available */ +int apbuart_inbyte_nonblocking(struct apbuart_regs *regs) +{ + if (regs->status & LEON_REG_UART_STATUS_ERR) { + regs->status = ~LEON_REG_UART_STATUS_ERR; + } + + if ((regs->status & LEON_REG_UART_STATUS_DR) == 0) + return EOF; + + return (int)regs->data; +} +#endif + +int apbuart_firstOpen(int major, int minor, void *arg) +{ + struct apbuart_priv *uart = (struct apbuart_priv *)minor; + rtems_libio_open_close_args_t *ioarg = arg; + + if ( ioarg && ioarg->iop ) + uart->cookie = ioarg->iop->data1; + else + uart->cookie = NULL; + + /* Enable TX/RX */ + uart->regs->ctrl |= LEON_REG_UART_CTRL_RE | LEON_REG_UART_CTRL_TE; + + if (uart->mode != TERMIOS_POLLED) { + /* Register interrupt and enable it */ + drvmgr_interrupt_register(uart->dev, 0, "apbuart", + apbuart_isr, uart); + + uart->sending = 0; + /* Turn on RX interrupts */ + uart->regs->ctrl |= LEON_REG_UART_CTRL_RI; + } + + return 0; +} + +int apbuart_lastClose(int major, int minor, void *arg) +{ + struct apbuart_priv *uart = (struct apbuart_priv *)minor; + + if (uart->mode != TERMIOS_POLLED) { + /* Turn off RX interrupts */ + uart->regs->ctrl &= ~(LEON_REG_UART_CTRL_RI); + + /**** Flush device ****/ + while (uart->sending) { + /* Wait until all data has been sent */ + } + + /* Disable and unregister interrupt handler */ + drvmgr_interrupt_unregister(uart->dev, 0, apbuart_isr, uart); + } + +#ifdef LEON3 + /* Disable TX/RX if not used for DEBUG */ + if (uart->regs != dbg_uart) + uart->regs->ctrl &= ~(LEON_REG_UART_CTRL_RE | LEON_REG_UART_CTRL_TE); +#endif + + return 0; +} + +int apbuart_pollRead(int minor) +{ + struct apbuart_priv *uart = (struct apbuart_priv *)minor; + + return apbuart_inbyte_nonblocking(uart->regs); +} + +int apbuart_pollRead_task(int minor) +{ + struct apbuart_priv *uart = (struct apbuart_priv *)minor; + int c, tot; + char buf[32]; + + tot = 0; + while ((c=apbuart_inbyte_nonblocking(uart->regs)) != EOF) { + buf[tot] = c; + tot++; + if (tot > 31) { + rtems_termios_enqueue_raw_characters(uart->cookie, buf, tot); + tot = 0; + } + } + if (tot > 0) + rtems_termios_enqueue_raw_characters(uart->cookie, buf, tot); + + return EOF; +} + +struct apbuart_baud { + unsigned int num; + unsigned int baud; +}; +struct apbuart_baud apbuart_baud_table[] = { + {B50, 50}, + {B75, 75}, + {B110, 110}, + {B134, 134}, + {B150, 150}, + {B200, 200}, + {B300, 300}, + {B600, 600}, + {B1200, 1200}, + {B1800, 1800}, + {B2400, 2400}, + {B4800, 4800}, + {B9600, 9600}, + {B19200, 19200}, + {B38400, 38400}, + {B57600, 57600}, + {B115200, 115200}, + {B230400, 230400}, + {B460800, 460800}, +}; +#define BAUD_NUM (sizeof(apbuart_baud_table)/sizeof(struct apbuart_baud)) + +int apbuart_baud_num2baud(unsigned int num) +{ + int i; + + for(i=0; i<BAUD_NUM; i++) + if (apbuart_baud_table[i].num == num) + return apbuart_baud_table[i].baud; + return -1; +} + +struct apbuart_baud *apbuart_baud_find_closest(unsigned int baud) +{ + int i, diff; + + for(i=0; i<BAUD_NUM-1; i++) { + diff = apbuart_baud_table[i+1].baud - + apbuart_baud_table[i].baud; + if (baud < (apbuart_baud_table[i].baud + diff/2)) + return &apbuart_baud_table[i]; + } + return &apbuart_baud_table[BAUD_NUM-1]; +} + +int apbuart_get_baud(struct apbuart_priv *uart) +{ + unsigned int core_clk_hz; + unsigned int scaler; + + /* Get current scaler setting */ + scaler = uart->regs->scaler; + + /* Get APBUART core frequency */ + drvmgr_freq_get(uart->dev, DEV_APB_SLV, &core_clk_hz); + + /* Calculate baud rate from generator "scaler" number */ + return core_clk_hz / ((scaler + 1) * 8); +} + +struct apbuart_baud *apbuart_get_baud_closest(struct apbuart_priv *uart) +{ + return apbuart_baud_find_closest(apbuart_get_baud(uart)); +} + +int apbuart_set_attributes(int minor, const struct termios *t) +{ + unsigned int core_clk_hz; + unsigned int scaler; + unsigned int ctrl; + int baud; + struct apbuart_priv *uart = (struct apbuart_priv *)minor; + + switch(t->c_cflag & CSIZE) { + default: + case CS5: + case CS6: + case CS7: + /* Hardware doesn't support other than CS8 */ + return -1; + case CS8: + break; + } + + /* Read out current value */ + ctrl = uart->regs->ctrl; + + switch(t->c_cflag & (PARENB|PARODD)){ + case (PARENB|PARODD): + /* Odd parity */ + ctrl |= LEON_REG_UART_CTRL_PE|LEON_REG_UART_CTRL_PS; + break; + + case PARENB: + /* Even parity */ + ctrl &= ~LEON_REG_UART_CTRL_PS; + ctrl |= LEON_REG_UART_CTRL_PE; + break; + + default: + case 0: + case PARODD: + /* No Parity */ + ctrl &= ~(LEON_REG_UART_CTRL_PS|LEON_REG_UART_CTRL_PE); + } + + if (!(t->c_cflag & CLOCAL)) + ctrl |= LEON_REG_UART_CTRL_FL; + else + ctrl &= ~LEON_REG_UART_CTRL_FL; + + /* Update new settings */ + uart->regs->ctrl = ctrl; + + /* Baud rate */ + baud = apbuart_baud_num2baud(t->c_cflag & CBAUD); + if (baud > 0){ + /* Get APBUART core frequency */ + drvmgr_freq_get(uart->dev, DEV_APB_SLV, &core_clk_hz); + + /* Calculate Baud rate generator "scaler" number */ + scaler = (((core_clk_hz*10)/(baud*8))-5)/10; + + /* Set new baud rate by setting scaler */ + uart->regs->scaler = scaler; + } + + return 0; +} + +void apbuart_get_attributes(struct console_dev *condev, struct termios *t) +{ + struct apbuart_priv *uart = (struct apbuart_priv *)condev; + unsigned int ctrl; + struct apbuart_baud *baud; + + t->c_cflag = t->c_cflag & ~(CSIZE|PARENB|PARODD|CLOCAL|CBAUD); + + /* Hardware support only CS8 */ + t->c_cflag |= CS8; + + /* Read out current parity */ + ctrl = uart->regs->ctrl; + if (ctrl & LEON_REG_UART_CTRL_PE) { + if (ctrl & LEON_REG_UART_CTRL_PS) + t->c_cflag |= PARENB|PARODD; /* Odd parity */ + else + t->c_cflag |= PARENB; /* Even parity */ + } + + if ((ctrl & LEON_REG_UART_CTRL_FL) == 0) + t->c_cflag |= CLOCAL; + + baud = apbuart_get_baud_closest(uart); + t->c_cflag |= baud->num; +} + +ssize_t apbuart_write_polled(int minor, const char *buf, size_t len) +{ + int nwrite = 0; + struct apbuart_priv *uart = (struct apbuart_priv *)minor; + + while (nwrite < len) { + apbuart_outbyte_polled(uart->regs, *buf++, 0, 0); + nwrite++; + } + return nwrite; +} + +ssize_t apbuart_write_intr(int minor, const char *buf, size_t len) +{ + struct apbuart_priv *uart = (struct apbuart_priv *)minor; + unsigned int oldLevel; + unsigned int ctrl; + + rtems_interrupt_disable(oldLevel); + + /* Enable TX interrupt */ + ctrl = uart->regs->ctrl; + uart->regs->ctrl = ctrl | LEON_REG_UART_CTRL_TI; + + if (ctrl & LEON_REG_UART_CTRL_FA) { + /* APBUART with FIFO.. Fill as many as FIFO allows */ + uart->sending = 0; + while (((uart->regs->status & LEON_REG_UART_STATUS_TF) == 0) && + (uart->sending < len)) { + uart->regs->data = *buf; + buf++; + uart->sending++; + } + } else { + /* start UART TX, this will result in an interrupt when done */ + uart->regs->data = *buf; + + uart->sending = 1; + } + + rtems_interrupt_enable(oldLevel); + + return 0; +} + +/* Handle UART interrupts */ +void apbuart_isr(void *arg) +{ + struct apbuart_priv *uart = arg; + unsigned int status; + char data; + int cnt; + + /* Get all received characters */ + if (uart->mode == TERMIOS_TASK_DRIVEN) { + if ((status=uart->regs->status) & LEON_REG_UART_STATUS_DR) + rtems_termios_rxirq_occured(uart->cookie); + } else { + while ((status=uart->regs->status) & LEON_REG_UART_STATUS_DR) { + /* Data has arrived, get new data */ + data = uart->regs->data; + + /* Tell termios layer about new character */ + rtems_termios_enqueue_raw_characters(uart->cookie, &data, 1); + } + } + + if (uart->sending && (status & LEON_REG_UART_STATUS_THE)) { + /* Sent the one char, we disable TX interrupts */ + uart->regs->ctrl &= ~LEON_REG_UART_CTRL_TI; + + /* Tell close that we sent everything */ + cnt = uart->sending; + uart->sending = 0; + + /* apbuart_write_intr() will get called from this function */ + rtems_termios_dequeue_characters(uart->cookie, cnt); + } +} diff --git a/c/src/lib/libbsp/sparc/shared/uart/apbuart_pci.c b/c/src/lib/libbsp/sparc/shared/uart/apbuart_pci.c deleted file mode 100644 index 54d14ce459..0000000000 --- a/c/src/lib/libbsp/sparc/shared/uart/apbuart_pci.c +++ /dev/null @@ -1,42 +0,0 @@ - -#undef DEBUG - -/* Set registered device name */ -#define APBUART_DEVNAME "/dev/apbupci0" -#define APBUART_DEVNAME_NO(devstr,no) ((devstr)[12]='0'+(no)) - -/* Any non-static function will begin with */ -#define APBUART_PREFIX(name) apbuartpci##name - -/* do nothing, assume that the interrupt handler is called - * setup externally calling apbuartpci_interrupt_handler. - */ -#define APBUART_REG_INT(handler,irq,arg) \ - if ( apbuart_pci_int_reg ) \ - apbuart_pci_int_reg(handler,irq,arg); - -void (*apbuart_pci_int_reg)(void *handler, int irq, void *arg) = 0; - -void apbuartpci_interrupt_handler(int irq, void *arg); - -/* AMBA Bus is clocked using the PCI clock (33.3MHz) */ -#define SYS_FREQ_HZ 33333333 - -#include "apbuart.c" - -int apbuart_pci_register(amba_confarea_type *bus) -{ - /* Setup configuration */ - - /* Register the driver */ - return APBUART_PREFIX(_register)(bus); -} - - -/* Call this from PCI interrupt handler - * irq = the irq number of the HW device local to that IRQMP controller - * - */ -void apbuartpci_interrupt_handler(int irq, void *arg){ - apbuart_interrupt(arg); -} diff --git a/c/src/lib/libbsp/sparc/shared/uart/apbuart_rasta.c b/c/src/lib/libbsp/sparc/shared/uart/apbuart_rasta.c deleted file mode 100644 index 4b8734fdb6..0000000000 --- a/c/src/lib/libbsp/sparc/shared/uart/apbuart_rasta.c +++ /dev/null @@ -1,42 +0,0 @@ - -#undef DEBUG - -/* Set registered device name */ -#define APBUART_DEVNAME "/dev/apburasta0" -#define APBUART_DEVNAME_NO(devstr,no) ((devstr)[14]='0'+(no)) - -/* Any non-static function will begin with */ -#define APBUART_PREFIX(name) apbuartrasta##name - -/* do nothing, assume that the interrupt handler is called - * setup externally calling apbuartrasta_interrupt_handler. - */ -#define APBUART_REG_INT(handler,irq,arg) \ - if ( apbuart_rasta_int_reg ) \ - apbuart_rasta_int_reg(handler,irq,arg); - -void (*apbuart_rasta_int_reg)(void *handler, int irq, void *arg) = 0; - -void apbuartrasta_interrupt_handler(int irq, void *arg); - -/* AMBA Bus is clocked using the RASTA internal clock (30MHz) */ -#define SYS_FREQ_HZ 30000000 - -#include "apbuart.c" - -int apbuart_rasta_register(amba_confarea_type *bus) -{ - /* Setup configuration */ - - /* Register the driver */ - return APBUART_PREFIX(_register)(bus); -} - - -/* Call this from RASTA interrupt handler - * irq = the irq number of the HW device local to that IRQMP controller - * - */ -void apbuartrasta_interrupt_handler(int irq, void *arg){ - apbuart_interrupt(arg); -} diff --git a/c/src/lib/libbsp/sparc/shared/uart/cons.c b/c/src/lib/libbsp/sparc/shared/uart/cons.c new file mode 100644 index 0000000000..981fb956d6 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/uart/cons.c @@ -0,0 +1,189 @@ +/* This file contains the TTY driver for the serial ports. The driver + * is layered so that different UART hardware can be used. It is implemented + * using the Driver Manager. + * + * This driver uses the termios pseudo driver. + * + * COPYRIGHT (c) 2010. + * Aeroflex Gaisler + * + * 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 <stdlib.h> +#include <rtems/libio.h> +#include <rtems/bspIo.h> +#include <cons.h> + +#ifdef RTEMS_DRVMGR_STARTUP + +/* Note that it is not possible to use the interrupt mode of the driver + * together with the "old" APBUART and -u to GRMON. However the new + * APBUART core (from 1.0.17-b2710) has the GRMON debug bit and can + * handle interrupts. + */ + +int console_initialized = 0; +rtems_device_major_number console_major = 0; + +#define FLAG_SYSCON 0x01 +struct console_priv { + unsigned char flags; /* 0x1=SystemConsole */ + unsigned char minor; + struct console_dev *dev; +}; + +#define CONSOLE_MAX BSP_NUMBER_OF_TERMIOS_PORTS +struct console_priv cons[CONSOLE_MAX] = {{0,0},}; + +/* Register Console to TERMIOS layer and initialize it */ +void console_dev_init(struct console_priv *con, int minor) +{ + char name[16], *fsname; + rtems_status_code status; + + if (!con->dev->fsname) { + strcpy(name, "/dev/console_a"); + /* Special console name and MINOR for SYSTEM CONSOLE */ + if (minor == 0) + name[12] = '\0'; /* /dev/console */ + name[13] += minor; /* when minor=0, this has no effect... */ + fsname = name; + } else { + fsname = con->dev->fsname; + } + status = rtems_io_register_name(fsname, console_major, minor); + if ((minor == 0) && (status != RTEMS_SUCCESSFUL)) + rtems_fatal_error_occurred(status); +} + +void console_dev_register(struct console_dev *dev) +{ + int i, minor = 0; + struct console_priv *con = NULL; + + if ((dev->flags & CONSOLE_FLAG_SYSCON) && !cons[0].dev) { + con = &cons[0]; + con->flags = FLAG_SYSCON; + } else { + for (i=1; i<CONSOLE_MAX; i++) { + if (!cons[i].dev) { + con = &cons[i]; + con->flags = 0; + minor = i; + break; + } + } + } + if (con == NULL) { + /* Not enough console structures */ + return; + } + + /* Assign Console */ + con->dev = dev; + con->minor = minor; + + /* Console layer is already initialized, that means that we can + * register termios interface directly. + */ + if (console_initialized) + console_dev_init(con, minor); +} + +#if 0 +void console_dev_unregister(struct console_dev *dev) +{ + +} +#endif + +rtems_device_driver console_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg) +{ + int i; + + console_major = major; + + rtems_termios_initialize(); + + /* Register all Console a file system device node */ + for (i=0; i<CONSOLE_MAX; i++) { + if (cons[i].dev) + console_dev_init(&cons[i], i); + } + + console_initialized = 1; + + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver console_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg) +{ + rtems_status_code status; + struct termios term; + + if ((minor >= CONSOLE_MAX) || !cons[minor].dev) + return RTEMS_INVALID_NUMBER; + + status = rtems_termios_open( + major, + (int)cons[minor].dev, + arg, + cons[minor].dev->callbacks); + + /* Inherit UART hardware parameters from bootloader on system console */ + if ((status == RTEMS_SUCCESSFUL) && (cons[minor].flags & FLAG_SYSCON) && + (cons[minor].dev->ops.get_uart_attrs != NULL)) { + if (tcgetattr(STDIN_FILENO, &term) >= 0) { + cons[minor].dev->ops.get_uart_attrs(cons[minor].dev, + &term); + term.c_oflag |= ONLCR; + tcsetattr(STDIN_FILENO, TCSANOW, &term); + } + } + + return status; +} + +rtems_device_driver console_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg) +{ + return rtems_termios_close(arg); +} + +rtems_device_driver console_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg) +{ + return rtems_termios_read(arg); +} + +rtems_device_driver console_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg) +{ + return rtems_termios_write(arg); +} + +rtems_device_driver console_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg) +{ + return rtems_termios_ioctl(arg); +} + +#endif diff --git a/c/src/lib/libcpu/sparc/Makefile.am b/c/src/lib/libcpu/sparc/Makefile.am index 7bebd9417e..711d6b1fa8 100644 --- a/c/src/lib/libcpu/sparc/Makefile.am +++ b/c/src/lib/libcpu/sparc/Makefile.am @@ -10,6 +10,8 @@ noinst_PROGRAMS = include_libcpudir = $(includedir)/libcpu include_libcpu_HEADERS = ../shared/include/cache.h +include_libcpu_HEADERS += include/libcpu/byteorder.h +include_libcpu_HEADERS += include/libcpu/access.h noinst_PROGRAMS += cache.rel cache_rel_SOURCES = cache/cache.c cache/cache_.h \ @@ -31,5 +33,10 @@ reg_win_rel_SOURCES = reg_win/window.S reg_win_rel_CPPFLAGS = $(AM_CPPFLAGS) reg_win_rel_LDFLAGS = $(RTEMS_RELLDFLAGS) +noinst_PROGRAMS += access.rel +access_rel_SOURCES = access/access.S access/access_le.c +access_rel_CPPFLAGS = $(AM_CPPFLAGS) +access_rel_LDFLAGS = $(RTEMS_RELLDFLAGS) + include $(srcdir)/preinstall.am include $(top_srcdir)/../../../automake/local.am diff --git a/c/src/lib/libcpu/sparc/access/access.S b/c/src/lib/libcpu/sparc/access/access.S new file mode 100644 index 0000000000..7e69f64c9d --- /dev/null +++ b/c/src/lib/libcpu/sparc/access/access.S @@ -0,0 +1,65 @@ +/* + * Access routines for SPARC + * + * COPYRIGHT (c) 2011 + * Aeroflex Gaisler. + * + * 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/asm.h> + + .align 4 + .seg "text" + PUBLIC(_ld8) + PUBLIC(_ld16) + PUBLIC(_ld32) + PUBLIC(_ld64) + PUBLIC(_st8) + PUBLIC(_st16) + PUBLIC(_st32) + PUBLIC(_st64) + PUBLIC(_ld_be16) + PUBLIC(_ld_be32) + PUBLIC(_st_be16) + PUBLIC(_st_be32) + +SYM(_ld8): + retl + ldub [%o0], %o0 + +SYM(_ld_be16): +SYM(_ld16): + retl + lduh [%o0], %o0 + +SYM(_ld_be32): +SYM(_ld32): + retl + ld [%o0], %o0 + +SYM(_ld_be64): +SYM(_ld64): + retl + ldd [%o0], %o0 + +SYM(_st8): + retl + stub %o1, [%o0] + +SYM(_st_be16): +SYM(_st16): + retl + stuh %o1, [%o0] + +SYM(_st_be32): +SYM(_st32): + retl + st %o1, [%o0] + +SYM(_st_be64): +SYM(_st64): + retl + std %o1, [%o0] diff --git a/c/src/lib/libcpu/sparc/access/access_le.c b/c/src/lib/libcpu/sparc/access/access_le.c new file mode 100644 index 0000000000..15031ccf50 --- /dev/null +++ b/c/src/lib/libcpu/sparc/access/access_le.c @@ -0,0 +1,32 @@ +/* + * Little-endian access routines for SPARC + * + * COPYRIGHT (c) 2011 + * Aeroflex Gaisler. + * + * 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 <libcpu/byteorder.h> + +uint16_t _ld_le16(uint16_t *addr) +{ + return ld_le16(addr); +} + +void _st_le16(uint16_t *addr, uint16_t val) +{ + st_le16(addr, val); +} + +uint32_t _ld_le32(uint32_t *addr) +{ + return ld_le32(addr); +} + +void _st_le32(uint32_t *addr, uint32_t val) +{ + st_le32(addr, val); +} diff --git a/c/src/lib/libcpu/sparc/include/libcpu/access.h b/c/src/lib/libcpu/sparc/include/libcpu/access.h new file mode 100644 index 0000000000..2d87c2ad48 --- /dev/null +++ b/c/src/lib/libcpu/sparc/include/libcpu/access.h @@ -0,0 +1,48 @@ +/* + * access.h - access routines for SPARC. SPARC is big endian only. + * + * COPYRIGHT (c) 2011 + * Aeroflex Gaisler. + * + * 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 _LIBCPU_ACCESS_H +#define _LIBCPU_ACCESS_H + +#include <rtems/system.h> +#include <rtems/score/cpu.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* "Raw" access */ +extern uint8_t _ld8(uint8_t *addr); +extern void _st8(uint8_t *addr, uint8_t val); +extern uint16_t _ld16(uint16_t *addr); +extern void _st16(uint16_t *addr, uint16_t val); +extern uint32_t _ld32(uint32_t *addr); +extern void _st32(uint32_t *addr, uint32_t val); +extern uint64_t _ld64(uint64_t *addr); +extern void _st64(uint64_t *addr, uint64_t val); + +/* Aliases for Big Endian */ +extern uint16_t _ld_be16(uint16_t *addr); +extern void _st_be16(uint16_t *addr, uint16_t val); +extern uint32_t _ld_be32(uint32_t *addr); +extern void _st_be32(uint32_t *addr, uint32_t val); + +/* Little endian */ +extern uint16_t _ld_le16(uint16_t *addr); +extern void _st_le16(uint16_t *addr, uint16_t val); +extern uint32_t _ld_le32(uint32_t *addr); +extern void _st_le32(uint32_t *addr, uint32_t val); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/lib/libcpu/sparc/include/libcpu/byteorder.h b/c/src/lib/libcpu/sparc/include/libcpu/byteorder.h new file mode 100644 index 0000000000..d626f28068 --- /dev/null +++ b/c/src/lib/libcpu/sparc/include/libcpu/byteorder.h @@ -0,0 +1,66 @@ +/* + * byteorder.h - Endian conversion for SPARC. SPARC is big endian only. + * + * COPYRIGHT (c) 2011 + * Aeroflex Gaisler. + * + * 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 _LIBCPU_BYTEORDER_H +#define _LIBCPU_BYTEORDER_H + +#include <rtems/system.h> +#include <rtems/score/cpu.h> + +#ifdef __cplusplus +extern "C" { +#endif + +RTEMS_INLINE_ROUTINE uint16_t ld_le16(volatile uint16_t *addr) +{ + return CPU_swap_u16(*addr); +} + +RTEMS_INLINE_ROUTINE void st_le16(volatile uint16_t *addr, uint16_t val) +{ + *addr = CPU_swap_u16(val); +} + +RTEMS_INLINE_ROUTINE uint32_t ld_le32(volatile uint32_t *addr) +{ + return CPU_swap_u32(*addr); +} + +RTEMS_INLINE_ROUTINE void st_le32(volatile uint32_t *addr, uint32_t val) +{ + *addr = CPU_swap_u32(val); +} + +RTEMS_INLINE_ROUTINE uint16_t ld_be16(volatile uint16_t *addr) +{ + return *addr; +} + +RTEMS_INLINE_ROUTINE void st_be16(volatile uint16_t *addr, uint16_t val) +{ + *addr = val; +} + +RTEMS_INLINE_ROUTINE uint32_t ld_be32(volatile uint32_t *addr) +{ + return *addr; +} + +RTEMS_INLINE_ROUTINE void st_be32(volatile uint32_t *addr, uint32_t val) +{ + *addr = val; +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/lib/libcpu/sparc/preinstall.am b/c/src/lib/libcpu/sparc/preinstall.am index 412b683681..2efe38bb30 100644 --- a/c/src/lib/libcpu/sparc/preinstall.am +++ b/c/src/lib/libcpu/sparc/preinstall.am @@ -22,3 +22,11 @@ $(PROJECT_INCLUDE)/libcpu/cache.h: ../shared/include/cache.h $(PROJECT_INCLUDE)/ $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/libcpu/cache.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/libcpu/cache.h +$(PROJECT_INCLUDE)/libcpu/byteorder.h: include/libcpu/byteorder.h $(PROJECT_INCLUDE)/libcpu/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/libcpu/byteorder.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/libcpu/byteorder.h + +$(PROJECT_INCLUDE)/libcpu/access.h: include/libcpu/access.h $(PROJECT_INCLUDE)/libcpu/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/libcpu/access.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/libcpu/access.h + diff --git a/c/src/lib/libcpu/sparc/reg_win/window.S b/c/src/lib/libcpu/sparc/reg_win/window.S index e28774d74e..3ec3f50a10 100644 --- a/c/src/lib/libcpu/sparc/reg_win/window.S +++ b/c/src/lib/libcpu/sparc/reg_win/window.S @@ -130,8 +130,7 @@ SYM(window_underflow_trap_handler): /* * Flush All Windows trap handler. * - * Flush all windows with valid contents except the current one - * and the one we will be returning to. + * Flush all windows with valid contents except the current one. * * In examining the set register windows, one may logically divide * the windows into sets (some of which may be empty) based on their @@ -154,8 +153,8 @@ SYM(window_underflow_trap_handler): * + 5 - current * + 6-7 - used * - * In this case, we only would save the used windows which we - * will not be returning to -- 6. + * In this case, we only would save the used windows 6 and 7, but + * not 5. * * Register Usage while saving the windows: * g1 = current PSR @@ -194,22 +193,9 @@ SYM(window_flush_trap_handler): mov 1, %g4 sll %g4, %g5, %g4 ! g4 = WIM mask for CWP+1 invalid - restore ! go back one register window - save_frame_loop: - sll %g4, 1, %g5 ! rotate the "wim" left 1 - srl %g4, SPARC_NUMBER_OF_REGISTER_WINDOWS - 1, %g4 - or %g4, %g5, %g4 ! g4 = wim if we do one restore - /* - * If a restore would not underflow, then continue. - */ - - andcc %g4, %g2, %g0 ! Any windows to flush? - bnz done_flushing ! No, then continue - nop - - restore ! back one window + restore ! go back one register window /* * Now save the window just as if we overflowed to it. @@ -225,8 +211,17 @@ save_frame_loop: std %i4, [%sp + CPU_STACK_FRAME_I4_OFFSET] std %i6, [%sp + CPU_STACK_FRAME_I6_FP_OFFSET] - ba save_frame_loop - nop + sll %g4, 1, %g5 ! rotate the "wim" left 1 + srl %g4, SPARC_NUMBER_OF_REGISTER_WINDOWS - 1, %g4 + or %g4, %g5, %g4 ! g4 = wim if we do one restore + + /* + * If a restore would not underflow, then continue. + */ + + andcc %g4, %g2, %g0 ! Any windows to flush? + be save_frame_loop ! Yes, then continue saving + nop done_flushing: diff --git a/c/src/libchip/network/greth.c b/c/src/libchip/network/greth.c index 257a16c928..6cd5bd198f 100644 --- a/c/src/libchip/network/greth.c +++ b/c/src/libchip/network/greth.c @@ -12,10 +12,10 @@ */ #include <rtems.h> - -#define GRETH_SUPPORTED #include <bsp.h> +#ifdef GRETH_SUPPORTED + #include <inttypes.h> #include <errno.h> #include <rtems/bspIo.h> @@ -42,19 +42,17 @@ #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 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 */ @@ -82,11 +80,6 @@ extern void ipalign(struct mbuf *m); /* event to send when tx buffers become available */ #define GRETH_TX_WAIT_EVENT RTEMS_EVENT_3 - /* suspend when all TX descriptors exhausted */ - /* -#define GRETH_SUSPEND_NOTXBUF - */ - #if (MCLBYTES < RBUF_SIZE) # error "Driver must have MCLBYTES > RBUF_SIZE" #endif @@ -95,6 +88,10 @@ extern void ipalign(struct mbuf *m); #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 @@ -118,8 +115,7 @@ struct greth_softc greth_regs *regs; int acceptBroadcast; - rtems_id rxDaemonTid; - rtems_id txDaemonTid; + rtems_id daemonTid; unsigned int tx_ptr; unsigned int tx_dptr; @@ -131,7 +127,13 @@ struct greth_softc greth_rxtxdesc *rxdesc; struct mbuf **rxmbuf; struct mbuf **txmbuf; - rtems_vector_number vector; + int irq; + + /* 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; @@ -140,7 +142,7 @@ struct greth_softc int gb; int gbit_mac; int auto_neg; - unsigned int auto_neg_time; + struct timespec auto_neg_time; /* * Statistics @@ -165,6 +167,9 @@ struct greth_softc static struct greth_softc greth; +int greth_process_tx_gbit(struct greth_softc *sc); +int greth_process_tx(struct greth_softc *sc); + static char *almalloc(int sz) { char *tmp; @@ -175,33 +180,39 @@ static char *almalloc(int sz) /* GRETH interrupt handler */ -static rtems_isr -greth_interrupt_handler (rtems_vector_number v) +void greth_interrupt_handler (void *arg) { uint32_t status; - /* read and clear interrupt cause */ + uint32_t ctrl; + rtems_event_set events = 0; + /* read and clear interrupt cause */ status = greth.regs->status; greth.regs->status = status; + ctrl = greth.regs->ctrl; /* Frame received? */ - if (status & (GRETH_STATUS_RXERR | GRETH_STATUS_RXIRQ)) + if ((ctrl & GRETH_CTRL_RXIRQ) && (status & (GRETH_STATUS_RXERR | GRETH_STATUS_RXIRQ))) { greth.rxInterrupts++; - rtems_event_send (greth.rxDaemonTid, INTERRUPT_EVENT); + /* Stop RX-Error and RX-Packet interrupts */ + ctrl &= ~GRETH_CTRL_RXIRQ; + events |= INTERRUPT_EVENT; } -#ifdef GRETH_SUSPEND_NOTXBUF - if (status & (GRETH_STATUS_TXERR | GRETH_STATUS_TXIRQ)) + + if ( (ctrl & GRETH_CTRL_TXIRQ) && (status & (GRETH_STATUS_TXERR | GRETH_STATUS_TXIRQ)) ) { greth.txInterrupts++; - rtems_event_send (greth.txDaemonTid, GRETH_TX_WAIT_EVENT); + ctrl &= ~GRETH_CTRL_TXIRQ; + events |= GRETH_TX_WAIT_EVENT; } -#endif - /* - #ifdef __leon__ - LEON_Clear_interrupt(v-0x10); - #endif - */ + + /* Clear interrupt sources */ + greth.regs->ctrl = ctrl; + + /* Send the event(s) */ + if ( events ) + rtems_event_send (greth.daemonTid, events); } static uint32_t read_mii(uint32_t phy_addr, uint32_t reg_addr) @@ -228,6 +239,9 @@ static void write_mii(uint32_t phy_addr, uint32_t reg_addr, uint32_t data) 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: "); @@ -244,8 +258,9 @@ static void print_init_info(struct greth_softc *sc) printf("Half Duplex\n"); } #ifdef GRETH_AUTONEGO_PRINT_TIME - if ( sc->auto_neg ){ - printf("Autonegotiation Time: %dms\n",sc->auto_neg_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 } @@ -264,8 +279,7 @@ greth_initialize_hardware (struct greth_softc *sc) int phystatus; int tmp1; int tmp2; - unsigned int msecs; - struct timeval tstart, tnow; + struct timespec tstart, tnow; greth_regs *regs; @@ -300,42 +314,27 @@ greth_initialize_hardware (struct greth_softc *sc) sc->fd = 0; sc->sp = 0; sc->auto_neg = 0; - sc->auto_neg_time = 0; + _Timespec_Set_to_zero(&sc->auto_neg_time); if ((phyctrl >> 12) & 1) { /*wait for auto negotiation to complete*/ - msecs = 0; sc->auto_neg = 1; - if ( rtems_clock_get_tod_timeval(&tstart) == RTEMS_NOT_DEFINED){ - /* Not inited, set to epoch */ - rtems_time_of_day time; - time.year = 1988; - time.month = 1; - time.day = 1; - time.hour = 0; - time.minute = 0; - time.second = 0; - time.ticks = 0; - rtems_clock_set(&time); - - tstart.tv_sec = 0; - tstart.tv_usec = 0; - rtems_clock_get_tod_timeval(&tstart); - } + if (rtems_clock_get_uptime(&tstart) != RTEMS_SUCCESSFUL) + printk("rtems_clock_get_uptime failed\n"); while (!(((phystatus = read_mii(phyaddr, 1)) >> 5) & 1)) { - if ( rtems_clock_get_tod_timeval(&tnow) != RTEMS_SUCCESSFUL ) - printk("rtems_clock_get_tod_timeval failed\n\r"); - msecs = (tnow.tv_sec-tstart.tv_sec)*1000+(tnow.tv_usec-tstart.tv_usec)/1000; - if ( msecs > GRETH_AUTONEGO_TIMEOUT_MS ){ - sc->auto_neg_time = msecs; - printk("Auto negotiation timed out. Selecting default config\n\r"); + 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(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->auto_neg_time = msecs; sc->phydev.adv = read_mii(phyaddr, 4); sc->phydev.part = read_mii(phyaddr, 5); if ((phystatus >> 8) & 1) { @@ -376,7 +375,7 @@ auto_neg_done: phystatus = read_mii(phyaddr, 1); /*Read out PHY info if extended registers are available */ - if (phystatus & 1) { + if (phystatus & 1) { tmp1 = read_mii(phyaddr, 2); tmp2 = read_mii(phyaddr, 3); @@ -463,24 +462,71 @@ auto_neg_done: mac_addr_lsb |= sc->arpcom.ac_enaddr[5]; regs->mac_addr_lsb = mac_addr_lsb; - /* install interrupt vector */ - set_vector(greth_interrupt_handler, sc->vector, 1); + 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; - -#ifdef GRETH_SUSPEND_NOTXBUF - regs->ctrl |= GRETH_CTRL_TXIRQ; -#endif + + /* install interrupt handler */ + rtems_interrupt_handler_install(sc->irq, "greth", RTEMS_INTERRUPT_SHARED, + greth_interrupt_handler, sc); regs->ctrl |= GRETH_CTRL_RXEN | (sc->fd << 4) | GRETH_CTRL_RXIRQ | (sc->sp << 7) | (sc->gb << 8); print_init_info(sc); } -static void -greth_rxDaemon (void *arg) +#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 *) &greth; @@ -488,18 +534,40 @@ greth_rxDaemon (void *arg) 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, + 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 = - dp->rxdesc[dp->rx_ptr].ctrl) & GRETH_RXD_ENABLE)) + GRETH_MEM_LOAD(&dp->rxdesc[dp->rx_ptr].ctrl)) & GRETH_RXD_ENABLE)) { bad = 0; if (len_status & GRETH_RXD_TOOLONG) @@ -543,12 +611,27 @@ greth_rxDaemon (void *arg) len - sizeof (struct ether_header); eh = mtod (m, struct ether_header *); + + /* 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) ); + m->m_data += sizeof (struct ether_header); #ifdef CPU_U32_FIX if(!(dp->gbit_mac)) 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); @@ -565,43 +648,53 @@ greth_rxDaemon (void *arg) } 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 void +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; + /* - * Waiting for Transmitter ready + * Is there a free descriptor available? */ - n = m; + if (GRETH_MEM_LOAD(&dp->txdesc[dp->tx_ptr].ctrl) & GRETH_TXD_ENABLE){ + /* No. */ + inside = 0; + return 1; + } - while (dp->txdesc[dp->tx_ptr].ctrl & GRETH_TXD_ENABLE) - { -#ifdef GRETH_SUSPEND_NOTXBUF - dp->txdesc[dp->tx_ptr].ctrl |= GRETH_TXD_IRQ; - rtems_event_set events; - rtems_bsdnet_event_receive (GRETH_TX_WAIT_EVENT, - RTEMS_WAIT | RTEMS_EVENT_ANY, - TOD_MILLISECONDS_TO_TICKS(500), &events); -#endif - } + /* Remember head of chain */ + n = m; len = 0; - temp = (unsigned char *) dp->txdesc[dp->tx_ptr].addr; + temp = (unsigned char *) GRETH_MEM_LOAD(&dp->txdesc[dp->tx_ptr].addr); #ifdef GRETH_DEBUG printf("TXD: 0x%08x : BUF: 0x%08x\n", (int) m->m_data, (int) temp); #endif @@ -633,179 +726,282 @@ sendpacket (struct ifnet *ifp, struct mbuf *m) dp->txdesc[dp->tx_ptr].ctrl = GRETH_TXD_WRAP | GRETH_TXD_ENABLE | len; } - dp->regs->ctrl = dp->regs->ctrl | GRETH_CTRL_TXEN; 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; } -static void +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; - /*printf("Send packet entered\n");*/ if (inside) printf ("error: sendpacket re-entered!!\n"); inside = 1; - /* - * Waiting for Transmitter ready - */ 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 (;;) { - while (dp->txdesc[dp->tx_ptr].ctrl & GRETH_TXD_ENABLE) - { -#ifdef GRETH_SUSPEND_NOTXBUF - dp->txdesc[dp->tx_ptr].ctrl |= GRETH_TXD_IRQ; - rtems_event_set events; - rtems_bsdnet_event_receive (GRETH_TX_WAIT_EVENT, - RTEMS_WAIT | RTEMS_EVENT_ANY, - TOD_MILLISECONDS_TO_TICKS(500), &events); -#endif - } + #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"); + 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; dp->txdesc[dp->tx_ptr].addr = (uint32_t *)m->m_data; + + /* Wrap around? */ if (dp->tx_ptr < dp->txbufs-1) { - if ((m->m_next) == NULL) { - dp->txdesc[dp->tx_ptr].ctrl = GRETH_TXD_ENABLE | GRETH_TXD_CS | m->m_len; - break; - } else { - dp->txdesc[dp->tx_ptr].ctrl = GRETH_TXD_ENABLE | GRETH_TXD_MORE | GRETH_TXD_CS | m->m_len; - } - } else { - if ((m->m_next) == NULL) { - dp->txdesc[dp->tx_ptr].ctrl = - GRETH_TXD_WRAP | GRETH_TXD_ENABLE | GRETH_TXD_CS | m->m_len; - break; - } else { - dp->txdesc[dp->tx_ptr].ctrl = - GRETH_TXD_WRAP | GRETH_TXD_ENABLE | GRETH_TXD_MORE | GRETH_TXD_CS | m->m_len; - } + 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; - dp->tx_ptr = (dp->tx_ptr + 1) % dp->txbufs; + rtems_interrupt_enable(level); + inside = 0; + + return 0; } -/* - * Driver transmit daemon - */ -void -greth_txDaemon (void *arg) +int greth_process_tx_gbit(struct greth_softc *sc) { - struct greth_softc *sc = (struct greth_softc *) arg; struct ifnet *ifp = &sc->arpcom.ac_if; struct mbuf *m; - rtems_event_set events; + rtems_interrupt_level level; + int first=1; - for (;;) - { + /* + * 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{ /* - * Wait for packet + * 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; + } + } - rtems_bsdnet_event_receive (START_TRANSMIT_EVENT, - RTEMS_EVENT_ANY | RTEMS_WAIT, - RTEMS_NO_TIMEOUT, &events); -#ifdef GRETH_DEBUG - printf ("t\n"); -#endif + /* 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 */ - /* - * Send packets till queue is empty + /* 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; - - for (;;) - { - /* - * Get the next mbuf chain to transmit. - */ - IF_DEQUEUE (&ifp->if_snd, m); - if (!m) - break; - sendpacket(ifp, 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; } - ifp->if_flags &= ~IFF_OACTIVE; + + return -1; + }else{ + /* Sent Ok, proceed to process more packets if available */ + } } + return 0; } -/* - * Driver transmit daemon - */ -void -greth_txDaemon_gbit (void *arg) +int greth_process_tx(struct greth_softc *sc) { - struct greth_softc *sc = (struct greth_softc *) arg; struct ifnet *ifp = &sc->arpcom.ac_if; struct mbuf *m; - rtems_event_set events; + rtems_interrupt_level level; + int first=1; - for (;;) - { + /* + * 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{ /* - * Wait for packet + * 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; + } + } - rtems_bsdnet_event_receive (START_TRANSMIT_EVENT, - RTEMS_EVENT_ANY | RTEMS_WAIT, - RTEMS_NO_TIMEOUT, &events); -#ifdef GRETH_DEBUG - printf ("t\n"); -#endif + /* Try to send packet, failed if it a non-zero number is returned. */ + if ( sendpacket(ifp, m) ){ + /* Not enough resources */ - /* - * Send packets till queue is empty + /* 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. */ - for (;;) - { - while((sc->tx_cnt > 0) && !(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--; - } - /* - * Get the next mbuf chain to transmit. - */ - IF_DEQUEUE (&ifp->if_snd, m); - if (!m) - break; - sendpacket_gbit(ifp, m); + 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; } - ifp->if_flags &= ~IFF_OACTIVE; + + 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; - ifp->if_flags |= IFF_OACTIVE; - rtems_event_send (sc->txDaemonTid, START_TRANSMIT_EVENT); + 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); + } } /* @@ -817,34 +1013,25 @@ greth_init (void *arg) struct greth_softc *sc = arg; struct ifnet *ifp = &sc->arpcom.ac_if; - if (sc->txDaemonTid == 0) - { + if (sc->daemonTid == 0) { - /* - * Set up GRETH hardware - */ - greth_initialize_hardware (sc); + /* + * Start driver tasks + */ + sc->daemonTid = rtems_bsdnet_newproc ("DCrxtx", 4096, + greth_Daemon, sc); - /* - * Start driver tasks - */ - sc->rxDaemonTid = rtems_bsdnet_newproc ("DCrx", 4096, - greth_rxDaemon, sc); - if (sc->gbit_mac) { - sc->txDaemonTid = rtems_bsdnet_newproc ("DCtx", 4096, - greth_txDaemon_gbit, sc); - } else { - sc->txDaemonTid = rtems_bsdnet_newproc ("DCtx", 4096, - greth_txDaemon, sc); - } + /* + * Set up GRETH hardware + */ + greth_initialize_hardware (sc); - } + } /* * Tell the world that we're running. */ ifp->if_flags |= IFF_RUNNING; - } /* @@ -860,6 +1047,8 @@ greth_stop (struct greth_softc *sc) 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; } @@ -876,6 +1065,8 @@ greth_stats (struct greth_softc *sc) 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); } /* @@ -967,7 +1158,7 @@ rtems_greth_driver_attach (struct rtems_bsdnet_ifconfig *config, sc->acceptBroadcast = !config->ignore_broadcast; sc->regs = (void *) chip->base_address; - sc->vector = chip->vector; + sc->irq = chip->irq; sc->txbufs = chip->txd_count; sc->rxbufs = chip->rxd_count; @@ -998,3 +1189,4 @@ rtems_greth_driver_attach (struct rtems_bsdnet_ifconfig *config, return 1; }; +#endif diff --git a/c/src/libchip/network/greth.h b/c/src/libchip/network/greth.h index 222a9efbc2..dbc1f22c55 100644 --- a/c/src/libchip/network/greth.h +++ b/c/src/libchip/network/greth.h @@ -18,7 +18,7 @@ typedef struct { uint32_t base_address; - uint32_t vector; + uint32_t irq; uint32_t txd_count; uint32_t rxd_count; } greth_configuration_t; diff --git a/c/src/libchip/network/smc91111.c b/c/src/libchip/network/smc91111.c index d959386463..f08c3e31b4 100644 --- a/c/src/libchip/network/smc91111.c +++ b/c/src/libchip/network/smc91111.c @@ -15,13 +15,14 @@ */ #if defined(__sparc__) - #define SMC91111_SUPPORTED + #include <bsp.h> + #if defined(LEON2) || defined(LEON3) + #define SMC91111_SUPPORTED + #endif #endif #if defined(SMC91111_SUPPORTED) -#include <bsp.h> - #include <stdlib.h> #include <stdio.h> #include <stdarg.h> @@ -58,12 +59,6 @@ #include "smc91111config.h" #include <libchip/smc91111.h> -#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 - struct lan91cxx_priv_data smc91111; int lan91cxx_hardware_init(struct lan91cxx_priv_data *cpd); @@ -87,9 +82,9 @@ static void lan91cxx_phy_configure(struct lan91cxx_priv_data *cpd); #define max(l,r) ((l) > (r) ? (l) : (r)) /* \ ------------- Interrupt ------------- \ */ -rtems_isr lan91cxx_interrupt_handler(rtems_vector_number v) +void lan91cxx_interrupt_handler(void *arg) { - struct lan91cxx_priv_data *cpd = &smc91111; + struct lan91cxx_priv_data *cpd = arg; unsigned short irq, event; unsigned short oldbase; unsigned short oldpointer; @@ -1048,15 +1043,18 @@ static void smc91111_stop(struct lan91cxx_priv_data *cpd) int lan91cxx_hardware_init(struct lan91cxx_priv_data *cpd) { unsigned short val; - int i; + int i, rc; DEBUG_FUNCTION(); cpd->txbusy = cpd->within_send = 0; /* install interrupt vector */ - db_printf("Install lan91cxx irqvector at %d\n", cpd->config.vector); - set_vector(lan91cxx_interrupt_handler, cpd->config.vector, 1); + db_printf("Install lan91cxx isr at irq %d\n", cpd->config.irq); + rc = rtems_interrupt_handler_install(cpd->config.irq, "smc91cxx", + RTEMS_INTERRUPT_SHARED, lan91cxx_interrupt_handler, cpd); + if (rc != RTEMS_SUCCESSFUL) + return 0; /* Reset chip */ put_reg(cpd, LAN91CXX_RCR, LAN91CXX_RCR_SOFT_RST); diff --git a/c/src/libchip/network/smc91111exp.h b/c/src/libchip/network/smc91111exp.h index 72e41fa0d9..c9698c9291 100644 --- a/c/src/libchip/network/smc91111exp.h +++ b/c/src/libchip/network/smc91111exp.h @@ -7,7 +7,7 @@ typedef struct scmv91111_configuration { void *baseaddr; - unsigned int vector; + int irq; unsigned int pio; unsigned int ctl_rspeed; unsigned int ctl_rfduplx; diff --git a/c/src/make/configure.ac b/c/src/make/configure.ac index ebb379636b..7ed21f29b1 100644 --- a/c/src/make/configure.ac +++ b/c/src/make/configure.ac @@ -22,6 +22,7 @@ RTEMS_ENABLE_POSIX RTEMS_ENABLE_ITRON RTEMS_ENABLE_NETWORKING RTEMS_ENABLE_CXX +RTEMS_ENABLE_DRVMGR RTEMS_ENV_RTEMSBSP diff --git a/configure.ac b/configure.ac index 597115e6f1..a37017edb1 100644 --- a/configure.ac +++ b/configure.ac @@ -30,6 +30,7 @@ RTEMS_ENABLE_TESTS RTEMS_ENABLE_RTEMS_DEBUG RTEMS_ENABLE_RTEMSBSP RTEMS_ENABLE_MULTILIB +RTEMS_ENABLE_DRVMGR AC_ARG_ENABLE([docs], [AS_HELP_STRING([--enable-docs],[enable building documentation diff --git a/cpukit/Makefile.am b/cpukit/Makefile.am index 943d1fd0dd..d84d2d3c95 100644 --- a/cpukit/Makefile.am +++ b/cpukit/Makefile.am @@ -10,7 +10,9 @@ include $(top_srcdir)/automake/multilib.am # librtemscpu SUBDIRS = . score rtems sapi posix itron SUBDIRS += libcsupport libblock libfs +SUBDIRS += libdrvmgr SUBDIRS += libnetworking librpc +SUBDIRS += libpci SUBDIRS += libi2c SUBDIRS += libmisc SUBDIRS += libmd @@ -181,6 +183,12 @@ include_rtems_HEADERS += libmisc/untar/untar.h ## fsmount include_rtems_HEADERS += libmisc/fsmount/fsmount.h +## Driver manager +include_drvmgrdir = $(includedir)/drvmgr +include_drvmgr_HEADERS = libdrvmgr/drvmgr.h +include_drvmgr_HEADERS += libdrvmgr/drvmgr_confdefs.h +include_drvmgr_HEADERS += libdrvmgr/drvmgr_list.h + include $(srcdir)/preinstall.am include $(top_srcdir)/automake/subdirs.am include $(top_srcdir)/automake/local.am diff --git a/cpukit/aclocal/enable-drvmgr.m4 b/cpukit/aclocal/enable-drvmgr.m4 new file mode 100644 index 0000000000..a9da288b11 --- /dev/null +++ b/cpukit/aclocal/enable-drvmgr.m4 @@ -0,0 +1,12 @@ +dnl $Id: enable-drvmgr.m4,v 1.0 + +AC_DEFUN([RTEMS_ENABLE_DRVMGR], +[ +AC_ARG_ENABLE(drvmgr, +AS_HELP_STRING(--enable-drvmgr,enable drvmgr at startup), +[case "${enableval}" in + yes) RTEMS_DRVMGR_STARTUP=yes ;; + no) RTEMS_DRVMGR_STARTUP=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for enable-drvmgr option) ;; +esac],[RTEMS_DRVMGR_STARTUP=yes]) +]) diff --git a/cpukit/configure.ac b/cpukit/configure.ac index b369b63e41..17ab9eae8d 100644 --- a/cpukit/configure.ac +++ b/cpukit/configure.ac @@ -20,6 +20,7 @@ RTEMS_ENABLE_RTEMS_DEBUG RTEMS_ENABLE_NETWORKING RTEMS_ENABLE_HTTPD RTEMS_ENABLE_SHTTPD +RTEMS_ENABLE_DRVMGR RTEMS_ENV_RTEMSCPU RTEMS_CHECK_RTEMS_DEBUG @@ -154,6 +155,11 @@ RTEMS_CPUOPT([RTEMS_NETWORKING], [1], [if networking is enabled]) +RTEMS_CPUOPT([RTEMS_DRVMGR_STARTUP], + [test x"$enable_drvmgr" = xyes], + [1], + [if driver manager api is supported]) + RTEMS_CPUOPT([RTEMS_VERSION], [true], ["]_RTEMS_VERSION["], @@ -322,11 +328,13 @@ score/cpu/no_cpu/Makefile posix/Makefile itron/Makefile libblock/Makefile +libdrvmgr/Makefile libfs/Makefile libfs/src/nfsclient/Makefile libgnat/Makefile libcsupport/Makefile libnetworking/Makefile +libpci/Makefile librpc/Makefile libmisc/Makefile libi2c/Makefile diff --git a/cpukit/libdrvmgr/Makefile.am b/cpukit/libdrvmgr/Makefile.am new file mode 100644 index 0000000000..471ae79421 --- /dev/null +++ b/cpukit/libdrvmgr/Makefile.am @@ -0,0 +1,31 @@ +## +## $Id: Makefile.am +## + +include $(top_srcdir)/automake/compile.am + +EXTRA_DIST= + +noinst_LIBRARIES = libdrvmgr.a + +libdrvmgr_a_SOURCES = drvmgr.c +libdrvmgr_a_SOURCES += drvmgr.h +libdrvmgr_a_SOURCES += drvmgr_by_name.c +libdrvmgr_a_SOURCES += drvmgr_by_id.c +libdrvmgr_a_SOURCES += drvmgr_drvinf.c +libdrvmgr_a_SOURCES += drvmgr_init.c +libdrvmgr_a_SOURCES += drvmgr_confdefs.h +libdrvmgr_a_SOURCES += drvmgr_for_each_dev.c +libdrvmgr_a_SOURCES += drvmgr_for_each_list_dev.c +libdrvmgr_a_SOURCES += drvmgr_func.c +libdrvmgr_a_SOURCES += drvmgr_func_call.c +libdrvmgr_a_SOURCES += drvmgr_list.c +libdrvmgr_a_SOURCES += drvmgr_list.h +libdrvmgr_a_SOURCES += drvmgr_lock.c +libdrvmgr_a_SOURCES += drvmgr_print.c +libdrvmgr_a_SOURCES += drvmgr_res.c +libdrvmgr_a_SOURCES += drvmgr_rw.c +libdrvmgr_a_SOURCES += drvmgr_translate.c +libdrvmgr_a_SOURCES += drvmgr_unregister.c + +include $(top_srcdir)/automake/local.am diff --git a/cpukit/libdrvmgr/README b/cpukit/libdrvmgr/README new file mode 100644 index 0000000000..51c668efb3 --- /dev/null +++ b/cpukit/libdrvmgr/README @@ -0,0 +1,113 @@ +DRIVER MANAGER +============== + +See documentation in Aeroflex Gaisler Driver manual. + + +INITIALIZATION +============== +The Driver Manager can be intialized in two different ways: + 1. during RTEMS startup + 2. started by user, typically in the Init task + +The driver manager is initalized during RTEMS startup in the +rtems_initialize_device_drivers() function when RTEMS is +configured with driver manager support. + +When RTEMS is not configured with the driver manager, the manager +may still be initialized by the user after system startup, typically +from the Init() task. + +The main difference between the two ways is when interrupt +is enabled. Interrupt is enabled for the first time by RTEMS when +the Init task is started. This means, for the first case, that +drivers can not use interrupt services until after the +initialization phase is over and the user request services from +the drivers. For the second case of initialization, this means +that driver writers must take extra care during initalization +when interrupt is enalbed so that spurious interrupts are not +generated and that the system does not hang in an infinite +IRQ loop. + +Most of the problems above are solved for the two methods by +specifying in which initialization levels IRQ handling is done. +See Level 1 and Level 2 below. + +Other differences is that IRQ, System Clock Timer, debug Console +and Console can be initalized by the help of the driver manager +when initialized during start up. Between Level0 and Level1 the +RTEMS I/O Manager drivers are initialized. The LEON3 BSP has +therefore two different versions of the basic drivers. + + +LEVEL0 +------ +The level of uninitialized devices that have been united with a +driver. + + +LEVEL1 - FIND/RESET/IRQ Clear +----------------------------- +The driver is for the first time informed of the presence of a +device. Only basic initialization. + +- Find all hardware needed for IRQ, Console, Timer and hardware + that need to be reset. +- Reset hardware, so that interrupts are not generated by mistake + when enabled later on. +- Init low level non-interrupt (polling-mode) services needed by + drivers init LEVEL2 and onwards, such as + * debug UART console for printk() + * Timer API (non-IRQ) + * GPIO (non-IRQ) + * special non-main memory configuration, washing +- Register IRQ controller at BSP IRQ library +- Register Timer for system clock +- Register Console UART + +During this intialization level interrupts may not be registered, +enabled or disabled at the IRQ controller. But, all IRQ sources +should be cleared to avoid spurious interrupts later on. + + +AFTER LEVEL1 - if initializaed during startup +--------------------------------------------- +The statically configured drivers are initialized as normally by RTEMS. The +hardware was found in LEVEL1. + +CONFIGURE_BSP_PREREQUISITE_DRIVERS may initialize IRQ driver, or +IRQ lib initialized when IRQ controller was registered during LEVEL1. + + +LEVEL2 +------ +Initialize other device drivers than IRQ, Timer, console: +- ISR can be registered, enabled, disabled at IRQ controller + (IRQ is still masked by CPU interrupt level if initialized during + RTEMS startup) +- Timer API that does not require IRQ can be used +- printf() can be used + +For standard peripherals this is the first initialization. + + +LEVEL3 +------ +Initialize drivers that require features/APIs provided by drivers +in LEVEL2. + +Such features may involve services that require IRQ. + + +LEVEL4 +------ +Unused extra level. + + + +LEVEL INACTIVE - NOT ENABLED DEVICES +------------------------------------ +List of devices that experienced: + - no driver found for device (not united) + - ignored (not united with a driver, forced by user) + - an error was reported by device driver during initialization diff --git a/cpukit/libdrvmgr/drvmgr.c b/cpukit/libdrvmgr/drvmgr.c new file mode 100644 index 0000000000..161f3ad7b9 --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr.c @@ -0,0 +1,646 @@ +/* Driver Manager Interface Implementation. + * + * COPYRIGHT (c) 2009-2011. + * 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. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <drvmgr/drvmgr.h> +#include <drvmgr/drvmgr_confdefs.h> + +#include "drvmgr_internal.h" + +/* Enable debugging */ +/*#define DEBUG 1*/ + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +struct rtems_driver_manager drv_mgr = { + .level = 0, + .initializing_objs = 0, + .lock = 0, + .root_dev = {0}, + .root_drv = NULL, + + .drivers = LIST_INITIALIZER(struct drvmgr_drv, next), + + .buses = { + LIST_INITIALIZER(struct drvmgr_bus, next), + LIST_INITIALIZER(struct drvmgr_bus, next), + LIST_INITIALIZER(struct drvmgr_bus, next), + LIST_INITIALIZER(struct drvmgr_bus, next), + LIST_INITIALIZER(struct drvmgr_bus, next), + }, + .buses_inactive = LIST_INITIALIZER(struct drvmgr_bus, next), + + .devices = { + LIST_INITIALIZER(struct drvmgr_dev, next), + LIST_INITIALIZER(struct drvmgr_dev, next), + LIST_INITIALIZER(struct drvmgr_dev, next), + LIST_INITIALIZER(struct drvmgr_dev, next), + LIST_INITIALIZER(struct drvmgr_dev, next), + }, + .devices_inactive = LIST_INITIALIZER(struct drvmgr_dev, next), +}; + +static int do_bus_init( + struct rtems_driver_manager *mgr, + struct drvmgr_bus *bus, + int level); +static int do_dev_init( + struct rtems_driver_manager *mgr, + struct drvmgr_dev *dev, + int level); + +/* DRIVER MANAGER */ + +void _DRV_Manager_init_level(int level) +{ + struct rtems_driver_manager *mgr = &drv_mgr; + + if (mgr->level >= level) + return; + + /* Set new Level */ + mgr->level = level; + + /* Initialize buses and devices into this new level */ + drvmgr_init_update(); +} + +/* Initialize Data structures of the driver manager and call driver + * register functions configured by the user. + */ +void _DRV_Manager_initialization(void) +{ + struct drvmgr_drv_reg_func *drvreg; + + /* drv_mgr is already initialized statically by compiler except + * the lock + */ + DRVMGR_LOCK_INIT(); + + /* Call driver register functions. */ + drvreg = &drvmgr_drivers[0]; + while (drvreg->drv_reg) { + /* Make driver register */ + drvreg->drv_reg(); + drvreg++; + } +} + +/* Take ready devices and buses into the correct init level step by step. + * Once a bus or a device has been registered there is no turning + * back - they are taken to the level of the driver manager. + */ +void drvmgr_init_update(void) +{ + struct rtems_driver_manager *mgr = &drv_mgr; + struct drvmgr_bus *bus; + struct drvmgr_dev *dev; + int bus_might_been_registered; + int level; + + /* "Lock" to make sure we don't use up the stack and that the lists + * remain consistent. + */ + DRVMGR_LOCK_WRITE(); + if (mgr->initializing_objs || (mgr->level == 0)) + goto out; + mgr->initializing_objs = 1; + +init_registered_buses: + /* Take all buses and devices ready into the same stage + * as the driver manager global level. + */ + for (level = 0; level < mgr->level; level++) { + + bus_might_been_registered = 0; + + /* Take buses into next level */ + + while ((bus = BUS_LIST_HEAD(&mgr->buses[level])) != NULL) { + + /* Remove first in the list (will be inserted in + * appropriate list by do_bus_init()) + */ + drvmgr_list_remove_head(&mgr->buses[level]); + + DRVMGR_UNLOCK(); + + /* Initialize Bus, this will register devices on + * the bus. Take bus into next level. + */ + do_bus_init(mgr, bus, level+1); + + DRVMGR_LOCK_WRITE(); + } + + /* Take devices into next level */ + while ((dev = DEV_LIST_HEAD(&mgr->devices[level])) != NULL) { + + /* Always process first in list */ + dev = DEV_LIST_HEAD(&mgr->devices[level]); + + /* Remove first in the list (will be inserted in + * appropriate list by do_dev_init()) + */ + drvmgr_list_remove_head(&mgr->devices[level]); + + DRVMGR_UNLOCK(); + + /* Initialize Device, this may register a new bus */ + do_dev_init(mgr, dev, level+1); + + DRVMGR_LOCK_WRITE(); + + bus_might_been_registered = 1; + } + + /* Make sure all buses registered and ready are taken at + * the same time into init level N. + */ + if (bus_might_been_registered) + goto init_registered_buses; + } + + /* Release bus/device initialization "Lock" */ + mgr->initializing_objs = 0; + +out: + DRVMGR_UNLOCK(); +} + +/* Take bus into next level */ +static int do_bus_init( + struct rtems_driver_manager *mgr, + struct drvmgr_bus *bus, + int level) +{ + int (*init)(struct drvmgr_bus *); + + /* If bridge device has failed during initialization, the bus is not + * initialized further. + */ + if (bus->dev->state & DEV_STATE_INIT_FAILED) { + bus->state |= BUS_STATE_DEPEND_FAILED; + goto inactivate_out; + } + + if (bus->ops && (init = bus->ops->init[level-1])) { + /* Note: This init1 function may register new devices */ + bus->error = init(bus); + if (bus->error != DRVMGR_OK) { + /* An error of some kind during bus initialization. + * + * Child devices and their buses are not inactived + * directly here, instead they will all be catched by + * do_dev_init() and do_bus_init() by checking if + * parent or bridge-device failed. We know that + * initialization will happen later for those devices. + */ + goto inactivate_out; + } + } + + DRVMGR_LOCK_WRITE(); + + /* Bus taken into the new level */ + bus->level = level; + + /* Put bus into list of buses reached level 'level'. + * Put at end of bus list so that init[N+1]() calls comes + * in the same order as init[N]() + */ + drvmgr_list_add_tail(&mgr->buses[level], bus); + + DRVMGR_UNLOCK(); + + return 0; + +inactivate_out: + DRVMGR_LOCK_WRITE(); + bus->state |= BUS_STATE_INIT_FAILED; + bus->state |= BUS_STATE_LIST_INACTIVE; + drvmgr_list_add_head(&mgr->buses_inactive, bus); + DRVMGR_UNLOCK(); + + DBG("do_bus_init(%d): (DEV: %s) failed\n", level, bus->dev->name); + + return 1; +} + +/* Take device to initialization level 1 */ +static int do_dev_init( + struct rtems_driver_manager *mgr, + struct drvmgr_dev *dev, + int level) +{ + int (*init)(struct drvmgr_dev *); + + /* Try to allocate Private Device Structure for driver if driver + * requests for this feature. + */ + if (dev->drv && dev->drv->dev_priv_size && !dev->priv) { + dev->priv = malloc(dev->drv->dev_priv_size); + memset(dev->priv, 0, dev->drv->dev_priv_size); + } + + /* If parent bus has failed during initialization, + * the device is not initialized further. + */ + if (dev->parent && (dev->parent->state & BUS_STATE_INIT_FAILED)) { + dev->state |= DEV_STATE_DEPEND_FAILED; + goto inactivate_out; + } + + /* Call Driver's Init Routine */ + if (dev->drv && (init = dev->drv->ops->init[level-1])) { + /* Note: This init function may register new devices */ + dev->error = init(dev); + if (dev->error != DRVMGR_OK) { + /* An error of some kind has occured in the + * driver/device, the failed device is put into the + * inactive list, this way Init2,3 and/or 4 will not + * be called for this device. + * + * The device is not removed from the bus (not + * unregistered). The driver can be used to find + * device information and debugging for example even + * if device initialization failed. + * + * Child buses and their devices are not inactived + * directly here, instead they will all be catched by + * do_dev_init() and do_bus_init() by checking if + * parent or bridge-device failed. We know that + * initialization will happen later for those devices. + */ + goto inactivate_out; + } + } + + DRVMGR_LOCK_WRITE(); + /* Dev taken into new level */ + dev->level = level; + + /* Put at end of device list so that init[N+1]() calls comes + * in the same order as init[N]() + */ + drvmgr_list_add_tail(&mgr->devices[level], dev); + DRVMGR_UNLOCK(); + + return 0; + +inactivate_out: + DRVMGR_LOCK_WRITE(); + dev->state |= DEV_STATE_INIT_FAILED; + dev->state |= DEV_STATE_LIST_INACTIVE; + drvmgr_list_add_head(&mgr->devices_inactive, dev); + DRVMGR_UNLOCK(); + + DBG("do_dev_init(%d): DRV: %s (DEV: %s) failed\n", + level, dev->drv->name, dev->name); + + return 1; /* Failed to take device into requested level */ +} + +/* Register Root device driver */ +int drvmgr_root_drv_register(struct drvmgr_drv *drv) +{ + struct rtems_driver_manager *mgr = &drv_mgr; + struct drvmgr_dev *root = &mgr->root_dev; + + if (mgr->root_drv) { + /* Only possible to register root device once */ + return DRVMGR_FAIL; + } + + /* Set root device driver */ + drv->next = NULL; + mgr->root_drv = drv; + + /* Init root device non-NULL fields */ + root->minor_drv = -1; + root->minor_bus = 0; + root->businfo = mgr; + root->name = "root bus"; + /* Custom Driver association */ + root->drv = mgr->root_drv; + + /* This registers the root device and a bus */ + drvmgr_dev_register(root); + + return DRVMGR_OK; +} + +/* Register a driver */ +int drvmgr_drv_register(struct drvmgr_drv *drv) +{ + struct rtems_driver_manager *mgr = &drv_mgr; + + /* All drivers must have been registered before start of init, + * because the manager does not scan all existing devices to find + * suitable hardware for this driver, and it is not protected with + * a lock therefore. + */ + if (mgr->level > 0) + return -1; + + drv->obj_type = DRVMGR_OBJ_DRV; + + /* Put driver into list of registered drivers */ + drvmgr_list_add_head(&mgr->drivers, drv); + + /* TODO: we could scan for devices that this new driver has support + * for. However, at this stage we assume that all drivers are + * registered before devices are registered. + * + * LOCK: From the same assumsion locking the driver list is not needed + * either. + */ + + return 0; +} + +/* Insert a device into a driver's device list and assign a driver minor number + * to the device. + * + * The devices are ordered by their minor number (sorted linked list of devices) + * the minor number is found by looking for a gap or at the end. + */ +static void drvmgr_insert_dev_into_drv( + struct drvmgr_drv *drv, + struct drvmgr_dev *dev) +{ + struct drvmgr_dev *curr, **pprevnext; + int minor; + + minor = 0; + pprevnext = &drv->dev; + curr = drv->dev; + + while (curr) { + if (minor < curr->minor_drv) { + /* Found a gap. Insert new device between prev + * and curr. */ + break; + } + minor++; + pprevnext = &curr->next_in_drv; + curr = curr->next_in_drv; + } + dev->next_in_drv = curr; + *pprevnext = dev; + + /* Set minor */ + dev->minor_drv = minor; + drv->dev_cnt++; +} + +/* Insert a device into a bus device list and assign a bus minor number to the + * device. + * + * The devices are ordered by their minor number (sorted linked list of devices) + * and by their registeration order if not using the same driver. + * + * The minor number is found by looking for a gap or at the end. + */ +static void drvmgr_insert_dev_into_bus( + struct drvmgr_bus *bus, + struct drvmgr_dev *dev) +{ + struct drvmgr_dev *curr, **pprevnext; + int minor; + + minor = 0; + pprevnext = &bus->children; + curr = bus->children; + + while (curr) { + if (dev->drv && (dev->drv == curr->drv)) { + if (minor < curr->minor_bus) { + /* Found a gap. Insert new device between prev + * and curr. */ + break; + } + minor++; + } + pprevnext = &curr->next_in_bus; + curr = curr->next_in_bus; + } + dev->next_in_bus = curr; + *pprevnext = dev; + + /* Set minor. Devices without driver are given -1 */ + if (dev->drv == NULL) + minor = -1; + dev->minor_bus = minor; + bus->dev_cnt++; +} + +/* Try to find a driver for a device (unite a device with driver). + * a device with a driver + */ +static struct drvmgr_drv *drvmgr_dev_find_drv( + struct drvmgr_dev *dev) +{ + struct rtems_driver_manager *mgr = &drv_mgr; + struct drvmgr_drv *drv; + + /* NOTE: No locking is needed here since Driver list is supposed to be + * initialized once during startup, we treat it as a static + * read-only list + */ + + /* Try to find a driver that can handle this device */ + for (drv = DRV_LIST_HEAD(&mgr->drivers); drv; drv = drv->next) + if (dev->parent->ops->unite(drv, dev) == 1) + break; + + return drv; +} + +/* Register a device */ +int drvmgr_dev_register(struct drvmgr_dev *dev) +{ + struct rtems_driver_manager *mgr = &drv_mgr; + struct drvmgr_drv *drv; + struct drvmgr_bus *bus = dev->parent; + struct drvmgr_key *keys; + struct drvmgr_list *init_list = &mgr->devices_inactive; + + DBG("DEV_REG: %s at bus \"%s\"\n", dev->name, + bus && bus->dev && bus->dev->name ? bus->dev->name : "UNKNOWN"); + + /* Custom driver assocation? */ + if (dev->drv) { + drv = dev->drv; + DBG("CUSTOM ASSOCIATION (%s to %s)\n", dev->name, drv->name); + } else { + /* Try to find a driver that can handle this device */ + dev->drv = drv = drvmgr_dev_find_drv(dev); + } + + DRVMGR_LOCK_WRITE(); + + /* Assign Bus Minor number and put into bus device list + * unless root device. + */ + if (bus) + drvmgr_insert_dev_into_bus(bus, dev); + + if (!drv) { + /* No driver found that can handle this device, put into + * inactive list + */ + dev->minor_drv = -1; + dev->state |= DEV_STATE_LIST_INACTIVE; + } else { + /* United device with driver. + * Put the device on the registered device list + */ + dev->state |= DEV_STATE_UNITED; + + /* Check if user want to skip this core. This is not a + * normal request, however in a multi-processor system + * the two(or more) RTEMS instances must not use the same + * devices in a system, not reporting a device to + * it's driver will effectively accomplish this. In a + * non Plug & Play system one can easily avoid this + * problem by not report the core, but in a Plug & Play + * system the bus driver will report all found cores. + * + * To stop the two RTEMS instances from using the same + * device the user can simply define a resource entry + * for a certain device but set the keys field to NULL. + */ + if (drvmgr_keys_get(dev, &keys) == 0 && keys == NULL) { + /* Found Driver resource entry point + * for this device, it was NULL, this + * indicates to skip the core. + * + * We put it into the inactive list + * marking it as ignored. + */ + dev->state |= DEV_STATE_IGNORED; + } else { + /* Assign Driver Minor number and put into driver's + * device list + */ + drvmgr_insert_dev_into_drv(drv, dev); + + /* Just register device, it will be initialized + * later together with bus. + * + * At the end of the list (breadth first search) + */ + init_list = &mgr->devices[0]; + + DBG("Registered %s (DRV: %s) on %s\n", + dev->name, drv->name, + bus ? bus->dev->name : "NO PARENT"); + } + } + + drvmgr_list_add_tail(init_list, dev); + + DRVMGR_UNLOCK(); + + /* Trigger Device initialization if not root device and + * has a driver + */ + if (bus && dev->drv) + drvmgr_init_update(); + + return 0; +} + +/* Register a bus */ +int drvmgr_bus_register(struct drvmgr_bus *bus) +{ + struct rtems_driver_manager *mgr = &drv_mgr; + struct drvmgr_bus *bus_up; + + /* Get bus architecture depth - the distance from root bus */ + bus->depth = 0; + bus_up = bus->dev->parent; + while (bus_up) { + bus->depth++; + bus_up = bus_up->dev->parent; + } + + DRVMGR_LOCK_WRITE(); + + /* Put driver into list of found buses */ + drvmgr_list_add_tail(&mgr->buses[0], bus); + + DRVMGR_UNLOCK(); + + /* Take bus into level1 and so on */ + drvmgr_init_update(); + + return 0; +} + +/* Allocate memory for a Device structure */ +int drvmgr_alloc_dev(struct drvmgr_dev **pdev, int extra) +{ + struct drvmgr_dev *dev; + int size; + + size = ((sizeof(struct drvmgr_dev) + 3) & ~0x3) + extra; + dev = (struct drvmgr_dev *)malloc(size); + if (!dev) { + /* Failed to allocate device structure - critical error */ + rtems_fatal_error_occurred(RTEMS_NO_MEMORY); + } + *pdev = dev; + memset(dev, 0, size); + dev->obj_type = DRVMGR_OBJ_DEV; + + return 0; +} + +/* Allocate memory for a Bus structure */ +int drvmgr_alloc_bus(struct drvmgr_bus **pbus, int extra) +{ + struct drvmgr_bus *bus; + int size; + + size = ((sizeof(struct drvmgr_bus) + 3) & ~0x3) + extra; + bus = (struct drvmgr_bus *)malloc(size); + if (!bus) { + /* Failed to allocate device structure - critical error */ + rtems_fatal_error_occurred(RTEMS_NO_MEMORY); + } + *pbus = bus; + memset(bus, 0, size); + bus->obj_type = DRVMGR_OBJ_BUS; + + return 0; +} + +/* Add driver resources to a bus instance */ +void drvmgr_bus_res_add(struct drvmgr_bus *bus, + struct drvmgr_bus_res *bres) +{ + /* insert first in bus resource list. Locking isn't needed since + * resources can only be added before resource requests are made. + * When bus has been registered resources are considered a read-only + * tree. + */ + bres->next = bus->reslist; + bus->reslist = bres; +} diff --git a/cpukit/libdrvmgr/drvmgr.h b/cpukit/libdrvmgr/drvmgr.h new file mode 100644 index 0000000000..fe8cd1ee3f --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr.h @@ -0,0 +1,889 @@ +/* Driver Manager Interface. + * + * COPYRIGHT (c) 2009-2011 + * 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 _DRIVER_MANAGER_H_ +#define _DRIVER_MANAGER_H_ + +#include <rtems.h> +#include <drvmgr/drvmgr_list.h> +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/*** Configure Driver manager ***/ + +/* Define the number of initialization levels of device drivers */ +#define DRVMGR_LEVEL_MAX 4 + +struct drvmgr_dev; /* Device */ +struct drvmgr_bus; /* Bus */ +struct drvmgr_drv; /* Driver */ + +/*** List Interface shortcuts ***/ +#define BUS_LIST_HEAD(list) LIST_HEAD(list, struct drvmgr_bus) +#define BUS_LIST_TAIL(list) LIST_TAIL(list, struct drvmgr_bus) +#define DEV_LIST_HEAD(list) LIST_HEAD(list, struct drvmgr_dev) +#define DEV_LIST_TAIL(list) LIST_TAIL(list, struct drvmgr_dev) +#define DRV_LIST_HEAD(list) LIST_HEAD(list, struct drvmgr_drv) +#define DRV_LIST_TAIL(list) LIST_TAIL(list, struct drvmgr_drv) + +/*** Bus indentification ***/ +#define DRVMGR_BUS_TYPE_NONE 0 /* Not a valid bus */ +#define DRVMGR_BUS_TYPE_ROOT 1 /* Hard coded bus */ +#define DRVMGR_BUS_TYPE_PCI 2 /* PCI bus */ +#define DRVMGR_BUS_TYPE_AMBAPP 3 /* AMBA Plug & Play bus */ +#define DRVMGR_BUS_TYPE_LEON2_AMBA 4 /* LEON2 hardcoded bus */ +#define DRVMGR_BUS_TYPE_AMBAPP_DIST 5 /* Distibuted AMBA Plug & Play bus accessed using a communication interface */ +#define DRVMGR_BUS_TYPE_SPW_RMAP 6 /* SpaceWire Network bus */ +#define DRVMGR_BUS_TYPE_AMBAPP_RMAP 7 /* SpaceWire RMAP accessed AMBA Plug & Play bus */ + +enum { + DRVMGR_OBJ_NONE = 0, + DRVMGR_OBJ_DRV = 1, + DRVMGR_OBJ_BUS = 2, + DRVMGR_OBJ_DEV = 3, +}; + +/*** Driver indentification *** + * + * 64-bit identification integer definition + * * Bus ID 8-bit [7..0] + * * Reserved 8-bit field [63..56] + * * Device ID specific for bus type 48-bit [55..8] (Different buses have + * different unique identifications for hardware/driver.) + * + * ID Rules + * * A root bus driver must always have device ID set to 0. There can only by + * one root bus driver for a certain bus type. + * * A Driver ID must identify a unique hardware core + * + */ + +/* Bus ID Mask */ +#define DRIVER_ID_BUS_MASK 0x00000000000000FFULL + +/* Reserved Mask for future use */ +#define DRIVER_ID_RSV_MASK 0xFF00000000000000ULL + +/* Reserved Mask for future use */ +#define DRIVER_ID_DEV_MASK 0x00FFFFFFFFFFFF00ULL + +/* Set Bus ID Mask. */ +#define DRIVER_ID(busid, devid) ((unsigned long long) \ + ((((unsigned long long)(devid) << 8) & DRIVER_ID_DEV_MASK) | \ + ((unsigned long long)(busid) & DRIVER_ID_BUS_MASK))) + +/* Get IDs */ +#define DRIVER_BUSID_GET(id) ((unsigned long long)(id) & DRIVER_ID_BUS_MASK) +#define DRIVER_DEVID_GET(id) (((unsigned long long)(id) & DRIVER_ID_DEV_MASK) >> 8) + +#define DRIVER_ROOTBUS_ID(bus_type) DRIVER_ID(bus_type, 0) + +/*** Root Bus drivers ***/ + +/* Generic Hard coded Root bus: Driver ID */ +#define DRIVER_ROOT_ID DRIVER_ROOTBUS_ID(DRVMGR_BUS_TYPE_ROOT) + +/* PCI Plug & Play bus: Driver ID */ +#define DRIVER_PCIBUS_ID DRIVER_ROOTBUS_ID(DRVMGR_BUS_TYPE_PCI) + +/* AMBA Plug & Play bus: Driver ID */ +#define DRIVER_GRLIB_AMBAPP_ID DRIVER_ROOTBUS_ID(DRVMGR_BUS_TYPE_AMBAPP) + +/* AMBA Hard coded bus: Driver ID */ +#define DRIVER_LEON2_AMBA_ID DRIVER_ROOTBUS_ID(DRVMGR_BUS_TYPE_LEON2_AMBA) + +/* Distributed AMBA Plug & Play bus: Driver ID */ +#define DRIVER_AMBAPP_DIST_ID DRIVER_ROOTBUS_ID(DRVMGR_BUS_TYPE_AMBAPP_DIST) + +/*! Bus parameters used by driver interface functions to aquire information + * about bus. All Bus drivers should implement the operation 'get_params' so + * that the driver interface routines can access bus dependent information in + * an non-dependent way. + */ +struct drvmgr_bus_params { + char *dev_prefix; /*!< Optional name prefix */ +}; + +/* Interrupt Service Routine (ISR) */ +typedef void (*drvmgr_isr)(void *arg); + +/*! Bus operations */ +struct drvmgr_bus_ops { + /* Functions used internally within driver manager */ + int (*init[DRVMGR_LEVEL_MAX])(struct drvmgr_bus *); + int (*remove)(struct drvmgr_bus *); + int (*unite)(struct drvmgr_drv *, struct drvmgr_dev *); /*!< Unite Hardware Device with Driver */ + + /* Functions called indirectly from drivers */ + int (*int_register)(struct drvmgr_dev *, int index, const char *info, drvmgr_isr isr, void *arg); + int (*int_unregister)(struct drvmgr_dev *, int index, drvmgr_isr isr, void *arg); + int (*int_clear)(struct drvmgr_dev *, int index); + int (*int_mask)(struct drvmgr_dev *, int index); + int (*int_unmask)(struct drvmgr_dev *, int index); + + /* Get Parameters */ + int (*get_params)(struct drvmgr_dev *, struct drvmgr_bus_params *); + /* Get Frequency of Bus */ + int (*freq_get)(struct drvmgr_dev*, int, unsigned int*); + /*! Function called to request information about a device. The bus + * driver interpret the bus-specific information about the device. + */ + void (*info_dev)(struct drvmgr_dev *, void (*print)(void *p, char *str), void *p); +}; +#define BUS_OPS_NUM (sizeof(struct drvmgr_bus_ops)/sizeof(void (*)(void))) + +struct drvmgr_func { + int funcid; + void *func; +}; +#define DRVMGR_FUNC(_ID_, _FUNC_) {(int)(_ID_), (void *)(_FUNC_)} +#define DRVMGR_FUNC_END {0, NULL} + +/*** Resource definitions *** + * + * Overview of structures: + * All bus resources entries (_bus_res) are linked together per bus + * (bus_info->reslist). One bus resource entry has a pointer to an array of + * driver resources (_drv_res). One driver resouces is made out of an array + * of keys (drvmgr_key). All keys belongs to the same driver and harwdare + * device. Each key has a Name, Type ID and Data interpreted differently + * depending on the Type ID (union drvmgr_key_value). + * + */ + +/* Key Data Types */ +#define KEY_TYPE_NONE 0 +#define KEY_TYPE_INT 1 +#define KEY_TYPE_STRING 2 +#define KEY_TYPE_POINTER 3 + +#define KEY_EMPTY {NULL, KEY_TYPE_NONE, {0}} +#define RES_EMPTY {0, 0, NULL} +#define MMAP_EMPTY {0, 0, 0} + +/*! Union of different values */ +union drvmgr_key_value { + unsigned int i; /*!< Key data type UNSIGNED INTEGER */ + char *str; /*!< Key data type STRING */ + void *ptr; /*!< Key data type ADDRESS/POINTER */ +}; + +/* One key. One Value. Holding information relevant to the driver. */ +struct drvmgr_key { + char *key_name; /* Name of key */ + int key_type; /* How to interpret key_value */ + union drvmgr_key_value key_value; /* The value or pointer to value */ +}; + +/*! Driver resource entry, Driver resources for a certain device instance, + * containing a number of keys where each key hold the data of interest. + */ +struct drvmgr_drv_res { + uint64_t drv_id; /*!< Identifies the driver this resource is aiming */ + int minor_bus; /*!< Indentifies a specfic device */ + struct drvmgr_key *keys; /*!< First key in key array, ended with KEY_EMPTY */ +}; + +/*! Bus resource list node */ +struct drvmgr_bus_res { + struct drvmgr_bus_res *next; /*!< Next resource node in list */ + struct drvmgr_drv_res resource[]; /*!< Array of resources, one per device instance */ +}; + +/*! MAP entry. Describes an linear address space translation. Untranslated + * Start, Translated Start and length. + * + * Used by bus drivers to describe the address translation needed for + * the translation driver interface. + */ +struct drvmgr_map_entry { + char *name; /*!< Map Name */ + unsigned int size; /*!< Size of map window */ + char *from_adr; /*!< Start address of access window used + * to reach into remote bus */ + char *to_adr; /*!< Start address of remote system + * address range */ +}; +#define DRVMGR_TRANSLATE_ONE2ONE NULL +#define DRVMGR_TRANSLATE_NO_BRIDGE ((void *)1) /* No bridge, error */ + +/*! Bus information. Describes a bus. */ +struct drvmgr_bus { + int obj_type; /*!< DRVMGR_OBJ_BUS */ + unsigned char bus_type; /*!< Type of bus */ + unsigned char depth; /*!< Bus level distance from root bus */ + struct drvmgr_bus *next; /*!< Next Bus */ + struct drvmgr_dev *dev; /*!< Bus device, the hardware... */ + void *priv; /*!< Private data structure used by BUS driver */ + struct drvmgr_dev *children; /*!< Hardware devices on this bus */ + struct drvmgr_bus_ops *ops; /*!< Bus operations supported by this bus driver */ + struct drvmgr_func *funcs; /*!< Extra operations */ + int dev_cnt; /*!< Number of devices this bus has */ + struct drvmgr_bus_res *reslist; /*!< Bus resources, head of a linked list of resources. */ + struct drvmgr_map_entry *maps_up; /*!< Map Translation, array of address spaces upstreams to CPU */ + struct drvmgr_map_entry *maps_down; /*!< Map Translation, array of address spaces downstreams to Hardware */ + + /* Bus status */ + int level; /*!< Initialization Level of Bus */ + int state; /*!< Init State of Bus, BUS_STATE_* */ + int error; /*!< Return code from bus->ops->initN() */ +}; + +/* States of a bus */ +#define BUS_STATE_INIT_FAILED 0x00000001 /* Initialization Failed */ +#define BUS_STATE_LIST_INACTIVE 0x00001000 /* In inactive bus list */ +#define BUS_STATE_DEPEND_FAILED 0x00000004 /* Device init failed */ + +/* States of a device */ +#define DEV_STATE_INIT_FAILED 0x00000001 /* Initialization Failed */ +#define DEV_STATE_INIT_DONE 0x00000002 /* All init levels completed */ +#define DEV_STATE_DEPEND_FAILED 0x00000004 /* Parent Bus init failed */ +#define DEV_STATE_UNITED 0x00000100 /* Device United with Device Driver */ +#define DEV_STATE_REMOVED 0x00000200 /* Device has been removed (unregistered) */ +#define DEV_STATE_IGNORED 0x00000400 /* Device was ignored according to user's request, the device + * was never reported to it's driver (as expected). + */ +#define DEV_STATE_LIST_INACTIVE 0x00001000 /* In inactive device list */ + +/*! Device information */ +struct drvmgr_dev { + int obj_type; /*!< DRVMGR_OBJ_DEV */ + struct drvmgr_dev *next; /*!< Next device */ + struct drvmgr_dev *next_in_bus; /*!< Next device on the same bus */ + struct drvmgr_dev *next_in_drv; /*!< Next device using the same driver */ + + struct drvmgr_drv *drv; /*!< The driver owning this device */ + struct drvmgr_bus *parent; /*!< Bus that this device resides on */ + short minor_drv; /*!< Device number within driver */ + short minor_bus; /*!< Device number on bus (for device separation) */ + char *name; /*!< Name of Device Hardware */ + void *priv; /*!< Pointer to driver private device structure */ + void *businfo; /*!< Host bus specific information */ + struct drvmgr_bus *bus; /*!< Pointer to bus, set only if this is a bridge */ + + /* Device Status */ + unsigned int state; /*!< State of device, see DEV_STATE_* */ + int level; /*!< Init Level */ + int error; /*!< Error state returned by driver */ +}; + +/*! Driver operations, function pointers. */ +struct drvmgr_drv_ops { + int (*init[DRVMGR_LEVEL_MAX])(struct drvmgr_dev *); /*! Function doing Init Stage 1 of a hardware device */ + int (*remove)(struct drvmgr_dev *); /*! Function called when device instance is to be removed */ + int (*info)(struct drvmgr_dev *, void (*print)(void *p, char *str), void *p, int, char *argv[]);/*! Function called to request information about a device or driver */ +}; +#define DRV_OPS_NUM (sizeof(struct drvmgr_drv_ops)/sizeof(void (*)(void))) + +/*! Device driver description */ +struct drvmgr_drv { + int obj_type; /*!< DRVMGR_OBJ_DRV */ + struct drvmgr_drv *next; /*!< Next Driver */ + struct drvmgr_dev *dev; /*!< Devices using this driver */ + + uint64_t drv_id; /*!< Unique Driver ID */ + char *name; /*!< Name of Driver */ + int bus_type; /*!< Type of Bus this driver supports */ + struct drvmgr_drv_ops *ops; /*!< Driver operations */ + struct drvmgr_func *funcs; /*!< Extra Operations */ + unsigned int dev_cnt; /*!< Number of devices in dev */ + unsigned int dev_priv_size; /*!< If non-zero DRVMGR will allocate memory for dev->priv */ +}; + +/*! Structure defines a function pointer called when driver manager is ready + * for drivers to register themselfs. Used to select drivers available to the + * driver manager. + */ +struct drvmgr_drv_reg_func { + void (*drv_reg)(void); +}; + +/*** DRIVER | DEVICE | BUS FUNCTIONS ***/ + +/* Return Codes */ +enum { + DRVMGR_OK = 0, + DRVMGR_NOMEM = 1, + DRVMGR_EIO = 2, + DRVMGR_EINVAL = 3, + DRVMGR_ENOSYS = 4, + DRVMGR_TIMEDOUT = 5, + DRVMGR_EBUSY = 6, + DRVMGR_ENORES = 7, /* Not enough resources */ + DRVMGR_FAIL = -1 +}; + +/*! Initialize data structures of the driver management system. + * Calls predefined register driver functions so that drivers can + * register themselves. + */ +extern void _DRV_Manager_initialization(void); + +/*! Take all devices into init level 'level', all devices registered later + * will directly be taken into this level as well, ensuring that all + * registerd devices has been taken into the level. + * + */ +extern void _DRV_Manager_init_level(int level); + +/*! Init driver manager all in one go, will call _DRV_Manager_initialization(), + * then _DRV_Manager_init_level([1..DRVMGR_LEVEL_MAX]). + * Typically called from Init task when user wants to initilize driver + * manager after startup, otherwise not used. + */ +extern int drvmgr_init(void); + +/* Take registered buses and devices into the correct init level, + * this function is called from _init_level() so normally + * we don't need to call it directly. + */ +extern void drvmgr_init_update(void); + +/*! Register Root Bus device driver */ +extern int drvmgr_root_drv_register(struct drvmgr_drv *drv); + +/*! Register a driver */ +extern int drvmgr_drv_register(struct drvmgr_drv *drv); + +/*! Register a device */ +extern int drvmgr_dev_register(struct drvmgr_dev *dev); + +/*! Remove a device, and all its children devices if device is a bus device. The + * device driver will be requested to remove the device and once gone from bus, + * device and driver list the device is put into a inactive list for debugging + * (this is optional by using remove argument). + * + * Removing the Root Bus Device is not supported. + * + * \param remove If non-zero the device will be deallocated, and not put into + * the inacitve list. + */ +extern int drvmgr_dev_unregister(struct drvmgr_dev *dev); + +/*! Register a bus */ +extern int drvmgr_bus_register(struct drvmgr_bus *bus); + +/*! Unregister a bus */ +extern int drvmgr_bus_unregister(struct drvmgr_bus *bus); + +/*! Unregister all child devices of a bus. + * + * This function is called from the bus driver, from a "safe" state where + * devices will not be added or removed on this particular bus at this time + */ +extern int drvmgr_children_unregister(struct drvmgr_bus *bus); + +/* Separate a device from the driver it has been united with */ +extern int drvmgr_dev_drv_separate(struct drvmgr_dev *dev); + +/*! Allocate a device structure, if no memory available + * rtems_error_fatal_occurred is called. + * The 'extra' argment tells how many bytes extra space is to be allocated after + * the device structure, this is typically used for "businfo" structures. The extra + * space is always aligned to a 4-byte boundary. + */ +extern int drvmgr_alloc_dev(struct drvmgr_dev **pdev, int extra); + +/*! Allocate a bus structure, if no memory available rtems_error_fatal_occurred + * is called. + * The 'extra' argment tells how many bytes extra space is to be allocated after + * the device structure, this is typically used for "businfo" structures. The + * extra space is always aligned to a 4-byte boundary. + */ +extern int drvmgr_alloc_bus(struct drvmgr_bus **pbus, int extra); + +/*** DRIVER RESOURCE FUNCTIONS ***/ + +/*! Add resources to a bus, typically used by a bus driver. + * + * \param bus The Bus to add the resources to. + * \param res An array with Driver resources, all together are called bus + * resources. + */ +extern void drvmgr_bus_res_add(struct drvmgr_bus *bus, + struct drvmgr_bus_res *bres); + +/*! Find all the resource keys for a device among all driver resources on a + * bus. Typically used by a device driver to get configuration options. + * + * \param dev Device to find resources for + * \param key Location where the pointer to the driver resource array (drvmgr_drv_res->keys) is stored. + */ +extern int drvmgr_keys_get(struct drvmgr_dev *dev, struct drvmgr_key **keys); + +/*! Return the one key that matches key name from a driver keys array. The keys + * can be obtained using drvmgr_keys_get(). + * + * \param keys An array of keys ended with KEY_EMPTY to search among. + * \param key_name Name of key to search for among the keys. + */ +extern struct drvmgr_key *drvmgr_key_get(struct drvmgr_key *keys, char *key_name); + +/*! Extract key value from the key in the keys array matching name and type. + * + * This function calls drvmgr_keys_get to get the key requested (from key + * name), then determines if the type is correct. A pointer to the key value + * is returned. + * + * \param keys An array of keys ended with KEY_EMPTY to search among. + * \param key_name Name of key to search for among the keys. + * \param key_type Data Type of value. INTEGER, ADDRESS, STRING. + * \return Returns NULL if no value found matching Key Name and Key + * Type. + */ +extern union drvmgr_key_value *drvmgr_key_val_get( + struct drvmgr_key *keys, + char *key_name, + int key_type); + +/*! Get key value from the bus resources matching [device, key name, key type] + * if no matching key is found NULL is returned. + * + * This is typically used by device drivers to find a particular device + * resource. + * + * \param dev The device to search resource for. + * \param key_name The key name to search for + * \param key_type The key type expected. + * \return Returns NULL if no value found matching Key Name and + * Key Type was found for device. + */ +extern union drvmgr_key_value *drvmgr_dev_key_get( + struct drvmgr_dev *dev, + char *key_name, + int key_type); + +/*** DRIVER INTERACE USED TO REQUEST INFORMATION/SERVICES FROM BUS DRIVER ***/ + +/*! Get parent bus */ +static inline struct drvmgr_bus *drvmgr_get_parent(struct drvmgr_dev *dev) +{ + if (dev) + return dev->parent; + else + return NULL; +} + +/*! Get Driver of device */ +static inline struct drvmgr_drv *drvmgr_get_drv(struct drvmgr_dev *dev) +{ + if (dev) + return dev->drv; + else + return NULL; +} + +/*! Calls func() for every device found in the device tree, regardless of + * device state or if a driver is assigned. With the options argument the user + * can decide to do either a depth-first or a breadth-first search. + * + * If the function func() returns a non-zero value then for_each_dev will + * return imediatly with the same return value as func() returned. + * + * \param func Function called on each device + * \param arg Custom function argument + * \param options Search Options, see DRVMGR_FED_* + * + */ +#define DRVMGR_FED_BF 1 /* Breadth-first search */ +#define DRVMGR_FED_DF 0 /* Depth first search */ +extern int drvmgr_for_each_dev( + int (*func)(struct drvmgr_dev *dev, void *arg), + void *arg, + int options); + +/*! Get Device pointer from Driver and Driver minor number + * + * \param drv Driver the device is united with. + * \param minor Driver minor number assigned to device. + * \param pdev Location where the Device point will be stored. + * \return Zero on success. -1 on failure, when device was not + * found in driver device list. + */ +extern int drvmgr_get_dev( + struct drvmgr_drv *drv, + int minor, + struct drvmgr_dev **pdev); + +/*! Get Bus frequency in Hertz. Frequency is stored into address of freq_hz. + * + * \param dev The Device to get Bus frequency for. + * \param options Bus-type specific options + * \param freq_hz Location where Bus Frequency will be stored. + */ +extern int drvmgr_freq_get( + struct drvmgr_dev *dev, + int options, + unsigned int *freq_hz); + +/*! Return 0 if dev is not located on the root bus, 1 if on root bus */ +extern int drvmgr_on_rootbus(struct drvmgr_dev *dev); + +/*! Get device name prefix, this name can be used to register a unique name in + * the bus->error filesystem or to get an idea where the device is located. + * + * \param dev The Device to get the device Prefix for. + * \param dev_prefix Location where the prefix will be stored. + */ +extern int drvmgr_get_dev_prefix(struct drvmgr_dev *dev, char *dev_prefix); + +/*! Register a shared interrupt handler. Since this service is shared among + * interrupt drivers/handlers the handler[arg] must be installed before the + * interrupt can be cleared or disabled. The handler is by default disabled + * after registration. + * + * \param index Index is used to identify the IRQ number if hardware has + * multiple IRQ sources. Normally Index is set to 0 to + * indicated the first and only IRQ source. + * A negative index is interpreted as a absolute bus IRQ + * number. + * \param isr Interrupt Service Routine. + * \param arg Optional ISR argument. + */ +extern int drvmgr_interrupt_register( + struct drvmgr_dev *dev, + int index, + const char *info, + drvmgr_isr isr, + void *arg); + +/*! Unregister an interrupt handler. This also disables the interrupt before + * unregistering the interrupt handler. + * \param index Index is used to identify the IRQ number if hardware has + * multiple IRQ sources. Normally Index is set to 0 to + * indicated the first and only IRQ source. + * A negative index is interpreted as a absolute bus IRQ + * number. + * \param isr Interrupt Service Routine, previously registered. + * \param arg Optional ISR argument, previously registered. + */ +extern int drvmgr_interrupt_unregister( + struct drvmgr_dev *dev, + int index, + drvmgr_isr isr, + void *arg); + +/*! Clear (ACK) pending interrupt + * + * \param dev Device to clear interrupt for. + * \param index Index is used to identify the IRQ number if hardware has multiple IRQ sources. + * Normally Index is set to 0 to indicated the first and only IRQ source. + * A negative index is interpreted as a absolute bus IRQ number. + * \param isr Interrupt Service Routine, previously registered. + * \param arg Optional ISR argument, previously registered. + */ +extern int drvmgr_interrupt_clear( + struct drvmgr_dev *dev, + int index); + +/*! Force unmasking/enableing an interrupt on the interrupt controller, this is not normally used, + * if used the caller has masked/disabled the interrupt just before. + * + * \param dev Device to clear interrupt for. + * \param index Index is used to identify the IRQ number if hardware has multiple IRQ sources. + * Normally Index is set to 0 to indicated the first and only IRQ source. + * A negative index is interpreted as a absolute bus IRQ number. + * \param isr Interrupt Service Routine, previously registered. + * \param arg Optional ISR argument, previously registered. + */ +extern int drvmgr_interrupt_unmask( + struct drvmgr_dev *dev, + int index); + +/*! Force masking/disable an interrupt on the interrupt controller, this is not normally performed + * since this will stop all other (shared) ISRs to be disabled until _unmask() is called. + * + * \param dev Device to mask interrupt for. + * \param index Index is used to identify the IRQ number if hardware has multiple IRQ sources. + * Normally Index is set to 0 to indicated the first and only IRQ source. + * A negative index is interpreted as a absolute bus IRQ number. + */ +extern int drvmgr_interrupt_mask( + struct drvmgr_dev *dev, + int index); + +/*! Translate an address on one bus to an address on another bus. + * + * The device determines source or destination bus, the root bus is always + * the other bus. It is assumed that the CPU is located on the root bus or + * that it can access it without address translation (mapped 1:1). + * + * cpu_addresses determines if the address is targeted for the CPU (1) or + * hardware (0) doing DMA. + * + * The address conversion can be done up-streams (towards the CPU) or down- + * streams (towards DMA hardware) the bus architecture. The CPU is assumed + * to be located on level 0 top most in the bus hierarchy. + * + * Source address is translated and the result is put into *dst_address, if + * the address is not accessible on the other bus -1 is returned. + * + * Two common operations is to translate a CPU accessible RAM address to an + * address that DMA units can access (dev=DMA-unit, cpu_address=0, upstream=0, + * src_address=CPU-RAM-ADR) and to translate an address of a PCI resource for + * example RAM mapped into a PCI BAR to an CPU accessible address + * (dev=PCI-device, cpu_address=1, upstream=1, src_address=PCI-BAR-ADR). + * + * \param dev Device to translate addresses for + * \param cpu_addresses Addresses are inteded for CPU(1) or DMA-Hardware(0) + * \param upsteam Select translation direction (0=towards hardware, + * 1=towards CPU) and thereby which bus src_address is + * valid for + * \param src_address Address to translate + * \param dst_address Location where translated address is stored + * + * Returns -1 if unable to translate. If no map is present src_address is + * translated 1:1 (just copied). If dev is on root-bus no translation is + * performed 0 is returned and src_address is stored in *dst_address. + */ +extern int drvmgr_translate( + struct drvmgr_dev *dev, + int cpu_addresses, + int upstream, + void *src_address, + void **dst_address); + +/* Translate addresses between buses, used internally to implement + * drvmgr_translate. Function is not limited to translate from/to root bus + * where CPU is resident, however buses must be on a straight path relative + * to each other (parent of parent of parent and so on). + * + * \param from src_address is given for this bus + * \param to src_address is translated to this bus + * \param reverse Selects translation method, if map entries are used in + * the reverse order (map_up->to is used as map_up->from) + * \param src_address Address to be translated + * \param dst_address Translated address is stored here on success (return=0) + * + * Returns -1 if failed to translate address between buses. 0 successfully + * translated, reuslt is in *dst_address + */ +extern int drvmgr_translate_bus( + struct drvmgr_bus *from, + struct drvmgr_bus *to, + int reverse, + void *src_address, + void **dst_address); + +/*! Get function pointer from Device Driver or Bus Driver. + * + * Returns 0 if function is available + */ +extern int drvmgr_func_get(void *obj, int funcid, void **func); + +/*! Lookup function and call it directly with the four optional arguments */ +extern int drvmgr_func_call(void *obj, int funcid, void *a, void *b, void *c, void *d); + +/* Builds a Function ID. + * + * Used to request optional functions by a bus or device driver + */ +#define DRVMGR_FUNCID(major, minor) ((((major) & 0xfff) << 20) | ((minor) & 0xfffff)) +#define DRVMGR_FUNCID_NONE 0 +#define DRVMGR_FUNCID_END DRVMGR_FUNCID(DRVMGR_FUNCID_NONE, 0) + +/* Major Function ID. Most significant 12-bits. */ +enum { + FUNCID_NONE = 0x000, + FUNCID_RW = 0x001, /* Read/Write functions */ +}; + +/* Select Sub-Function Read/Write function by ID */ +#define RW_SIZE_1 0x00001 /* Access Size */ +#define RW_SIZE_2 0x00002 +#define RW_SIZE_4 0x00004 +#define RW_SIZE_8 0x00008 +#define RW_SIZE_ANY 0x00000 +#define RW_SIZE(id) ((unsigned int)(id) & 0xf) + +#define RW_DIR_ANY 0x00000 /* Access Direction */ +#define RW_READ 0x00000 /* Read */ +#define RW_WRITE 0x00010 /* Write */ +#define RW_SET 0x00020 /* Write with same value (memset) */ +#define RW_DIR(id) (((unsigned int)(id) >> 4) & 0x3) + +#define RW_RAW 0x00000 /* Raw access - no swapping (machine default) */ +#define RW_LITTLE 0x00040 /* Little Endian */ +#define RW_BIG 0x00080 /* Big Endian */ +#define RW_ENDIAN(id) (((unsigned int)(id) >> 6) & 0x3) + +#define RW_TYPE_ANY 0x00000 /* Access type */ +#define RW_REG 0x00100 +#define RW_MEM 0x00200 +#define RW_MEMREG 0x00300 +#define RW_CFG 0x00400 +#define RW_TYPE(id) (((unsigned int)(id) >> 8) & 0xf) + +#define RW_ARG 0x01000 /* Optional Argument */ +#define RW_ERR 0x02000 /* Optional Error Handler */ + +/* Build a Read/Write function ID */ +#define DRVMGR_RWFUNC(minor) DRVMGR_FUNCID(FUNCID_RW, minor) + +/* Argument to Read/Write functions, the "void *arg" pointer is returned by + * RW_ARG. If NULL is returned no argument is needed. + */ +struct drvmgr_rw_arg { + void *arg; + struct drvmgr_dev *dev; +}; + +/* Standard Read/Write function types */ +typedef uint8_t (*drvmgr_r8)(uint8_t *srcadr); +typedef uint16_t (*drvmgr_r16)(uint16_t *srcadr); +typedef uint32_t (*drvmgr_r32)(uint32_t *srcadr); +typedef uint64_t (*drvmgr_r64)(uint64_t *srcadr); +typedef void (*drvmgr_w8)(uint8_t *dstadr, uint8_t data); +typedef void (*drvmgr_w16)(uint16_t *dstadr, uint16_t data); +typedef void (*drvmgr_w32)(uint32_t *dstadr, uint32_t data); +typedef void (*drvmgr_w64)(uint64_t *dstadr, uint64_t data); +/* READ/COPY a memory area located on bus into CPU memory. + * From 'src' (remote) to the destination 'dest' (local), n=number of bytes + */ +typedef int (*drvmgr_rmem)(void *dest, const void *src, int n); +/* WRITE/COPY a user buffer located in CPU memory to a location on the bus. + * From 'src' (local) to the destination 'dest' (remote), n=number of bytes + */ +typedef int (*drvmgr_wmem)(void *dest, const void *src, int n); +/* Set a memory area to the byte value given in c, see LIBC memset(). Memset is + * implemented by calling wmem() multiple times with a "large" buffer. + */ +typedef int (*drvmgr_memset)(void *dstadr, int c, size_t n); + +/* Read/Write function types with additional argument */ +typedef uint8_t (*drvmgr_r8_arg)(uint8_t *srcadr, void *a); +typedef uint16_t (*drvmgr_r16_arg)(uint16_t *srcadr, void *a); +typedef uint32_t (*drvmgr_r32_arg)(uint32_t *srcadr, void *a); +typedef uint64_t (*drvmgr_r64_arg)(uint64_t *srcadr, void *a); +typedef void (*drvmgr_w8_arg)(uint8_t *dstadr, uint8_t data, void *a); +typedef void (*drvmgr_w16_arg)(uint16_t *dstadr, uint16_t data, void *a); +typedef void (*drvmgr_w32_arg)(uint32_t *dstadr, uint32_t data, void *a); +typedef void (*drvmgr_w64_arg)(uint64_t *dstadr, uint64_t data, void *a); +typedef int (*drvmgr_rmem_arg)(void *dest, const void *src, int n, void *a); +typedef int (*drvmgr_wmem_arg)(void *dest, const void *src, int n, void *a); +typedef int (*drvmgr_memset_arg)(void *dstadr, int c, size_t n, void *a); + +/* Report an error to the parent bus of the device */ +typedef void (*drvmgr_rw_err)(struct drvmgr_rw_arg *a, struct drvmgr_bus *bus, + int funcid, void *adr); + +/* Helper function for buses that implement the memset() over wmem() */ +extern void drvmgr_rw_memset( + void *dstadr, + int c, + size_t n, + void *a, + drvmgr_wmem_arg wmem + ); + +/*** PRINT INFORMATION ABOUT DRIVER MANAGER ***/ + +/*! Calls func() for every device found matching the search requirements of + * set_mask and clr_mask. Each bit set in set_mask must be set in the + * device state bit mask (dev->state), and Each bit in the clr_mask must + * be cleared in the device state bit mask (dev->state). There are three + * special cases: + * + * 1. If state_set_mask and state_clr_mask are zero the state bits are + * ignored and all cores are treated as a match. + * + * 2. If state_set_mask is zero the function func will not be called due to + * a bit being set in the state mask. + * + * 3. If state_clr_mask is zero the function func will not be called due to + * a bit being cleared in the state mask. + * + * If the function func() returns a non-zero value then for_each_dev will + * return imediatly with the same return value as func() returned. + * + * \param devlist The list to iterate though searching for devices. + * \param state_set_mask Defines the bits that must be set in dev->state + * \param state_clr_mask Defines the bits that must be cleared in dev->state + * \param func Function called on each + * + */ +extern int drvmgr_for_each_listdev( + struct drvmgr_list *devlist, + unsigned int state_set_mask, + unsigned int state_clr_mask, + int (*func)(struct drvmgr_dev *dev, void *arg), + void *arg); + +/* Print all devices */ +#define PRINT_DEVS_FAILED 0x01 /* Failed during initialization */ +#define PRINT_DEVS_ASSIGNED 0x02 /* Driver assigned */ +#define PRINT_DEVS_UNASSIGNED 0x04 /* Driver not assigned */ +#define PRINT_DEVS_IGNORED 0x08 /* Device ignored on user's request */ +#define PRINT_DEVS_ALL (PRINT_DEVS_FAILED | \ + PRINT_DEVS_ASSIGNED | \ + PRINT_DEVS_UNASSIGNED |\ + PRINT_DEVS_IGNORED) + +/*! Print number of devices, buses and drivers */ +extern void drvmgr_summary(void); + +/*! Print devices with certain condictions met according to 'options' */ +extern void drvmgr_print_devs(unsigned int options); + +/*! Print device/bus topology */ +extern void drvmgr_print_topo(void); + +/*! Print the memory usage + * Only accounts for data structures. Not for the text size. + */ +extern void drvmgr_print_mem(void); + +#define OPTION_DEV_GENINFO 0x00000001 +#define OPTION_DEV_BUSINFO 0x00000002 +#define OPTION_DEV_DRVINFO 0x00000004 +#define OPTION_DRV_DEVS 0x00000100 +#define OPTION_BUS_DEVS 0x00010000 +#define OPTION_RECURSIVE 0x01000000 +#define OPTION_INFO_ALL 0xffffffff + +/*! Print information about a driver manager object (device, driver, bus) */ +extern void drvmgr_info(void *id, unsigned int options); + +/*! Get information about a device */ +extern void drvmgr_info_dev(struct drvmgr_dev *dev, unsigned int options); + +/*! Get information about a bus */ +extern void drvmgr_info_bus(struct drvmgr_bus *bus, unsigned int options); + +/*! Get information about a driver */ +extern void drvmgr_info_drv(struct drvmgr_drv *drv, unsigned int options); + +/*! Get information about all devices on a bus */ +extern void drvmgr_info_devs_on_bus(struct drvmgr_bus *bus, unsigned int options); + +/*! Get information about all devices in the system (on all buses) */ +extern void drvmgr_info_devs(unsigned int options); + +/*! Get information about all drivers in the system */ +extern void drvmgr_info_drvs(unsigned int options); + +/*! Get information about all buses in the system */ +extern void drvmgr_info_buses(unsigned int options); + +/*! Get Driver by Driver ID */ +extern struct drvmgr_drv *drvmgr_drv_by_id(uint64_t id); + +/*! Get Driver by Driver Name */ +extern struct drvmgr_drv *drvmgr_drv_by_name(const char *name); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/cpukit/libdrvmgr/drvmgr_by_id.c b/cpukit/libdrvmgr/drvmgr_by_id.c new file mode 100644 index 0000000000..b7782c3839 --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_by_id.c @@ -0,0 +1,34 @@ +/* Find driver by driver-ID + * + * COPYRIGHT (c) 2011 + * 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. + * + */ + +#include <drvmgr/drvmgr.h> +#include "drvmgr_internal.h" + +/* Get driver from driver name */ +struct drvmgr_drv *drvmgr_drv_by_id(uint64_t id) +{ + struct rtems_driver_manager *mgr = &drv_mgr; + struct drvmgr_drv *drv = NULL; + + /* NOTE: No locking is needed here since Driver list is supposed to be + * initialized once during startup, we treat it as a static + * read-only list + */ + + drv = DRV_LIST_HEAD(&mgr->drivers); + while (drv) { + if (drv->drv_id == id) + break; + drv = drv->next; + } + + return drv; +} diff --git a/cpukit/libdrvmgr/drvmgr_by_name.c b/cpukit/libdrvmgr/drvmgr_by_name.c new file mode 100644 index 0000000000..619333f76b --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_by_name.c @@ -0,0 +1,38 @@ +/* Find driver by driver-name + * + * COPYRIGHT (c) 2011 + * 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. + * + */ + +#include <string.h> +#include <drvmgr/drvmgr.h> +#include "drvmgr_internal.h" + +/* Get driver from driver name */ +struct drvmgr_drv *drvmgr_drv_by_name(const char *name) +{ + struct rtems_driver_manager *mgr = &drv_mgr; + struct drvmgr_drv *drv = NULL; + + if (!name) + return NULL; + + /* NOTE: No locking is needed here since Driver list is supposed to be + * initialized once during startup, we treat it as a static + * read-only list + */ + + drv = DRV_LIST_HEAD(&mgr->drivers); + while (drv) { + if (drv->name && (strcmp(drv->name, name) == 0)) + break; + drv = drv->next; + } + + return drv; +} diff --git a/cpukit/libdrvmgr/drvmgr_confdefs.h b/cpukit/libdrvmgr/drvmgr_confdefs.h new file mode 100644 index 0000000000..3a08c73968 --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_confdefs.h @@ -0,0 +1,346 @@ +/* Driver Manager Configuration file. + * + * COPYRIGHT (c) 2009-2011 + * 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. + * + * The configuration consist of an array with function pointers that + * register one or more drivers that will be used by the Driver Manger. + * + * The Functions are called in the order they are declared. + * + */ + +#ifndef _DRIVER_MANAGER_CONFDEFS_H_ +#define _DRIVER_MANAGER_CONFDEFS_H_ + +/*#include <drvmgr/drvmgr.h>*/ +#include "drvmgr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct drvmgr_drv_reg_func drvmgr_drivers[]; + +#ifdef CONFIGURE_INIT + +/*** AMBA Plug & Play Drivers ***/ +#define DRIVER_AMBAPP_GAISLER_GPTIMER_REG {gptimer_register_drv} +extern void gptimer_register_drv(void); + +#define DRIVER_AMBAPP_GAISLER_APBUART_REG {apbuart_cons_register_drv} +extern void apbuart_cons_register_drv(void); + +#define DRIVER_AMBAPP_GAISLER_GRETH_REG {greth_register_drv} +extern void greth_register_drv(void); + +#define DRIVER_AMBAPP_GAISLER_GRSPW_REG {grspw_register_drv} +extern void grspw_register_drv(void); + +#define DRIVER_AMBAPP_GAISLER_GRSPW2_REG {grspw2_register_drv} +extern void grspw2_register_drv(void); + +#define DRIVER_AMBAPP_GAISLER_GRCAN_REG {grcan_register_drv} +extern void grcan_register_drv(void); + +#define DRIVER_AMBAPP_GAISLER_OCCAN_REG {occan_register_drv} +extern void occan_register_drv(void); + +#define DRIVER_AMBAPP_GAISLER_GR1553B_REG {gr1553_register} +extern void gr1553_register(void); + +#define DRIVER_AMBAPP_GAISLER_GR1553BC_REG {gr1553bc_register} +extern void gr1553bc_register(void); + +#define DRIVER_AMBAPP_GAISLER_GR1553BM_REG {gr1553bm_register} +extern void gr1553bm_register(void); + +#define DRIVER_AMBAPP_GAISLER_GR1553RT_REG {gr1553rt_register} +extern void gr1553rt_register(void); + +#define DRIVER_AMBAPP_GAISLER_B1553BRM_REG {b1553brm_register_drv} +extern void b1553brm_register_drv(void); + +#define DRIVER_AMBAPP_GAISLER_B1553RT_REG {b1553rt_register_drv} +extern void b1553rt_register_drv(void); + +#define DRIVER_AMBAPP_GAISLER_GRTM_REG {grtm_register_drv} +extern void grtm_register_drv(void); + +#define DRIVER_AMBAPP_GAISLER_GRTC_REG {grtc_register_drv} +extern void grtc_register_drv(void); + +#define DRIVER_AMBAPP_MCTRL_REG {mctrl_register_drv} +extern void mctrl_register_drv(void); + +#define DRIVER_AMBAPP_GAISLER_PCIF_REG {pcif_register_drv} +extern void pcif_register_drv(void); + +#define DRIVER_AMBAPP_GAISLER_GRPCI_REG {grpci_register_drv} +extern void grpci_register_drv(void); + +#define DRIVER_AMBAPP_GAISLER_GRPCI2_REG {grpci2_register_drv} +extern void grpci2_register_drv(void); + +#define DRIVER_AMBAPP_GAISLER_SPICTRL_REG {spictrl_register_drv} +extern void spictrl_register_drv(void); + +#define DRIVER_AMBAPP_GAISLER_I2CMST_REG {i2cmst_register_drv} +extern void i2cmst_register_drv(void); + +#define DRIVER_AMBAPP_GAISLER_GRGPIO_REG {grgpio_register_drv} +extern void grgpio_register_drv(void); + +#define DRIVER_AMBAPP_GAISLER_GRPWM_REG {grpwm_register_drv} +extern void grpwm_register_drv(void); + +#define DRIVER_AMBAPP_GAISLER_GRADCDAC_REG {gradcdac_register_drv} +extern void gradcdac_register_drv(void); + +#define DRIVER_AMBAPP_GAISLER_SPWCUC_REG {spwcuc_register} +extern void spwcuc_register(void); + +#define DRIVER_AMBAPP_GAISLER_GRCTM_REG {grctm_register} +extern void grctm_register(void); + +#define DRIVER_AMBAPP_GAISLER_SPW_ROUTER_REG {router_register_drv} +extern void router_register_drv(void); + +#define DRIVER_AMBAPP_GAISLER_AHBSTAT_REG {ahbstat_register_drv} +extern void ahbstat_register_drv(void); + +#define DRIVER_AMBAPP_GAISLER_GRAES_REG {graes_register_drv} +extern void graes_register_drv(void); + +#define DRIVER_AMBAPP_GAISLER_GRPWRX_REG {grpwrx_register_drv} +extern void grpwrx_register_drv(void); + + +/*** LEON2 AMBA Hard coded bus Drivers ***/ +#define DRIVER_LEON2_AT697PCI_REG {at697pci_register_drv} +extern void at697pci_register_drv(void); + +#define DRIVER_LEON2_AMBAPP_REG {ambapp_leon2_register} +extern void ambapp_leon2_register(void); + + +/*** PCI Bus Drivers (PCI Target drivers) ***/ +#define DRIVER_PCI_GR_RASTA_ADCDAC {gr_rasta_adcdac_register_drv} +extern void gr_rasta_adcdac_register_drv(void); + +#define DRIVER_PCI_GR_RASTA_IO {gr_rasta_io_register_drv} +extern void gr_rasta_io_register_drv(void); + +#define DRIVER_PCI_GR_RASTA_TMTC {gr_rasta_tmtc_register_drv} +extern void gr_rasta_tmtc_register_drv(void); + +#define DRIVER_PCI_GR_701 {gr701_register_drv} +extern void gr701_register_drv(void); + +#define DRIVER_PCI_GR_TMTC_1553 {gr_tmtc_1553_register_drv} +extern void gr_tmtc_1553_register_drv(void); + +#define DRIVER_PCI_GR_RASTA_SPW_ROUTER {gr_rasta_spw_router_register_drv} +extern void gr_rasta_spw_router_register_drv(void); + +/*** SpaceWire Node Drivers ***/ +#define DRIVER_SPW_RMAP_AMBAPP {ambapp_rmap_register} +extern void ambapp_rmap_register(void); + + +/*** AMBA Plug&Play over SpaceWire (RMAP) drivers ***/ +#define DRIVER_RMAP_AMBAPP_MCTRL {mctrl_rmap_register_drv} +extern void mctrl_rmap_register_drv(void); + +#define DRIVER_RMAP_AMBAPP_GRTC {grtc_rmap_register_drv} +extern void grtc_rmap_register_drv(void); + +#define DRIVER_RMAP_AMBAPP_GRTM {grtm_rmap_register_drv} +extern void grtm_rmap_register_drv(void); + +/* CONFIGURE DRIVER MANAGER */ +struct drvmgr_drv_reg_func drvmgr_drivers[] = { + /*** AMBA Plug & Play Drivers ***/ +#ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_GPTIMER + DRIVER_AMBAPP_GAISLER_GPTIMER_REG, +#endif +#ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_APBUART + DRIVER_AMBAPP_GAISLER_APBUART_REG, +#endif +#ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_GRETH + DRIVER_AMBAPP_GAISLER_GRETH_REG, +#endif +#ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_GRSPW + DRIVER_AMBAPP_GAISLER_GRSPW_REG, +#endif +#ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_GRSPW2 + DRIVER_AMBAPP_GAISLER_GRSPW2_REG, +#endif +#ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_GRCAN + DRIVER_AMBAPP_GAISLER_GRCAN_REG, +#endif +#ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_OCCAN + DRIVER_AMBAPP_GAISLER_OCCAN_REG, +#endif +#ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_GR1553B + DRIVER_AMBAPP_GAISLER_GR1553B_REG, +#endif +#ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_GR1553BC + DRIVER_AMBAPP_GAISLER_GR1553BC_REG, +#endif +#ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_GR1553BM + DRIVER_AMBAPP_GAISLER_GR1553BM_REG, +#endif +#ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_GR1553RT + DRIVER_AMBAPP_GAISLER_GR1553RT_REG, +#endif +#ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_B1553BRM + DRIVER_AMBAPP_GAISLER_B1553BRM_REG, +#endif +#ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_B1553RT + DRIVER_AMBAPP_GAISLER_B1553RT_REG, +#endif +#ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_GRTM + DRIVER_AMBAPP_GAISLER_GRTM_REG, +#endif +#ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_GRTC + DRIVER_AMBAPP_GAISLER_GRTC_REG, +#endif +#ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_PCIF + DRIVER_AMBAPP_GAISLER_PCIF_REG, +#endif +#ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_GRPCI + DRIVER_AMBAPP_GAISLER_GRPCI_REG, +#endif +#ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_GRPCI2 + DRIVER_AMBAPP_GAISLER_GRPCI2_REG, +#endif +#ifdef CONFIGURE_DRIVER_AMBAPP_MCTRL + DRIVER_AMBAPP_MCTRL_REG, +#endif +#ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_SPICTRL + DRIVER_AMBAPP_GAISLER_SPICTRL_REG, +#endif +#ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_I2CMST + DRIVER_AMBAPP_GAISLER_I2CMST_REG, +#endif +#ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_GRGPIO + DRIVER_AMBAPP_GAISLER_GRGPIO_REG, +#endif +#ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_GRPWM + DRIVER_AMBAPP_GAISLER_GRPWM_REG, +#endif +#ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_GRADCDAC + DRIVER_AMBAPP_GAISLER_GRADCDAC_REG, +#endif +#ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_SPWCUC + DRIVER_AMBAPP_GAISLER_SPWCUC_REG, +#endif +#ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_GRCTM + DRIVER_AMBAPP_GAISLER_GRCTM_REG, +#endif +#ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_SPW_ROUTER + DRIVER_AMBAPP_GAISLER_SPW_ROUTER_REG, +#endif +#ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_AHBSTAT + DRIVER_AMBAPP_GAISLER_AHBSTAT_REG, +#endif +#ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_GRAES + DRIVER_AMBAPP_GAISLER_GRAES_REG, +#endif +#ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_GRPWRX + DRIVER_AMBAPP_GAISLER_GRPWRX_REG, +#endif + + + /*** LEON2 AMBA Drivers ***/ +#ifdef CONFIGURE_DRIVER_LEON2_AT697PCI + DRIVER_LEON2_AT697PCI_REG, +#endif +#ifdef CONFIGURE_DRIVER_LEON2_AMBAPP + DRIVER_LEON2_AMBAPP_REG, +#endif + + /*** PCI Target Drivers ***/ +#ifdef CONFIGURE_DRIVER_PCI_GR_RASTA_ADCDAC + DRIVER_PCI_GR_RASTA_ADCDAC, +#endif +#ifdef CONFIGURE_DRIVER_PCI_GR_RASTA_IO + DRIVER_PCI_GR_RASTA_IO, +#endif +#ifdef CONFIGURE_DRIVER_PCI_GR_RASTA_TMTC + DRIVER_PCI_GR_RASTA_TMTC, +#endif +#ifdef CONFIGURE_DRIVER_PCI_GR_701 + DRIVER_PCI_GR_701, +#endif +#ifdef CONFIGURE_DRIVER_PCI_GR_TMTC_1553 + DRIVER_PCI_GR_TMTC_1553, +#endif +#ifdef CONFIGURE_DRIVER_PCI_GR_RASTA_SPW_ROUTER + DRIVER_PCI_GR_RASTA_SPW_ROUTER, +#endif + + /*** SpaceWire Node Drivers ***/ +#ifdef CONFIGURE_DRIVER_SPW_RMAP_AMBAPP + DRIVER_SPW_RMAP_AMBAPP, +#endif + + /*** AMBA Plug&Play over SpaceWire (RMAP) drivers ***/ +#ifdef CONFIGURE_DRIVER_RMAP_AMBAPP_MCTRL + DRIVER_RMAP_AMBAPP_MCTRL, +#endif +#ifdef CONFIGURE_DRIVER_RMAP_AMBAPP_GRTC + DRIVER_RMAP_AMBAPP_GRTC, +#endif +#ifdef CONFIGURE_DRIVER_RMAP_AMBAPP_GRTM + DRIVER_RMAP_AMBAPP_GRTM, +#endif + + +/* Macros for adding custom drivers without needing to recompile + * kernel. + */ +#ifdef CONFIGURE_DRIVER_CUSTOM1 + DRIVER_CUSTOM1_REG, +#endif +#ifdef CONFIGURE_DRIVER_CUSTOM2 + DRIVER_CUSTOM2_REG, +#endif +#ifdef CONFIGURE_DRIVER_CUSTOM3 + DRIVER_CUSTOM3_REG, +#endif +#ifdef CONFIGURE_DRIVER_CUSTOM4 + DRIVER_CUSTOM4_REG, +#endif +#ifdef CONFIGURE_DRIVER_CUSTOM5 + DRIVER_CUSTOM5_REG, +#endif +#ifdef CONFIGURE_DRIVER_CUSTOM6 + DRIVER_CUSTOM6_REG, +#endif +#ifdef CONFIGURE_DRIVER_CUSTOM7 + DRIVER_CUSTOM7_REG, +#endif +#ifdef CONFIGURE_DRIVER_CUSTOM8 + DRIVER_CUSTOM8_REG, +#endif +#ifdef CONFIGURE_DRIVER_CUSTOM9 + DRIVER_CUSTOM9_REG, +#endif + + /* End array with NULL */ + {NULL} +}; + +#endif /* CONFIGURE_INIT */ + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_MANAGER_CONFDEFS_H_ */ diff --git a/cpukit/libdrvmgr/drvmgr_drvinf.c b/cpukit/libdrvmgr/drvmgr_drvinf.c new file mode 100644 index 0000000000..621ec1641b --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_drvinf.c @@ -0,0 +1,145 @@ +/* Driver Manager Driver Interface Implementation. + * + * 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. + * + * This is the part the device driver API, the functions rely on that the + * parent bus driver has implemented the neccessary operations correctly. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <drvmgr/drvmgr.h> +#include "drvmgr_internal.h" + +/* Get device pointer from knowing the Driver and the Driver minor + * that was assigned to it + */ +int drvmgr_get_dev( + struct drvmgr_drv *drv, + int minor, + struct drvmgr_dev **pdev) +{ + struct drvmgr_dev *dev; + if (!drv) + return -1; + + DRVMGR_LOCK_READ(); + dev = drv->dev; + while (dev) { + if (dev->minor_drv == minor) + break; + dev = dev->next_in_drv; + } + DRVMGR_UNLOCK(); + if (!dev) + return -1; + if (pdev) + *pdev = dev; + return 0; +} + +/* Get Bus frequency in HZ from bus driver */ +int drvmgr_freq_get( + struct drvmgr_dev *dev, + int options, + unsigned int *freq_hz) +{ + if (!dev || !dev->parent || !dev->parent->ops->freq_get) + return -1; + + return dev->parent->ops->freq_get(dev, options, freq_hz); +} + +/* Get driver prefix */ +int drvmgr_get_dev_prefix(struct drvmgr_dev *dev, char *dev_prefix) +{ + struct drvmgr_bus_params params; + if (!dev || !dev->parent || !dev->parent->ops->get_params) + return -1; + + dev->parent->ops->get_params(dev, ¶ms); + if (!params.dev_prefix) + return -1; + if (dev_prefix) + strcpy(dev_prefix, params.dev_prefix); + return 0; +} + +/* Register an interrupt */ +int drvmgr_interrupt_register( + struct drvmgr_dev *dev, + int index, + const char *info, + drvmgr_isr isr, + void *arg) +{ + if (!dev || !dev->parent || !dev->parent->ops->int_register) + return -1; + + if (!isr) + return -1; + + return dev->parent->ops->int_register(dev, index, info, isr, arg); +} + +/* Unregister an interrupt */ +int drvmgr_interrupt_unregister( + struct drvmgr_dev *dev, + int index, + drvmgr_isr isr, + void *arg) +{ + if (!dev || !dev->parent || !dev->parent->ops->int_unregister) + return -1; + + if (!isr) + return -1; + + return dev->parent->ops->int_unregister(dev, index, isr, arg); +} + +int drvmgr_interrupt_clear( + struct drvmgr_dev *dev, + int index) +{ + if (!dev || !dev->parent || !dev->parent->ops->int_clear) + return -1; + + return dev->parent->ops->int_clear(dev, index); +} + +int drvmgr_interrupt_unmask( + struct drvmgr_dev *dev, + int index) +{ + if (!dev || !dev->parent || !dev->parent->ops->int_unmask) + return -1; + + return dev->parent->ops->int_unmask(dev, index); +} + +int drvmgr_interrupt_mask( + struct drvmgr_dev *dev, + int index) +{ + if (!dev || !dev->parent || !dev->parent->ops->int_mask) + return -1; + + return dev->parent->ops->int_mask(dev, index); +} + +int drvmgr_on_rootbus(struct drvmgr_dev *dev) +{ + if (dev->parent && dev->parent->dev && dev->parent->dev->parent) + return 0; + else + return 1; +} diff --git a/cpukit/libdrvmgr/drvmgr_for_each_dev.c b/cpukit/libdrvmgr/drvmgr_for_each_dev.c new file mode 100644 index 0000000000..8fdbe83a58 --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_for_each_dev.c @@ -0,0 +1,105 @@ +/* Iterate over device tree topology, breadth or depth-first + * + * COPYRIGHT (c) 2009-2011 + * 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. + * + */ + +#include <string.h> +#include <drvmgr/drvmgr.h> +#include <drvmgr/drvmgr_list.h> +#include "drvmgr_internal.h" + +/* Traverse device tree breadth-first. Supports up to 31 buses */ +static int drvmgr_for_each_dev_breadth( + int (*func)(struct drvmgr_dev *dev, void *arg), + void *arg + ) +{ + int ret = 0, i, pos; + struct drvmgr_bus *bus, *buses[32]; + struct drvmgr_dev *dev; + + pos = 0; + memset(&buses[0], 0, sizeof(buses)); + buses[pos++] = drv_mgr.root_dev.bus; /* Get root bus */ + + for (i = 0, bus = buses[0]; buses[i]; i++, bus = buses[i]) { + dev = bus->children; + while (dev) { + ret = func(dev, arg); + if (ret != 0) + break; + if (dev->bus && pos < 31) + buses[pos++] = dev->bus; + + dev = dev->next_in_bus; + } + } + + return ret; +} + +/* Traverse device tree depth-first. */ +static int drvmgr_for_each_dev_depth( + int (*func)(struct drvmgr_dev *dev, void *arg), + void *arg + ) +{ + int ret = 0; + struct drvmgr_dev *dev; + + /* Get first device */ + dev = drv_mgr.root_dev.bus->children; + + while (dev) { + ret = func(dev, arg); + if (ret != 0) + break; + if (dev->bus && dev->bus->children) { + dev = dev->bus->children; + } else { +next_dev: + if (dev->next_in_bus == NULL) { + /* Step up one level... back to parent bus */ + dev = dev->parent->dev; + if (dev == &drv_mgr.root_dev) + break; + goto next_dev; + } else { + dev = dev->next_in_bus; + } + } + } + + return ret; +} + +/* Traverse device tree depth-first or breadth-first */ +int drvmgr_for_each_dev( + int (*func)(struct drvmgr_dev *dev, void *arg), + void *arg, + int options + ) +{ + int ret; + + DRVMGR_LOCK_READ(); + + /* Get Root Device */ + if (drv_mgr.root_dev.bus->children != NULL) { + if (options & DRVMGR_FED_BF) + ret = drvmgr_for_each_dev_breadth(func, arg); + else + ret = drvmgr_for_each_dev_depth(func, arg); + } else + ret = 0; + + DRVMGR_UNLOCK(); + + return ret; +} diff --git a/cpukit/libdrvmgr/drvmgr_for_each_list_dev.c b/cpukit/libdrvmgr/drvmgr_for_each_list_dev.c new file mode 100644 index 0000000000..f15004472e --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_for_each_list_dev.c @@ -0,0 +1,45 @@ +/* Iterate over one list of devices used internally by driver manager + * + * COPYRIGHT (c) 2009-2011 + * 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. + * + */ + +#include <drvmgr/drvmgr.h> +#include <drvmgr/drvmgr_list.h> +#include "drvmgr_internal.h" + +int drvmgr_for_each_listdev( + struct drvmgr_list *devlist, + unsigned int state_set_mask, + unsigned int state_clr_mask, + int (*func)(struct drvmgr_dev *dev, void *arg), + void *arg + ) +{ + struct drvmgr_dev *dev; + int ret = 0; + + DRVMGR_LOCK_READ(); + + /* Get First Device */ + dev = DEV_LIST_HEAD(devlist); + while (dev) { + if (((state_set_mask != 0) && ((dev->state & state_set_mask) == state_set_mask)) || + ((state_clr_mask != 0) && ((dev->state & state_clr_mask) == 0)) || + ((state_set_mask == 0) && (state_clr_mask == 0))) { + ret = func(dev, arg); + if (ret != 0) + break; + } + dev = dev->next; + } + + DRVMGR_UNLOCK(); + + return ret; +} diff --git a/cpukit/libdrvmgr/drvmgr_func.c b/cpukit/libdrvmgr/drvmgr_func.c new file mode 100644 index 0000000000..def53aaa9d --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_func.c @@ -0,0 +1,43 @@ +/* Driver Manager optional dynamic function interface + * + * COPYRIGHT (c) 2011 + * 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. + * + */ + +#include <drvmgr/drvmgr.h> + +/* Get Function from Function ID */ +int drvmgr_func_get(void *obj, int funcid, void **func) +{ + int objtype; + struct drvmgr_func *f; + + if (!obj) + return DRVMGR_FAIL; + objtype = *(int *)obj; + + if (objtype == DRVMGR_OBJ_BUS) + f = ((struct drvmgr_bus *)obj)->funcs; + else if (objtype == DRVMGR_OBJ_DRV) + f = ((struct drvmgr_drv *)obj)->funcs; + else + return DRVMGR_FAIL; + + if (f == NULL) + return DRVMGR_FAIL; + + while (f->funcid != DRVMGR_FUNCID_NONE) { + if (f->funcid == funcid) { + *func = f->func; + return DRVMGR_OK; + } + f++; + } + + return DRVMGR_FAIL; +} diff --git a/cpukit/libdrvmgr/drvmgr_func_call.c b/cpukit/libdrvmgr/drvmgr_func_call.c new file mode 100644 index 0000000000..9b74ef2a71 --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_func_call.c @@ -0,0 +1,22 @@ +/* Driver Manager optional dynamic function interface + * + * COPYRIGHT (c) 2011 + * 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. + * + */ + +#include <drvmgr/drvmgr.h> + +/* Lookup function from function ID and call it using given arguments */ +int drvmgr_func_call(void *obj, int funcid, void *a, void *b, void *c, void *d) +{ + int (*func)(void *arg1, void *arg2, void *arg3, void *arg4) = NULL; + + if (drvmgr_func_get(obj, funcid, (void *)&func) != DRVMGR_OK) + return DRVMGR_FAIL; + return func(a, b, c, d); +} diff --git a/cpukit/libdrvmgr/drvmgr_init.c b/cpukit/libdrvmgr/drvmgr_init.c new file mode 100644 index 0000000000..a13fb5bc03 --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_init.c @@ -0,0 +1,27 @@ +/* Driver Manager Initialization + * + * COPYRIGHT (c) 2009-2011 + * 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. + * + */ + +#include <drvmgr/drvmgr.h> + +/* Init driver manager - all in one go. Typically called from Init task when + * user wants to initilize driver manager after startup, otherwise not used. + */ +int drvmgr_init(void) +{ + int level; + + _DRV_Manager_initialization(); + + for (level = 1; level <= DRVMGR_LEVEL_MAX; level++) + _DRV_Manager_init_level(level); + + return 0; +} diff --git a/cpukit/libdrvmgr/drvmgr_internal.h b/cpukit/libdrvmgr/drvmgr_internal.h new file mode 100644 index 0000000000..631480564d --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_internal.h @@ -0,0 +1,62 @@ +/* Private driver manager declarations + * + * COPYRIGHT (c) 2009-2011 + * 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. + * + * Structure hold all information the driver manager needs to know of. Used + * internally by Driver Manager routines. + */ + +struct rtems_driver_manager { + int level; + int initializing_objs; + + /* Device tree Lock */ + rtems_id lock; + + /* The first device - The root device and it's driver */ + struct drvmgr_drv *root_drv; + struct drvmgr_dev root_dev; + + /*!< Linked list of all registered drivers */ + struct drvmgr_list drivers; + + /* Buses that reached a certain initialization level. + * Lists by Level: + * N=0 - Not intialized, just registered + * N=1..MAX-1 - Reached init level N + * N=MAX - Successfully initialized bus + */ + struct drvmgr_list buses[DRVMGR_LEVEL_MAX+1]; + /* Buses failed to initialize or has been removed by not freed */ + struct drvmgr_list buses_inactive; + + /* Devices that reached a certain initialization level. + * Lists by Level: + * N=0 - Not intialized, just registered + * N=1..MAX-1 - Reached init level N + * N=MAX - Successfully initialized device + */ + struct drvmgr_list devices[DRVMGR_LEVEL_MAX+1]; + /*!< Devices failed to initialize, removed, ignored, no driver */ + struct drvmgr_list devices_inactive; +}; + +extern struct rtems_driver_manager drv_mgr; + +extern void _DRV_Manager_Lock(void); +extern void _DRV_Manager_Unlock(void); +extern int _DRV_Manager_Init_Lock(void); + +/* The best solution is to implement the locking with a RW lock, however there + * is no such API available. Care must be taken so that dead-lock isn't created + * for example in recursive functions. + */ +#define DRVMGR_LOCK_INIT() _DRV_Manager_Init_Lock() +#define DRVMGR_LOCK_WRITE() _DRV_Manager_Lock() +#define DRVMGR_LOCK_READ() _DRV_Manager_Lock() +#define DRVMGR_UNLOCK() _DRV_Manager_Unlock() diff --git a/cpukit/libdrvmgr/drvmgr_list.c b/cpukit/libdrvmgr/drvmgr_list.c new file mode 100644 index 0000000000..e25f496827 --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_list.c @@ -0,0 +1,68 @@ +/* Driver Manager List Interface Implementation. + * + * 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. + * + */ + +#include <stdlib.h> +#include <drvmgr/drvmgr_list.h> + +/* LIST interface */ + +void drvmgr_list_init(struct drvmgr_list *list, int offset) +{ + list->head = list->tail = NULL; + list->ofs = offset; +} + +void drvmgr_list_empty(struct drvmgr_list *list) +{ + list->head = list->tail = NULL; +} + +void drvmgr_list_add_head(struct drvmgr_list *list, void *entry) +{ + LIST_FIELD(list, entry) = list->head; + if (list->head == NULL) + list->tail = entry; + list->head = entry; +} + +void drvmgr_list_add_tail(struct drvmgr_list *list, void *entry) +{ + if (list->tail == NULL) + list->head = entry; + else + LIST_FIELD(list, list->tail) = entry; + LIST_FIELD(list, entry) = NULL; + list->tail = entry; +} + +void drvmgr_list_remove_head(struct drvmgr_list *list) +{ + list->head = LIST_FIELD(list, list->head); + if (list->head == NULL) + list->tail = NULL; +} + +void drvmgr_list_remove(struct drvmgr_list *list, void *entry) +{ + void **prevptr = &list->head; + void *curr, *prev; + + prev = NULL; + curr = list->head; + while (curr != entry) { + prev = curr; + prevptr = &LIST_FIELD(list, curr); + curr = LIST_FIELD(list, curr); + } + *prevptr = LIST_FIELD(list, entry); + if (list->tail == entry) + list->tail = prev; +} diff --git a/cpukit/libdrvmgr/drvmgr_list.h b/cpukit/libdrvmgr/drvmgr_list.h new file mode 100644 index 0000000000..9497d6d55c --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_list.h @@ -0,0 +1,77 @@ +/* Linked list help functions used by driver manager. + * + * 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. + * + * Help functions for the Driver Manager. Implements a singly linked list + * with head and tail pointers for fast insertions/deleations to head and + * tail in list. + */ + +#ifndef _DRVIVER_MANAGER_LIST_H_ +#define _DRVIVER_MANAGER_LIST_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/*! List description, Singly link list with head and tail pointers. */ +struct drvmgr_list { + void *head; /*!< First entry in queue */ + void *tail; /*!< Last entry in queue */ + int ofs; /*!< Offset into head and tail to find next field */ +}; + +/* Static initialization of list */ +#define LIST_INITIALIZER(type, field) {NULL, NULL, offsetof(type, field)} + +/* Return the first element in list */ +#define LIST_HEAD(list, type) ((type *)(list)->head) + +/* Return the last element in list */ +#define LIST_TAIL(list, type) ((type *)(list)->tail) + +/* Get the next pointer of an entry */ +#define LIST_FIELD(list, entry) (*(void **)((char *)(entry) + (list)->ofs)) + +/* Return the next emlement in list */ +#define LIST_NEXT(list, entry, type) ((type *)(LIST_FIELD(list, entry))) + +/* Iterate through all entries in list */ +#define LIST_FOR_EACH(list, entry, type) \ + for (entry = LIST_HEAD(list, type); \ + entry; \ + entry = LIST_NEXT(list, entry, type)) + +/*! Initialize a list during runtime + * + * \param list The list to initialize + * \param offset The number of bytes into the entry structure the next pointer + * is found + */ +extern void drvmgr_list_init(struct drvmgr_list *list, int offset); + +/*! Clear list */ +extern void drvmgr_list_empty(struct drvmgr_list *list); + +/*! Add entry to front of list */ +extern void drvmgr_list_add_head(struct drvmgr_list *list, void *entry); + +/*! Add entry to end of list */ +extern void drvmgr_list_add_tail(struct drvmgr_list *list, void *entry); + +/*! Remove entry from front of list */ +extern void drvmgr_list_remove_head(struct drvmgr_list *list); + +/*! Remove entry from anywhere in list */ +extern void drvmgr_list_remove(struct drvmgr_list *list, void *entry); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/cpukit/libdrvmgr/drvmgr_lock.c b/cpukit/libdrvmgr/drvmgr_lock.c new file mode 100644 index 0000000000..91b597d653 --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_lock.c @@ -0,0 +1,39 @@ +/* Driver Manager Internal locking implementation + * + * 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. + * + */ + +#include <rtems.h> +#include <drvmgr/drvmgr.h> +#include "drvmgr_internal.h" + +void _DRV_Manager_Lock(void) +{ + rtems_semaphore_obtain(drv_mgr.lock, RTEMS_WAIT, RTEMS_NO_TIMEOUT); +} + +void _DRV_Manager_Unlock(void) +{ + rtems_semaphore_release(drv_mgr.lock); +} + +int _DRV_Manager_Init_Lock(void) +{ + int rc; + + rc = rtems_semaphore_create( + rtems_build_name('D', 'R', 'V', 'M'), + 1, + RTEMS_DEFAULT_ATTRIBUTES, + 0, + &drv_mgr.lock); + if (rc != RTEMS_SUCCESSFUL) + return -1; + return 0; +} diff --git a/cpukit/libdrvmgr/drvmgr_print.c b/cpukit/libdrvmgr/drvmgr_print.c new file mode 100644 index 0000000000..d8e4a465dd --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_print.c @@ -0,0 +1,455 @@ +/* Driver Manager Information printing Interface Implementation + * + * 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. + * + * These functions print stuff about the driver manager, what devices were + * found and were united with a driver, the Bus topology, memory taken, etc. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <drvmgr/drvmgr.h> +#include "drvmgr_internal.h" + +typedef void (*fun_ptr)(void); + +static int print_dev_found(struct drvmgr_dev *dev, void *arg) +{ + char **pparg = arg; + + if (pparg && *pparg) { + printf(*pparg); + *pparg = NULL; + } + + printf(" DEV %p %s on bus %p\n", dev, + dev->name ? dev->name : "NO_NAME", dev->parent); + + return 0; /* Continue to next device */ +} + +void drvmgr_print_devs(unsigned int options) +{ + struct rtems_driver_manager *mgr = &drv_mgr; + char *parg; + + /* Print Drivers */ + if (options & PRINT_DEVS_ASSIGNED) { + parg = " --- DEVICES ASSIGNED TO DRIVER ---\n"; + drvmgr_for_each_listdev(&mgr->devices[DRVMGR_LEVEL_MAX], + DEV_STATE_UNITED, 0, print_dev_found, &parg); + if (parg != NULL) + printf("\n NO DEVICES WERE ASSIGNED A DRIVER\n"); + } + + if (options & PRINT_DEVS_UNASSIGNED) { + parg = "\n --- DEVICES WITHOUT DRIVER ---\n"; + drvmgr_for_each_listdev(&mgr->devices_inactive, 0, + DEV_STATE_UNITED, print_dev_found, &parg); + if (parg != NULL) + printf("\n NO DEVICES WERE WITHOUT DRIVER\n"); + } + + if (options & PRINT_DEVS_FAILED) { + parg = "\n --- DEVICES FAILED TO INITIALIZE ---\n"; + drvmgr_for_each_listdev(&mgr->devices_inactive, + DEV_STATE_INIT_FAILED, 0, print_dev_found, &parg); + if (parg != NULL) + printf("\n NO DEVICES FAILED TO INITIALIZE\n"); + } + + if (options & PRINT_DEVS_IGNORED) { + parg = "\n --- DEVICES IGNORED ---\n"; + drvmgr_for_each_listdev(&mgr->devices_inactive, + DEV_STATE_IGNORED, 0, print_dev_found, &parg); + if (parg != NULL) + printf("\n NO DEVICES WERE IGNORED\n"); + } + + printf("\n\n"); +} + +int drvmgr_topo_func(struct drvmgr_dev *dev, void *arg) +{ + char prefix[32]; + int depth = dev->parent->depth; + + if (depth > 30) + return 0; /* depth more than 30 not supported */ + memset(prefix, ' ', depth + 1); + prefix[depth + 1] = '\0'; + + printf(" %s|-> DEV %p %s\n", prefix, dev, + dev->name ? dev->name : "NO_NAME"); + return 0; +} + +void drvmgr_print_topo(void) +{ + /* Print Bus topology */ + printf(" --- BUS TOPOLOGY ---\n"); + drvmgr_for_each_dev(drvmgr_topo_func, NULL, DRVMGR_FED_DF); + printf("\n\n"); +} + +/* Print the memory usage */ +void drvmgr_print_mem(void) +{ + struct rtems_driver_manager *mgr = &drv_mgr; + struct drvmgr_bus *bus; + struct drvmgr_dev *dev; + struct drvmgr_drv *drv; + + struct drvmgr_bus_res *node; + struct drvmgr_drv_res *res; + struct drvmgr_key *key; + + unsigned int busmem = 0; + unsigned int devmem = 0; + unsigned int drvmem = 0; + unsigned int resmem = 0; + unsigned int devprivmem = 0; + + DRVMGR_LOCK_READ(); + + bus = BUS_LIST_HEAD(&mgr->buses[DRVMGR_LEVEL_MAX]); + while (bus) { + busmem += sizeof(struct drvmgr_bus); + + /* Get size of resources on this bus */ + node = bus->reslist; + while (node) { + resmem += sizeof(struct drvmgr_bus_res); + + res = node->resource; + while (res->keys) { + resmem += sizeof(struct drvmgr_drv_res); + + key = res->keys; + while (key->key_type != KEY_TYPE_NONE) { + resmem += sizeof + (struct drvmgr_key); + key++; + } + resmem += sizeof(struct drvmgr_key); + res++; + } + + node = node->next; + } + + bus = bus->next; + } + + drv = DRV_LIST_HEAD(&mgr->drivers); + while (drv) { + drvmem += sizeof(struct drvmgr_drv); + drv = drv->next; + } + + dev = DEV_LIST_HEAD(&mgr->devices[DRVMGR_LEVEL_MAX]); + while (dev) { + devmem += sizeof(struct drvmgr_dev); + if (dev->drv && dev->drv->dev_priv_size > 0) + devprivmem += dev->drv->dev_priv_size; + dev = dev->next; + } + + DRVMGR_UNLOCK(); + + printf(" --- MEMORY USAGE ---\n"); + printf(" BUS: %d bytes\n", busmem); + printf(" DRV: %d bytes\n", drvmem); + printf(" DEV: %d bytes\n", devmem); + printf(" DEV private: %d bytes\n", devprivmem); + printf(" RES: %d bytes\n", resmem); + printf(" TOTAL: %d bytes\n", + busmem + drvmem + devmem + devprivmem + resmem); + printf("\n\n"); +} + +/* Print the memory usage */ +void drvmgr_summary(void) +{ + struct rtems_driver_manager *mgr = &drv_mgr; + struct drvmgr_bus *bus; + struct drvmgr_dev *dev; + struct drvmgr_drv *drv; + int i, buscnt = 0, devcnt = 0, drvcnt = 0; + + printf(" --- SUMMARY ---\n"); + + drv = DRV_LIST_HEAD(&mgr->drivers); + while (drv) { + drvcnt++; + drv = drv->next; + } + printf(" NUMBER OF DRIVERS: %d\n", drvcnt); + + DRVMGR_LOCK_READ(); + + for (i = 0; i <= DRVMGR_LEVEL_MAX; i++) { + buscnt = 0; + bus = BUS_LIST_HEAD(&mgr->buses[i]); + while (bus) { + buscnt++; + bus = bus->next; + } + if (buscnt > 0) { + printf(" NUMBER OF BUSES IN LEVEL[%d]: %d\n", + i, buscnt); + } + } + + for (i = 0; i <= DRVMGR_LEVEL_MAX; i++) { + devcnt = 0; + dev = DEV_LIST_HEAD(&mgr->devices[i]); + while (dev) { + devcnt++; + dev = dev->next; + } + if (devcnt > 0) { + printf(" NUMBER OF DEVS IN LEVEL[%d]: %d\n", + i, devcnt); + } + } + + DRVMGR_UNLOCK(); + + printf("\n\n"); +} + +static void print_info(void *p, char *str) +{ + printf(" "); + puts(str); +} + +void drvmgr_info_dev(struct drvmgr_dev *dev, unsigned int options) +{ + if (!dev) + return; + + printf(" -- DEVICE %p --\n", dev); + if (options & OPTION_DEV_GENINFO) { + printf(" PARENT BUS: %p\n", dev->parent); + printf(" NAME: %s\n", dev->name ? dev->name : "NO_NAME"); + printf(" STATE: 0x%08x\n", dev->state); + if (dev->bus) + printf(" BRIDGE TO: %p\n", dev->bus); + printf(" INIT LEVEL: %d\n", dev->level); + printf(" ERROR: %d\n", dev->error); + printf(" MINOR BUS: %d\n", dev->minor_bus); + if (dev->drv) { + printf(" MINOR DRV: %d\n", dev->minor_drv); + printf(" DRIVER: %p (%s)\n", dev->drv, + dev->drv->name ? dev->drv->name : "NO_NAME"); + printf(" PRIVATE: %p\n", dev->priv); + } + } + + if (options & OPTION_DEV_BUSINFO) { + printf(" --- DEVICE INFO FROM BUS DRIVER ---\n"); + if (!dev->parent) + printf(" !! device has no parent bus !!\n"); + else if (dev->parent->ops->info_dev) + dev->parent->ops->info_dev(dev, print_info, NULL); + else + printf(" Bus doesn't implement info_dev func\n"); + } + + if (options & OPTION_DEV_DRVINFO) { + if (dev->drv) { + printf(" --- DEVICE INFO FROM DEVICE DRIVER ---\n"); + if (dev->drv->ops->info) + dev->drv->ops->info(dev, print_info, NULL, 0, 0); + else + printf(" Driver doesn't implement info func\n"); + } + } +} + +void drvmgr_info_bus_map(struct drvmgr_map_entry *map) +{ + if (map == NULL) + printf(" Addresses mapped 1:1\n"); + else if (map == DRVMGR_TRANSLATE_NO_BRIDGE) + printf(" No bridge in this direction\n"); + else { + while (map->size != 0) { + printf(" 0x%08lx-0x%08lx => 0x%08lx-0x%08lx %s\n", + (unsigned long)map->from_adr, + (unsigned long)(map->from_adr + map->size - 1), + (unsigned long)map->to_adr, + (unsigned long)(map->to_adr + map->size - 1), + map->name ? map->name : "no label"); + map++; + } + } +} + +void drvmgr_info_bus(struct drvmgr_bus *bus, unsigned int options) +{ + struct drvmgr_dev *dev; + + /* Print Driver */ + printf("-- BUS %p --\n", bus); + printf(" BUS TYPE: %d\n", bus->bus_type); + printf(" DEVICE: %p (%s)\n", bus->dev, + bus->dev->name ? bus->dev->name : "NO_NAME"); + printf(" OPS: %p\n", bus->ops); + printf(" CHILDREN: %d devices\n", bus->dev_cnt); + printf(" LEVEL: %d\n", bus->level); + printf(" STATE: 0x%08x\n", bus->state); + printf(" ERROR: %d\n", bus->error); + + /* Print address mappings up- (to parent) and down- (from parent to + * this bus) stream the bridge of this bus + */ + printf(" DOWN STREAMS BRIDGE MAPPINGS (from parent to this bus)\n"); + drvmgr_info_bus_map(bus->maps_down); + printf(" UP STREAMS BRIDGE MAPPINGS (from this bus to parent)\n"); + drvmgr_info_bus_map(bus->maps_up); + + /* Print Devices on this bus? */ + if (options & OPTION_BUS_DEVS) { + printf(" CHILDREN:\n"); + DRVMGR_LOCK_READ(); + dev = bus->children; + while (dev) { + printf(" |- DEV[%02d]: %p %s\n", dev->minor_bus, + dev, dev->name ? dev->name : "NO_NAME"); + dev = dev->next_in_bus; + } + DRVMGR_UNLOCK(); + } +} + +char *drv_ops_names[DRV_OPS_NUM] = { + "init[1]:", + "init[2]:", + "init[3]:", + "init[4]:", + "remove: ", + "info: " +}; + +void drvmgr_info_drv(struct drvmgr_drv *drv, unsigned int options) +{ + struct drvmgr_dev *dev; + fun_ptr *ppfunc; + int i; + + /* Print Driver */ + printf(" -- DRIVER %p --\n", drv); + printf(" DRIVER ID: 0x%llx\n", drv->drv_id); + printf(" NAME: %s\n", drv->name ? drv->name : "NO_NAME"); + printf(" BUS TYPE: %d\n", drv->bus_type); + printf(" OPERATIONS:\n"); + for (i = 0, ppfunc = (fun_ptr *)&drv->ops->init[0]; i<DRV_OPS_NUM; i++) + printf(" %s %p\n", drv_ops_names[i], ppfunc[i]); + printf(" NO. DEVICES: %d\n", drv->dev_cnt); + + /* Print devices united with this driver? */ + if (options & OPTION_DRV_DEVS) { + DRVMGR_LOCK_READ(); + dev = drv->dev; + while (dev) { + printf(" DEV[%02d]: %p %s\n", dev->minor_drv, + dev, dev->name ? dev->name : "NO_NAME"); + dev = dev->next_in_drv; + } + DRVMGR_UNLOCK(); + } +} + +void (*info_obj[3])(void *obj, unsigned int) = { + /* DRVMGR_OBJ_DRV */ (void (*)(void *, unsigned int))drvmgr_info_drv, + /* DRVMGR_OBJ_BUS */ (void (*)(void *, unsigned int))drvmgr_info_bus, + /* DRVMGR_OBJ_DEV */ (void (*)(void *, unsigned int))drvmgr_info_dev, +}; + +/* Get information about a device/bus/driver */ +void drvmgr_info(void *id, unsigned int options) +{ + int obj_type; + void (*func)(void *, unsigned int); + + if (!id) + return; + obj_type = *(int *)id; + if ((obj_type < DRVMGR_OBJ_DRV) || (obj_type > DRVMGR_OBJ_DEV)) + return; + func = info_obj[obj_type - 1]; + func(id, options); +} + +void drvmgr_info_devs_on_bus(struct drvmgr_bus *bus, unsigned int options) +{ + struct drvmgr_dev *dev; + + /* Print All Devices on Bus */ + printf("\n\n -= All Devices on BUS %p =-\n\n", bus); + dev = bus->children; + while (dev) { + drvmgr_info_dev(dev, options); + puts(""); + dev = dev->next_in_bus; + } + + if ((options & OPTION_RECURSIVE) == 0) + return; + + /* This device provides a bus, print the bus */ + dev = bus->children; + while (dev) { + if (dev->bus) + drvmgr_info_devs_on_bus(dev->bus, options); + dev = dev->next_in_bus; + } +} + +void drvmgr_info_devs(unsigned int options) +{ + struct rtems_driver_manager *mgr = &drv_mgr; + struct drvmgr_dev *dev; + + /* Print device information of all devices and their child devices */ + dev = &mgr->root_dev; + drvmgr_info_devs_on_bus(dev->bus, options); + printf("\n\n"); +} + +void drvmgr_info_drvs(unsigned int options) +{ + struct rtems_driver_manager *mgr = &drv_mgr; + struct drvmgr_drv *drv; + + drv = DRV_LIST_HEAD(&mgr->drivers); + while (drv) { + drvmgr_info_drv(drv, options); + puts("\n"); + drv = drv->next; + } +} + +void drvmgr_info_buses(unsigned int options) +{ + struct rtems_driver_manager *mgr = &drv_mgr; + struct drvmgr_bus *bus; + + bus = BUS_LIST_HEAD(&mgr->buses[DRVMGR_LEVEL_MAX]); + while (bus) { + drvmgr_info_bus(bus, options); + puts("\n"); + bus = bus->next; + } +} diff --git a/cpukit/libdrvmgr/drvmgr_res.c b/cpukit/libdrvmgr/drvmgr_res.c new file mode 100644 index 0000000000..e8545d78ee --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_res.c @@ -0,0 +1,103 @@ +/* Driver Manager Driver Resource Interface Implementation. + * + * 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. + * + */ + +#include <string.h> +#include <drvmgr/drvmgr.h> + +/* Find all the resource keys for a device among all bus resources */ +int drvmgr_keys_get(struct drvmgr_dev *dev, struct drvmgr_key **keys) +{ + struct drvmgr_bus *bus; + struct drvmgr_bus_res *node; + struct drvmgr_drv_res *res; + uint64_t drv_id; + + bus = dev->parent; + if (!bus || !dev->drv) + return -1; + + drv_id = dev->drv->drv_id; + + /* Loop all resource arrays */ + node = bus->reslist; + while (node) { + /* Find driver ID in resource array */ + res = &node->resource[0]; + while (res->drv_id) { + if (res->drv_id == drv_id) { + /* Found resource matching driver, now check + * that this resource is for this device. + */ + if (dev->minor_bus == res->minor_bus) { + /* Matching driver and core number */ + if (keys) + *keys = res->keys; + return 0; + } + } + res++; + } + node = node->next; + } + if (keys) + *keys = NULL; + return 1; +} + +/* Return key that matches key name */ +struct drvmgr_key *drvmgr_key_get( + struct drvmgr_key *keys, + char *key_name) +{ + struct drvmgr_key *key; + + if (!keys) + return NULL; + + key = keys; + while (key->key_type != KEY_TYPE_NONE) { + if (strcmp(key_name, key->key_name) == 0) + return key; + key++; + } + return NULL; +} + +union drvmgr_key_value *drvmgr_key_val_get( + struct drvmgr_key *keys, + char *key_name, + int key_type) +{ + struct drvmgr_key *key_match; + + key_match = drvmgr_key_get(keys, key_name); + if (key_match) { + /* Found key, put pointer to value into */ + if ((key_type == -1) || (key_match->key_type == key_type)) + return &key_match->key_value; + } + return NULL; +} + +union drvmgr_key_value *drvmgr_dev_key_get( + struct drvmgr_dev *dev, + char *key_name, + int key_type) +{ + struct drvmgr_key *keys = NULL; + + /* Find first entry in key array for the device */ + if (drvmgr_keys_get(dev, &keys)) + return NULL; + + /* Find a specific key among the device keys */ + return drvmgr_key_val_get(keys, key_name, key_type); +} diff --git a/cpukit/libdrvmgr/drvmgr_rw.c b/cpukit/libdrvmgr/drvmgr_rw.c new file mode 100644 index 0000000000..4a65e953ff --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_rw.c @@ -0,0 +1,53 @@ +/* Driver Manager Read/Write Interface Implementation. + * + * 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. + * + */ + +#include <string.h> +#include <drvmgr/drvmgr.h> + +/* Set a range of memory in 128 byte chunks. + * This call will take 128 bytes for buffer on stack + */ +void drvmgr_rw_memset( + void *dstadr, + int c, + size_t n, + void *a, + drvmgr_wmem_arg wmem + ) +{ + unsigned long long buf[16+1]; /* Extra bytes after data are reserved + * for optimizations by write_mem */ + int txlen, status; + char *adr; + + if (n <= 0) + return; + + if (n > sizeof(unsigned long long)*16) + txlen = sizeof(unsigned long long)*16; + else + txlen = n; + + memset(buf, c, txlen); + + adr = dstadr; + do { + wmem(adr, (const void *)&buf[0], txlen, a); + adr += txlen; + n -= txlen; + + /* next length to transmitt */ + if (n > 16*sizeof(unsigned long long)) + txlen = 16*sizeof(unsigned long long); + else + txlen = n; + } while (n > 0); +} diff --git a/cpukit/libdrvmgr/drvmgr_translate.c b/cpukit/libdrvmgr/drvmgr_translate.c new file mode 100644 index 0000000000..630ba6f007 --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_translate.c @@ -0,0 +1,152 @@ +/* Driver Manager Driver Translate Interface Implementation + * + * COPYRIGHT (c) 2010. + * 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. + * + * Used by device drivers. The functions rely on that the parent bus driver + * has implemented the neccessary operations correctly. + * + * The translate functions are used to translate addresses between buses + * for DMA cores located on a "remote" bus, or for memory-mapped obtaining + * an address that can be used to access an remote bus. + * + * For example, PCI I/O might be memory-mapped at the PCI Host bridge, + * say address 0xfff10000-0xfff1ffff is mapped to the PCI I/O address + * of 0x00000000-0x0000ffff. The PCI Host bridge driver may then set up + * a map so that a driver that get PCI address 0x100 can translate that + * into 0xfff10100. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <drvmgr/drvmgr.h> +#include "drvmgr_internal.h" + +int drvmgr_translate_bus( + struct drvmgr_bus *from, + struct drvmgr_bus *to, + int reverse, + void *src_address, + void **dst_address) +{ + struct drvmgr_bus *path[16]; + int upstream, ret, depth, i; + void *dst, *from_adr, *to_adr; + struct drvmgr_map_entry *map; + struct drvmgr_bus *bus; + + dst = src_address; + ret = 0; + + if (from == to) /* no need traslating addresses when on same bus */ + goto out; + + if (from->depth > to->depth) { + /* up-streams */ + upstream = 1; + depth = from->depth - to->depth; + if (depth >= 16) + return -1; /* Does not support such a big depth */ + + /* Intensionally we skip the last bus since its bridge is + * not used in this translation + */ + path[0] = from; + for (i=1; i < depth; i++) + path[i] = path[i-1]->dev->parent; + } else { + /* down-streams */ + upstream = 0; + depth = to->depth - from->depth; + if (depth >= 16) + return -1; /* Does not support such a big depth */ + + /* Intensionally we skip the last bus since its bridge is + * not used in this translation + */ + path[depth-1] = to; + for (i=depth-1; i > 0; i--) + path[i-1] = path[i]->dev->parent; + } + + /* Translate address */ + for (i=0; i < depth && ret == 0; i++) { + bus = path[i]; + + if ((upstream && reverse) || (!upstream && !reverse)) + map = bus->maps_down; + else + map = bus->maps_up; + + if (map == NULL) + continue; /* No translation needed - 1:1 mapping */ + + ret = -1; + + if (map == DRVMGR_TRANSLATE_NO_BRIDGE) + break; /* No bridge interface in this direction */ + + while (map->size != 0) { + if (reverse) { + /* Opposite direction */ + from_adr = map->to_adr; + to_adr = map->from_adr; + } else { + from_adr = map->from_adr; + to_adr = map->to_adr; + } + + if ((dst >= from_adr) && + (dst <= (from_adr + (map->size - 1)))) { + dst = (dst - from_adr) + to_adr; + ret = 0; + break; + } + map++; + } + } + +out: + if (dst_address) + *dst_address = dst; + + return ret; +} + +/* Translate Address, used by drivers when an address need to be + * converted in order to access a remote address or for a remote + * hardware to access (DMA) to access CPU local RAM. + * - from remote address to CPU local + * - from CPU local to remote + */ +int drvmgr_translate( + struct drvmgr_dev *dev, + int cpu_addresses, + int upstream, + void *src_address, + void **dst_address) +{ + struct drvmgr_bus *to, *from; + int rev; + + if (upstream) { + from = dev->parent; + to = drv_mgr.root_dev.bus; + } else { + from = drv_mgr.root_dev.bus; + to = dev->parent; + } + + if (cpu_addresses) + rev = upstream; + else + rev = !upstream; + + return drvmgr_translate_bus(from, to, rev, src_address, dst_address); +} diff --git a/cpukit/libdrvmgr/drvmgr_unregister.c b/cpukit/libdrvmgr/drvmgr_unregister.c new file mode 100644 index 0000000000..e6154ff165 --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_unregister.c @@ -0,0 +1,184 @@ +/* Driver Manager Device Unregister (removal) implementation + * + * COPYRIGHT (c) 2011. + * 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. + * + */ + +#include <stdlib.h> + +#include <drvmgr/drvmgr.h> +#include <drvmgr/drvmgr_list.h> +#include "drvmgr_internal.h" + +/* Unregister all children on a bus. + * + * This function is called from the bus driver, from a "safe" state where + * devices will not be added or removed on this particular bus at this time + */ +int drvmgr_children_unregister(struct drvmgr_bus *bus) +{ + while (bus->children != NULL) { + bus->children->error = drvmgr_dev_unregister(bus->children); + if (bus->children->error != DRVMGR_OK) { + /* An error occured */ + return bus->children->error; + } + } + + return DRVMGR_OK; +} + +/* Unregister a BUS and all it's devices. + * + * It is up to the bus driver to remove all it's devices, either manually + * one by one calling drvmgr_dev_unregister(), or by letting the driver + * manager unregister all children by calling drvmgr_children_unregister(). + */ +int drvmgr_bus_unregister(struct drvmgr_bus *bus) +{ + struct rtems_driver_manager *mgr = &drv_mgr; + struct drvmgr_list *list; + + if (bus->ops->remove == NULL) + return DRVMGR_ENOSYS; + + /* Call Bus driver to clean things up, it must remove all children */ + bus->error = bus->ops->remove(bus); + if (bus->error != DRVMGR_OK) + return bus->error; + /* Check that bus driver has done its job and removed all children */ + if (bus->children != NULL) + return DRVMGR_FAIL; + /* Remove References to bus */ + bus->dev->bus = NULL; + + DRVMGR_LOCK_WRITE(); + + /* Remove bus from bus-list */ + if (bus->state & BUS_STATE_LIST_INACTIVE) + list = &mgr->buses_inactive; + else + list = &mgr->buses[bus->level]; + drvmgr_list_remove(list, bus); + + DRVMGR_UNLOCK(); + + /* All references to this bus has been removed at this point */ + free(bus); + + return DRVMGR_OK; +} + +/* Separate Driver and Device from each other */ +int drvmgr_dev_drv_separate(struct drvmgr_dev *dev) +{ + struct rtems_driver_manager *mgr = &drv_mgr; + struct drvmgr_dev *subdev, **pprev; + int rc; + + /* Remove children if this device exports a bus of devices. All + * children must be removed first as they depend upon the bus + * services this bridge provide. + */ + if (dev->bus) { + rc = drvmgr_bus_unregister(dev->bus); + if (rc != DRVMGR_OK) + return rc; + } + + if (dev->drv == NULL) + return DRVMGR_OK; + + /* Remove device by letting assigned driver take care of hardware + * issues + */ + if (!dev->drv->ops->remove) { + /* No remove function is considered severe when someone + * is trying to remove the device + */ + return DRVMGR_ENOSYS; + } + dev->error = dev->drv->ops->remove(dev); + if (dev->error != DRVMGR_OK) + return DRVMGR_FAIL; + + DRVMGR_LOCK_WRITE(); + + /* Delete device from driver's device list */ + pprev = &dev->drv->dev; + subdev = dev->drv->dev; + while (subdev != dev) { + pprev = &subdev->next_in_drv; + subdev = subdev->next_in_drv; + } + *pprev = subdev->next_in_drv; + dev->drv->dev_cnt--; + + /* Move device to inactive list */ + drvmgr_list_remove(&mgr->devices[dev->level], dev); + dev->level = 0; + dev->state &= ~(DEV_STATE_UNITED|DEV_STATE_INIT_DONE); + dev->state |= DEV_STATE_LIST_INACTIVE; + drvmgr_list_add_tail(&mgr->devices_inactive, dev); + + DRVMGR_UNLOCK(); + + /* Free Device Driver Private memory if allocated previously by + * Driver manager. + */ + if (dev->drv->dev_priv_size && dev->priv) { + free(dev->priv); + dev->priv = NULL; + } + dev->drv = NULL; + + return DRVMGR_OK; +} + +/* Unregister device, + * - let assigned driver handle deletion + * - remove from device list + * - remove from driver list + * - remove from bus list + */ +int drvmgr_dev_unregister(struct drvmgr_dev *dev) +{ + struct rtems_driver_manager *mgr = &drv_mgr; + struct drvmgr_dev *subdev, **pprev; + int err; + + /* Separate device from driver, if the device is united with a driver. + * + * If this device is a bridge all child buses/devices are also removed. + */ + err = drvmgr_dev_drv_separate(dev); + if (err != DRVMGR_OK) + return err; + + DRVMGR_LOCK_WRITE(); + + /* Remove it from inactive list */ + drvmgr_list_remove(&mgr->devices_inactive, dev); + + /* Remove device from parent bus list (no check if dev not in list) */ + pprev = &dev->parent->children; + subdev = dev->parent->children; + while (subdev != dev) { + pprev = &subdev->next_in_bus; + subdev = subdev->next_in_bus; + } + *pprev = subdev->next_in_bus; + dev->parent->dev_cnt--; + + DRVMGR_UNLOCK(); + + /* All references to this device has been removed at this point */ + free(dev); + + return DRVMGR_OK; +} diff --git a/cpukit/libmisc/Makefile.am b/cpukit/libmisc/Makefile.am index 2a7ba78d41..fbb6d096c8 100644 --- a/cpukit/libmisc/Makefile.am +++ b/cpukit/libmisc/Makefile.am @@ -97,7 +97,8 @@ libshell_a_SOURCES = shell/cat_file.c shell/cmds.c shell/internal.h \ shell/hexdump-odsyntax.c shell/hexdump-parse.c shell/hexsyntax.c \ shell/main_time.c shell/main_mknod.c \ shell/main_setenv.c shell/main_getenv.c shell/main_unsetenv.c \ - shell/main_mkrfs.c shell/main_debugrfs.c + shell/main_mkrfs.c shell/main_debugrfs.c \ + shell/main_drvmgr.c shell/main_pci.c if LIBNETWORKING libshell_a_SOURCES += \ diff --git a/cpukit/libmisc/shell/main_drvmgr.c b/cpukit/libmisc/shell/main_drvmgr.c new file mode 100644 index 0000000000..1a2563af7f --- /dev/null +++ b/cpukit/libmisc/shell/main_drvmgr.c @@ -0,0 +1,422 @@ +/* + * DRVMGR Command Implmentation + * + * 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. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <drvmgr/drvmgr.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include "internal.h" + +static void usage(void); + +static void *get_obj_adr(char *arg) +{ + unsigned long obj_adr; + + obj_adr = strtoul(arg, NULL, 16); + if (obj_adr == ULONG_MAX || obj_adr == 0) { + puts(" Not a valid ID"); + return NULL; + } + + return (void *)obj_adr; +} + +/* General info, root bus, number of devices etc. */ +void show_drvmgr_info(void) +{ + drvmgr_summary(); + drvmgr_print_devs(PRINT_DEVS_ALL); +} + +int shell_drvmgr_topo(int argc, char *argv[]) +{ + drvmgr_print_topo(); + return 0; +} + +int shell_drvmgr_short(int argc, char *argv[]) +{ + void *obj; + + if (argc < 2) + return -1; + if (argc < 3) { + /* All Devices */ + drvmgr_info_drvs(0); + drvmgr_info_buses(0); + drvmgr_info_devs(OPTION_DEV_GENINFO); + return 0; + } + + /* Get ID from string */ + obj = get_obj_adr(argv[2]); + if (!obj) + return -3; + + drvmgr_info(obj, OPTION_DEV_GENINFO); + + return 0; +} + +int shell_drvmgr_info(int argc, char *argv[]) +{ + void *obj; + + if (argc < 2) + return -1; + if (argc < 3) { + /* All Drivers, Buses and Devices */ + drvmgr_info_drvs(OPTION_INFO_ALL); + drvmgr_info_buses(OPTION_INFO_ALL); + drvmgr_info_devs(OPTION_INFO_ALL); + return 0; + } + + /* Get ID from string */ + obj = get_obj_adr(argv[2]); + if (!obj) + return -3; + + drvmgr_info(obj, OPTION_INFO_ALL); + + return 0; +} + +int shell_drvmgr_remove(int argc, char *argv[]) +{ + puts(" Not implemented"); + return 0; +} + +int shell_drvmgr_parent(int argc, char *argv[]) +{ + void *obj; + int obj_type; + struct drvmgr_dev *dev; + struct drvmgr_bus *bus; + + /* Get ID from string */ + if (argc < 3) + return -2; + obj = get_obj_adr(argv[2]); + if (!obj) + return -3; + + obj_type = *(int *)obj; + if (obj_type == DRVMGR_OBJ_BUS) { + bus = obj; + if (!bus->dev) { + puts(" bus has no bridge device"); + } else if(!bus->dev->parent) { + puts(" bridge device has no parent"); + } else { + dev = bus->dev; + printf(" BUSID=%p\n", dev->parent); + } + } else if (obj_type == DRVMGR_OBJ_DEV) { + dev = obj; + if (!dev->parent) { + puts(" device has no parent bus"); + } else { + printf(" BUSID=%p\n", dev->parent); + } + } else { + puts(" ID is not a device or bus"); + return 1; + } + + return 0; +} + +void shell_drvmgr_print_key_array(struct drvmgr_key *keys) +{ + struct drvmgr_key *key; + static char *type_strs[4] = {"UNKNOWN","INTEGER","STRING ","POINTER"}; + int type; + union drvmgr_key_value *val; + + if (keys == NULL) { + printf(" DEV HAS NO KEYS\n"); + return; + } + + key = &keys[0]; + while (key->key_type != KEY_TYPE_NONE) { + if (key->key_type > KEY_TYPE_POINTER) + type = 0; + else + type = key->key_type; + printf(" NAME=%-14s TYPE=%s VALUE=", key->key_name, type_strs[type]); + val = &key->key_value; + switch (type) { + default: + case 0: + case KEY_TYPE_INT: + printf("0x%x (%d)\n", val->i, val->i); + break; + case KEY_TYPE_STRING: + printf("%s\n", val->str); + break; + case KEY_TYPE_POINTER: + printf("%p\n", val->ptr); + break; + } + key++; + } +} + +void shell_drvmgr_print_res_array(struct drvmgr_drv_res *resources) +{ + struct drvmgr_drv_res *res = &resources[0]; + struct drvmgr_drv *drv; + char *drv_name; + + while (res->drv_id) { + /* Find Driver in order to print name of driver */ + drv = drvmgr_drv_by_id(res->drv_id); + if (drv && drv->name) + drv_name = drv->name; + else + drv_name = "UNKNOWN"; + printf(" RESOURCES FOR DEVICE[%02d] DRIVER[0x%llx (%s)]\n", + res->minor_bus, res->drv_id, drv_name); + shell_drvmgr_print_key_array(res->keys); + res++; + } +} + +int shell_drvmgr_res(int argc, char *argv[]) +{ + void *obj; + int obj_type; + struct drvmgr_dev *dev; + struct drvmgr_bus *bus; + struct drvmgr_key *keys; + struct drvmgr_bus_res *lst; + int i; + + /* Get ID from string */ + if (argc < 3) + return -2; + obj = get_obj_adr(argv[2]); + if (!obj) + return -3; + + obj_type = *(int *)obj; + if (obj_type == DRVMGR_OBJ_BUS) { + bus = obj; + lst = bus->reslist; + if (lst == NULL) { + puts(" BUS does not have resources\n"); + return 0; + } + i = 0; + while (lst) { + printf(" -- RESOURCES ARRAY %d --\n", i); + shell_drvmgr_print_res_array(lst->resource); + puts(""); + i++; + lst = lst->next; + } + } else if (obj_type == DRVMGR_OBJ_DEV) { + dev = obj; + if (dev->drv == NULL) { + puts(" DEVICE has no driver ==> resources not available\n"); + return 0; + } + drvmgr_keys_get(dev, &keys); + if (keys == NULL) { + puts(" DEVICE does not have resources\n"); + return 0; + } + shell_drvmgr_print_key_array(keys); + } else { + puts(" ID is not a device or bus"); + return 1; + } + + return 0; +} + +int shell_drvmgr_buses(int argc, char *argv[]) +{ + drvmgr_info_buses(OPTION_INFO_ALL); + return 0; +} + +int shell_drvmgr_devs(int argc, char *argv[]) +{ + drvmgr_info_devs(OPTION_INFO_ALL); + return 0; +} + +int shell_drvmgr_drvs(int argc, char *argv[]) +{ + drvmgr_info_drvs(OPTION_INFO_ALL); + return 0; +} + +int shell_drvmgr_mem(int argc, char *argv[]) +{ + drvmgr_print_mem(); + return 0; +} + +int shell_drvmgr_translate(int argc, char *argv[]) +{ + int rc, cpu, up, obj_type; + void *obj, *dst; + unsigned long src, tmp; + + if (argc != 5) + return -1; + + obj = get_obj_adr(argv[2]); + if (!obj) + return -3; + + obj_type = *(int *)obj; + if (obj_type != DRVMGR_OBJ_DEV) { + puts(" ID is not a device\n"); + return 0; + } + + tmp = strtoul(argv[3], NULL, 0); + if (tmp > 3) { + puts(" Not a valid option OPT, only [0..3] is valid"); + return 0; + } + cpu = tmp & 1; + up = (tmp >> 1) & 1; + + src = strtoul(argv[4], NULL, 0); + if (src == ULONG_MAX && errno == ERANGE) { + puts(" Not a valid source address"); + return 0; + } + + rc = drvmgr_translate((struct drvmgr_dev *)obj, cpu, up, (void *)src, &dst); + if (rc != 0) + printf(" Address %p could not be translated\n", (void *)src); + else + printf(" %p => %p\n", (void *)src, dst); + + return 0; +} + +const char drvmgr_usage_str[] = + " usage:\n" + " drvmgr buses List bus specfic information on all buses\n" + " drvmgr devs List general and driver specfic information\n" + " about all devices\n" + " drvmgr drvs List driver specfic information on all drivers\n" + " drvmgr info [ID] List general and driver specfic information\n" + " about all devices or one device, bus or driver\n" + " drvmgr mem Dynamically memory usage\n" + " drvmgr parent ID Short info about parent bus of a device\n" + " drvmgr remove ID Remove a device or a bus\n" + " drvmgr res ID List Resources of a device or bus\n" + " drvmgr short [ID] Short info about all devices/buses or one\n" + " device/bus\n" + " drvmgr topo Show bus topology with all devices\n" + " drvmgr tr ID OPT ADR Translate hw(0)/cpu(1) (OPT bit0) address ADR\n" + " down(0)/up(1) streams (OPT bit1) for device\n" + " drvmgr --help\n"; + +static void usage(void) +{ + puts(drvmgr_usage_str); +} + +int shell_drvmgr_usage(int argc, char *argv[]) +{ + usage(); + return 0; +} + +struct shell_drvmgr_modifier { + char *name; + int (*func)(int argc, char *argv[]); +}; + +#define MODIFIER_NUM 12 +static struct shell_drvmgr_modifier shell_drvmgr_modifiers[MODIFIER_NUM] = +{ + {"buses", shell_drvmgr_buses}, + {"devs", shell_drvmgr_devs}, + {"drvs", shell_drvmgr_drvs}, + {"info", shell_drvmgr_info}, + {"mem", shell_drvmgr_mem}, + {"parent", shell_drvmgr_parent}, + {"remove", shell_drvmgr_remove}, + {"res", shell_drvmgr_res}, + {"short", shell_drvmgr_short}, + {"topo", shell_drvmgr_topo}, + {"tr", shell_drvmgr_translate}, + {"--help", shell_drvmgr_usage}, +}; + +struct shell_drvmgr_modifier *shell_drvmgr_find_modifier(char *name) +{ + struct shell_drvmgr_modifier *mod; + int i; + + if (name == NULL) + return NULL; + + for (i=0, mod=&shell_drvmgr_modifiers[0]; i<MODIFIER_NUM; i++, mod++) { + if (strcmp(name, mod->name) == 0) + return mod; + } + + return NULL; +} + +int rtems_shell_main_drvmgr( + int argc, + char *argv[] +) +{ + struct shell_drvmgr_modifier *mod; + int rc; + + if (argc < 2) { + show_drvmgr_info(); + rc = 0; + } else if ((mod=shell_drvmgr_find_modifier(argv[1])) != NULL) { + rc = mod->func(argc, argv); + } else { + rc = -1; + } + + if (rc < 0) { + printf(" invalid argument\n"); + usage(); + } + + return rc; +} + +rtems_shell_cmd_t rtems_shell_DRVMGR_Command = { + "drvmgr", /* name */ + drvmgr_usage_str, /* usage */ + "system", /* topic */ + rtems_shell_main_drvmgr, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_pci.c b/cpukit/libmisc/shell/main_pci.c new file mode 100644 index 0000000000..56190c05eb --- /dev/null +++ b/cpukit/libmisc/shell/main_pci.c @@ -0,0 +1,493 @@ +/* + * LIBPCI Command Implmentation + * + * 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. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <pci.h> +#include <pci/cfg.h> +#include <pci/access.h> +#include <rtems/endian.h> +#include <bsp.h> /* For PCI endianness config */ + +#include <rtems.h> +#include <rtems/shell.h> +#include "internal.h" + +static void usage(void); + +struct shell_pci_modifier { + char *name; + int (*func)(int argc, char *argv[], struct shell_pci_modifier *mod); + int data; +}; + +static unsigned long get_pciid_from_string(char *arg) +{ + unsigned long pciid; + char *bus_str, *dev_str, *fun_str; + unsigned long busno, devno, funno; + + dev_str = strstr(arg, ":"); + if (dev_str == NULL) { + /* PCIID */ + pciid = strtoul(arg, NULL, 16); + if (pciid == ULONG_MAX) + return ~0; + } else { + /* bus:dev:fun */ + bus_str = arg; + *dev_str = '\0'; + dev_str++; + fun_str = strstr(dev_str, ":"); + if (fun_str == NULL) + return ~0; + *fun_str = '\0'; + fun_str++; + + busno = strtoul(bus_str, NULL, 16); + if (busno == ULONG_MAX) + return ~0; + devno = strtoul(dev_str, NULL, 16); + if (devno == ULONG_MAX) + return ~0; + funno = strtoul(fun_str, NULL, 16); + if (funno == ULONG_MAX) + return ~0; + pciid = PCI_DEV(busno, devno, funno); + } + + return pciid; +} + +/* Print current PCI configuration that can be used in a static/peripheral PCI + * configuration setup. + */ +int shell_pci_pcfg(int argc, char *argv[], struct shell_pci_modifier *mod) +{ + if (argc != 2) + return -1; + + pci_cfg_print(); + + return 0; +} + +int shell_pci_ls(int argc, char *argv[], struct shell_pci_modifier *mod) +{ + unsigned long pciid; + + if (argc == 2) { + /* List all devices */ + pci_print(); + } else if (argc > 3) { + return -1; + } else { + pciid = get_pciid_from_string(argv[2]); + if ((pciid & 0xffff0000) != 0) + return -1; + + pci_print_dev((pci_dev_t)pciid); + } + return 0; +} + +int shell_pci_rX(unsigned long pciid, int offset, int size) +{ + uint8_t data8; + uint16_t data16; + uint32_t data32; + int result; + + switch(size) { + case 1: + result = pci_cfg_r8(pciid, offset, &data8); + if (result == PCISTS_OK) + printf(" r08[0x%02x]: 0x%02x DEC=%d\n", offset, data8, data8); + break; + + case 2: + result = pci_cfg_r16(pciid, offset, &data16); + if (result == PCISTS_OK) + printf(" r16[0x%02x]: 0x%04x DEC=%d\n", offset, data16, data16); + break; + + case 4: + result = pci_cfg_r32(pciid, offset, &data32); + if (result == PCISTS_OK) + printf(" r32[0x%02x]: 0x%08lx DEC=%lu\n", offset, data32, data32); + break; + + default: + return PCISTS_EINVAL; + } + return result; +} + +int shell_pci_wX(unsigned long pciid, int offset, uint32_t data, int size) +{ + uint8_t data8; + uint16_t data16; + int result; + + switch(size) { + case 1: + if (data > 0xff) + return PCISTS_EINVAL; + data8 = data & 0xff; + result = pci_cfg_w8(pciid, offset, data8); + if (result == PCISTS_OK) + printf(" w08[0x%02x]: 0x%02x DEC=%d\n", offset, data8, data8); + break; + + case 2: + if (data > 0xffff) + return PCISTS_EINVAL; + data16 = data & 0xffff; + result = pci_cfg_w16(pciid, offset, data16); + if (result == PCISTS_OK) + printf(" w16[0x%02x]: 0x%04x DEC=%d\n", offset, data16, data16); + break; + + case 4: + result = pci_cfg_w32(pciid, offset, data); + if (result == PCISTS_OK) + printf(" w32[0x%02x]: 0x%08lx DEC=%lu\n", offset, data, data); + break; + + default: + return PCISTS_EINVAL; + } + return result; +} + +int shell_pci_read(int argc, char *argv[], struct shell_pci_modifier *mod) +{ + unsigned long pciid, offset; + int result, size; + + if (argc != 4) + return -1; + + pciid = get_pciid_from_string(argv[2]); + if ((pciid & 0xffff0000) != 0) + return -1; + + offset = strtoul(argv[3], NULL, 0); + if (offset > 256) + return -1; + + size = mod->data; + result = shell_pci_rX(pciid, offset, size); + switch (result) { + default: + case PCISTS_OK: + break; + + case PCISTS_ERR: + case PCISTS_EINVAL: + puts(" Bad input argument\n"); + return PCISTS_EINVAL; + + case PCISTS_MSTABRT: + puts(" Master abort while reading configuration space"); + return PCISTS_MSTABRT; + } + + return 0; +} + +int shell_pci_write(int argc, char *argv[], struct shell_pci_modifier *mod) +{ + unsigned long pciid, offset; + int result, size; + uint32_t data; + + if (argc != 5) + return -1; + + pciid = get_pciid_from_string(argv[2]); + if ((pciid & 0xffff0000) != 0) + return -1; + + offset = strtoul(argv[3], NULL, 0); + if (offset > 256) + return -1; + + data = strtoul(argv[4], NULL, 0); + if (data == ULONG_MAX && errno == ERANGE) + return -1; + + size = mod->data; + result = shell_pci_wX(pciid, offset, data, size); + switch (result) { + default: + case PCISTS_OK: + break; + + case PCISTS_ERR: + case PCISTS_EINVAL: + puts(" Bad input argument\n"); + return PCISTS_EINVAL; + + case PCISTS_MSTABRT: + puts(" Master abort while reading configuration space"); + return PCISTS_MSTABRT; + } + return 0; +} + +int shell_pci_pciid(int argc, char *argv[], struct shell_pci_modifier *mod) +{ + unsigned long pciid; + + if (argc != 3) + return -1; + + pciid = get_pciid_from_string(argv[2]); + if ((pciid & 0xffff0000) != 0) + return -1; + + printf(" PCIID: 0x%lx [%lx:%lx:%lx]\n", pciid, PCI_DEV_EXPAND(pciid)); + return 0; +} + +int shell_pci_getdev(int argc, char *argv[], struct shell_pci_modifier *mod) +{ + unsigned long pciid; + struct pci_dev *dev; + + if (argc != 3) + return -1; + + pciid = get_pciid_from_string(argv[2]); + if ((pciid & 0xffff0000) != 0) + return -1; + + if (pci_get_dev(pciid, &dev)) { + printf(" GETDEV: no device on [%lx:%lx:%lx]\n", PCI_DEV_EXPAND(pciid)); + return 0; + } + + printf(" PCI RAM DEVICE: %p\n", dev); + return 0; +} + +int shell_pci_infodev(int argc, char *argv[], struct shell_pci_modifier *mod) +{ + unsigned long arg; + struct pci_dev *dev; + struct pci_bus *bus; + struct pci_res *res; + char *type_str, *str1, *res_types[3] = {" IO16", "MEMIO", "MEM"}; + int i, res_avail; + + if (argc != 3) + return -1; + + arg = strtoul(argv[2], NULL, 0); + if (arg == ULONG_MAX && errno == ERANGE) + return -1; + + dev = (struct pci_dev *)arg; + if (!dev) { + printf(" INFODEV: invalid device\n"); + return 0; + } + + if (dev->flags & PCI_DEV_BRIDGE) { + type_str = "PCI-to-PCI BRIDGE"; + if (!dev->bus) + type_str = "PCI HOST BRIDGE"; + } else + type_str = "PCI DEVICE"; + printf(" %s at [%x:%x:%x]\n", type_str, PCI_DEV_EXPAND(dev->busdevfun)); + + bus = (struct pci_bus *)dev; + if (bus) { + printf(" PRIMARY: BUS 0x%x\n", bus->pri); + printf(" SECONDARY: BUS 0x%x\n", bus->num); + printf(" SUB ORDINATE: BUS 0x%x\n", bus->sord); + } + + printf(" PCIID: 0x%04x\n", dev->busdevfun); + bus = dev->bus; + if (!bus) { + printf(" AT BUS: 0x%x via Host Bridge\n", bus->num); + } else { + printf(" AT BUS: 0x%x via Bridge at [%x:%x:%x]\n", bus->num, + PCI_DEV_EXPAND(bus->dev.busdevfun)); + } + printf(" VENDOR: 0x%04x\n", dev->vendor); + printf(" DEVICE: 0x%04x\n", dev->device); + printf(" SUB VENDOR: 0x%04x\n", dev->subvendor); + printf(" SUB DEVICE: 0x%04x\n", dev->subdevice); + printf(" CLASS: 0x%06lx\n", dev->classrev >> 8); + printf(" REVISION: 0x%02lx\n", dev->classrev & 0xff); + printf(" IRQ: %d\n", dev->sysirq); + + res_avail = 0; + for (i = 0; i < DEV_RES_CNT; i++) { + res = &dev->resources[i]; + + if ((res->flags & PCI_RES_TYPE_MASK) == 0) + continue; + + str1 = res_types[(res->flags & PCI_RES_TYPE_MASK) - 1]; + if (res->flags & PCI_RES_IO32) + str1 = " IO32"; + + if (res_avail == 0) { + puts(" RESOURCES:"); + res_avail = 1; + } + + if (res->flags & PCI_RES_FAIL) { + printf(" %s[%d]: NOT ASSIGNED", str1, i); + continue; + } + + printf(" %s[%d]: %08lx-%08lx\n", str1, i, res->start, res->end - 1); + } + + if (res_avail == 0) + puts(" NO CONFIGURED RESOURCES AVAILABLE"); + + return 0; +} + +int pci_summary(void) +{ + char *str; + char *cfglib_strs[5] = {"NONE", "AUTO", "STATIC", "READ", "PERIPHERAL"}; + + if (pci_system_type == PCI_SYSTEM_HOST) + str = "HOST"; + else if (pci_system_type == PCI_SYSTEM_PERIPHERAL) + str = "PERIPHERAL"; + else + str = "UNKNOWN / UNINITIALIZED"; + printf(" SYSTEM: %s\n", str); + + if (pci_config_lib_type > PCI_CONFIG_LIB_PERIPHERAL) { + puts(" Bad configuration library"); + return 1; + } + printf(" CFG LIBRARY: %s\n", cfglib_strs[pci_config_lib_type]); + printf(" NO. PCI BUSES: %d buses\n", pci_bus_count()); + printf(" PCI ENDIAN: %s\n", pci_endian ? "Big" : "Little"); +#if (CPU_LITTLE_ENDIAN == TRUE) + puts(" MACHINE ENDIAN: Little"); +#else + puts(" MACHINE ENDIAN: Big"); +#endif + + return 0; +} + +const char pci_usage_str[] = + " usage:\n" + " pci ls [bus:dev:fun|PCIID] List one or all devices\n" + " pci r{8|16|32} bus:dev:fun OFS Configuration space read\n" + " pci r{8|16|32} PCIID OFS Configuration space read\n" + " access by PCIID\n" + " pci w{8|16|32} bus:dev:fun OFS D Configuration space write\n" + " pci w{8|16|32} PCIID OFS D Configuration space write\n" + " access by PCIID\n" + " pci pciid bus:dev:fun Print PCIID for bus:dev:fun\n" + " pci pciid PCIID Print bus:dev:fun for PCIID\n" + " pci pcfg Print current PCI config for\n" + " static configuration library\n" + " pci getdev {PCIID|bus:dev:fun} Get PCI Device from RAM tree\n" + " pci infodev DEV_ADR Info about a PCI RAM Device\n" + " pci --help\n"; + +static void usage(void) +{ + puts(pci_usage_str); +} + +int shell_pci_usage(int argc, char *argv[], struct shell_pci_modifier *mod) +{ + usage(); + return 0; +} + +#define MODIFIER_NUM 12 +static struct shell_pci_modifier shell_pci_modifiers[MODIFIER_NUM] = +{ + {"ls", shell_pci_ls, 0}, + {"r8", shell_pci_read, 1}, + {"r16", shell_pci_read, 2}, + {"r32", shell_pci_read, 4}, + {"w8", shell_pci_write, 1}, + {"w16", shell_pci_write, 2}, + {"w32", shell_pci_write, 4}, + {"pciid", shell_pci_pciid, 0}, + {"pcfg", shell_pci_pcfg, 0}, + {"getdev", shell_pci_getdev, 0}, + {"infodev", shell_pci_infodev, 0}, + {"--help", shell_pci_usage}, +}; + +struct shell_pci_modifier *shell_pci_find_modifier(char *name) +{ + struct shell_pci_modifier *mod; + int i; + + if (name == NULL) + return NULL; + + for (i=0, mod=&shell_pci_modifiers[0]; i<MODIFIER_NUM; i++, mod++) { + if (strcmp(name, mod->name) == 0) + return mod; + } + + return NULL; +} + +int rtems_shell_main_pci( + int argc, + char *argv[] +) +{ + struct shell_pci_modifier *mod; + int rc; + + if (argc < 2) { + /* without arguments */ + pci_summary(); + rc = 0; + } else if ((mod=shell_pci_find_modifier(argv[1])) != NULL) { + rc = mod->func(argc, argv, mod); + } else { + rc = -1; + } + + if (rc < 0) { + printf(" invalid argument\n"); + usage(); + } + + return rc; +} + +rtems_shell_cmd_t rtems_shell_PCI_Command = { + "pci", /* name */ + pci_usage_str, /* usage */ + "system", /* topic */ + rtems_shell_main_pci, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/shellconfig.h b/cpukit/libmisc/shell/shellconfig.h index cfc475095e..d9319f2396 100644 --- a/cpukit/libmisc/shell/shellconfig.h +++ b/cpukit/libmisc/shell/shellconfig.h @@ -80,6 +80,12 @@ extern rtems_shell_cmd_t rtems_shell_MALLOC_INFO_Command; extern rtems_shell_cmd_t rtems_shell_NETSTATS_Command; #endif +/* + * Extern for System commands + */ +extern rtems_shell_cmd_t rtems_shell_DRVMGR_Command; +extern rtems_shell_cmd_t rtems_shell_PCI_Command; + extern rtems_shell_cmd_t *rtems_shell_Initial_commands[]; /* @@ -425,6 +431,25 @@ extern rtems_shell_alias_t *rtems_shell_Initial_aliases[]; #endif /* + * System related commands + */ + #if defined(RTEMS_DRVMGR_STARTUP) || defined(CONFIGURE_SHELL_COMMAND_DRVMGR) + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_DRVMGR)) || \ + defined(CONFIGURE_SHELL_COMMAND_DRVMGR) + &rtems_shell_DRVMGR_Command, + #endif + #endif + + #if defined(RTEMS_PCI_CONFIG_LIB) + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_PCI)) || \ + defined(CONFIGURE_SHELL_COMMAND_PCI) + &rtems_shell_PCI_Command, + #endif + #endif + + /* * User defined shell commands */ #if defined(CONFIGURE_SHELL_USER_COMMANDS) diff --git a/cpukit/libpci/CHANGES b/cpukit/libpci/CHANGES new file mode 100644 index 0000000000..46064f0faa --- /dev/null +++ b/cpukit/libpci/CHANGES @@ -0,0 +1,46 @@ + 2011-03-03, Daniel Hellstrom <daniel@gaisler.com> + Added support for ROM BARs at devices and PCI-PCI bridges. + + 2011-02-11, Daniel Hellstrom <daniel@gaisler.com> + Split Library into different parts, this enables PCI initialization to be done + outside of the PCI Host driver and smaller systems that don't want + Configuration Space to be setup. + - Access Library (Configuration, Memory and I/O Space read/write routines) + - Configuration Libarary + A. Auto Config + B. Static Config (not implemented yet) + - Interrupt Library (shared interrupt support rely on BSP) + This file created. + + 2011-02-11, Daniel Hellstrom <daniel@gaisler.com> + Changed library to use 16-bit identifiers (pci_dev_t), instead to 3 integers + (BUS,SLOT,FUNC), this reduces the footprint. + + 2010-09-29, Kristoffer Glembo <kristoffer@gaisler.com> + Fixed I/O BAR size calculation of bridges. Reading/Writing to 0x1C instead of + faulty 0x1E. + + 2010-06-10, Daniel Hellstrom <daniel@gaisler.com> + Fix in pci_res_insert(), where the above mentioned optimization failed due to + bad compare statement. Optimization only affects systems with multiple PCI + buses. + + 2010-04-19, Daniel Hellstrom <daniel@gaisler.com> + Optimized resource allocation when bridges are present: the resources lists + are sorted by boundary instead of size and a reorder aligorithm introduced + that move resources into unused areas if possible. + + 2010-04-19, Daniel Hellstrom <daniel@gaisler.com> + Fixed autoconf issue when bridges are present + + 2010-02-03, Daniel Hellstrom <daniel@gaisler.com> + Fixed initialization problem when first device is a bridge. + + 2010-02-03, Daniel Hellstrom <daniel@gaisler.com> + PCI Library rewritten from scratch. Support multiple buses/bridges, print + current PCI configuration space setup, BAR assigment sort implementation + speeded up drastically (bootup time noticable shorter), interrupt assignment + implemented, PCI Host driver extracted from library, support for I/O areas. + + +.... not updated ... lots of more changes diff --git a/cpukit/libpci/Makefile b/cpukit/libpci/Makefile new file mode 100644 index 0000000000..f304f893d6 --- /dev/null +++ b/cpukit/libpci/Makefile @@ -0,0 +1,31 @@ +### Debug Makefile for just building LIBPCI + +CC=/opt/rtems-4.10/bin/sparc-rtems-gcc +CFLAGS=-Wall -I. -I/opt/rtems-4.10/sparc-rtems/leon3/lib/include -g3 -O3 -c + +.PHONY: drvmgr pci + +drvmgr: + $(CC) $(CFLAGS) pci_bus.c + +pci: + $(CC) $(CFLAGS) pci_access.c + $(CC) $(CFLAGS) pci_access_func.c + $(CC) $(CFLAGS) pci_access_io.c + $(CC) $(CFLAGS) pci_access_mem_be.c + $(CC) $(CFLAGS) pci_access_mem.c + $(CC) $(CFLAGS) pci_access_mem_le.c + $(CC) $(CFLAGS) pci_cfg_auto.c + $(CC) $(CFLAGS) pci_cfg.c + $(CC) $(CFLAGS) pci_cfg_peripheral.c + $(CC) $(CFLAGS) pci_cfg_print_code.c + $(CC) $(CFLAGS) pci_cfg_read.c + $(CC) $(CFLAGS) pci_cfg_static.c + $(CC) $(CFLAGS) pci_find.c + $(CC) $(CFLAGS) pci_find_dev.c + $(CC) $(CFLAGS) pci_for_each.c + $(CC) $(CFLAGS) pci_for_each_child.c + $(CC) $(CFLAGS) pci_for_each_dev.c + $(CC) $(CFLAGS) pci_get_dev.c + $(CC) $(CFLAGS) pci_irq.c + $(CC) $(CFLAGS) pci_print.c diff --git a/cpukit/libpci/Makefile.am b/cpukit/libpci/Makefile.am new file mode 100644 index 0000000000..aa08c32c7d --- /dev/null +++ b/cpukit/libpci/Makefile.am @@ -0,0 +1,47 @@ +## +## $Id: Makefile.am +## + +include $(top_srcdir)/automake/compile.am +include $(top_srcdir)/automake/multilib.am + +EXTRA_DIST= + +## PCI Library +include_HEADERS = pci.h +include_pcidir = $(includedir)/pci +include_pci_HEADERS = pci/access.h pci/cfg.h \ + pci/cfg_auto.h pci/cfg_static.h \ + pci/cfg_peripheral.h pci/cfg_read.h \ + pci/ids.h pci/ids_extra.h pci/irq.h + +noinst_LIBRARIES = libpci.a + +libpci_a_SOURCES = pci_access.c +libpci_a_SOURCES += pci_access_func.c +libpci_a_SOURCES += pci_access_io.c +libpci_a_SOURCES += pci_access_mem.c +libpci_a_SOURCES += pci_access_mem_be.c +libpci_a_SOURCES += pci_access_mem_le.c +libpci_a_SOURCES += pci_cfg.c +libpci_a_SOURCES += pci_cfg_auto.c +libpci_a_SOURCES += pci_cfg_print_code.c +libpci_a_SOURCES += pci_cfg_read.c +libpci_a_SOURCES += pci_cfg_static.c +libpci_a_SOURCES += pci_cfg_peripheral.c +libpci_a_SOURCES += pci_find.c +libpci_a_SOURCES += pci_find_dev.c +libpci_a_SOURCES += pci_for_each.c +libpci_a_SOURCES += pci_for_each_dev.c +libpci_a_SOURCES += pci_for_each_child.c +libpci_a_SOURCES += pci_get_dev.c +libpci_a_SOURCES += pci_irq.c +libpci_a_SOURCES += pci_print.c + +# Driver manager PCI bus +libpci_a_SOURCES += pci_bus.c +include_drvmgrdir = $(includedir)/drvmgr +include_drvmgr_HEADERS = pci_bus.h + +include $(srcdir)/preinstall.am +include $(top_srcdir)/automake/local.am diff --git a/cpukit/libpci/README b/cpukit/libpci/README new file mode 100644 index 0000000000..334f3a9271 --- /dev/null +++ b/cpukit/libpci/README @@ -0,0 +1,4 @@ +PCI Library + +LIBPCI is documented in the ../../doc directory, see ../../doc/README how +to build documentation. diff --git a/cpukit/libpci/drvmgr/drvmgr.h b/cpukit/libpci/drvmgr/drvmgr.h new file mode 120000 index 0000000000..ed6d0e7378 --- /dev/null +++ b/cpukit/libpci/drvmgr/drvmgr.h @@ -0,0 +1 @@ +../../libdrvmgr/drvmgr.h
\ No newline at end of file diff --git a/cpukit/libpci/drvmgr/drvmgr_confdefs.h b/cpukit/libpci/drvmgr/drvmgr_confdefs.h new file mode 120000 index 0000000000..2bbeea7c62 --- /dev/null +++ b/cpukit/libpci/drvmgr/drvmgr_confdefs.h @@ -0,0 +1 @@ +../../libdrvmgr/drvmgr_confdefs.h
\ No newline at end of file diff --git a/cpukit/libpci/drvmgr/drvmgr_list.h b/cpukit/libpci/drvmgr/drvmgr_list.h new file mode 120000 index 0000000000..a5268c86d4 --- /dev/null +++ b/cpukit/libpci/drvmgr/drvmgr_list.h @@ -0,0 +1 @@ +../../libdrvmgr/drvmgr_list.h
\ No newline at end of file diff --git a/cpukit/libpci/drvmgr/pci_bus.h b/cpukit/libpci/drvmgr/pci_bus.h new file mode 120000 index 0000000000..6fe4e56579 --- /dev/null +++ b/cpukit/libpci/drvmgr/pci_bus.h @@ -0,0 +1 @@ +../pci_bus.h
\ No newline at end of file diff --git a/cpukit/libpci/pci.h b/cpukit/libpci/pci.h new file mode 100644 index 0000000000..ad656f3e08 --- /dev/null +++ b/cpukit/libpci/pci.h @@ -0,0 +1,374 @@ +/* + * + * PCI defines and function prototypes + * Copyright 1994, Drew Eckhardt + * Copyright 1997, 1998 Martin Mares <mj@atrey.karlin.mff.cuni.cz> + * Copyright 2009-2011, Aeroflex Gaisler + * + * For more information, please consult the following manuals (look at + * http://www.pcisig.com/ for how to get them): + * + * PCI BIOS Specification + * PCI Local Bus Specification + * PCI to PCI Bridge Specification + * PCI System Design Guide + * + * pci.h,v 1.2.4.2 2004/11/10 22:15:01 joel Exp + */ + +#ifndef __PCI_H__ +#define __PCI_H__ + +#include <pci/ids.h> + +/* + * Under PCI, each device has 256 bytes of configuration address space, + * of which the first 64 bytes are standardized as follows: + */ +#define PCI_VENDOR_ID 0x00 /* 16 bits */ +#define PCI_DEVICE_ID 0x02 /* 16 bits */ +#define PCI_COMMAND 0x04 /* 16 bits */ +#define PCI_COMMAND_IO 0x1 /* Enable response in I/O space */ +#define PCI_COMMAND_MEMORY 0x2 /* Enable response in Memory space */ +#define PCI_COMMAND_MASTER 0x4 /* Enable bus mastering */ +#define PCI_COMMAND_SPECIAL 0x8 /* Enable response to special cycles */ +#define PCI_COMMAND_INVALIDATE 0x10 /* Use memory write and invalidate */ +#define PCI_COMMAND_VGA_PALETTE 0x20 /* Enable palette snooping */ +#define PCI_COMMAND_PARITY 0x40 /* Enable parity checking */ +#define PCI_COMMAND_WAIT 0x80 /* Enable address/data stepping */ +#define PCI_COMMAND_SERR 0x100 /* Enable SERR */ +#define PCI_COMMAND_FAST_BACK 0x200 /* Enable back-to-back writes */ + +#define PCI_STATUS 0x06 /* 16 bits */ +#define PCI_STATUS_66MHZ 0x20 /* Support 66 Mhz PCI 2.1 bus */ +#define PCI_STATUS_UDF 0x40 /* Support User Definable Features */ + +#define PCI_STATUS_FAST_BACK 0x80 /* Accept fast-back to back */ +#define PCI_STATUS_PARITY 0x100 /* Detected parity error */ +#define PCI_STATUS_DEVSEL_MASK 0x600 /* DEVSEL timing */ +#define PCI_STATUS_DEVSEL_FAST 0x000 +#define PCI_STATUS_DEVSEL_MEDIUM 0x200 +#define PCI_STATUS_DEVSEL_SLOW 0x400 +#define PCI_STATUS_SIG_TARGET_ABORT 0x800 /* Set on target abort */ +#define PCI_STATUS_REC_TARGET_ABORT 0x1000 /* Master ack of " */ +#define PCI_STATUS_REC_MASTER_ABORT 0x2000 /* Set on master abort */ +#define PCI_STATUS_SIG_SYSTEM_ERROR 0x4000 /* Set when we drive SERR */ +#define PCI_STATUS_DETECTED_PARITY 0x8000 /* Set on parity error */ + +#define PCI_CLASS_REVISION 0x08 /* High 24 bits are class, low 8 + revision */ +#define PCI_REVISION_ID 0x08 /* Revision ID */ +#define PCI_CLASS_PROG 0x09 /* Reg. Level Programming Interface */ +#define PCI_CLASS_DEVICE 0x0a /* Device class */ + +#define PCI_CACHE_LINE_SIZE 0x0c /* 8 bits */ +#define PCI_LATENCY_TIMER 0x0d /* 8 bits */ +#define PCI_HEADER_TYPE 0x0e /* 8 bits */ +#define PCI_HEADER_TYPE_NORMAL 0 +#define PCI_HEADER_TYPE_BRIDGE 1 +#define PCI_HEADER_TYPE_CARDBUS 2 + +#define PCI_BIST 0x0f /* 8 bits */ +#define PCI_BIST_CODE_MASK 0x0f /* Return result */ +#define PCI_BIST_START 0x40 /* 1 to start BIST, 2 secs or less */ +#define PCI_BIST_CAPABLE 0x80 /* 1 if BIST capable */ + +/* + * Base addresses specify locations in memory or I/O space. + * Decoded size can be determined by writing a value of + * 0xffffffff to the register, and reading it back. Only + * 1 bits are decoded. + */ +#define PCI_BASE_ADDRESS_0 0x10 /* 32 bits */ +#define PCI_BASE_ADDRESS_1 0x14 /* 32 bits [htype 0,1 only] */ +#define PCI_BASE_ADDRESS_2 0x18 /* 32 bits [htype 0 only] */ +#define PCI_BASE_ADDRESS_3 0x1c /* 32 bits */ +#define PCI_BASE_ADDRESS_4 0x20 /* 32 bits */ +#define PCI_BASE_ADDRESS_5 0x24 /* 32 bits */ +#define PCI_BASE_ADDRESS_SPACE 0x01 /* 0 = memory, 1 = I/O */ +#define PCI_BASE_ADDRESS_SPACE_IO 0x01 +#define PCI_BASE_ADDRESS_SPACE_MEMORY 0x00 +#define PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x06 +#define PCI_BASE_ADDRESS_MEM_TYPE_32 0x00 /* 32 bit address */ +#define PCI_BASE_ADDRESS_MEM_TYPE_1M 0x02 /* Below 1M */ +#define PCI_BASE_ADDRESS_MEM_TYPE_64 0x04 /* 64 bit address */ +#define PCI_BASE_ADDRESS_MEM_PREFETCH 0x08 /* prefetchable? */ +#define PCI_BASE_ADDRESS_MEM_MASK (~0x0fUL) +#define PCI_BASE_ADDRESS_IO_MASK (~0x03UL) +/* bit 1 is reserved if address_space = 1 */ + +/* Header type 0 (normal devices) */ +#define PCI_CARDBUS_CIS 0x28 +#define PCI_SUBSYSTEM_VENDOR_ID 0x2c +#define PCI_SUBSYSTEM_ID 0x2e +#define PCI_ROM_ADDRESS 0x30 /* Bits 31..11 are address, 10..1 reserved */ +#define PCI_ROM_ADDRESS_ENABLE 0x01 +#define PCI_ROM_ADDRESS_MASK (~0x7ffUL) + +/* 0x34 Capabilities Pointer (PCI 2.3) */ +#define PCI_CAP_PTR 0x34 /* 8 bits */ + +/* 0x35-0x3b are reserved */ +#define PCI_INTERRUPT_LINE 0x3c /* 8 bits */ +#define PCI_INTERRUPT_PIN 0x3d /* 8 bits */ +#define PCI_MIN_GNT 0x3e /* 8 bits */ +#define PCI_MAX_LAT 0x3f /* 8 bits */ + +/* Header type 1 (PCI-to-PCI bridges) */ +#define PCI_PRIMARY_BUS 0x18 /* Primary bus number */ +#define PCI_SECONDARY_BUS 0x19 /* Secondary bus number */ +#define PCI_SUBORDINATE_BUS 0x1a /* Highest bus number behind the bridge */ +#define PCI_SEC_LATENCY_TIMER 0x1b /* Latency timer for secondary interface */ +#define PCI_IO_BASE 0x1c /* I/O range behind the bridge */ +#define PCI_IO_LIMIT 0x1d +#define PCI_IO_RANGE_TYPE_MASK 0x0f /* I/O bridging type */ +#define PCI_IO_RANGE_TYPE_16 0x00 +#define PCI_IO_RANGE_TYPE_32 0x01 +#define PCI_IO_RANGE_MASK (~0x0f) +#define PCI_SEC_STATUS 0x1e /* Secondary status register, only bit 14 used */ +#define PCI_MEMORY_BASE 0x20 /* Memory range behind */ +#define PCI_MEMORY_LIMIT 0x22 +#define PCI_MEMORY_RANGE_TYPE_MASK 0x0f +#define PCI_MEMORY_RANGE_MASK (~0x0f) +#define PCI_PREF_MEMORY_BASE 0x24 /* Prefetchable memory range behind */ +#define PCI_PREF_MEMORY_LIMIT 0x26 +#define PCI_PREF_RANGE_TYPE_MASK 0x0f +#define PCI_PREF_RANGE_TYPE_32 0x00 +#define PCI_PREF_RANGE_TYPE_64 0x01 +#define PCI_PREF_RANGE_MASK (~0x0f) +#define PCI_PREF_BASE_UPPER32 0x28 /* Upper half of prefetchable memory range */ +#define PCI_PREF_LIMIT_UPPER32 0x2c +#define PCI_IO_BASE_UPPER16 0x30 /* Upper half of I/O addresses */ +#define PCI_IO_LIMIT_UPPER16 0x32 +/* 0x34-0x3b is reserved */ +#define PCI_ROM_ADDRESS1 0x38 /* Same as PCI_ROM_ADDRESS, but for htype 1 */ +/* 0x3c-0x3d are same as for htype 0 */ +#define PCI_BRIDGE_CONTROL 0x3e +#define PCI_BRIDGE_CTL_PARITY 0x01 /* Enable parity detection on secondary interface */ +#define PCI_BRIDGE_CTL_SERR 0x02 /* The same for SERR forwarding */ +#define PCI_BRIDGE_CTL_NO_ISA 0x04 /* Disable bridging of ISA ports */ +#define PCI_BRIDGE_CTL_VGA 0x08 /* Forward VGA addresses */ +#define PCI_BRIDGE_CTL_MASTER_ABORT 0x20 /* Report master aborts */ +#define PCI_BRIDGE_CTL_BUS_RESET 0x40 /* Secondary bus reset */ +#define PCI_BRIDGE_CTL_FAST_BACK 0x80 /* Fast Back2Back enabled on secondary interface */ + +/* Header type 2 (CardBus bridges) */ +/* 0x14-0x15 reserved */ +#define PCI_CB_SEC_STATUS 0x16 /* Secondary status */ +#define PCI_CB_PRIMARY_BUS 0x18 /* PCI bus number */ +#define PCI_CB_CARD_BUS 0x19 /* CardBus bus number */ +#define PCI_CB_SUBORDINATE_BUS 0x1a /* Subordinate bus number */ +#define PCI_CB_LATENCY_TIMER 0x1b /* CardBus latency timer */ +#define PCI_CB_MEMORY_BASE_0 0x1c +#define PCI_CB_MEMORY_LIMIT_0 0x20 +#define PCI_CB_MEMORY_BASE_1 0x24 +#define PCI_CB_MEMORY_LIMIT_1 0x28 +#define PCI_CB_IO_BASE_0 0x2c +#define PCI_CB_IO_BASE_0_HI 0x2e +#define PCI_CB_IO_LIMIT_0 0x30 +#define PCI_CB_IO_LIMIT_0_HI 0x32 +#define PCI_CB_IO_BASE_1 0x34 +#define PCI_CB_IO_BASE_1_HI 0x36 +#define PCI_CB_IO_LIMIT_1 0x38 +#define PCI_CB_IO_LIMIT_1_HI 0x3a +#define PCI_CB_IO_RANGE_MASK (~0x03) +/* 0x3c-0x3d are same as for htype 0 */ +#define PCI_CB_BRIDGE_CONTROL 0x3e +#define PCI_CB_BRIDGE_CTL_PARITY 0x01 /* Similar to standard bridge control register */ +#define PCI_CB_BRIDGE_CTL_SERR 0x02 +#define PCI_CB_BRIDGE_CTL_ISA 0x04 +#define PCI_CB_BRIDGE_CTL_VGA 0x08 +#define PCI_CB_BRIDGE_CTL_MASTER_ABORT 0x20 +#define PCI_CB_BRIDGE_CTL_CB_RESET 0x40 /* CardBus reset */ +#define PCI_CB_BRIDGE_CTL_16BIT_INT 0x80 /* Enable interrupt for 16-bit cards */ +#define PCI_CB_BRIDGE_CTL_PREFETCH_MEM0 0x100 /* Prefetch enable for both memory regions */ +#define PCI_CB_BRIDGE_CTL_PREFETCH_MEM1 0x200 +#define PCI_CB_BRIDGE_CTL_POST_WRITES 0x400 +#define PCI_CB_SUBSYSTEM_VENDOR_ID 0x40 +#define PCI_CB_SUBSYSTEM_ID 0x42 +#define PCI_CB_LEGACY_MODE_BASE 0x44 /* 16-bit PC Card legacy mode base address (ExCa) */ +/* 0x48-0x7f reserved */ + +/* Device classes and subclasses */ + +#define PCI_CLASS_NOT_DEFINED 0x0000 +#define PCI_CLASS_NOT_DEFINED_VGA 0x0001 + +#define PCI_BASE_CLASS_STORAGE 0x01 +#define PCI_CLASS_STORAGE_SCSI 0x0100 +#define PCI_CLASS_STORAGE_IDE 0x0101 +#define PCI_CLASS_STORAGE_FLOPPY 0x0102 +#define PCI_CLASS_STORAGE_IPI 0x0103 +#define PCI_CLASS_STORAGE_RAID 0x0104 +#define PCI_CLASS_STORAGE_OTHER 0x0180 + +#define PCI_BASE_CLASS_NETWORK 0x02 +#define PCI_CLASS_NETWORK_ETHERNET 0x0200 +#define PCI_CLASS_NETWORK_TOKEN_RING 0x0201 +#define PCI_CLASS_NETWORK_FDDI 0x0202 +#define PCI_CLASS_NETWORK_ATM 0x0203 +#define PCI_CLASS_NETWORK_OTHER 0x0280 + +#define PCI_BASE_CLASS_DISPLAY 0x03 +#define PCI_CLASS_DISPLAY_VGA 0x0300 +#define PCI_CLASS_DISPLAY_XGA 0x0301 +#define PCI_CLASS_DISPLAY_OTHER 0x0380 + +#define PCI_BASE_CLASS_MULTIMEDIA 0x04 +#define PCI_CLASS_MULTIMEDIA_VIDEO 0x0400 +#define PCI_CLASS_MULTIMEDIA_AUDIO 0x0401 +#define PCI_CLASS_MULTIMEDIA_OTHER 0x0480 + +#define PCI_BASE_CLASS_MEMORY 0x05 +#define PCI_CLASS_MEMORY_RAM 0x0500 +#define PCI_CLASS_MEMORY_FLASH 0x0501 +#define PCI_CLASS_MEMORY_OTHER 0x0580 + +#define PCI_BASE_CLASS_BRIDGE 0x06 +#define PCI_CLASS_BRIDGE_HOST 0x0600 +#define PCI_CLASS_BRIDGE_ISA 0x0601 +#define PCI_CLASS_BRIDGE_EISA 0x0602 +#define PCI_CLASS_BRIDGE_MC 0x0603 +#define PCI_CLASS_BRIDGE_PCI 0x0604 +#define PCI_CLASS_BRIDGE_PCMCIA 0x0605 +#define PCI_CLASS_BRIDGE_NUBUS 0x0606 +#define PCI_CLASS_BRIDGE_CARDBUS 0x0607 +#define PCI_CLASS_BRIDGE_OTHER 0x0680 + +#define PCI_BASE_CLASS_COMMUNICATION 0x07 +#define PCI_CLASS_COMMUNICATION_SERIAL 0x0700 +#define PCI_CLASS_COMMUNICATION_PARALLEL 0x0701 +#define PCI_CLASS_COMMUNICATION_OTHER 0x0780 + +#define PCI_BASE_CLASS_SYSTEM 0x08 +#define PCI_CLASS_SYSTEM_PIC 0x0800 +#define PCI_CLASS_SYSTEM_DMA 0x0801 +#define PCI_CLASS_SYSTEM_TIMER 0x0802 +#define PCI_CLASS_SYSTEM_RTC 0x0803 +#define PCI_CLASS_SYSTEM_OTHER 0x0880 + +#define PCI_BASE_CLASS_INPUT 0x09 +#define PCI_CLASS_INPUT_KEYBOARD 0x0900 +#define PCI_CLASS_INPUT_PEN 0x0901 +#define PCI_CLASS_INPUT_MOUSE 0x0902 +#define PCI_CLASS_INPUT_OTHER 0x0980 + +#define PCI_BASE_CLASS_DOCKING 0x0a +#define PCI_CLASS_DOCKING_GENERIC 0x0a00 +#define PCI_CLASS_DOCKING_OTHER 0x0a01 + +#define PCI_BASE_CLASS_PROCESSOR 0x0b +#define PCI_CLASS_PROCESSOR_386 0x0b00 +#define PCI_CLASS_PROCESSOR_486 0x0b01 +#define PCI_CLASS_PROCESSOR_PENTIUM 0x0b02 +#define PCI_CLASS_PROCESSOR_ALPHA 0x0b10 +#define PCI_CLASS_PROCESSOR_POWERPC 0x0b20 +#define PCI_CLASS_PROCESSOR_CO 0x0b40 + +#define PCI_BASE_CLASS_SERIAL 0x0c +#define PCI_CLASS_SERIAL_FIREWIRE 0x0c00 +#define PCI_CLASS_SERIAL_ACCESS 0x0c01 +#define PCI_CLASS_SERIAL_SSA 0x0c02 +#define PCI_CLASS_SERIAL_USB 0x0c03 +#define PCI_CLASS_SERIAL_FIBER 0x0c04 + +#define PCI_CLASS_OTHERS 0xff + +#define PCI_INVALID_VENDORDEVICEID 0xffffffff +#define PCI_MULTI_FUNCTION 0x80 + +#define PCI_MAX_DEVICES 32 +#define PCI_MAX_FUNCTIONS 8 + +#include <pci/access.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* The PCI Library have the following build time configuration options. It is + * up to the BSP header file (bsp.h) to set options properly. + * + * BSP_PCI_BIG_ENDIAN - Access inline routines will be for a big-endian PCI + * bus, if not defined the routines will assume that + * PCI is as the standard defines: little-endian. + * + * Note that drivers may be run-time configurable, + * meaning that they may adopt to either big-endian or + * little-endian PCI bus, the host driver or BSP may + * detect endianness during run-time. + */ + +/* Error return values */ +enum { + PCISTS_ERR = -1, /* Undefined Error */ + PCISTS_OK = 0, + PCISTS_EINVAL = 1, /* Bad input arguments */ + PCISTS_MSTABRT = 2, /* CFG space access error (can be ignored) */ +}; + +/* PCI System type can be used to determine system for drivers. Normally + * the system is Host, but the peripheral configuration library also supports + * being PCI peripheral not allowed to access configuration space. + * + * The active configuration Library set this variable. + */ +enum { + PCI_SYSTEM_NONE = 0, + PCI_SYSTEM_HOST = 1, + PCI_SYSTEM_PERIPHERAL = 2, +}; +extern int pci_system_type; + +/* PCI Bus Endianness. The PCI specification is little endian, however on some + * embedded systems (AT697-LEON2 for example) the PCI bus is defined as big + * endian (non-standard) in order to avoid byte-twisting. + */ +enum { + PCI_LITTLE_ENDIAN = 0, + PCI_BIG_ENDIAN = 1, +}; +extern int pci_endian; + +/* Return the number of PCI busses in the system */ +extern int pci_bus_count(void); + +/* Scan the PCI bus and print the PCI device/functions/bridges and their + * current resources and size to the system console. + */ +extern void pci_print(void); + +/* Print current configuration of a single PCI device by reading PCI + * configuration space + */ +extern void pci_print_dev(pci_dev_t dev); +extern void pci_print_device(int bus, int slot, int function); + +/*** PCI Configuration Space direct access routines ***/ + +/* Function iterates over all PCI buses/devices/functions and calls + * func(PCIDEV,arg) for each present device. The iteration is stopped if + * func() returns non-zero result the same result is returned. As long + * as func() returns zero the function will keep on iterating, when all + * devices has been processed the function return zero. + * + * The function iterates over all devices/functions on all buses by accessing + * configuration space directly (PCI RAM data structures not used). This + * function is valid to call after PCI buses have been enumrated. + */ +extern int pci_for_each(int (*func)(pci_dev_t, void*), void *arg); + +/* Get PCI Configuration space BUS|SLOT|FUNC for a device matching PCI + * Vendor, Device and instance number 'index'. + * + * Return Values + * -1 pci_find_dev did not find a device matching the criterion. + * 0 device was found, *pdev was updated with the device's BUS|SLOT|FUNC + */ +extern int pci_find(uint16_t ven, uint16_t dev, int index, pci_dev_t *pdev); + +#ifdef __cplusplus +} +#endif + +#endif /* __PCI_H__ */ diff --git a/cpukit/libpci/pci/access.h b/cpukit/libpci/pci/access.h new file mode 100644 index 0000000000..a3f351f60b --- /dev/null +++ b/cpukit/libpci/pci/access.h @@ -0,0 +1,315 @@ +/* Routines to access PCI memory/configuration space and other PCI related + * functions the PCI Library provides. + */ + +#ifndef __PCI_ACCESS_H__ +#define __PCI_ACCESS_H__ + +#include <stdint.h> +#include <libcpu/byteorder.h> +#include <pci.h> + +/* Let BSP configure load/store from PCI */ +#include <bsp.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* Identification of a PCI configuration space device (16-bit) */ +typedef uint16_t pci_dev_t; +/* Create a PCI Configuration Space ID */ +#define PCI_DEV(bus, slot, func) (((bus)<<8) | ((slot)<<3) | (func)) +/* Get Bus of a PCI Configuration Space ID */ +#define PCI_DEV_BUS(dev) (((dev) >> 8) & 0xff) +/* Get Slot/Device of a PCI Configuration Space ID */ +#define PCI_DEV_SLOT(dev) (((dev) >> 3) & 0x1f) +/* Get Function of a PCI Configuration Space ID */ +#define PCI_DEV_FUNC(dev) ((dev) & 0x7) +/* Get Device and Function of a PCI Configuration Space ID */ +#define PCI_DEV_DEVFUNC(dev) ((dev) & 0xff) +/* Expand Device into argument lists */ +#define PCI_DEV_EXPAND(dev) PCI_DEV_BUS((dev)), PCI_DEV_SLOT((dev)), PCI_DEV_FUNC((dev)) + +/* Configuration Space Read/Write Operations */ +struct pci_cfg_ops { + /* Configuration Space Access and Setup Routines */ + int (*read8)(pci_dev_t dev, int ofs, uint8_t *data); + int (*read16)(pci_dev_t dev, int ofs, uint16_t *data); + int (*read32)(pci_dev_t dev, int ofs, uint32_t *data); + int (*write8)(pci_dev_t dev, int ofs, uint8_t data); + int (*write16)(pci_dev_t dev, int ofs, uint16_t data); + int (*write32)(pci_dev_t dev, int ofs, uint32_t data); +}; + +/* Read a register over PCI I/O Space, and swap it if necessary (due to + * PCI endianness) + */ +struct pci_io_ops { + uint8_t (*read8)(uint8_t *adr); + uint16_t(*read16)(uint16_t *adr); + uint32_t (*read32)(uint32_t *adr); + void (*write8)(uint8_t *adr, uint8_t data); + void (*write16)(uint16_t *adr, uint16_t data); + void (*write32)(uint32_t *adr, uint32_t data); +}; + +/* Read a register over PCI Memory Space (non-prefetchable memory), and + * swap it if necessary (due to PCI endianness) + */ +struct pci_memreg_ops { + uint8_t (*ld8)(uint8_t *adr); + void (*st8)(uint8_t *adr, uint8_t data); + + uint16_t(*ld_le16)(uint16_t *adr); + void (*st_le16)(uint16_t *adr, uint16_t data); + uint16_t(*ld_be16)(uint16_t *adr); + void (*st_be16)(uint16_t *adr, uint16_t data); + + uint32_t (*ld_le32)(uint32_t *adr); + void (*st_le32)(uint32_t *adr, uint32_t data); + uint32_t (*ld_be32)(uint32_t *adr); + void (*st_be32)(uint32_t *adr, uint32_t data); +}; + +typedef uint8_t (*pci_ld8_t)(uint8_t *adr); +typedef void (*pci_st8_t)(uint8_t *adr, uint8_t data); +typedef uint16_t(pci_ld16_t)(uint16_t *adr); +typedef void (*pci_st16_t)(uint16_t *adr, uint16_t data); +typedef uint32_t (*pci_ld32_t)(uint32_t *adr); +typedef void (*pci_st32_t)(uint32_t *adr, uint32_t data); + +struct pci_access_drv { + /* Configuration */ + struct pci_cfg_ops cfg; + + /* I/O Access operations */ + struct pci_io_ops io; + + /* Registers over Memory Access operations. Note that these funcs + * are only for code that need to be compatible with both Big-Endian + * and Little-Endian PCI bus or for some other reason need function + * pointers to access functions. Normally drivers use the inline + * functions for Registers-over-Memory access to avoid extra function + * call. + */ + struct pci_memreg_ops *memreg; + + /* Translate from PCI address to CPU address (dir=0). Translate + * CPU address to PCI address (dir!=0). The address will can be + * used to perform I/O access or memory access by CPU or PCI DMA + * peripheral. + * + * address In/Out. CPU address or PCI address. + * type Access type. 1=I/O, 2=MEMIO, 3=MEM + * dir Translate direction. 0=PCI-to-CPU, 0!=CPU-to-PCI, + * + * Return Value + * 0 = Success + * -1 = Requested Address not mapped into other address space + * i.e. not accessible + */ + int (*translate)(uint32_t *address, int type, int dir); +}; + +/* Access Routines valid after a PCI-Access-Driver has registered */ +extern struct pci_access_drv pci_access_ops; + +/* Register PCI Access Driver */ +extern int pci_access_drv_register(struct pci_access_drv *drv); + +/* Set/unset bits in command and status register of a PCI device */ +extern void pci_modify_cmdsts(pci_dev_t dev, uint32_t mask, uint32_t val); + +/* Enable Memory in command register */ +static inline void pci_mem_enable(pci_dev_t dev) +{ + pci_modify_cmdsts(dev, PCI_COMMAND_MEMORY, PCI_COMMAND_MEMORY); +} + +static inline void pci_mem_disable(pci_dev_t dev) +{ + pci_modify_cmdsts(dev, PCI_COMMAND_MEMORY, 0); +} + +static inline void pci_io_enable(pci_dev_t dev) +{ + pci_modify_cmdsts(dev, PCI_COMMAND_IO, PCI_COMMAND_IO); +} + +static inline void pci_io_disable(pci_dev_t dev) +{ + pci_modify_cmdsts(dev, PCI_COMMAND_IO, 0); +} + +static inline void pci_master_enable(pci_dev_t dev) +{ + pci_modify_cmdsts(dev, PCI_COMMAND_MASTER, PCI_COMMAND_MASTER); +} + +static inline void pci_master_disable(pci_dev_t dev) +{ + pci_modify_cmdsts(dev, PCI_COMMAND_MASTER, 0); +} + +/* Configuration Space Access Read Routines */ +extern int pci_cfg_r8(pci_dev_t dev, int ofs, uint8_t *data); +extern int pci_cfg_r16(pci_dev_t dev, int ofs, uint16_t *data); +extern int pci_cfg_r32(pci_dev_t dev, int ofs, uint32_t *data); + +/* Configuration Space Access Write Routines */ +extern int pci_cfg_w8(pci_dev_t dev, int ofs, uint8_t data); +extern int pci_cfg_w16(pci_dev_t dev, int ofs, uint16_t data); +extern int pci_cfg_w32(pci_dev_t dev, int ofs, uint32_t data); + +/* Read a register over PCI I/O Space */ +extern uint8_t pci_io_r8(uint32_t adr); +extern uint16_t pci_io_r16(uint32_t adr); +extern uint32_t pci_io_r32(uint32_t adr); + +/* Write a register over PCI I/O Space */ +extern void pci_io_w8(uint32_t adr, uint8_t data); +extern void pci_io_w16(uint32_t adr, uint16_t data); +extern void pci_io_w32(uint32_t adr, uint32_t data); + +/* Translate PCI address into CPU accessible address */ +static inline int pci_pci2cpu(uint32_t *address, int type) +{ + return pci_access_ops.translate(address, type, 0); +} + +/* Translate CPU accessible address into PCI address (for DMA) */ +static inline int pci_cpu2pci(uint32_t *address, int type) +{ + return pci_access_ops.translate(address, type, 1); +} + +/*** Read/Write a register over PCI Memory Space ***/ + +static inline uint8_t pci_ld8(volatile uint8_t *addr) +{ + return *addr; +} + +static inline void pci_st8(volatile uint8_t *addr, uint8_t val) +{ + *addr = val; +} + +#ifdef BSP_PCI_BIG_ENDIAN + +/* BSP has decided Big Endian PCI Bus (non-standard) */ + +static inline uint16_t pci_ld_le16(volatile uint16_t *addr) +{ + return ld_be16(addr); +} + +static inline void pci_st_le16(volatile uint16_t *addr, uint16_t val) +{ + st_be16(addr, val); +} + +static inline uint32_t pci_ld_le32(volatile uint32_t *addr) +{ + return ld_be32(addr); +} + +static inline void pci_st_le32(volatile uint32_t *addr, uint32_t val) +{ + st_be32(addr, val); +} + +static inline uint16_t pci_ld_be16(volatile uint16_t *addr) +{ + return ld_le16(addr); +} + +static inline void pci_st_be16(volatile uint16_t *addr, uint16_t val) +{ + st_le16(addr, val); +} + +static inline uint32_t pci_ld_be32(volatile uint32_t *addr) +{ + return ld_le32(addr); +} + +static inline void pci_st_be32(volatile uint32_t *addr, uint32_t val) +{ + st_le32(addr, val); +} + +#else + +/* Little Endian PCI Bus */ + +static inline uint16_t pci_ld_le16(volatile uint16_t *addr) +{ + return ld_le16(addr); +} + +static inline void pci_st_le16(volatile uint16_t *addr, uint16_t val) +{ + st_le16(addr, val); +} + +static inline uint32_t pci_ld_le32(volatile uint32_t *addr) +{ + return ld_le32(addr); +} + +static inline void pci_st_le32(volatile uint32_t *addr, uint32_t val) +{ + st_le32(addr, val); +} + +static inline uint16_t pci_ld_be16(volatile uint16_t *addr) +{ + return ld_be16(addr); +} + +static inline void pci_st_be16(volatile uint16_t *addr, uint16_t val) +{ + st_be16(addr, val); +} + +static inline uint32_t pci_ld_be32(volatile uint32_t *addr) +{ + return ld_be32(addr); +} + +static inline void pci_st_be32(volatile uint32_t *addr, uint32_t val) +{ + st_be32(addr, val); +} + +#endif + +/* Get Read/Write function for accessing a register over PCI Memory Space + * (non-inline functions). + * + * Arguments + * wr 0(Read), 1(Write) + * size 1(Byte), 2(Word), 4(Double Word) + * func Where function pointer will be stored + * endian PCI_LITTLE_ENDIAN or PCI_BIG_ENDIAN + * type 1(I/O), 3(REG over MEM), 4(CFG) + * + * Return + * 0 Found function + * others No such function defined by host driver or BSP + */ +extern int pci_access_func(int wr, int size, void **func, int endian, int type); + +/* Predefined functions for Host drivers or BSPs that define the + * register-over-memory space functions operations. + */ +extern struct pci_memreg_ops pci_mem_le_ops; /* For Little-Endian PCI bus */ +extern struct pci_memreg_ops pci_mem_be_ops; /* For Big-Endian PCI bus */ + +#ifdef __cplusplus +} +#endif + +#endif /* !__PCI_ACCESS_H__ */ diff --git a/cpukit/libpci/pci/cfg.h b/cpukit/libpci/pci/cfg.h new file mode 100644 index 0000000000..6018ca5fa3 --- /dev/null +++ b/cpukit/libpci/pci/cfg.h @@ -0,0 +1,237 @@ +/* PCI Configuration Library, two versions of the library exists: + * - auto configuration (default) + * - static configuration (user defined config) + * both versions are defined here. + * + */ + +#ifndef __PCI_CFG_H__ +#define __PCI_CFG_H__ + +#include <pci.h> + +/* PCI Configuration library */ + +/* Return the number of PCI buses in system */ +extern int pci_bus_count(void); + +/* PCI Address assigned to BARs which failed to fit into the PCI Window or + * is disabled by any other cause. + */ +extern uint32_t pci_invalid_address; + +/* PCI Configuration Library of the system */ +enum { + PCI_CONFIG_LIB_NONE = 0, + PCI_CONFIG_LIB_AUTO = 1, + PCI_CONFIG_LIB_STATIC = 2, + PCI_CONFIG_LIB_READ = 3, + PCI_CONFIG_LIB_PERIPHERAL = 4, +}; +extern const int pci_config_lib_type; + +/* Configuration library function pointers, these are set in <rtems/confdefs.h> + * by project configuration or by the BSP. The configuration will pull in the + * PCI Library needed and the PCI initialization functions will call these + * functions on initialization from the host driver. + */ +extern int (*pci_config_lib_init)(void); +extern void (*pci_config_lib_register)(void *config); + +/* Configure PCI devices and bridges, and setup the RAM data structures + * describing the PCI devices currently present in the system. + * + * Returns 0 on success, -1 on failure. + */ +extern int pci_config_init(void); + +/* Register a config-library specific configuration used by the libarary in + * pci_config_init(). + */ +extern void pci_config_register(void *config); + +/* Print current PCI configuration (C-code) to terminal, can be used in + * static and peripheral PCI configuration library. The configuration is + * taken from the current configuration library setup. + */ +extern void pci_cfg_print(void); + +struct pci_bus; /* Bridge Device and secondary bus information */ +struct pci_dev; /* Device/function */ +struct pci_res; /* Resource: BAR, ROM or Bridge Window */ + +/* The Host Bridge and all subdevices (the PCI RAM data structure) */ +extern struct pci_bus pci_hb; + +/* Iterate over all PCI devices on a bus (see search options) and call func(), + * iteration is stopped if a non-zero value is returned by func(). + * + * The function iterates over the PCI RAM data structure, it is not + * available until after all devices have been found and pci_hb is populated, + * typically after pci_config_init() is called. + * + * search options: 0 (no child buses), 1 (depth first, recursive) + * + * Return Values + * 0 All PCI devices were processed, func() returned 0 on every call + * X func() returned non-zero X value, the search was stopped + */ +#define SEARCH_DEPTH 1 +extern int pci_for_each_child( + struct pci_bus *bus, + int (*func)(struct pci_dev *, void *arg), + void *arg, + int search); + +/* Depth first search of all PCI devices in PCI RAM data structure and call + * func(dev, arg), iteration is stopped if a non-zero value is returned by + * func(). + * + * The function iterates over the PCI RAM data structure, it is not + * available until after all devices have been found and pci_hb is populated, + * typically after pci_config_init() is called. + * + * Return Values + * 0 All PCI devices were processed, func() returned 0 on every call + * X func() returned non-zero X value, the search was stopped + */ +extern int pci_for_each_dev( + int (*func)(struct pci_dev *, void *arg), + void *arg); + +/* Get PCI device from RAM device tree for a device matching PCI Vendor, Device + * and instance number 'index'. + * + * Return Values + * -1 pci_find_dev did not find a device matching the criterion. + * 0 device was found, *ppdev was updated with the PCI device address + */ +extern int pci_find_dev(uint16_t ven, uint16_t dev, int index, + struct pci_dev **ppdev); + +/* Get PCI device from RAM device tree by BUS|SLOT|FUNC. + * + * Return Values + * -1 pci_get_dev did not find a device matching the criterion + * 0 device was found, *ppdev was updated with the PCI device address + */ +extern int pci_get_dev(pci_dev_t pcidev, struct pci_dev **ppdev); + +/* Resource flags */ +#define PCI_RES_IO 1 +#define PCI_RES_MEMIO 2 +#define PCI_RES_MEM_PREFETCH 1 +#define PCI_RES_MEM (PCI_RES_MEMIO | PCI_RES_MEM_PREFETCH) +#define PCI_RES_TYPE_MASK 0x3 +#define PCI_RES_IO32 0x08 +#define PCI_RES_FAIL 0x10 /* Alloc Failed */ + +/* BAR Resouces entry */ +struct pci_res { + struct pci_res *next; + uint32_t size; + uint32_t boundary; + unsigned char flags; /* I/O, MEM or MEMIO */ + unsigned char bar; + + /* Assigned Resource (PCI address), zero if not assigned */ + uint32_t start; + uint32_t end; +}; + +/* Get Device from resource pointer */ +#define RES2DEV(res) ((struct pci_dev *) \ + ((void *)res - (res->bar * (sizeof(struct pci_res))))) + +/* Device flags */ +#define PCI_DEV_BRIDGE 0x01 /* Device is a Bridge (struct pci_bus) */ +#define PCI_DEV_RES_FAIL 0x02 /* Resource alloction for device BARs failed */ + +/* Bus Flags */ +#define PCI_BUS_IO 0x01 /* 16-bit I/O address decoding */ +#define PCI_BUS_MEMIO 0x02 /* Bus support non-prefetchable mem (always) */ +#define PCI_BUS_MEM 0x04 /* Bus support prefetchable memory space */ +#define PCI_BUS_IO32 0x08 /* 32-bit I/O address decoding */ + +#define BRIDGE_RES_COUNT 2 /* Number of BAR resources a bridge can have */ +#define BUS_RES_START BRIDGE_RES_COUNT + +/* Bus Resources Array */ +enum { + BUS_RES_IO = 0, + BUS_RES_MEMIO = 1, + BUS_RES_MEM = 2, +}; + +/* Device Resource array index meaning */ +enum { + /* A Device has up to 6 BARs and an optional ROM BAR */ + DEV_RES_BAR1 = 0, + DEV_RES_BAR2 = 1, + DEV_RES_BAR3 = 2, + DEV_RES_BAR4 = 3, + DEV_RES_BAR5 = 4, + DEV_RES_BAR6 = 5, + DEV_RES_ROM = 6, + + /* Bridges have 2 BARs (BAR1 and BAR2) and 3 Windows to secondary bus + * and an optional ROM BAR + */ + BRIDGE_RES_BAR1 = 0, + BRIDGE_RES_BAR2 = 1, + BRIDGE_RES_IO = 2, + BRIDGE_RES_MEMIO = 3, + BRIDGE_RES_MEM = 4, + BRIDGE_RES_UNUSED1 = 5, + BRIDGE_RES_ROM = 6, +}; + +/* Maximum Number of Resources of a device */ +#define DEV_RES_CNT (DEV_RES_ROM + 1) + +/* PCI Device (Bus|Slot|Function) description */ +struct pci_dev { + struct pci_res resources[DEV_RES_CNT]; /* must be topmost field */ + struct pci_dev *next; + struct pci_bus *bus; + pci_dev_t busdevfun; + uint8_t flags; + uint8_t sysirq; + uint16_t vendor; + uint16_t device; + uint16_t subvendor; + uint16_t subdevice; + uint32_t classrev; + + /* static configuration settings */ + uint16_t command; +}; + +/* PCI Bus description */ +struct pci_bus { + struct pci_dev dev; /* PCI Bridge */ + struct pci_dev *devs; /* Devices on child (secondary) Bus */ + unsigned int flags; + + /* Bridge Information */ + int num; /* Bus number (0=Root-PCI-bus) */ + int pri; /* Primary Bus Number */ + int sord; /* Subordinate Buses (Child bus count) */ + +#if defined(PCI_CFG_AUTO_LIB) + /* Resources of devices on bus. USED INTERNALLY IN AUTO-CFG LIBRARY. + * + * BUS_RES_IO = 0: I/O resources + * BUS_RES_MEMIO = 1: Prefetchable memory resources + * BUS_RES_MEM = 2: Non-Prefetchable memory resources + */ + struct pci_res *busres[3]; +#endif +}; + +#include <pci/cfg_auto.h> +#include <pci/cfg_static.h> +#include <pci/cfg_read.h> +#include <pci/cfg_peripheral.h> + +#endif diff --git a/cpukit/libpci/pci/cfg_auto.h b/cpukit/libpci/pci/cfg_auto.h new file mode 100644 index 0000000000..aa55f501f6 --- /dev/null +++ b/cpukit/libpci/pci/cfg_auto.h @@ -0,0 +1,51 @@ +/* PCI Auto Configuration Library */ + +#ifndef __PCI_CFG_AUTO_H__ +#define __PCI_CFG_AUTO_H__ + +#define CFGOPT_NOSETUP_IRQ 0x1 /* Skip IRQ setup */ + +/* PCI Memory Layout setup, used by the auto-config library in order to + * determine the addresses of PCI BARs and Buses. + * + * All addresses are in PCI address space, the actual address the CPU access + * may be different, and taken care of elsewhere. + */ +struct pci_auto_setup { + int options; + + /* PCI prefetchable Memory space (OPTIONAL) */ + uint32_t mem_start; + uint32_t mem_size; /* 0 = Use MEMIO space for prefetchable mem BARs */ + + /* PCI non-prefetchable Memory */ + uint32_t memio_start; + uint32_t memio_size; + + /* PCI I/O space (OPTIONAL) */ + uint32_t io_start; + uint32_t io_size; /* 0 = No I/O space */ + + /* Get System IRQ connected to a PCI line of a PCI device on bus0. + * The return IRQ value zero equals no IRQ (IRQ disabled). + */ + uint8_t (*irq_map)(pci_dev_t dev, int irq_pin); + + /* IRQ Bridge routing. Returns the interrupt pin (0..3 = A..D) that + * a device is connected to on parent bus. + */ + int (*irq_route)(pci_dev_t dev, int irq_pin); +}; + +/* Do PCI initialization: Enumrate buses, scan buses for devices, assign + * I/O MEM and MEMIO resources, assign IRQ and so on. + */ +extern int pci_config_auto(void); + +/* Register a configuration for the auto library (struct pci_auto_setup *) */ +extern void pci_config_auto_register(void *config); + +/* PCI memory map */ +extern struct pci_auto_setup pci_auto_cfg; + +#endif diff --git a/cpukit/libpci/pci/cfg_peripheral.h b/cpukit/libpci/pci/cfg_peripheral.h new file mode 100644 index 0000000000..f54121d40c --- /dev/null +++ b/cpukit/libpci/pci/cfg_peripheral.h @@ -0,0 +1,12 @@ +/* PCI Peripheral Configuration Library */ + +#ifndef __PCI_CFG_PERIPHERAL_H__ +#define __PCI_CFG_PERIPHERAL_H__ + +/* The user must provide a PCI configuration using the "struct pci_bus pci_hb" + * structure. Nothing else than setting pci_system_type and pci_bus_cnt is done + * by the peripheral library. + */ +extern int pci_config_peripheral(void); + +#endif diff --git a/cpukit/libpci/pci/cfg_read.h b/cpukit/libpci/pci/cfg_read.h new file mode 100644 index 0000000000..09fe354736 --- /dev/null +++ b/cpukit/libpci/pci/cfg_read.h @@ -0,0 +1,14 @@ +/* PCI Read Configuration Library. Read current config that bootloader/BIOS + * has setup. + */ + +#ifndef __PCI_CFG_READ_H__ +#define __PCI_CFG_READ_H__ + +/* Build PCI device tree in "struct pci_bus pci_hb" according to current setup + * in hardware. Devices/buses are created by reading the resource assignments + * that the BIOS/bootloader has already setup for us. + */ +extern int pci_config_read(void); + +#endif diff --git a/cpukit/libpci/pci/cfg_static.h b/cpukit/libpci/pci/cfg_static.h new file mode 100644 index 0000000000..7d305c32aa --- /dev/null +++ b/cpukit/libpci/pci/cfg_static.h @@ -0,0 +1,14 @@ +/* Static PCI Auto Configuration Library */ + +#ifndef __PCI_CFG_STATIC_H__ +#define __PCI_CFG_STATIC_H__ + +/* This function initializes all buses and device accorind to a user defined + * "static" configuration. The configuration can manually created with C + * data structures. Or it can be automatically created on a running target + * using the pci_cfg_print() routine after the AUTO or READ Configuration + * Library has setup the PCI bus + */ +extern int pci_config_static(void); + +#endif diff --git a/c/src/lib/libbsp/sparc/shared/include/pci.h b/cpukit/libpci/pci/ids.h index 35259fb2b8..2d2592bc03 100644 --- a/c/src/lib/libbsp/sparc/shared/include/pci.h +++ b/cpukit/libpci/pci/ids.h @@ -1,276 +1,12 @@ -/* - * - * PCI defines and function prototypes - * Copyright 1994, Drew Eckhardt - * Copyright 1997, 1998 Martin Mares <mj@atrey.karlin.mff.cuni.cz> - * - * For more information, please consult the following manuals (look at - * http://www.pcisig.com/ for how to get them): - * - * PCI BIOS Specification - * PCI Local Bus Specification - * PCI to PCI Bridge Specification - * PCI System Design Guide - * - * pci.h,v 1.2.4.2 2004/11/10 22:15:01 joel Exp - */ - -#ifndef RTEMS_PCI_H -#define RTEMS_PCI_H +/* PCI Identifiers - auto generated */ +#ifndef __PCI_IDS_H__ +#define __PCI_IDS_H__ -#ifdef __cplusplus -extern "C" { -#endif - -/* - * Under PCI, each device has 256 bytes of configuration address space, - * of which the first 64 bytes are standardized as follows: - */ -#define PCI_VENDOR_ID 0x00 /* 16 bits */ -#define PCI_DEVICE_ID 0x02 /* 16 bits */ -#define PCI_COMMAND 0x04 /* 16 bits */ -#define PCI_COMMAND_IO 0x1 /* Enable response in I/O space */ -#define PCI_COMMAND_MEMORY 0x2 /* Enable response in Memory space */ -#define PCI_COMMAND_MASTER 0x4 /* Enable bus mastering */ -#define PCI_COMMAND_SPECIAL 0x8 /* Enable response to special cycles */ -#define PCI_COMMAND_INVALIDATE 0x10 /* Use memory write and invalidate */ -#define PCI_COMMAND_VGA_PALETTE 0x20 /* Enable palette snooping */ -#define PCI_COMMAND_PARITY 0x40 /* Enable parity checking */ -#define PCI_COMMAND_WAIT 0x80 /* Enable address/data stepping */ -#define PCI_COMMAND_SERR 0x100 /* Enable SERR */ -#define PCI_COMMAND_FAST_BACK 0x200 /* Enable back-to-back writes */ - -#define PCI_STATUS 0x06 /* 16 bits */ -#define PCI_STATUS_66MHZ 0x20 /* Support 66 Mhz PCI 2.1 bus */ -#define PCI_STATUS_UDF 0x40 /* Support User Definable Features */ - -#define PCI_STATUS_FAST_BACK 0x80 /* Accept fast-back to back */ -#define PCI_STATUS_PARITY 0x100 /* Detected parity error */ -#define PCI_STATUS_DEVSEL_MASK 0x600 /* DEVSEL timing */ -#define PCI_STATUS_DEVSEL_FAST 0x000 -#define PCI_STATUS_DEVSEL_MEDIUM 0x200 -#define PCI_STATUS_DEVSEL_SLOW 0x400 -#define PCI_STATUS_SIG_TARGET_ABORT 0x800 /* Set on target abort */ -#define PCI_STATUS_REC_TARGET_ABORT 0x1000 /* Master ack of " */ -#define PCI_STATUS_REC_MASTER_ABORT 0x2000 /* Set on master abort */ -#define PCI_STATUS_SIG_SYSTEM_ERROR 0x4000 /* Set when we drive SERR */ -#define PCI_STATUS_DETECTED_PARITY 0x8000 /* Set on parity error */ - -#define PCI_CLASS_REVISION 0x08 /* High 24 bits are class, low 8 - revision */ -#define PCI_REVISION_ID 0x08 /* Revision ID */ -#define PCI_CLASS_PROG 0x09 /* Reg. Level Programming Interface */ -#define PCI_CLASS_DEVICE 0x0a /* Device class */ - -#define PCI_CACHE_LINE_SIZE 0x0c /* 8 bits */ -#define PCI_LATENCY_TIMER 0x0d /* 8 bits */ -#define PCI_HEADER_TYPE 0x0e /* 8 bits */ -#define PCI_HEADER_TYPE_NORMAL 0 -#define PCI_HEADER_TYPE_BRIDGE 1 -#define PCI_HEADER_TYPE_CARDBUS 2 - -#define PCI_BIST 0x0f /* 8 bits */ -#define PCI_BIST_CODE_MASK 0x0f /* Return result */ -#define PCI_BIST_START 0x40 /* 1 to start BIST, 2 secs or less */ -#define PCI_BIST_CAPABLE 0x80 /* 1 if BIST capable */ +/* Include non-public PCI ids (not auto generated) */ +#include <pci/ids_extra.h> -/* - * Base addresses specify locations in memory or I/O space. - * Decoded size can be determined by writing a value of - * 0xffffffff to the register, and reading it back. Only - * 1 bits are decoded. - */ -#define PCI_BASE_ADDRESS_0 0x10 /* 32 bits */ -#define PCI_BASE_ADDRESS_1 0x14 /* 32 bits [htype 0,1 only] */ -#define PCI_BASE_ADDRESS_2 0x18 /* 32 bits [htype 0 only] */ -#define PCI_BASE_ADDRESS_3 0x1c /* 32 bits */ -#define PCI_BASE_ADDRESS_4 0x20 /* 32 bits */ -#define PCI_BASE_ADDRESS_5 0x24 /* 32 bits */ -#define PCI_BASE_ADDRESS_SPACE 0x01 /* 0 = memory, 1 = I/O */ -#define PCI_BASE_ADDRESS_SPACE_IO 0x01 -#define PCI_BASE_ADDRESS_SPACE_MEMORY 0x00 -#define PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x06 -#define PCI_BASE_ADDRESS_MEM_TYPE_32 0x00 /* 32 bit address */ -#define PCI_BASE_ADDRESS_MEM_TYPE_1M 0x02 /* Below 1M */ -#define PCI_BASE_ADDRESS_MEM_TYPE_64 0x04 /* 64 bit address */ -#define PCI_BASE_ADDRESS_MEM_PREFETCH 0x08 /* prefetchable? */ -#define PCI_BASE_ADDRESS_MEM_MASK (~0x0fUL) -#define PCI_BASE_ADDRESS_IO_MASK (~0x03UL) -/* bit 1 is reserved if address_space = 1 */ - -/* Header type 0 (normal devices) */ -#define PCI_CARDBUS_CIS 0x28 -#define PCI_SUBSYSTEM_VENDOR_ID 0x2c -#define PCI_SUBSYSTEM_ID 0x2e -#define PCI_ROM_ADDRESS 0x30 /* Bits 31..11 are address, 10..1 reserved */ -#define PCI_ROM_ADDRESS_ENABLE 0x01 -#define PCI_ROM_ADDRESS_MASK (~0x7ffUL) - -/* 0x34-0x3b are reserved */ -#define PCI_INTERRUPT_LINE 0x3c /* 8 bits */ -#define PCI_INTERRUPT_PIN 0x3d /* 8 bits */ -#define PCI_MIN_GNT 0x3e /* 8 bits */ -#define PCI_MAX_LAT 0x3f /* 8 bits */ - -/* Header type 1 (PCI-to-PCI bridges) */ -#define PCI_PRIMARY_BUS 0x18 /* Primary bus number */ -#define PCI_SECONDARY_BUS 0x19 /* Secondary bus number */ -#define PCI_SUBORDINATE_BUS 0x1a /* Highest bus number behind the bridge */ -#define PCI_SEC_LATENCY_TIMER 0x1b /* Latency timer for secondary interface */ -#define PCI_IO_BASE 0x1c /* I/O range behind the bridge */ -#define PCI_IO_LIMIT 0x1d -#define PCI_IO_RANGE_TYPE_MASK 0x0f /* I/O bridging type */ -#define PCI_IO_RANGE_TYPE_16 0x00 -#define PCI_IO_RANGE_TYPE_32 0x01 -#define PCI_IO_RANGE_MASK ~0x0f -#define PCI_SEC_STATUS 0x1e /* Secondary status register, only bit 14 used */ -#define PCI_MEMORY_BASE 0x20 /* Memory range behind */ -#define PCI_MEMORY_LIMIT 0x22 -#define PCI_MEMORY_RANGE_TYPE_MASK 0x0f -#define PCI_MEMORY_RANGE_MASK ~0x0f -#define PCI_PREF_MEMORY_BASE 0x24 /* Prefetchable memory range behind */ -#define PCI_PREF_MEMORY_LIMIT 0x26 -#define PCI_PREF_RANGE_TYPE_MASK 0x0f -#define PCI_PREF_RANGE_TYPE_32 0x00 -#define PCI_PREF_RANGE_TYPE_64 0x01 -#define PCI_PREF_RANGE_MASK ~0x0f -#define PCI_PREF_BASE_UPPER32 0x28 /* Upper half of prefetchable memory range */ -#define PCI_PREF_LIMIT_UPPER32 0x2c -#define PCI_IO_BASE_UPPER16 0x30 /* Upper half of I/O addresses */ -#define PCI_IO_LIMIT_UPPER16 0x32 -/* 0x34-0x3b is reserved */ -#define PCI_ROM_ADDRESS1 0x38 /* Same as PCI_ROM_ADDRESS, but for htype 1 */ -/* 0x3c-0x3d are same as for htype 0 */ -#define PCI_BRIDGE_CONTROL 0x3e -#define PCI_BRIDGE_CTL_PARITY 0x01 /* Enable parity detection on secondary interface */ -#define PCI_BRIDGE_CTL_SERR 0x02 /* The same for SERR forwarding */ -#define PCI_BRIDGE_CTL_NO_ISA 0x04 /* Disable bridging of ISA ports */ -#define PCI_BRIDGE_CTL_VGA 0x08 /* Forward VGA addresses */ -#define PCI_BRIDGE_CTL_MASTER_ABORT 0x20 /* Report master aborts */ -#define PCI_BRIDGE_CTL_BUS_RESET 0x40 /* Secondary bus reset */ -#define PCI_BRIDGE_CTL_FAST_BACK 0x80 /* Fast Back2Back enabled on secondary interface */ - -/* Header type 2 (CardBus bridges) */ -/* 0x14-0x15 reserved */ -#define PCI_CB_SEC_STATUS 0x16 /* Secondary status */ -#define PCI_CB_PRIMARY_BUS 0x18 /* PCI bus number */ -#define PCI_CB_CARD_BUS 0x19 /* CardBus bus number */ -#define PCI_CB_SUBORDINATE_BUS 0x1a /* Subordinate bus number */ -#define PCI_CB_LATENCY_TIMER 0x1b /* CardBus latency timer */ -#define PCI_CB_MEMORY_BASE_0 0x1c -#define PCI_CB_MEMORY_LIMIT_0 0x20 -#define PCI_CB_MEMORY_BASE_1 0x24 -#define PCI_CB_MEMORY_LIMIT_1 0x28 -#define PCI_CB_IO_BASE_0 0x2c -#define PCI_CB_IO_BASE_0_HI 0x2e -#define PCI_CB_IO_LIMIT_0 0x30 -#define PCI_CB_IO_LIMIT_0_HI 0x32 -#define PCI_CB_IO_BASE_1 0x34 -#define PCI_CB_IO_BASE_1_HI 0x36 -#define PCI_CB_IO_LIMIT_1 0x38 -#define PCI_CB_IO_LIMIT_1_HI 0x3a -#define PCI_CB_IO_RANGE_MASK ~0x03 -/* 0x3c-0x3d are same as for htype 0 */ -#define PCI_CB_BRIDGE_CONTROL 0x3e -#define PCI_CB_BRIDGE_CTL_PARITY 0x01 /* Similar to standard bridge control register */ -#define PCI_CB_BRIDGE_CTL_SERR 0x02 -#define PCI_CB_BRIDGE_CTL_ISA 0x04 -#define PCI_CB_BRIDGE_CTL_VGA 0x08 -#define PCI_CB_BRIDGE_CTL_MASTER_ABORT 0x20 -#define PCI_CB_BRIDGE_CTL_CB_RESET 0x40 /* CardBus reset */ -#define PCI_CB_BRIDGE_CTL_16BIT_INT 0x80 /* Enable interrupt for 16-bit cards */ -#define PCI_CB_BRIDGE_CTL_PREFETCH_MEM0 0x100 /* Prefetch enable for both memory regions */ -#define PCI_CB_BRIDGE_CTL_PREFETCH_MEM1 0x200 -#define PCI_CB_BRIDGE_CTL_POST_WRITES 0x400 -#define PCI_CB_SUBSYSTEM_VENDOR_ID 0x40 -#define PCI_CB_SUBSYSTEM_ID 0x42 -#define PCI_CB_LEGACY_MODE_BASE 0x44 /* 16-bit PC Card legacy mode base address (ExCa) */ -/* 0x48-0x7f reserved */ - -/* Device classes and subclasses */ - -#define PCI_CLASS_NOT_DEFINED 0x0000 -#define PCI_CLASS_NOT_DEFINED_VGA 0x0001 - -#define PCI_BASE_CLASS_STORAGE 0x01 -#define PCI_CLASS_STORAGE_SCSI 0x0100 -#define PCI_CLASS_STORAGE_IDE 0x0101 -#define PCI_CLASS_STORAGE_FLOPPY 0x0102 -#define PCI_CLASS_STORAGE_IPI 0x0103 -#define PCI_CLASS_STORAGE_RAID 0x0104 -#define PCI_CLASS_STORAGE_OTHER 0x0180 - -#define PCI_BASE_CLASS_NETWORK 0x02 -#define PCI_CLASS_NETWORK_ETHERNET 0x0200 -#define PCI_CLASS_NETWORK_TOKEN_RING 0x0201 -#define PCI_CLASS_NETWORK_FDDI 0x0202 -#define PCI_CLASS_NETWORK_ATM 0x0203 -#define PCI_CLASS_NETWORK_OTHER 0x0280 - -#define PCI_BASE_CLASS_DISPLAY 0x03 -#define PCI_CLASS_DISPLAY_VGA 0x0300 -#define PCI_CLASS_DISPLAY_XGA 0x0301 -#define PCI_CLASS_DISPLAY_OTHER 0x0380 - -#define PCI_BASE_CLASS_MULTIMEDIA 0x04 -#define PCI_CLASS_MULTIMEDIA_VIDEO 0x0400 -#define PCI_CLASS_MULTIMEDIA_AUDIO 0x0401 -#define PCI_CLASS_MULTIMEDIA_OTHER 0x0480 - -#define PCI_BASE_CLASS_MEMORY 0x05 -#define PCI_CLASS_MEMORY_RAM 0x0500 -#define PCI_CLASS_MEMORY_FLASH 0x0501 -#define PCI_CLASS_MEMORY_OTHER 0x0580 - -#define PCI_BASE_CLASS_BRIDGE 0x06 -#define PCI_CLASS_BRIDGE_HOST 0x0600 -#define PCI_CLASS_BRIDGE_ISA 0x0601 -#define PCI_CLASS_BRIDGE_EISA 0x0602 -#define PCI_CLASS_BRIDGE_MC 0x0603 -#define PCI_CLASS_BRIDGE_PCI 0x0604 -#define PCI_CLASS_BRIDGE_PCMCIA 0x0605 -#define PCI_CLASS_BRIDGE_NUBUS 0x0606 -#define PCI_CLASS_BRIDGE_CARDBUS 0x0607 -#define PCI_CLASS_BRIDGE_OTHER 0x0680 - -#define PCI_BASE_CLASS_COMMUNICATION 0x07 -#define PCI_CLASS_COMMUNICATION_SERIAL 0x0700 -#define PCI_CLASS_COMMUNICATION_PARALLEL 0x0701 -#define PCI_CLASS_COMMUNICATION_OTHER 0x0780 - -#define PCI_BASE_CLASS_SYSTEM 0x08 -#define PCI_CLASS_SYSTEM_PIC 0x0800 -#define PCI_CLASS_SYSTEM_DMA 0x0801 -#define PCI_CLASS_SYSTEM_TIMER 0x0802 -#define PCI_CLASS_SYSTEM_RTC 0x0803 -#define PCI_CLASS_SYSTEM_OTHER 0x0880 - -#define PCI_BASE_CLASS_INPUT 0x09 -#define PCI_CLASS_INPUT_KEYBOARD 0x0900 -#define PCI_CLASS_INPUT_PEN 0x0901 -#define PCI_CLASS_INPUT_MOUSE 0x0902 -#define PCI_CLASS_INPUT_OTHER 0x0980 - -#define PCI_BASE_CLASS_DOCKING 0x0a -#define PCI_CLASS_DOCKING_GENERIC 0x0a00 -#define PCI_CLASS_DOCKING_OTHER 0x0a01 - -#define PCI_BASE_CLASS_PROCESSOR 0x0b -#define PCI_CLASS_PROCESSOR_386 0x0b00 -#define PCI_CLASS_PROCESSOR_486 0x0b01 -#define PCI_CLASS_PROCESSOR_PENTIUM 0x0b02 -#define PCI_CLASS_PROCESSOR_ALPHA 0x0b10 -#define PCI_CLASS_PROCESSOR_POWERPC 0x0b20 -#define PCI_CLASS_PROCESSOR_CO 0x0b40 - -#define PCI_BASE_CLASS_SERIAL 0x0c -#define PCI_CLASS_SERIAL_FIREWIRE 0x0c00 -#define PCI_CLASS_SERIAL_ACCESS 0x0c01 -#define PCI_CLASS_SERIAL_SSA 0x0c02 -#define PCI_CLASS_SERIAL_USB 0x0c03 -#define PCI_CLASS_SERIAL_FIBER 0x0c04 - -#define PCI_CLASS_OTHERS 0xff +/* Not a valid ID, used to match any device ID */ +#define PCI_ID_ANY 0xffff /* * Vendor and card ID's: sort these numerically according to vendor @@ -1063,117 +799,4 @@ extern "C" { #define PCI_DEVICE_ID_ARK_STINGARK 0xa099 #define PCI_DEVICE_ID_ARK_2000MT 0xa0a1 -/* - * The PCI interface treats multi-function devices as independent - * devices. The slot/function address of each device is encoded - * in a single byte as follows: - * - * 7:3 = slot - * 2:0 = function - */ -#define PCI_DEVFN(slot,func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) -#define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f) -#define PCI_FUNC(devfn) ((devfn) & 0x07) - -/* - * Error values that may be returned by the PCI bios. - */ -#define PCIBIOS_SUCCESSFUL 0x00 -#define PCIBIOS_FUNC_NOT_SUPPORTED 0x81 -#define PCIBIOS_BAD_VENDOR_ID 0x83 -#define PCIBIOS_DEVICE_NOT_FOUND 0x86 -#define PCIBIOS_BAD_REGISTER_NUMBER 0x87 -#define PCIBIOS_SET_FAILED 0x88 -#define PCIBIOS_BUFFER_TOO_SMALL 0x89 - -/* T. Straumann, 7/31/2001: increased to 32 - PMC slots are not - * scanned on mvme2306 otherwise - */ -#define PCI_MAX_DEVICES 32 -#define PCI_MAX_FUNCTIONS 8 - -typedef struct { - int (*read_config_byte)(unsigned char, unsigned char, unsigned char, - unsigned char, unsigned char *); - int (*read_config_word)(unsigned char, unsigned char, unsigned char, - unsigned char, unsigned short *); - int (*read_config_dword)(unsigned char, unsigned char, unsigned char, - unsigned char, unsigned int *); - int (*write_config_byte)(unsigned char, unsigned char, unsigned char, - unsigned char, unsigned char); - int (*write_config_word)(unsigned char, unsigned char, unsigned char, - unsigned char, unsigned short); - int (*write_config_dword)(unsigned char, unsigned char, unsigned char, - unsigned char, unsigned int); -} pci_config_access_functions; - -typedef struct { - volatile unsigned char* pci_config_addr; - volatile unsigned char* pci_config_data; - const pci_config_access_functions* pci_functions; -} rtems_pci_config_t; - -extern rtems_pci_config_t BSP_pci_configuration; - -extern inline int -pci_read_config_byte(unsigned char bus, unsigned char slot, unsigned char function, - unsigned char where, unsigned char * val) { - return BSP_pci_configuration.pci_functions->read_config_byte(bus, slot, function, where, val); -} - -extern inline int -pci_read_config_word(unsigned char bus, unsigned char slot, unsigned char function, - unsigned char where, unsigned short * val) { - return BSP_pci_configuration.pci_functions->read_config_word(bus, slot, function, where, val); -} - -extern inline int -pci_read_config_dword(unsigned char bus, unsigned char slot, unsigned char function, - unsigned char where, unsigned int * val) { - return BSP_pci_configuration.pci_functions->read_config_dword(bus, slot, function, where, val); -} - -extern inline int -pci_write_config_byte(unsigned char bus, unsigned char slot, unsigned char function, - unsigned char where, unsigned char val) { - return BSP_pci_configuration.pci_functions->write_config_byte(bus, slot, function, where, val); -} - -extern inline int -pci_write_config_word(unsigned char bus, unsigned char slot, unsigned char function, - unsigned char where, unsigned short val) { - return BSP_pci_configuration.pci_functions->write_config_word(bus, slot, function, where, val); -} - -extern inline int -pci_write_config_dword(unsigned char bus, unsigned char slot, unsigned char function, - unsigned char where, unsigned int val) { - return BSP_pci_configuration.pci_functions->write_config_dword(bus, slot, function, where, val); -} - -/* - * Return the number of PCI busses in the system - */ -extern unsigned char BusCountPCI(void); -extern int init_pci(void); - -extern int dma_to_pci(unsigned int addr, unsigned int paddr, unsigned int len); -extern int dma_from_pci(unsigned int addr, unsigned int paddr, unsigned int len); -extern void pci_mem_enable(unsigned char bus, unsigned char slot, unsigned char function); -extern void pci_master_enable(unsigned char bus, unsigned char slot, unsigned char function); - -/* scan for a specific device */ -/* find a particular PCI device - * (currently, only bus0 is scanned for device/fun0) - * - * RETURNS: zero on success, bus/dev/fun in *pbus / *pdev / *pfun - */ -int -BSP_pciFindDevice(unsigned short vendorid, unsigned short deviceid, - int instance, int *pbus, int *pdev, int *pfun); - -#ifdef __cplusplus -} -#endif - -#endif /* RTEMS_PCI_H */ +#endif /* !__PCI_IDS_H__ */ diff --git a/cpukit/libpci/pci/ids_extra.h b/cpukit/libpci/pci/ids_extra.h new file mode 100644 index 0000000000..ffa18cb271 --- /dev/null +++ b/cpukit/libpci/pci/ids_extra.h @@ -0,0 +1,19 @@ +/* RTEMS local PCI data base */ + +/* Only included from pci_ids.h */ +#ifndef __PCI_IDS_H__ +#error pci/ids_extra.h should only be included from pci/ids.h +#endif + +/* Gaisler PCI IDs */ +#define PCIID_VENDOR_GAISLER 0x1AC8 +#define PCIID_VENDOR_GAISLER_OLD 0x16E3 + +/* Gaisler PCI Devices */ +#define PCIID_DEVICE_GR_RASTA_IO 0x0010 /* GR-RASTA-IO */ +#define PCIID_DEVICE_GR_RASTA_IO_OLD 0x0210 /* old GR-RASTA-IO ID*/ +#define PCIID_DEVICE_GR_RASTA_TMTC 0x0011 /* GR-RASTA-TMTC */ +#define PCIID_DEVICE_GR_RASTA_ADCDAC 0x0014 /* GR-RASTA-ADCDAC */ +#define PCIID_DEVICE_GR_701 0x0701 /* GR-701 */ +#define PCIID_DEVICE_GR_TMTC_1553 0x0198 /* GR-TMTC-1553 */ +#define PCIID_DEVICE_GR_RASTA_SPW_RTR 0x0062 /* GR-RASTA-SPW-ROUTER */ diff --git a/cpukit/libpci/pci/irq.h b/cpukit/libpci/pci/irq.h new file mode 100644 index 0000000000..87ca154073 --- /dev/null +++ b/cpukit/libpci/pci/irq.h @@ -0,0 +1,98 @@ +/* PCI IRQ Library + * + * IRQ handling does not have so much with PCI to do, this library depends + * on the BSP to implement shared interrupts. + */ + +#ifndef __PCI_IRQ_H__ +#define __PCI_IRQ_H__ + +#include <bsp.h> + +/* PCI Handler (ISR) called when IRQ is generated by any of the PCI devices + * connected to the same PCI IRQ Pin. This is been defined the same way as + * rtems_interrupt_handler in order for BSPs to "direct-map" the register + * and unregister functions rtems_interrupt_handler_install/remove + */ +typedef void (*pci_isr)(void *arg); + +/* Get assigned system IRQ to a PCI Device. If no IRQ 0 is returned */ +extern int pci_dev_irq(pci_dev_t dev); + +/* Register shared PCI IRQ handler, but does not enable it. The system interrupt + * number is read from the PCI board's PCI configuration space header iline + * field. The iline field is initialized by the PCI subsystem during start up, + * the ipin field is translated into a system IRQ and written to iline. The + * board's driver should use the iline field as the irq argument to this + * function. + * + * Arguments + * irq System IRQ number, normally taken from the PCI configuration area + * isr Function pointer to the ISR + * arg Second argument to function isr + */ +static inline int pci_interrupt_register(int irq, const char *info, + pci_isr isr, void *arg) +{ + return BSP_PCI_shared_interrupt_register(irq, info, isr, arg); +} + +/* Unregister previously registered shared PCI IRQ handler + * + * Arguments + * irq System IRQ number, normally taken from the PCI configuration area + * isr Function pointer to the ISR + * arg Second argument to function isr + */ +static inline int pci_interrupt_unregister(int irq, pci_isr isr, void *arg) +{ + return BSP_PCI_shared_interrupt_unregister(irq, isr, arg); +} + +/* Enable shared PCI IRQ handler. This function will unmask the interrupt + * controller and mark this interrupt handler ready to handle interrupts. Note + * that since it is a shared interrupt handler service the interrupt may + * already be enabled, however no calls to this specific handler is made + * until it is enabled. + * + * Arguments + * irq System IRQ number, normally taken from the PCI configuration area + * isr Function pointer to the ISR + * arg Second argument to function isr + */ +static inline void pci_interrupt_unmask(int irq) +{ + BSP_PCI_shared_interrupt_unmask(irq); +} + +/* Disable shared PCI IRQ handler. This function will mask the interrupt + * controller and mark this interrupt handler not ready to receive interrupts. + * Note that since it is a shared interrupt handler service the interrupt may + * still be enabled, however no calls to this specific handler is made + * while it is disabled. + * + * Arguments + * irq System IRQ number, normally taken from the PCI configuration area + * isr Function pointer to the ISR + * arg Second argument to function isr + */ +static inline void pci_interrupt_mask(int irq) +{ + BSP_PCI_shared_interrupt_mask(irq); +} + +/* Acknowledge the interrupt controller by writing to the interrupt controller. + * Note that since it is a shared interrupt handler service, clearing the + * interrupt source may affect other ISRs registered to this IRQ. + * + * Arguments + * irq System IRQ number, normally taken from the PCI configuration area + * isr Function pointer to the ISR + * arg Second argument to function isr + */ +static inline void pci_interrupt_clear(int irq) +{ + BSP_PCI_shared_interrupt_clear(irq); +} + +#endif /* !__PCI_IRQ_H__ */ diff --git a/cpukit/libpci/pci_access.c b/cpukit/libpci/pci_access.c new file mode 100644 index 0000000000..bf14328e06 --- /dev/null +++ b/cpukit/libpci/pci_access.c @@ -0,0 +1,77 @@ +/* PCI Access Library + * + * COPYRIGHT (c) 2010-2011. + * Aeroflex Gaisler Research + * + * 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. + * + * 2011-02-11, Daniel Hellstrom <daniel@gaisler.com> + * created + */ + +#include <pci.h> +#include <pci/access.h> + +/* Access Routines valid after a PCI-Access-Driver has registered */ +struct pci_access_drv pci_access_ops = { + .cfg = {.read8 = 0}, +}; + +/* Read a 8-bit register over configuration space */ +int pci_cfg_r8(pci_dev_t dev, int ofs, uint8_t *data) +{ + return pci_access_ops.cfg.read8(dev, ofs, data); +} + +/* Read a 16-bit register over configuration space */ +int pci_cfg_r16(pci_dev_t dev, int ofs, uint16_t *data) +{ + return pci_access_ops.cfg.read16(dev, ofs, data); +} + +/* Read a 32-bit register over configuration space */ +int pci_cfg_r32(pci_dev_t dev, int ofs, uint32_t *data) +{ + return pci_access_ops.cfg.read32(dev, ofs, data); +} + +/* Write a 8-bit register over configuration space */ +int pci_cfg_w8(pci_dev_t dev, int ofs, uint8_t data) +{ + return pci_access_ops.cfg.write8(dev, ofs, data); +} + +/* Write a 16-bit register over configuration space */ +int pci_cfg_w16(pci_dev_t dev, int ofs, uint16_t data) +{ + return pci_access_ops.cfg.write16(dev, ofs, data); +} + +/* Write a 32-bit register over configuration space */ +int pci_cfg_w32(pci_dev_t dev, int ofs, uint32_t data) +{ + return pci_access_ops.cfg.write32(dev, ofs, data); +} + +void pci_modify_cmdsts(pci_dev_t dev, uint32_t mask, uint32_t val) +{ + uint32_t data; + + pci_cfg_r32(dev, PCI_COMMAND, &data); + data &= ~mask; + data |= val; + pci_cfg_w32(dev, PCI_COMMAND, data); +} + +/* Register a driver for handling access to PCI */ +int pci_access_drv_register(struct pci_access_drv *drv) +{ + if (pci_access_ops.cfg.read8) + return -1; /* Already registered a driver.. */ + + pci_access_ops = *drv; + + return 0; +} diff --git a/cpukit/libpci/pci_access_func.c b/cpukit/libpci/pci_access_func.c new file mode 100644 index 0000000000..5ef1a4d223 --- /dev/null +++ b/cpukit/libpci/pci_access_func.c @@ -0,0 +1,63 @@ +#include <pci.h> + +/* Get PCI I/O or Configuration space access function */ +static int pci_ioc_func(int wr, int size, void **func, void **ops) +{ + int ofs; + + ofs = 0; + if (wr) + ofs += 3; + if (size == 4) + size = 3; + ofs += (size & 0x3) - 1; + if (ops[ofs] == NULL) + return -1; + if (func) + *func = ops[ofs]; + return 0; +} + +/* Get Registers-over-Memory Space access function */ +static int pci_memreg_func(int wr, int size, void **func, int endian) +{ + void **ops; + int ofs = 0; + + ops = (void **)pci_access_ops.memreg; + if (!ops) + return -1; + + if (size == 2) + ofs += 2; + else if (size == 4) + ofs += 6; + + if (size != 1 && endian == PCI_BIG_ENDIAN) + ofs += 2; + + if (wr) + ofs += 1; + + if (ops[ofs] == NULL) + return -1; + if (func) + *func = ops[ofs]; + return 0; +} + +/* Get function pointer from Host/BSP driver definitions */ +int pci_access_func(int wr, int size, void **func, int endian, int type) +{ + switch (type) { + default: + case 2: /* Memory Space - not implemented */ + return -1; + case 1: /* I/O space */ + return pci_ioc_func(wr, size, func, (void**)&pci_access_ops.cfg); + case 3: /* Registers over Memory space */ + return pci_memreg_func(wr, size, func, endian); + case 4: /* Configuration space */ + return pci_ioc_func(wr, size, func, (void**)&pci_access_ops.io); + } +} diff --git a/cpukit/libpci/pci_access_io.c b/cpukit/libpci/pci_access_io.c new file mode 100644 index 0000000000..4413aee3e1 --- /dev/null +++ b/cpukit/libpci/pci_access_io.c @@ -0,0 +1,51 @@ +/* PCI Access Library + * + * COPYRIGHT (c) 2010-2011. + * Aeroflex Gaisler Research + * + * 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. + * + * 2011-02-11, Daniel Hellstrom <daniel@gaisler.com> + * created + */ + +#include <pci.h> +#include <pci/access.h> + +/* Read a 8-bit register over PCI I/O Space */ +uint8_t pci_io_r8(uint32_t adr) +{ + return pci_access_ops.io.read8((uint8_t *)adr); +} + +/* Read a 16-bit I/O Register */ +uint16_t pci_io_r16(uint32_t adr) +{ + return pci_access_ops.io.read16((uint16_t *)adr); +} + +/* Read a 32-bit I/O Register */ +uint32_t pci_io_r32(uint32_t adr) +{ + return pci_access_ops.io.read32((uint32_t *)adr); +} + +/* Write a 8-bit I/O Register */ +void pci_io_w8(uint32_t adr, uint8_t data) +{ + pci_access_ops.io.write8((uint8_t *)adr, data); +} + +/* Write a 16-bit I/O Register */ +void pci_io_w16(uint32_t adr, uint16_t data) +{ + pci_access_ops.io.write16((uint16_t *)adr, data); +} + +/* Write a 32-bit I/O Register */ +void pci_io_w32(uint32_t adr, uint32_t data) +{ + pci_access_ops.io.write32((uint32_t *)adr, data); +} diff --git a/cpukit/libpci/pci_access_mem.c b/cpukit/libpci/pci_access_mem.c new file mode 100644 index 0000000000..dcdd0a6a15 --- /dev/null +++ b/cpukit/libpci/pci_access_mem.c @@ -0,0 +1,11 @@ +#include <pci.h> + +uint8_t pci_mem_ld8(uint8_t *adr) +{ + return *adr; +} + +void pci_mem_st8(uint8_t *adr, uint8_t data) +{ + *adr = data; +} diff --git a/cpukit/libpci/pci_access_mem_be.c b/cpukit/libpci/pci_access_mem_be.c new file mode 100644 index 0000000000..0746588d13 --- /dev/null +++ b/cpukit/libpci/pci_access_mem_be.c @@ -0,0 +1,62 @@ +/* Registers-over-Memory Space - Generic Big endian PCI bus definitions */ + +#include <pci.h> + +/* Same for Little and Big endian PCI buses */ +extern uint8_t pci_mem_ld8(uint8_t *adr); +extern void pci_mem_st8(uint8_t *adr, uint8_t data); + +uint16_t pci_mem_be_ld_le16(uint16_t *adr) +{ + return ld_be16(adr); +} + +uint16_t pci_mem_be_ld_be16(uint16_t *adr) +{ + return ld_le16(adr); +} + +uint32_t pci_mem_be_ld_le32(uint32_t *adr) +{ + return ld_be32(adr); +} + +uint32_t pci_mem_be_ld_be32(uint32_t *adr) +{ + return ld_le32(adr); +} + +void pci_mem_be_st_le16(uint16_t *adr, uint16_t data) +{ + st_be16(adr, data); +} + +void pci_mem_be_st_be16(uint16_t *adr, uint16_t data) +{ + st_le16(adr, data); +} + +void pci_mem_be_st_le32(uint32_t *adr, uint32_t data) +{ + st_be32(adr, data); +} + +void pci_mem_be_st_be32(uint32_t *adr, uint32_t data) +{ + st_le32(adr, data); +} + +struct pci_memreg_ops pci_mem_be_ops = { + .ld8 = pci_mem_ld8, + .st8 = pci_mem_st8, + + .ld_le16 = pci_mem_be_ld_le16, + .st_le16 = pci_mem_be_st_le16, + .ld_be16 = pci_mem_be_ld_be16, + .st_be16 = pci_mem_be_st_be16, + + .ld_le32 = pci_mem_be_ld_le32, + .st_le32 = pci_mem_be_st_le32, + .ld_be32 = pci_mem_be_ld_be32, + .st_be32 = pci_mem_be_st_be32, +}; diff --git a/cpukit/libpci/pci_access_mem_le.c b/cpukit/libpci/pci_access_mem_le.c new file mode 100644 index 0000000000..d00e13121a --- /dev/null +++ b/cpukit/libpci/pci_access_mem_le.c @@ -0,0 +1,61 @@ +/* Registers-over-Memory Space - Generic Little endian PCI bus definitions */ + +#include <pci.h> + +/* Same for Little and Big endian PCI buses */ +extern uint8_t pci_mem_ld8(uint8_t *adr); +extern void pci_mem_st8(uint8_t *adr, uint8_t data); + +uint16_t pci_mem_le_ld_le16(uint16_t *adr) +{ + return ld_le16(adr); +} + +uint16_t pci_mem_le_ld_be16(uint16_t *adr) +{ + return ld_be16(adr); +} + +uint32_t pci_mem_le_ld_le32(uint32_t *adr) +{ + return ld_le32(adr); +} + +uint32_t pci_mem_le_ld_be32(uint32_t *adr) +{ + return ld_be32(adr); +} + +void pci_mem_le_st_le16(uint16_t *adr, uint16_t data) +{ + st_le16(adr, data); +} + +void pci_mem_le_st_be16(uint16_t *adr, uint16_t data) +{ + st_be16(adr, data); +} + +void pci_mem_le_st_le32(uint32_t *adr, uint32_t data) +{ + st_le32(adr, data); +} + +void pci_mem_le_st_be32(uint32_t *adr, uint32_t data) +{ + st_be32(adr, data); +} + +struct pci_memreg_ops pci_mem_le_ops = { + .ld8 = pci_mem_ld8, + .st8 = pci_mem_st8, + + .ld_le16 = pci_mem_le_ld_le16, + .st_le16 = pci_mem_le_st_le16, + .ld_be16 = pci_mem_le_ld_be16, + .st_be16 = pci_mem_le_st_be16, + .ld_le32 = pci_mem_le_ld_le32, + .st_le32 = pci_mem_le_st_le32, + .ld_be32 = pci_mem_le_ld_be32, + .st_be32 = pci_mem_le_st_be32, +}; diff --git a/cpukit/libpci/pci_bus.c b/cpukit/libpci/pci_bus.c new file mode 100644 index 0000000000..b8b4b8bb8d --- /dev/null +++ b/cpukit/libpci/pci_bus.c @@ -0,0 +1,564 @@ +/* PCI bus driver. + * + * COPYRIGHT (c) 2008. + * Aeroflex Gaisler. + * + * General part of PCI Bus driver. The driver is typically + * initialized from the PCI host driver separating the host + * driver from the common parts in PCI drivers. + * The PCI library must be initialized before starting the + * PCI bus driver. The PCI library have set up BARs and + * assigned system IRQs for targets. + * This PCI bus driver rely on the PCI library (pci.c) for + * interrupt registeration (pci_interrupt_register) and PCI + * target set up. + * + * 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. + * + * 2008-12-03, Daniel Hellstrom <daniel@gaisler.com> + * Created + * + */ + +/* Use PCI Configuration libarary pci_hb RAM device structure to find devices, + * undefine to access PCI configuration space directly. + */ +#define USE_PCI_CFG_LIB + +/* On small systems undefine PCIBUS_INFO to avoid sprintf get dragged in */ +#define PCIBUS_INFO + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <pci.h> +#ifdef USE_PCI_CFG_LIB +#include <pci/cfg.h> +#endif +#include <pci/irq.h> + +#include <drvmgr/drvmgr.h> +#include <drvmgr/pci_bus.h> + + +#define DBG(args...) +/*#define DBG(args...) printk(args)*/ +int pcibus_bus_init1(struct drvmgr_bus *bus); +int pcibus_unite(struct drvmgr_drv *drv, struct drvmgr_dev *dev); +int pcibus_int_register( + struct drvmgr_dev *dev, + int index, + const char *info, + drvmgr_isr isr, + void *arg); +int pcibus_int_unregister( + struct drvmgr_dev *dev, + int index, + drvmgr_isr isr, + void *arg); +int pcibus_int_clear( + struct drvmgr_dev *dev, + int index); +int pcibus_freq_get( + struct drvmgr_dev *dev, + int options, + unsigned int *freq_hz); + +int pcibus_get_params(struct drvmgr_dev *dev, struct drvmgr_bus_params *params); + +void pcibus_dev_info( + struct drvmgr_dev *dev, + void (*print_line)(void *p, char *str), + void *p); + +struct drvmgr_bus_ops pcibus_ops = { + .init = { + pcibus_bus_init1, + NULL, + NULL, + NULL + }, + .remove = NULL, + .unite = pcibus_unite, + .int_register = pcibus_int_register, + .int_unregister = pcibus_int_unregister, +#if 0 + .int_enable = pcibus_int_enable, + .int_disable = pcibus_int_disable, +#endif + .int_clear = pcibus_int_clear, + .int_mask = NULL, + .int_unmask = NULL, + .get_params = pcibus_get_params, + .freq_get = pcibus_freq_get, +#ifdef PCIBUS_INFO + .info_dev = pcibus_dev_info, +#endif +}; + +struct drvmgr_func pcibus_funcs[] = { + DRVMGR_FUNC(PCI_FUNC_MREG_R8, NULL), + DRVMGR_FUNC(PCI_FUNC_MREG_R16, NULL), + DRVMGR_FUNC(PCI_FUNC_MREG_R32, NULL), + DRVMGR_FUNC(PCI_FUNC_MREG_W8, NULL), + DRVMGR_FUNC(PCI_FUNC_MREG_W16, NULL), + DRVMGR_FUNC(PCI_FUNC_MREG_W32, NULL), + DRVMGR_FUNC_END +}; + +/* Driver resources configuration for the PCI bus. It is declared weak so that + * the user may override it from the project file, if the default settings are + * not enough. + */ +struct drvmgr_bus_res pcibus_drv_resources __attribute__((weak)) = { + .next = NULL, + .resource = { + RES_EMPTY, + }, +}; + +struct pcibus_priv { + struct drvmgr_dev *dev; +}; + +static int compatible(struct pci_dev_id *id, struct pci_dev_id_match *drv) +{ + if (((drv->vendor==PCI_ID_ANY) || (id->vendor==drv->vendor)) && + ((drv->device==PCI_ID_ANY) || (id->device==drv->device)) && + ((drv->subvendor==PCI_ID_ANY) || (id->subvendor==drv->subvendor)) && + ((drv->subdevice==PCI_ID_ANY) || (id->subdevice==drv->subdevice)) && + ((id->class & drv->class_mask) == drv->class)) + return 1; + else + return 0; +} + +int pcibus_unite(struct drvmgr_drv *drv, + struct drvmgr_dev *dev) +{ + struct pci_drv_info *pdrv; + struct pci_dev_id_match *drvid; + struct pci_dev_info *pci; + + if (!drv || !dev || !dev->parent) + return 0; + + if ((drv->bus_type != DRVMGR_BUS_TYPE_PCI) || + (dev->parent->bus_type != DRVMGR_BUS_TYPE_PCI)) + return 0; + + pci = (struct pci_dev_info *)dev->businfo; + if (!pci) + return 0; + + pdrv = (struct pci_drv_info *)drv; + drvid = pdrv->ids; + if (!drvid) + return 0; + while (drvid->vendor != 0) { + if (compatible(&pci->id, drvid)) { + /* Unite device and driver */ + DBG("DRV %p and DEV %p united\n", drv, dev); + return 1; + } + drvid++; + } + + return 0; +} + +static int pcibus_int_get(struct drvmgr_dev *dev, int index) +{ + int irq; + + /* Relative (positive) or absolute (negative) IRQ number */ + if (index > 0) { + /* PCI devices only have one IRQ per function */ + return -1; + } else if (index == 0) { + /* IRQ Index relative to Cores base IRQ */ + + /* Get Base IRQ */ + irq = ((struct pci_dev_info *)dev->businfo)->irq; + if (irq <= 0) + return -1; + } else { + /* Absolute IRQ number */ + irq = -index; + } + return irq; +} + +/* Use standard PCI facility to register interrupt handler */ +int pcibus_int_register( + struct drvmgr_dev *dev, + int index, + const char *info, + drvmgr_isr isr, + void *arg) +{ + struct drvmgr_dev *busdev; + int irq; + + busdev = dev->parent->dev; + + /* Get IRQ number from index and device information */ + irq = pcibus_int_get(dev, index); + if (irq < 0) + return -1; + + DBG("Register PCI interrupt on %p for dev %p (IRQ: %d)\n", + busdev, dev, irq); + + return pci_interrupt_register(irq, info, isr, arg); +} + +/* Use standard PCI facility to unregister interrupt handler */ +int pcibus_int_unregister( + struct drvmgr_dev *dev, + int index, + drvmgr_isr isr, + void *arg) +{ + struct drvmgr_dev *busdev; + int irq; + + busdev = dev->parent->dev; + + /* Get IRQ number from index and device information */ + irq = pcibus_int_get(dev, index); + if (irq < 0) + return -1; + + DBG("Unregister PCI interrupt on %p for dev %p (IRQ: %d)\n", + busdev, dev, irq); + + return pci_interrupt_unregister(irq, isr, arg); +} + +/* Use standard PCI facility to clear interrupt */ +int pcibus_int_clear( + struct drvmgr_dev *dev, + int index) +{ + int irq; + + /* Get IRQ number from index and device information */ + irq = pcibus_int_get(dev, index); + if (irq < 0) + return -1; + + pci_interrupt_clear(irq); + + return 0; +} + +int pcibus_freq_get( + struct drvmgr_dev *dev, + int options, + unsigned int *freq_hz) +{ + /* Standard PCI Bus frequency */ + *freq_hz = 33000000; + return 0; +} + +int pcibus_get_params(struct drvmgr_dev *dev, struct drvmgr_bus_params *params) +{ + /* No device prefix */ + params->dev_prefix = NULL; + + return 0; +} + +#ifdef PCIBUS_INFO +void pcibus_dev_info( + struct drvmgr_dev *dev, + void (*print_line)(void *p, char *str), + void *p) +{ + struct pci_dev_info *devinfo; + struct pcibus_res *pcibusres; + struct pci_res *res; + char buf[64]; + int i; + char *str1, *res_types[3] = {" IO16", "MEMIO", " MEM"}; + uint32_t pcistart; + + if (!dev) + return; + + devinfo = (struct pci_dev_info *)dev->businfo; + if (!devinfo) + return; + + if ((devinfo->id.class >> 8) == PCI_CLASS_BRIDGE_PCI) + print_line(p, "PCI BRIDGE DEVICE"); + else + print_line(p, "PCI DEVICE"); + sprintf(buf, "LOCATION: BUS:SLOT:FUNCTION [%x:%x:%x]", + PCI_DEV_EXPAND(devinfo->pcidev)); + print_line(p, buf); + sprintf(buf, "PCIID 0x%lx", (uint32_t)devinfo->pcidev); + print_line(p, buf); + sprintf(buf, "VENDOR ID: %04x", devinfo->id.vendor); + print_line(p, buf); + sprintf(buf, "DEVICE ID: %04x", devinfo->id.device); + print_line(p, buf); + sprintf(buf, "SUBVEN ID: %04x", devinfo->id.subvendor); + print_line(p, buf); + sprintf(buf, "SUBDEV ID: %04x", devinfo->id.subdevice); + print_line(p, buf); + sprintf(buf, "CLASS: %lx", devinfo->id.class); + print_line(p, buf); + sprintf(buf, "REVISION: %x", devinfo->rev); + print_line(p, buf); + sprintf(buf, "IRQ: %d", devinfo->irq); + print_line(p, buf); + sprintf(buf, "PCIDEV ptr: %p", devinfo->pci_device); + print_line(p, buf); + + /* List Resources */ + print_line(p, "RESOURCES"); + for (i = 0; i < PCIDEV_RES_CNT; i++) { + pcibusres = &devinfo->resources[i]; + + str1 = " RES"; + pcistart = -1; + res = pcibusres->res; + if (res && (res->flags & PCI_RES_TYPE_MASK)) { + str1 = res_types[(res->flags & PCI_RES_TYPE_MASK) - 1]; + if (res->flags & PCI_RES_IO32) + str1 = " IO32"; + pcistart = res->start; + } + + if (res && (res->flags & PCI_RES_FAIL)) { + sprintf(buf, " %s[%d]: NOT ASSIGNED", str1, i); + print_line(p, buf); + continue; + } + if (!pcibusres->size) + continue; + + sprintf(buf, " %s[%d]: %08lx-%08lx [PCIADR %lx]", + str1, i, pcibusres->address, + pcibusres->address + pcibusres->size - 1, pcistart); + print_line(p, buf); + } +} +#endif + +#ifdef USE_PCI_CFG_LIB + +int pcibus_dev_register(struct pci_dev *dev, void *arg) +{ + struct drvmgr_bus *pcibus = arg; + struct drvmgr_dev *newdev; + struct pci_dev_info *pciinfo; + int i, type; + struct pcibus_res *pcibusres; + struct pci_res *pcires; + + pci_dev_t pcidev = dev->busdevfun; + + DBG("PCI DEV REGISTER: %x:%x:%x\n", PCI_DEV_EXPAND(pcidev)); + + /* Allocate a device */ + drvmgr_alloc_dev(&newdev, 24 + sizeof(struct pci_dev_info)); + newdev->next = NULL; + newdev->parent = pcibus; /* Ourselfs */ + newdev->minor_drv = 0; + newdev->minor_bus = 0; + newdev->priv = NULL; + newdev->drv = NULL; + newdev->name = (char *)(newdev + 1); + newdev->next_in_drv = NULL; + newdev->bus = NULL; + + /* Init PnP information, Assign Core interfaces with this device */ + pciinfo = (struct pci_dev_info *)((char *)(newdev + 1) + 24); + + /* Read Device and Vendor */ + pciinfo->id.vendor = dev->vendor; + pciinfo->id.device = dev->device; + pciinfo->id.subvendor = dev->subvendor; + pciinfo->id.subdevice = dev->subdevice; + pciinfo->rev = dev->classrev & 0xff; + pciinfo->id.class = (dev->classrev >> 8) & 0xffffff; + + /* Read IRQ information set by PCI layer */ + pciinfo->irq = dev->sysirq; + + /* Save Location on PCI bus */ + pciinfo->pcidev = pcidev; + + /* Connect device with PCI data structure */ + pciinfo->pci_device = dev; + + /* Build resources so that PCI device drivers doesn't have to scan + * configuration space themselves, also the address is translated + * into CPU accessible addresses. + */ + for (i = 0; i < PCIDEV_RES_CNT; i++) { + pcibusres = &pciinfo->resources[i]; + pcires = &dev->resources[i]; + type = pcires->flags & PCI_RES_TYPE_MASK; + if (type == 0 || (pcires->flags & PCI_RES_FAIL)) + continue; /* size=0 */ + + pcibusres->address = pcires->start; + if (pci_pci2cpu(&pcibusres->address, type)) + continue; /* size=0 */ + pcibusres->res = pcires; + pcibusres->size = pcires->end - pcires->start; + } + + /* Connect device with PCI information */ + newdev->businfo = (void *)pciinfo; + + /* Create Device Name */ + sprintf(newdev->name, "PCI_%x:%x:%x_%04x:%04x", + PCI_DEV_BUS(pcidev), PCI_DEV_SLOT(pcidev), PCI_DEV_FUNC(pcidev), + pciinfo->id.vendor, pciinfo->id.device); + + /* Register New Device */ + drvmgr_dev_register(newdev); + + return 0; +} + +#else + +int pcibus_dev_register(pci_dev_t pcidev, void *arg) +{ + struct drvmgr_bus *pcibus = arg; + struct drvmgr_dev *newdev; + struct pci_dev_info *pciinfo; + + DBG("PCI DEV REGISTER: %x:%x:%x\n", PCI_DEV_EXPAND(pcidev)); + + /* Allocate a device */ + drvmgr_alloc_dev(&newdev, 24 + sizeof(struct pci_dev_info)); + newdev->next = NULL; + newdev->parent = pcibus; /* Ourselfs */ + newdev->minor_drv = 0; + newdev->minor_bus = 0; + newdev->priv = NULL; + newdev->drv = NULL; + newdev->name = (char *)(newdev + 1); + newdev->next_in_drv = NULL; + newdev->bus = NULL; + + /* Init PnP information, Assign Core interfaces with this device */ + pciinfo = (struct pci_dev_info *)((char *)(newdev + 1) + 24); + + /* Read Device and Vendor */ + pci_cfg_r16(pcidev, PCI_VENDOR_ID, &pciinfo->id.vendor); + pci_cfg_r16(pcidev, PCI_DEVICE_ID, &pciinfo->id.device); + pci_cfg_r32(pcidev, PCI_CLASS_REVISION, &pciinfo->id.class); + pciinfo->rev = pciinfo->id.class & 0xff; + pciinfo->id.class = pciinfo->id.class >> 8; + + /* Devices have subsytem device and vendor ID */ + if ((pciinfo->id.class >> 8) != PCI_CLASS_BRIDGE_PCI) { + pci_cfg_r16(pcidev, PCI_SUBSYSTEM_VENDOR_ID, + &pciinfo->id.subvendor); + pci_cfg_r16(pcidev, PCI_SUBSYSTEM_ID, &pciinfo->id.subdevice); + } else { + pciinfo->id.subvendor = 0; + pciinfo->id.subdevice = 0; + } + + /* Read IRQ information set by PCI layer */ + pci_cfg_r8(pcidev, PCI_INTERRUPT_LINE, &pciinfo->irq); + + /* Save Location */ + pciinfo->pcidev = pcidev; + + /* There is no way we can know this information this way */ + pciinfo->pci_device = NULL; + + /* Connect device with PCI information */ + newdev->businfo = (void *)pciinfo; + + /* Create Device Name */ + sprintf(newdev->name, "PCI_%d:%d:%d_%04x:%04x", + PCI_DEV_BUS(pcidev), PCI_DEV_SLOT(pcidev), PCI_DEV_FUNC(pcidev), + pciinfo->id.vendor, pciinfo->id.device); + + /* Register New Device */ + drvmgr_dev_register(newdev); + + return 0; +} + +#endif + +/* Register all AMBA devices available on the AMBAPP bus */ +static int pcibus_devs_register(struct drvmgr_bus *bus) +{ + /* return value 0=DRVMGR_OK works with pci_for_each/pci_for_each_dev */ +#ifdef USE_PCI_CFG_LIB + /* Walk the PCI device tree in RAM */ + return pci_for_each_dev(pcibus_dev_register, bus); +#else + /* Scan PCI Configuration space */ + return pci_for_each(pcibus_dev_register, bus); +#endif +} + +/*** DEVICE FUNCTIONS ***/ + +int pcibus_register(struct drvmgr_dev *dev, struct pcibus_config *config) +{ + struct pcibus_priv *priv; + int i, fid, rc; + + DBG("PCI BUS: initializing\n"); + + /* Create BUS */ + drvmgr_alloc_bus(&dev->bus, sizeof(struct pcibus_priv)); + dev->bus->bus_type = DRVMGR_BUS_TYPE_PCI; + dev->bus->next = NULL; + dev->bus->dev = dev; + dev->bus->children = NULL; + dev->bus->ops = &pcibus_ops; + dev->bus->dev_cnt = 0; + dev->bus->reslist = NULL; + dev->bus->maps_up = config->maps_up; + dev->bus->maps_down = config->maps_down; + dev->bus->funcs = &pcibus_funcs[0]; + + /* Copy function definitions from PCI Layer */ + for (i=0; i<6; i++) { + fid = pcibus_funcs[i].funcid; + rc = pci_access_func(RW_DIR(fid), RW_SIZE(fid), + &pcibus_funcs[i].func, PCI_LITTLE_ENDIAN, 3); + if (rc != 0) + DBG("PCI BUS: MEMREG 0x%x function not defined\n", fid); + } + + /* Add resource configuration if user overrided the default empty cfg */ + if (pcibus_drv_resources.resource[0].drv_id != 0) + drvmgr_bus_res_add(dev->bus, &pcibus_drv_resources); + + /* Init BUS private structures */ + priv = (struct pcibus_priv *)(dev->bus + 1); + dev->bus->priv = priv; + + /* Register BUS */ + drvmgr_bus_register(dev->bus); + + return DRVMGR_OK; +} + +/*** BUS INITIALIZE FUNCTIONS ***/ + +int pcibus_bus_init1(struct drvmgr_bus *bus) +{ + return pcibus_devs_register(bus); +} diff --git a/cpukit/libpci/pci_bus.h b/cpukit/libpci/pci_bus.h new file mode 100644 index 0000000000..76a5e945ac --- /dev/null +++ b/cpukit/libpci/pci_bus.h @@ -0,0 +1,159 @@ +/* PCI bus driver Interface. + * + * COPYRIGHT (c) 2008. + * Aeroflex Gaisler. + * + * General part of PCI Bus driver. The driver is typically + * initialized from the PCI host driver separating the host + * driver from the common parts in PCI drivers. + * The PCI library must be initialized before starting the + * PCI bus driver. The PCI library have set up BARs and + * assigned system IRQs for targets. + * This PCI bus driver rely on the PCI library (pci.c) for + * interrupt registeration (pci_interrupt_register) and PCI + * target set up. + * + * 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 __PCI_BUS_H__ +#define __PCI_BUS_H__ + +#include <drvmgr/drvmgr.h> +#include <pci.h> +#include <pci/access.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* PCI Driver ID generation (VENDOR: 16-bit, DEVICE: 16-bit) */ +#define DRIVER_PCI_ID(vendor, device) \ + DRIVER_ID(DRVMGR_BUS_TYPE_PCI, \ + ((((vendor) & 0xffff) << 16) | ((device) & 0xffff))) + +/* PCI Driver ID generation (CLASS: 24-bit) */ +#define DRIVER_PCI_CLASS(class) \ + DRIVER_ID(DRVMGR_BUS_TYPE_PCI, ((1 << 32) | ((class) & 0xffffff))) + +/* PCI driver IDs (DRIVER_PCI_VENDOR_DEVICE or DRIVER_PCI_CLASS_NAME) */ +#define DRIVER_PCI_GAISLER_RASTAIO_ID DRIVER_PCI_ID(PCIID_VENDOR_GAISLER, PCIID_DEVICE_GR_RASTA_IO) +#define DRIVER_PCI_GAISLER_RASTATMTC_ID DRIVER_PCI_ID(PCIID_VENDOR_GAISLER, PCIID_DEVICE_GR_RASTA_TMTC) +#define DRIVER_PCI_GAISLER_GR701_ID DRIVER_PCI_ID(PCIID_VENDOR_GAISLER, PCIID_DEVICE_GR_701) +#define DRIVER_PCI_GAISLER_RASTAADCDAC_ID DRIVER_PCI_ID(PCIID_VENDOR_GAISLER, PCIID_DEVICE_GR_RASTA_ADCDAC) +#define DRIVER_PCI_GAISLER_TMTC_1553_ID DRIVER_PCI_ID(PCIID_VENDOR_GAISLER, PCIID_DEVICE_GR_TMTC_1553) +#define DRIVER_PCI_GAISLER_RASTA_SPW_ROUTER_ID DRIVER_PCI_ID(PCIID_VENDOR_GAISLER, PCIID_DEVICE_GR_RASTA_SPW_RTR) + +struct pci_dev_id { + uint16_t vendor; + uint16_t device; + uint16_t subvendor; + uint16_t subdevice; + uint32_t class; /* 24 lower bits */ +}; + +struct pci_dev_id_match { + uint16_t vendor; + uint16_t device; + uint16_t subvendor; + uint16_t subdevice; + uint32_t class; /* 24 lower bits */ + uint32_t class_mask; /* 24 lower bits */ +}; +#define PCIID_DEVVEND(vendor, device) \ + {vendor, device, PCI_ID_ANY, PCI_ID_ANY, 0, 0} +#define PCIID_END_TABLE {0, 0, 0, 0, 0, 0} + +enum { + /* A Device has up to 6 BARs and an optional ROM BAR */ + PCIDEV_RES_BAR1 = 0, + PCIDEV_RES_BAR2 = 1, + PCIDEV_RES_BAR3 = 2, + PCIDEV_RES_BAR4 = 3, + PCIDEV_RES_BAR5 = 4, + PCIDEV_RES_BAR6 = 5, + PCIDEV_RES_ROM = 6, +}; +/* Maximum Number of Resources of a device */ +#define PCIDEV_RES_CNT (PCIDEV_RES_ROM + 1) + +/* IO, MEMIO or MEM resource. Can be BAR, ROM or Bridge Window */ +struct pcibus_res { + uint32_t address; /* Base Address, CPU accessible */ + uint32_t size; /* 0=Unimplemented, 0!=Resource Size */ + struct pci_res *res; /* PCI-layer resource */ +}; + +struct pci_dev_info { + struct pci_dev_id id; + uint8_t rev; + uint8_t irq; /* 0 = NO IRQ */ + pci_dev_t pcidev; + struct pcibus_res resources[PCIDEV_RES_CNT]; + struct pci_dev *pci_device; +}; + +struct pci_drv_info { + struct drvmgr_drv general; /* General bus info */ + /* PCI specific bus information */ + struct pci_dev_id_match *ids; /* Supported hardware */ +}; + +/* Access routines */ +struct pcibus_regmem_ops { + drvmgr_r8 r8; + drvmgr_r16 r16; + drvmgr_r32 r32; + drvmgr_r64 r64; + drvmgr_w8 w8; + drvmgr_w16 w16; + drvmgr_w32 w32; + drvmgr_w64 w64; +}; + +/* Let driver configure PCI bus driver */ +struct pcibus_config { + struct drvmgr_map_entry *maps_up; + struct drvmgr_map_entry *maps_down; +}; + +/* PCI Configuration Space Access - Not implemented (use PCI Lib directly) */ +#define PCI_FUNC_CFG_R8 DRVMGR_RWFUNC(RW_SIZE_1|RW_READ|RW_CFG) +#define PCI_FUNC_CFG_R16 DRVMGR_RWFUNC(RW_SIZE_2|RW_READ|RW_CFG) +#define PCI_FUNC_CFG_R32 DRVMGR_RWFUNC(RW_SIZE_4|RW_READ|RW_CFG) +#define PCI_FUNC_CFG_W8 DRVMGR_RWFUNC(RW_SIZE_1|RW_WRITE|RW_CFG) +#define PCI_FUNC_CFG_W16 DRVMGR_RWFUNC(RW_SIZE_2|RW_WRITE|RW_CFG) +#define PCI_FUNC_CFG_W32 DRVMGR_RWFUNC(RW_SIZE_4|RW_WRITE|RW_CFG) + +/* PCI I/O Register Access - Not implemented (use PCI Lib directly) */ +#define PCI_FUNC_IO_R8 DRVMGR_RWFUNC(RW_SIZE_1|RW_READ|RW_IO) +#define PCI_FUNC_IO_R16 DRVMGR_RWFUNC(RW_SIZE_2|RW_READ|RW_IO) +#define PCI_FUNC_IO_R32 DRVMGR_RWFUNC(RW_SIZE_4|RW_READ|RW_IO) +#define PCI_FUNC_IO_W8 DRVMGR_RWFUNC(RW_SIZE_1|RW_WRITE|RW_IO) +#define PCI_FUNC_IO_W16 DRVMGR_RWFUNC(RW_SIZE_2|RW_WRITE|RW_IO) +#define PCI_FUNC_IO_W32 DRVMGR_RWFUNC(RW_SIZE_4|RW_WRITE|RW_IO) + +/* PCI Register Access over Memory Space (Little Endian) */ +#define PCI_FUNC_MREG_R8 DRVMGR_RWFUNC(RW_SIZE_1|RW_READ|RW_MEMREG) +#define PCI_FUNC_MREG_R16 DRVMGR_RWFUNC(RW_SIZE_2|RW_READ|RW_MEMREG|RW_LITTLE) +#define PCI_FUNC_MREG_R32 DRVMGR_RWFUNC(RW_SIZE_4|RW_READ|RW_MEMREG|RW_LITTLE) +#define PCI_FUNC_MREG_W8 DRVMGR_RWFUNC(RW_SIZE_1|RW_WRITE|RW_MEMREG) +#define PCI_FUNC_MREG_W16 DRVMGR_RWFUNC(RW_SIZE_2|RW_WRITE|RW_MEMREG|RW_LITTLE) +#define PCI_FUNC_MREG_W32 DRVMGR_RWFUNC(RW_SIZE_4|RW_WRITE|RW_MEMREG|RW_LITTLE) + +/* Weak default PCI driver resources, override this from project configuration + * to set PCI Bus resources used to configure PCI device drivers. + */ +extern struct drvmgr_bus_res pcibus_drv_resources; + +/* Attach a PCI bus on top of a PCI Host device */ +extern int pcibus_register(struct drvmgr_dev *dev, struct pcibus_config *cfg); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/cpukit/libpci/pci_cfg.c b/cpukit/libpci/pci_cfg.c new file mode 100644 index 0000000000..3aeb5a27df --- /dev/null +++ b/cpukit/libpci/pci_cfg.c @@ -0,0 +1,58 @@ +/* PCI Configuration Library + * + * COPYRIGHT (c) 2010-2011. + * Aeroflex Gaisler Research + * + * 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. + * + * 2011-02-11, Daniel Hellstrom <daniel@gaisler.com> + * created + */ + +#include <pci/cfg.h> + +/* Number of buses. This is set from respective library */ +int pci_bus_cnt = 0; + +/* PCI Address assigned to BARs which failed to fit into the PCI Window or + * is disabled by any other cause. + */ +uint32_t pci_invalid_address = 0; + +/* PCI System type. Configuration Library setup this */ +int pci_system_type = PCI_SYSTEM_NONE; + +/* PCI Endianness. + * + * Host driver or BSP must override this be writing here if bus is defined + * as non-standard big-endian. + */ +int pci_endian = PCI_LITTLE_ENDIAN; + +/* Configure PCI devices and bridges, and setup the RAM data structures + * describing the PCI devices currently present in the system + */ +int pci_config_init(void) +{ + if (pci_config_lib_init) + return pci_config_lib_init(); + else + return 0; +} + +void pci_config_register(void *config) +{ + if (pci_config_lib_register) + pci_config_lib_register(config); +} + +/* Return the number of PCI busses available in the system, note that + * there are always one bus (bus0) after the PCI library has been + * initialized and a driver has been registered. + */ +int pci_bus_count(void) +{ + return pci_bus_cnt; +} diff --git a/cpukit/libpci/pci_cfg_auto.c b/cpukit/libpci/pci_cfg_auto.c new file mode 100644 index 0000000000..b0f02b0e1e --- /dev/null +++ b/cpukit/libpci/pci_cfg_auto.c @@ -0,0 +1,1040 @@ +/* PCI (Auto) configuration Library. Setup PCI configuration space and IRQ. + * + * COPYRIGHT (c) 2010-2011. + * Aeroflex Gaisler Research + * + * 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. + * + * 2010-02-03, Daniel Hellstrom <daniel@gaisler.com> + * PCI Library rewritten from scratch. Support multiple buses/bridges, + * print current PCI configuration space setup, BAR assigment sort + * implementation speeded up drastically (bootup time noticable + * shorter), interrupt assignment implemented, PCI Host driver + * extracted from library, support for I/O areas. + * 2010-02-03, Daniel Hellstrom <daniel@gaisler.com> + * Fixed initialization problem when first device is a bridge. + * 2010-04-19, Daniel Hellstrom <daniel@gaisler.com> + * Fixed autoconf issue when bridges are present + * 2010-04-19, Daniel Hellstrom <daniel@gaisler.com> + * Optimized resource allocation when bridges are present: the + * resources lists are sorted by boundary instead of size and a + * reorder aligorithm introduced that move resources into unused + * areas if possible. + * 2010-06-10, Daniel Hellstrom <daniel@gaisler.com> + * Fix in pci_res_insert(), where the above mentioned optimization + * failed due to bad compare statement. Optimization only affects + * systems with multiple PCI buses. + * 2010-09-29, Kristoffer Glembo <kristoffer@gaisler.com> + * Fixed I/O BAR size calculation of bridges. Reading/Writing to 0x1C + * instead of faulty 0x1E. + * 2011-02-11, Daniel Hellstrom <daniel@gaisler.com> + * Changed library to use 16-bit identifiers (pci_dev_t), instead to 3 + * integers (BUS,SLOT,FUNC), this reduces the footprint. + * 2011-02-11, Daniel Hellstrom <daniel@gaisler.com> + * Split Library into different parts, this enables PCI initialization + * to be done outside of the PCI Host driver and smaller systems that + * don't want Configuration Space to be setup. + * - Access Library (Configuration, Memory and I/O Space read/write + * routines) + * - Configuration Libarary + * A. Auto Config + * B. Static Config (not implemented yet) + * - Interrupt Library (shared interrupt support rely on BSP) + * Added CHANGES file + */ + +#include <rtems.h> +#include <stdlib.h> +#include <rtems/bspIo.h> +#include <string.h> + +/* Configure headers */ +#define PCI_CFG_AUTO_LIB + +#include <pci.h> +#include <pci/access.h> +#include <pci/cfg.h> + +/* Define PCI_INFO_ON_STARTUP to get a listing of configured devices at boot + * time + */ +#undef PCI_INFO_ON_STARTUP + +/* #define DEBUG */ + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +/* PCI Library + * (For debugging it might be good to use other functions or the driver's + * directly) + */ +#define PCI_CFG_R8(dev, args...) pci_cfg_r8(dev, args) +#define PCI_CFG_R16(dev, args...) pci_cfg_r16(dev, args) +#define PCI_CFG_R32(dev, args...) pci_cfg_r32(dev, args) +#define PCI_CFG_W8(dev, args...) pci_cfg_w8(dev, args) +#define PCI_CFG_W16(dev, args...) pci_cfg_w16(dev, args) +#define PCI_CFG_W32(dev, args...) pci_cfg_w32(dev, args) + +/* Number of PCI buses */ +extern int pci_bus_cnt; + +int pci_config_auto_initialized = 0; + +/* Configuration setup */ +struct pci_auto_setup pci_auto_cfg; + +/* Insert BAR into the sorted resources list. The BARs are sorted on the + * BAR size/alignment need. + */ +void pci_res_insert(struct pci_res **root, struct pci_res *res) +{ + struct pci_res *curr, *last; + unsigned long curr_size_resulting_boundary, size_resulting_boundary; + unsigned long boundary, size; + + res->start = 0; + res->end = 0; + boundary = res->boundary; + size = res->size; + + /* Insert the resources depending on the boundary needs + * Normally the boundary=size of the BAR, however when + * PCI bridges are involved the bridge's boundary may be + * smaller that the size due to the fact that a bridge + * may have different-sized BARs behind, the largest BAR + * (also the BAR with the largest boundary) will decide + * the alignment need. + */ + last = NULL; + curr = *root; + + /* Order List after boundary, the boundary is maintained + * when the size is on an equal boundary, normally it is + * but may not be with bridges. So in second hand it is + * sorted after resulting boundary - the boundary after + * the resource. + */ + while (curr && (curr->boundary >= boundary)) { + if (curr->boundary == boundary) { + /* Find Resulting boundary of size */ + size_resulting_boundary = 1; + while ((size & size_resulting_boundary) == 0) + size_resulting_boundary = + size_resulting_boundary << 1; + + /* Find Resulting boundary of curr->size */ + curr_size_resulting_boundary = 1; + while ((curr->size & curr_size_resulting_boundary) == 0) + curr_size_resulting_boundary = + curr_size_resulting_boundary << 1; + + if (size_resulting_boundary >= + curr_size_resulting_boundary) + break; + } + last = curr; + curr = curr->next; + } + + if (last == NULL) { + /* Insert first in list */ + res->next = *root; + *root = res; + } else { + last->next = res; + res->next = curr; + } +} + +#ifdef DEBUG +void pci_res_list_print(struct pci_res *root) +{ + if (root == NULL) + return; + + printf("RESOURCE LIST:\n"); + while (root) { + printf(" SIZE: 0x%08x, BOUNDARY: 0x%08x\n", root->size, + root->boundary); + root = root->next; + } +} +#endif + +/* Reorder a size/alignment ordered resources list. The idea is to + * avoid unused due to alignment/size restriction. + * + * NOTE: The first element is always untouched. + * NOTE: If less than three elements in list, nothing will be done + * + * Normally a BAR has the same alignment requirements as the size of the + * BAR. However, when bridges are invloved the alignment need may be smaller + * that the size, because a bridge resource consist or multiple BARs. + * For example, say that a bridge with a 256Mb and a 16Mb BAR is found, then + * the alignment is required to be 256Mb but the size 256+16Mb. + * + * In order to minimize dead space on the bus, the bounadry ordered list + * is reordered, example: + * BUS0 + * | BUS1 + * |------------| + * | |-- BAR0: SIZE=256Mb, ALIGNMENT=256MB + * | |-- BAR1: SIZE=16Mb, ALIGNMENT=16MB + * | | + * | | + * | | + * | | BUS2 (BAR_BRIDGE1: SIZE=256+16, ALIGNEMENT=256) + * | |----------| + * | | |-- BAR2: SIZE=256Mb, ALIGNMENT=256Mb + * | | |-- BAR3: SIZE=16Mb, ALIGNMENT=16MB + * + * A alignement/boundary ordered list of BUS1 will look like: + * - BAR_BRIDGE1 + * - BAR0 (ALIGMENT NEED 256Mb) + * - BAR1 + * + * However, Between BAR_BRIDGE1 and BAR0 will be a unused hole of 256-16Mb. + * We can put BAR1 before BAR0 to avoid the problem. + */ +void pci_res_reorder(struct pci_res *root) +{ + struct pci_res *curr, *last, *curr2, *last2; + unsigned int start, start_next, hole_size, hole_boundary; + + if (root == NULL) + return; + + /* Make up a start address with the boundary of the + * First element. + */ + start = root->boundary + root->size; + last = root; + curr = root->next; + while (curr) { + + /* Find start address of resource */ + start_next = (start + (curr->boundary - 1)) & + ~(curr->boundary - 1); + + /* Find hole size, the unsed space inbetween last resource + *and next */ + hole_size = start_next - start; + + /* Find Boundary of START */ + hole_boundary = 1; + while ((start & hole_boundary) == 0) + hole_boundary = hole_boundary<<1; + + /* Detect dead hole */ + if (hole_size > 0) { + /* Step through list and try to find a resource that + * can fit into hole. Take into account hole start + * boundary and hole size. + */ + last2 = curr; + curr2 = curr->next; + while (curr2) { + if ((curr2->boundary <= hole_boundary) && + (curr2->size <= hole_size)) { + /* Found matching resource. Move it + * first in the hole. Then rescan, now + * that the hole has changed in + * size/boundary. + */ + last2->next = curr2->next; + curr2->next = curr; + last->next = curr2; + + /* New Start address */ + start_next = (start + + (curr2->boundary - 1)) & + ~(curr2->boundary - 1); + /* Since we inserted the resource before + * curr we need to re-evaluate curr one + * more, more resources may fit into the + * shrunken hole. + */ + curr = curr2; + break; + } + last2 = curr2; + curr2 = curr2->next; + } + } + + /* No hole or nothing fitted into hole. */ + start = start_next; + + last = curr; + curr = curr->next; + } +} + +/* Find the total size required in PCI address space needed by a resource list*/ +unsigned int pci_res_size(struct pci_res *root) +{ + struct pci_res *curr; + unsigned int size; + + /* Get total size of all resources */ + size = 0; + curr = root; + while (curr) { + size = (size + (curr->boundary - 1)) & ~(curr->boundary - 1); + size += curr->size; + curr = curr->next; + } + + return size; +} + +/* Free a device and secondary bus if device is a bridge */ +void pci_dev_free(struct pci_dev *dev) +{ + struct pci_dev *subdev; + struct pci_bus *bus; + + if (dev->flags & PCI_DEV_BRIDGE) { + bus = (struct pci_bus *)dev; + for (subdev = bus->devs; subdev ; subdev = subdev->next) + pci_dev_free(dev); + } + + free(dev); +} + +struct pci_dev *pci_dev_create(int isbus) +{ + void *ptr; + int size; + + if (isbus) + size = sizeof(struct pci_bus); + else + size = sizeof(struct pci_dev); + + ptr = malloc(size); + if (!ptr) + rtems_fatal_error_occurred(RTEMS_NO_MEMORY); + memset(ptr, 0, size); + return ptr; +} + +void pci_find_devs(struct pci_bus *bus) +{ + uint32_t id, tmp; + uint8_t header; + int slot, func, fail; + struct pci_dev *dev, **listptr; + struct pci_bus *bridge; + pci_dev_t pcidev; + + DBG("Scanning bus %d\n", bus->num); + + listptr = &bus->devs; + slot = 0; + if (bus->num == 0) + slot = 1; /* Skip PCI HOST BRIDGE */ + for (; slot < PCI_MAX_DEVICES; slot++) { + + /* Slot address */ + pcidev = PCI_DEV(bus->num, slot, 0); + + for (func = 0; func < PCI_MAX_FUNCTIONS; func++, pcidev++) { + + fail = PCI_CFG_R32(pcidev, PCI_VENDOR_ID, &id); + if (fail || id == 0xffffffff || id == 0) { + /* + * This slot is empty + */ + if (func == 0) + break; + else + continue; + } + + DBG("Found PCIDEV 0x%x at (bus %x, slot %x, func %x)\n", + id, bus, slot, func); + + /* Set command to reset values, it disables bus + * mastering and address responses. + */ + PCI_CFG_W16(pcidev, PCI_COMMAND, 0); + + /* Clear any already set status bits */ + PCI_CFG_W16(pcidev, PCI_STATUS, 0xf900); + + /* Set latency timer to 64 */ + PCI_CFG_W8(pcidev, PCI_LATENCY_TIMER, 64); + + PCI_CFG_R32(pcidev, PCI_CLASS_REVISION, &tmp); + tmp >>= 16; + dev = pci_dev_create(tmp == PCI_CLASS_BRIDGE_PCI); + *listptr = dev; + listptr = &dev->next; + + dev->busdevfun = pcidev; + dev->bus = bus; + PCI_CFG_R16(pcidev, PCI_VENDOR_ID, &dev->vendor); + PCI_CFG_R16(pcidev, PCI_DEVICE_ID, &dev->device); + PCI_CFG_R32(pcidev, PCI_CLASS_REVISION, &dev->classrev); + + if (tmp == PCI_CLASS_BRIDGE_PCI) { + DBG("Found PCI-PCI Bridge 0x%x at " + "(bus %x, slot %x, func %x)\n", + id, bus, slot, func); + dev->flags = PCI_DEV_BRIDGE; + dev->subvendor = 0; + dev->subdevice = 0; + bridge = (struct pci_bus *)dev; + bridge->num = bus->sord + 1; + bridge->pri = bus->num; + bridge->sord = bus->sord + 1; + + /* Configure bridge (no support for 64-bit) */ + PCI_CFG_W32(pcidev, 0x28, 0); + PCI_CFG_W32(pcidev, 0x2C, 0); + tmp = (64 << 24) | (0xff << 16) | + (bridge->num << 8) | bridge->pri; + PCI_CFG_W32(pcidev, PCI_PRIMARY_BUS, tmp); + + /* Scan Secondary Bus */ + pci_find_devs(bridge); + + /* sord might have been updated */ + PCI_CFG_W8(pcidev, 0x1a, bridge->sord); + bus->sord = bridge->sord; + + DBG("PCI-PCI BRIDGE: Primary %x, Secondary %x, " + "Subordinate %x\n", + bridge->pri, bridge->num, bridge->sord); + } else { + /* Disable Cardbus CIS Pointer */ + PCI_CFG_W32(pcidev, PCI_CARDBUS_CIS, 0); + + /* Devices have subsytem device and vendor ID */ + PCI_CFG_R16(pcidev, PCI_SUBSYSTEM_VENDOR_ID, + &dev->subvendor); + PCI_CFG_R16(pcidev, PCI_SUBSYSTEM_ID, + &dev->subdevice); + } + + /* Stop if not a multi-function device */ + if (func == 0) { + pci_cfg_r8(pcidev, PCI_HEADER_TYPE, &header); + if ((header & PCI_MULTI_FUNCTION) == 0) + break; + } + } + } +} + +void pci_find_bar(struct pci_dev *dev, int bar) +{ + uint32_t size, disable, mask; + struct pci_res *res = &dev->resources[bar]; + char *str; + pci_dev_t pcidev = dev->busdevfun; + int ofs; + + DBG("Bus: %x, Slot: %x, function: %x, bar%d\n", + PCI_DEV_EXPAND(pcidev), bar); + + res->bar = bar; + if (bar == DEV_RES_ROM) { + if (dev->flags & PCI_DEV_BRIDGE) + ofs = PCI_ROM_ADDRESS1; + else + ofs = PCI_ROM_ADDRESS; + disable = 0; /* ROM BARs have a unique enable bit per BAR */ + } else { + ofs = PCI_BASE_ADDRESS_0 + (bar << 2); + disable = pci_invalid_address; + } + + PCI_CFG_W32(pcidev, ofs, 0xffffffff); + PCI_CFG_R32(pcidev, ofs, &size); + PCI_CFG_W32(pcidev, ofs, disable); + + if (size == 0 || size == 0xffffffff) + return; + if (bar == DEV_RES_ROM) { + mask = PCI_ROM_ADDRESS_MASK; + str = "ROM"; + if (dev->bus->flags & PCI_BUS_MEM) + res->flags = PCI_RES_MEM; + else + res->flags = PCI_RES_MEMIO; + } else if (((size & 0x1) == 0) && (size & 0x6)) { + /* unsupported Memory type */ + PCI_CFG_W32(pcidev, ofs, 0); + return; + } else { + mask = ~0xf; + if (size & 0x1) { + /* I/O */ + mask = ~0x3; + res->flags = PCI_RES_IO; + str = "I/O"; + if (size & 0xffff0000) + res->flags |= PCI_RES_IO32; + /* Limit size of I/O space to 256 byte */ + size |= 0xffffff00; + if ((dev->bus->flags & PCI_BUS_IO) == 0) { + res->flags |= PCI_RES_FAIL; + dev->flags |= PCI_DEV_RES_FAIL; + } + } else { + /* Memory. We convert Prefetchable Memory BARs to Memory + * BARs in case the Bridge does not support prefetchable + * memory. + */ + if ((size & 0x8) && (dev->bus->flags & PCI_BUS_MEM)) { + /* Prefetchable and Bus supports it */ + res->flags = PCI_RES_MEM; + str = "MEM"; + } else { + res->flags = PCI_RES_MEMIO; + str = "MEMIO"; + } + } + } + size &= mask; + res->size = ~size + 1; + res->boundary = ~size + 1; + + DBG("Bus: %x, Slot: %x, function: %x, %s bar%d size: %x\n", + PCI_DEV_EXPAND(pcidev), str, bar, res->size); +} + +int pci_find_res_dev(struct pci_dev *dev, void *unused) +{ + struct pci_bus *bridge; + uint32_t tmp; + uint16_t tmp16; + pci_dev_t pcidev = dev->busdevfun; + int i, maxbars; + + if (dev->flags & PCI_DEV_BRIDGE) { + /* PCI-PCI Bridge */ + bridge = (struct pci_bus *)dev; + + /* Only 2 Bridge BARs */ + maxbars = 2; + + /* Probe Bridge Spaces (MEMIO space always implemented), the + * probe disables all space-decoding at the same time + */ + PCI_CFG_W32(pcidev, 0x30, 0); + PCI_CFG_W16(pcidev, 0x1c, 0x00f0); + PCI_CFG_R16(pcidev, 0x1c, &tmp16); + if (tmp16 != 0) { + bridge->flags |= PCI_BUS_IO; + if (tmp16 & 0x1) + bridge->flags |= PCI_BUS_IO32; + } + + PCI_CFG_W32(pcidev, 0x24, 0x0000ffff); + PCI_CFG_R32(pcidev, 0x24, &tmp); + if (tmp != 0) + bridge->flags |= PCI_BUS_MEM; + + PCI_CFG_W32(pcidev, 0x20, 0x0000ffff); + bridge->flags |= PCI_BUS_MEMIO; + } else { + /* Normal PCI Device as max 6 BARs */ + maxbars = 6; + } + + /* Probe BARs */ + for (i = 0; i < maxbars; i++) + pci_find_bar(dev, i); + pci_find_bar(dev, DEV_RES_ROM); + + return 0; +} + +int pci_add_res_dev(struct pci_dev *dev, void *arg); + +void pci_add_res_bus(struct pci_bus *bus, int type) +{ + int tindex = type - 1; + + /* Clear old resources */ + bus->busres[tindex] = NULL; + + /* Add resources of devices behind bridge if bridge supports + * resource type. If MEM space not supported by bridge, they are + * converted to MEMIO in the process. + */ + if (!((type == PCI_BUS_IO) && ((bus->flags & PCI_BUS_IO) == 0))) { + pci_for_each_child(bus, pci_add_res_dev, (void *)type, 0); + + /* Reorder Bus resources to fit more optimally (avoid dead + * PCI space). Currently they are sorted by boundary and size. + * + * This is especially important when multiple buses (bridges) + * are present. + */ + pci_res_reorder(bus->busres[tindex]); + } +} + +int pci_add_res_dev(struct pci_dev *dev, void *arg) +{ + int tindex, type = (int)arg; + struct pci_bus *bridge; + struct pci_res *res, *first_busres; + int i; + uint32_t bbound; + + /* Type index in Bus resource */ + tindex = type - 1; + + if (dev->flags & PCI_DEV_BRIDGE) { + /* PCI-PCI Bridge. Add all sub-bus resources first */ + bridge = (struct pci_bus *)dev; + + /* Add all child device's resources to this type */ + pci_add_res_bus(bridge, type); + + /* Propagate the resources from child bus to BAR on + * this bus, by adding a "fake" BAR per type. + */ + res = &bridge->dev.resources[BUS_RES_START + tindex]; + res->bar = BUS_RES_START + tindex; + res->start = 0; + res->end = 0; + res->flags = 0; /* mark BAR resource not available */ + first_busres = bridge->busres[tindex]; + if (first_busres) { + res->flags = type; + res->size = pci_res_size(first_busres); + res->boundary = first_busres->boundary; + if (type == PCI_RES_IO) { + bbound = 0x1000; /* Bridge I/O min 4KB */ + } else { + bbound = 0x100000; /* Bridge MEM min 1MB */ + + /* Convert MEM to MEMIO if not supported by + * this bridge + */ + if ((bridge->flags & PCI_BUS_MEM) == 0) + res->flags = PCI_RES_MEMIO; + } + /* Fulfil minimum bridge boundary */ + if (res->boundary < bbound) + res->boundary = bbound; + /* Make sure that size is atleast bridge boundary */ + if (res->size > bbound && (res->size & (bbound-1))) + res->size = (res->size | (bbound-1)) + 1; + } + } + + /* Normal PCI Device as max 6 BARs and a ROM Bar. + * Insert BARs into the sorted resource list. + */ + for (i = 0; i < DEV_RES_CNT; i++) { + res = &dev->resources[i]; + if ((res->flags & PCI_RES_TYPE_MASK) != type) + continue; + pci_res_insert(&dev->bus->busres[tindex], res); + } + + return 0; +} + +/* Function assumes that base is properly aligned to the requirement of the + * largest BAR in the system. + */ +uint32_t pci_alloc_res(struct pci_bus *bus, int type, + uint32_t start, uint32_t end) +{ + struct pci_dev *dev; + struct pci_res *res, **prev_next; + unsigned long starttmp; + struct pci_bus *bridge; + int removed, sec_type; + + /* The resources are sorted on their size (size and alignment is the + * same) + */ + prev_next = &bus->busres[type - 1]; + while ((res = *prev_next) != NULL) { + + dev = RES2DEV(res); + removed = 0; + + /* Align start to this reource's need, only needed after + * a bridge resource has been allocated. + */ + starttmp = (start + (res->boundary-1)) & ~(res->boundary-1); + + if ((starttmp + res->size - 1) > end) { + /* Not enough memory available for this resource */ + printk("PCI[%x:%x:%x]: DEV BAR%d (%d): no resource " + "assigned\n", + PCI_DEV_EXPAND(dev->busdevfun), + res->bar, res->flags & PCI_RES_TYPE_MASK); + res->start = res->end = 0; + + /* If this resources is a bridge window to the + * secondary bus, the secondary resources are not + * changed which has the following effect: + * I/O : Will never be assigned + * MEMIO : Will never be assigned + * MEM : Will stay marked as MEM, but bridge window + * is changed into MEMIO, when the window is + * assigned a MEMIO address the secondary + * resources will also be assigned. + */ + + if (type == PCI_RES_MEM) { + /* Try prefetchable as non-prefetchable mem */ + res->flags &= ~PCI_RES_MEM_PREFETCH; + /* Remove resource from MEM list, ideally we + * should regenerate this list in order to fit + * the comming BARs more optimially... + */ + *prev_next = res->next; + /* We should not update prev_next here since + * we just removed the resource from the list + */ + removed = 1; + } else { + res->flags |= PCI_RES_FAIL; + dev->flags |= PCI_DEV_RES_FAIL; + } + } else { + start = starttmp; + + res->start = start; + res->end = start + res->size; + + /* "Virtual BAR" on a bridge? A bridge resource need all + * its child devices resources allocated + */ + if ((res->bar != DEV_RES_ROM) && + (dev->flags & PCI_DEV_BRIDGE) && + (res->bar >= BUS_RES_START)) { + bridge = (struct pci_bus *)dev; + /* If MEM bar was changed into a MEMIO the + * secondary MEM resources are still set to MEM, + */ + if (type == PCI_BUS_MEMIO && + res->bar == BRIDGE_RES_MEM) + sec_type = PCI_RES_MEM; + else + sec_type = type; + + pci_alloc_res(bridge, sec_type, res->start, + res->end); + } + + start += res->size; + } + if (removed == 0) + prev_next = &res->next; + } + + return start; +} + +void pci_set_bar(struct pci_dev *dev, int residx) +{ + uint32_t tmp; + uint16_t tmp16; + pci_dev_t pcidev; + struct pci_res *res; + int is_bridge, ofs; + + res = &dev->resources[residx]; + pcidev = dev->busdevfun; + + if ((res->flags == 0) || (res->flags & PCI_RES_FAIL)) + return; + + is_bridge = dev->flags & PCI_DEV_BRIDGE; + + if (res->bar == DEV_RES_ROM) { + /* ROM: 32-bit prefetchable memory BAR */ + if (is_bridge) + ofs = PCI_ROM_ADDRESS1; + else + ofs = PCI_ROM_ADDRESS; + PCI_CFG_W32(pcidev, ofs, res->start | PCI_ROM_ADDRESS_ENABLE); + DBG("PCI[%x:%x:%x]: ROM BAR: 0x%x-0x%x\n", + PCI_DEV_EXPAND(pcidev), res->start, res->end); + } else if (is_bridge && (res->bar == BRIDGE_RES_IO)) { + /* PCI Bridge I/O BAR */ + DBG("PCI[%x:%x:%x]: BAR 1C: 0x%x-0x%x\n", + PCI_DEV_EXPAND(pcidev), res->start, res->end); + + /* Limit and Base */ + tmp16 = ((res->end-1) & 0x0000f000) | + ((res->start & 0x0000f000) >> 8); + tmp = ((res->end-1) & 0xffff0000) | (res->start >> 16); + + DBG("PCI[%x:%x:%x]: BRIDGE BAR 0x%x: 0x%08x [0x30: 0x%x]\n", + PCI_DEV_EXPAND(pcidev), 0x1C, tmp, tmp2); + PCI_CFG_W16(pcidev, 0x1C, tmp16); + PCI_CFG_W32(pcidev, 0x30, tmp); + } else if (is_bridge && (res->bar >= BRIDGE_RES_MEMIO)) { + /* PCI Bridge MEM and MEMIO Space */ + + /* Limit and Base */ + tmp = ((res->end-1) & 0xfff00000) | (res->start >> 16); + + DBG("PCI[%x:%x:%x]: BRIDGE BAR 0x%x: 0x%08x\n", + PCI_DEV_EXPAND(pcidev), + 0x20 + (res->bar-BRIDGE_RES_MEMIO)*4, tmp); + PCI_CFG_W32(pcidev, 0x20+(res->bar-BRIDGE_RES_MEMIO)*4, tmp); + } else { + /* PCI Device */ + DBG("PCI[%x:%x:%x]: DEV BAR%d: 0x%08x\n", + PCI_DEV_EXPAND(pcidev), res->bar, res->start); + ofs = PCI_BASE_ADDRESS_0 + res->bar*4; + PCI_CFG_W32(pcidev, ofs, res->start); + } + + /* Enable Memory or I/O responses */ + if ((res->flags & PCI_RES_TYPE_MASK) == PCI_RES_IO) + pci_io_enable(pcidev); + else + pci_mem_enable(pcidev); + + /* Enable Master if bridge */ + if (is_bridge) + pci_master_enable(pcidev); +} + +int pci_set_res_dev(struct pci_dev *dev, void *unused) +{ + int i, maxbars; + + if (dev->flags & PCI_DEV_BRIDGE) + maxbars = 2 + 3; /* 2 BARs + 3 Bridge-Windows "Virtual BARs" */ + else + maxbars = 6; /* Normal PCI Device as max 6 BARs. */ + + /* Set BAR resources with previous allocated values */ + for (i = 0; i < maxbars; i++) + pci_set_bar(dev, i); + pci_set_bar(dev, DEV_RES_ROM); + + return 0; +} + +/* Route IRQ through PCI-PCI Bridges */ +int pci_route_irq(pci_dev_t dev, int irq_pin) +{ + int slot_grp; + + if (PCI_DEV_BUS(dev) == 0) + return irq_pin; + + slot_grp = PCI_DEV_SLOT(dev) & 0x3; + + return (((irq_pin - 1) + slot_grp) & 0x3) + 1; +} + +/* Put assigned system IRQ into PCI interrupt line information field. + * This is to make it possible for drivers to read system IRQ / Vector from + * configuration space later on. + * + * 1. Get Interrupt PIN + * 2. Route PIN to bus 0, if not on root PCI bus + * 3. Get System interrupt number assignment for PIN + * 4. Set Interrupt LINE + */ +int pci_set_irq_dev(struct pci_dev *dev, void *cfg) +{ + struct pci_auto_setup *autocfg = cfg; + uint8_t irq_pin, irq_line, *psysirq; + pci_dev_t pcidev; + + psysirq = &dev->sysirq; + pcidev = dev->busdevfun; + PCI_CFG_R8(pcidev, PCI_INTERRUPT_PIN, &irq_pin); + + /* If behind bridge, perform IRQ routing */ + while (dev->bus->dev.bus && irq_pin != 0) { + irq_pin = autocfg->irq_route(dev->busdevfun, irq_pin); + dev = &dev->bus->dev; + } + + /* Get IRQ from PIN on PCI bus0 */ + if (irq_pin != 0) + irq_line = autocfg->irq_map(dev->busdevfun, irq_pin); + else + irq_line = 0; + + *psysirq = irq_line; + + /* Set System Interrupt/Vector for device. 0 means no-IRQ */ + PCI_CFG_W8(pcidev, PCI_INTERRUPT_LINE, irq_line); + + return 0; +} + +/* This routine assumes that PCI access library has been successfully + * initialized. All information about the PCI bus needed is found in + * the argument. + * + * The PCI buses are enumerated as bridges are found, PCI devices are + * setup with BARs and IRQs, etc. + */ +int pci_config_auto(void) +{ + uint32_t end; + uint32_t startmemio, endmemio; + uint32_t startmem, endmem; + uint32_t startio, endio; + struct pci_auto_setup *autocfg = &pci_auto_cfg; +#ifdef DEBUG + uint32_t start; +#endif + + if (pci_config_auto_initialized == 0) + return -1; /* no config given to library */ + +#ifdef DEBUG + DBG("\n--- PCI MEMORY AVAILABLE ---\n"); + if (autocfg->mem_size) { + start = autocfg->mem_start; + end = autocfg->mem_start + autocfg->mem_size - 1; + DBG(" MEM AVAIL [0x%08x-0x%08x]\n", start, end); + } else { + /* One big memory space */ + DBG(" MEM share the space with MEMIO\n"); + } + /* no-prefetchable memory space need separate memory space. + * For example PCI controller maps this region non-cachable. + */ + start = autocfg->memio_start; + end = autocfg->memio_start + autocfg->memio_size - 1; + DBG(" MEMIO AVAIL [0x%08x-0x%08x]\n", start, end); + if (autocfg->io_size) { + start = autocfg->io_start; + end = autocfg->io_start + autocfg->io_size - 1; + DBG(" I/O AVAIL [0x%08x-0x%08x]\n", start, end); + } else { + DBG(" I/O Space not available\n"); + } +#endif + + /* Init Host-Bridge */ + memset(&pci_hb, 0, sizeof(pci_hb)); + pci_hb.dev.flags = PCI_DEV_BRIDGE; + if (autocfg->memio_size <= 0) + return -1; + pci_hb.flags = PCI_BUS_MEMIO; + if (autocfg->mem_size) + pci_hb.flags |= PCI_BUS_MEM; + if (autocfg->io_size) + pci_hb.flags |= PCI_BUS_IO; + + /* Find all PCI devices/functions on all buses. The buses will be + * enumrated (assigned a unique PCI Bus ID 0..255). + */ + DBG("\n--- PCI SCANNING ---\n"); + pci_find_devs(&pci_hb); + pci_bus_cnt = pci_hb.sord + 1; + if (pci_hb.devs == NULL) + return 0; + + pci_system_type = PCI_SYSTEM_HOST; + + /* Find all resources (MEM/MEMIO/IO BARs) of all devices/functions + * on all buses. + * + * Device resources behind bridges which does not support prefetchable + * memory are already marked as non-prefetchable memory. + * Devices which as I/O resources behind a bridge that do not support + * I/O space are marked DISABLED. + * + * All BARs and Bridge Spaces are disabled after this. Only the ones + * that are allocated an address are initilized later on. + */ + DBG("\n\n--- PCI RESOURCES ---\n"); + pci_for_each_dev(pci_find_res_dev, 0); + + /* Add all device's resources to bus and sort them to fit in the PCI + * Window. The device resources are propagated upwards through bridges + * by adding a "virtual" BAR (boundary != BAR size). + * + * We wait with MEMIO (non-prefetchable memory) resources to after MEM + * resources have been allocated, so that MEM resources can be changed + * into MEMIO resources if not enough space. + */ + pci_add_res_bus(&pci_hb, PCI_RES_IO); + pci_add_res_bus(&pci_hb, PCI_RES_MEM); + + /* Start assigning found resource according to the sorted order. */ + + /* Allocate resources to I/O areas */ + if (pci_hb.busres[BUS_RES_IO]) { + startio = autocfg->io_start; + end = startio + autocfg->io_size; + endio = pci_alloc_res(&pci_hb, PCI_RES_IO, startio, end); + } + + /* Allocate resources to prefetchable memory */ + if (pci_hb.busres[BUS_RES_MEM]) { + startmem = autocfg->mem_start; + end = startmem + autocfg->mem_size; + endmem = pci_alloc_res(&pci_hb, PCI_RES_MEM, startmem, end); + } + + /* Add non-prefetchable memory resources and not fitting prefetchable + * memory resources. + * + * Some prefetchable memory resources may not have fitted into PCI + * window. Prefetchable memory can be mapped into non-prefetchable + * memory window. The failing BARs have been marked as MEMIO instead. + */ + pci_add_res_bus(&pci_hb, PCI_RES_MEMIO); + + /* Allocate resources to non-prefetchable memory */ + if (pci_hb.busres[BUS_RES_MEMIO]) { + startmemio = autocfg->memio_start; + end = startmemio + autocfg->memio_size; + endmemio = pci_alloc_res(&pci_hb, PCI_RES_MEMIO, startmemio, + end); + } + + DBG("\n--- PCI ALLOCATED SPACE RANGES ---\n"); + DBG(" MEM NON-PREFETCHABLE: [0x%08x-0x%08x]\n", startmemio, endmemio); + DBG(" MEM PREFETCHABLE: [0x%08x-0x%08x]\n", startmem, endmem); + DBG(" I/O: [0x%08x-0x%08x]\n", startio, endio); + + /* Set all allocated BARs and Bridge Windows */ + pci_for_each_dev(pci_set_res_dev, NULL); + + /* Initialize IRQs of all devices. According to the PCI-PCI bridge + * specification the IRQs are routed differently depending on slot + * number. On bus 0 PCI IRQs are routed 1:1. Drivers can override + * the default routing if a motherboard requires it. + */ + if ((autocfg->options & CFGOPT_NOSETUP_IRQ) == 0) { + if (autocfg->irq_route == NULL) /* use standard irq routing */ + autocfg->irq_route = pci_route_irq; + pci_for_each_dev(pci_set_irq_dev, autocfg); + } + + DBG("PCI resource allocation done\n"); + + return 0; +} + +void pci_config_auto_register(void *config) +{ + pci_config_auto_initialized = 1; + memcpy(&pci_auto_cfg, config, sizeof(struct pci_auto_setup)); +} diff --git a/cpukit/libpci/pci_cfg_peripheral.c b/cpukit/libpci/pci_cfg_peripheral.c new file mode 100644 index 0000000000..bd9c3c2343 --- /dev/null +++ b/cpukit/libpci/pci_cfg_peripheral.c @@ -0,0 +1,32 @@ +/* PCI (Peripheral) Configuration Library + * + * COPYRIGHT (c) 2011. + * Aeroflex Gaisler Research + * + * 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. + * + * 2011-05-23, Daniel Hellstrom <daniel@gaisler.com> + * created + * + * The Host Bridge bus must be declared by user. It contain the static + * configuration used to setup the devices/functions. + */ + +/* Configure headers */ +#define PCI_CFG_PERIPHERAL_LIB + +#include <pci/cfg.h> + +/* Number of buses */ +extern int pci_bus_cnt; + +/* Assume that user has defined static setup array in pci_hb */ +int pci_config_peripheral(void) +{ + pci_bus_cnt = pci_hb.sord + 1; + pci_system_type = PCI_SYSTEM_PERIPHERAL; + + return 0; +} diff --git a/cpukit/libpci/pci_cfg_print_code.c b/cpukit/libpci/pci_cfg_print_code.c new file mode 100644 index 0000000000..c6d75a96fe --- /dev/null +++ b/cpukit/libpci/pci_cfg_print_code.c @@ -0,0 +1,163 @@ +/* PCI Configuration C code console printout routines */ + +#include <rtems.h> +#include <stdio.h> +#include <pci/cfg.h> + +int pci_cfg_print_bus(struct pci_bus *bus); + +static void get_bus_name(struct pci_bus *bus, char *buf) +{ + if (bus->num == 0) + strcpy(buf, "pci_hb"); + else + sprintf(buf, "bus%d", bus->num); +} + +static void get_device_name(struct pci_dev *dev, char *buf) +{ + char busname[64]; + + if (dev->flags & PCI_DEV_BRIDGE) { + get_bus_name((struct pci_bus *)dev, busname); + sprintf(buf, "%s.dev", busname); + } else { + sprintf(buf, "dev_%x_%x_%x", PCI_DEV_EXPAND(dev->busdevfun)); + } +} + +void pci_cfg_print_resources(struct pci_res *resources, char *prefix) +{ + struct pci_res *res; + int i; + + for (i = 0; i < DEV_RES_CNT; i++) { + res = &resources[i]; + if (((res->flags & PCI_RES_TYPE_MASK) == 0) || + ((res->flags & PCI_RES_FAIL) == PCI_RES_FAIL)) { + printf("%sPCIRES_EMPTY,\n", prefix); + continue; + } + printf("%s{\n", prefix); + printf("%s\t.next = NULL,\n", prefix); + printf("%s\t.size = 0x%08lx,\n", prefix, res->size); + printf("%s\t.boundary = 0x%08lx,\n", prefix, res->boundary); + printf("%s\t.flags = 0x%x,\n", prefix, res->flags); + printf("%s\t.bar = %d,\n", prefix, i); + printf("%s\t.start = 0x%08lx,\n", prefix, res->start); + printf("%s\t.end = 0x%08lx,\n", prefix, res->end); + printf("%s},\n", prefix); + } +} + +void pci_cfg_print_device(struct pci_dev *dev, char *prefix) +{ + char name[32]; + char buf[8]; + printf("%s.resources = {\n", prefix); + strcpy(buf, prefix); + strcat(buf, "\t"); + pci_cfg_print_resources(dev->resources, buf); + printf("%s},\n", prefix); + if (dev->next == NULL) { + printf("%s.next = NULL,\n", prefix); + } else { + get_device_name(dev->next, name); + printf("%s.next = &%s,\n", prefix, name); + } + if (!dev->bus) { /* Host Bridge? */ + printf("%s.bus = NULL,\n", prefix); + } else { + get_bus_name(dev->bus, name); + printf("%s.bus = &%s,\n", prefix, name); + } + + printf("%s.busdevfun = 0x%04x,\n", prefix, dev->busdevfun); + printf("%s.flags = 0x%x,\n", prefix, dev->flags); + printf("%s.sysirq = %d,\n", prefix, dev->sysirq); + printf("%s.vendor = 0x%04x,\n", prefix, dev->vendor); + printf("%s.device = 0x%04x,\n", prefix, dev->device); + printf("%s.subvendor = 0x%04x,\n", prefix, dev->subvendor); + printf("%s.subdevice = 0x%04x,\n", prefix, dev->subdevice); + printf("%s.classrev = 0x%08lx,\n", prefix, dev->classrev); + printf("%s.command = 0,\n", prefix); +} + +int pci_cfg_print_dev(struct pci_dev *dev, void *unused) +{ + if (dev->flags & PCI_DEV_BRIDGE) { + pci_cfg_print_bus((struct pci_bus *)dev); + } else { + printf("\n\n/* PCI DEV at [%x:%x:%x] */\n", + PCI_DEV_EXPAND(dev->busdevfun)); + printf("static struct pci_dev dev_%x_%x_%x = {\n", + PCI_DEV_EXPAND(dev->busdevfun)); + pci_cfg_print_device(dev, "\t"); + printf("};\n"); + } + return 0; +} + +int pci_cfg_print_bus(struct pci_bus *bus) +{ + char name[32]; + + /* Print Bus */ + printf("\n\n/* PCI BUS %d - Bridge at [%x:%x:%x] */\n\n", + bus->num, PCI_DEV_EXPAND(bus->dev.busdevfun)); + get_bus_name(bus, name); + printf("%sstruct pci_bus %s = {\n", + bus->num == 0 ? "" : "static ", name); + printf("\t.dev = {\n"); + pci_cfg_print_device(&bus->dev, "\t\t"); + printf("\t},\n"); + if (bus->devs == NULL) { + printf("\t.devs = NULL,\n"); + } else { + get_device_name(bus->devs, name); + printf("\t.devs = &%s,\n", name); + } + printf("\t.flags = 0x%x,\n", bus->flags); + printf("\t.num = %d,\n", bus->num); + printf("\t.pri = %d,\n", bus->pri); + printf("\t.sord = %d,\n", bus->sord); + printf("};\n"); + + /* Print all child devices */ + pci_for_each_child(bus, pci_cfg_print_dev, NULL, 0); + + return 0; +} + +int pci_cfg_print_forw_dev(struct pci_dev *dev, void *unused) +{ + if ((dev->flags & PCI_DEV_BRIDGE) == 0) { + printf("static struct pci_dev dev_%x_%x_%x;\n", + PCI_DEV_EXPAND(dev->busdevfun)); + } + return 0; +} + +void pci_cfg_print(void) +{ + int i; + + printf("\n\n/*** PCI Configuration ***/\n\n"); + printf("#include <stdlib.h>\n"); + printf("#define PCI_CFG_STATIC_LIB\n"); + printf("#include <pci/cfg.h>\n\n"); + printf("#define PCIRES_EMPTY {0}\n\n"); + + /* Forward declaration for all devices / buses */ + printf("/* FORWARD BUS DECLARATIONS */\n"); + for (i = 0; i < pci_bus_count(); i++) { + if (i == 0) + printf("struct pci_bus pci_hb;\n"); + else + printf("static struct pci_bus bus%d;\n", i); + } + printf("\n/* FORWARD DEVICE DECLARATIONS */\n"); + pci_for_each_dev(pci_cfg_print_forw_dev, NULL); + + pci_cfg_print_bus(&pci_hb); +} diff --git a/cpukit/libpci/pci_cfg_read.c b/cpukit/libpci/pci_cfg_read.c new file mode 100644 index 0000000000..7efcb57282 --- /dev/null +++ b/cpukit/libpci/pci_cfg_read.c @@ -0,0 +1,360 @@ +/* Read current PCI configuration that bootloader or BIOS has already setup + * and initialize the PCI structures. + * + * COPYRIGHT (c) 2010-2011. + * Aeroflex Gaisler Research + * + * 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. + * + * 2011-03-04, Daniel Hellstrom <daniel@gaisler.com> + * created + */ + +#include <rtems.h> +#include <stdlib.h> +#include <rtems/bspIo.h> +#include <pci/cfg.h> +#include <pci/access.h> + +/* PCI Library + * (For debugging it might be good to use other functions or the driver's + * directly) + */ +#define PCI_CFG_R8(dev, args...) pci_cfg_r8(dev, args) +#define PCI_CFG_R16(dev, args...) pci_cfg_r16(dev, args) +#define PCI_CFG_R32(dev, args...) pci_cfg_r32(dev, args) +#define PCI_CFG_W8(dev, args...) pci_cfg_w8(dev, args) +#define PCI_CFG_W16(dev, args...) pci_cfg_w16(dev, args) +#define PCI_CFG_W32(dev, args...) pci_cfg_w32(dev, args) + +#ifdef DEBUG +#define DBG(args...) printk(args) +#else +#define DBG(args...) +#endif + +/* Number of buses */ +extern int pci_bus_cnt; + +/* The Host Bridge bus is initialized here */ +extern struct pci_bus pci_hb; + +struct pci_dev *pci_dev_create(int isbus) +{ + void *ptr; + int size; + + if (isbus) + size = sizeof(struct pci_bus); + else + size = sizeof(struct pci_dev); + + ptr = malloc(size); + if (!ptr) + rtems_fatal_error_occurred(RTEMS_NO_MEMORY); + memset(ptr, 0, size); + return ptr; +} + +/* Check if address is accessible from host */ +int pci_read_addressable(struct pci_dev *dev, struct pci_res *res) +{ + struct pci_bus *bus = dev->bus; + int type = res->flags & PCI_RES_TYPE_MASK; + struct pci_res *range0, *range1; + + if (type == PCI_BUS_IO && (bus->flags & PCI_BUS_IO) == 0) + return 0; + + /* Assume that host bridge can access all */ + if (bus->pri == 0) + return 1; + + range1 = NULL; + switch (type) { + case PCI_RES_IO: + range0 = &bus->dev.resources[BRIDGE_RES_IO]; + break; + case PCI_RES_MEM: + range1 = &bus->dev.resources[BRIDGE_RES_MEM]; + default: + case PCI_RES_MEMIO: + range0 = &bus->dev.resources[BRIDGE_RES_MEMIO]; + break; + } + if ((res->start >= range0->start) && (res->end <= range0->end)) { + return pci_read_addressable(&bus->dev, range0); + } else if (range1 && (res->start >= range1->start) && + (res->end <= range1->end)) { + return pci_read_addressable(&bus->dev, range1); + } + + return 0; +} + +void pci_read_bar(struct pci_dev *dev, int bar) +{ + uint32_t orig, size, disable, mask; + struct pci_res *res = &dev->resources[bar]; + char *str; + pci_dev_t pcidev = dev->busdevfun; + int ofs; + + DBG("Bus: %x, Slot: %x, function: %x, bar%d\n", + PCI_DEV_EXPAND(pcidev), bar); + + res->bar = bar; + if (bar == DEV_RES_ROM) { + if (dev->flags & PCI_DEV_BRIDGE) + ofs = PCI_ROM_ADDRESS1; + else + ofs = PCI_ROM_ADDRESS; + disable = 0; /* ROM BARs have a unique enable bit per BAR */ + } else { + ofs = PCI_BASE_ADDRESS_0 + (bar << 2); + disable = pci_invalid_address; + } + + PCI_CFG_R32(pcidev, ofs, &orig); + PCI_CFG_W32(pcidev, ofs, 0xffffffff); + PCI_CFG_R32(pcidev, ofs, &size); + PCI_CFG_W32(pcidev, ofs, orig); + + if (size == 0 || size == 0xffffffff) + return; + if (bar == DEV_RES_ROM) { + mask = PCI_ROM_ADDRESS_MASK; + str = "ROM"; + if (dev->bus->flags & PCI_BUS_MEM) + res->flags = PCI_RES_MEM; + else + res->flags = PCI_RES_MEMIO; + } else if (((size & 0x1) == 0) && (size & 0x6)) { + /* unsupported Memory type */ + return; + } else { + mask = ~0xf; + if (size & 0x1) { + /* I/O */ + mask = ~0x3; + res->flags = PCI_RES_IO; + str = "I/O"; + if (size & 0xffff0000) + res->flags |= PCI_RES_IO32; + /* Limit size of I/O space to 256 byte */ + size |= 0xffffff00; + if ((dev->bus->flags & PCI_BUS_IO) == 0) { + res->flags |= PCI_RES_FAIL; + dev->flags |= PCI_DEV_RES_FAIL; + } + } else { + /* Memory */ + if (size & 0x8) { + /* Prefetchable */ + res->flags = PCI_RES_MEM; + str = "MEM"; + } else { + res->flags = PCI_RES_MEMIO; + str = "MEMIO"; + } + } + } + res->start = orig & mask; + size &= mask; + res->size = ~size + 1; + res->boundary = res->size; + res->end = res->start + res->size; + + DBG("Bus: %x, Slot: %x, function: %x, %s bar%d size: %x\n", + PCI_DEV_EXPAND(pcidev), str, bar, res->size); + + /* Check if BAR is addressable by host */ + if (pci_read_addressable(dev, res) == 0) { + /* No matching bridge window contains this BAR */ + res->flags |= PCI_RES_FAIL; + dev->flags |= PCI_DEV_RES_FAIL; + } +} + +void pci_read_devs(struct pci_bus *bus) +{ + uint32_t id, tmp; + uint16_t tmp16; + uint8_t header; + int slot, func, fail, i, maxbars, max_sord; + struct pci_dev *dev, **listptr; + struct pci_bus *bridge; + pci_dev_t pcidev; + struct pci_res *res; + + DBG("Scanning bus %d\n", bus->num); + + max_sord = bus->num; + listptr = &bus->devs; + slot = 0; + if (bus->num == 0) + slot = 1; /* Skip PCI HOST BRIDGE */ + for (; slot < PCI_MAX_DEVICES; slot++) { + + /* Slot address */ + pcidev = PCI_DEV(bus->num, slot, 0); + + for (func = 0; func < PCI_MAX_FUNCTIONS; func++, pcidev++) { + + fail = PCI_CFG_R32(pcidev, PCI_VENDOR_ID, &id); + if (fail || id == 0xffffffff || id == 0) { + /* + * This slot is empty + */ + if (func == 0) + break; + else + continue; + } + + DBG("Found PCIDEV 0x%x at (bus %x, slot %x, func %x)\n", + id, bus, slot, func); + + PCI_CFG_R32(pcidev, PCI_CLASS_REVISION, &tmp); + tmp >>= 16; + dev = pci_dev_create(tmp == PCI_CLASS_BRIDGE_PCI); + *listptr = dev; + listptr = &dev->next; + + dev->busdevfun = pcidev; + dev->bus = bus; + PCI_CFG_R16(pcidev, PCI_VENDOR_ID, &dev->vendor); + PCI_CFG_R16(pcidev, PCI_DEVICE_ID, &dev->device); + PCI_CFG_R32(pcidev, PCI_CLASS_REVISION, &dev->classrev); + + if (tmp == PCI_CLASS_BRIDGE_PCI) { + DBG("Found PCI-PCI Bridge 0x%x at " + "(bus %x, slot %x, func %x)\n", + id, bus, slot, func); + dev->flags = PCI_DEV_BRIDGE; + bridge = (struct pci_bus *)dev; + + PCI_CFG_R32(pcidev, PCI_PRIMARY_BUS, &tmp); + bridge->pri = tmp & 0xff; + bridge->num = (tmp >> 8) & 0xff; + bridge->sord = (tmp >> 16) & 0xff; + if (bridge->sord > max_sord) + max_sord = bridge->sord; + + DBG(" Primary %x, Secondary %x, " + "Subordinate %x\n", + bridge->pri, bridge->num, bridge->sord); + + /*** Probe Bridge Spaces ***/ + + /* MEMIO Window - always implemented */ + bridge->flags = PCI_BUS_MEMIO; + res = &bridge->dev.resources[BRIDGE_RES_MEMIO]; + res->flags = PCI_RES_MEMIO; + res->bar = BRIDGE_RES_MEMIO; + PCI_CFG_R32(pcidev, 0x20, &tmp); + res->start = (tmp & 0xfff0) << 16; + res->end = 1 + ((tmp & 0xfff00000) | 0xfffff); + if (res->end <= res->start) { + /* Window disabled */ + res->end = res->start = 0; + } + res->size = res->end - res->start; + + /* I/O Window - optional */ + res = &bridge->dev.resources[BRIDGE_RES_IO]; + res->bar = BRIDGE_RES_IO; + PCI_CFG_R32(pcidev, 0x30, &tmp); + PCI_CFG_R16(pcidev, 0x1c, &tmp16); + if (tmp != 0 || tmp16 != 0) { + bridge->flags |= PCI_BUS_IO; + res->flags = PCI_RES_IO; + if (tmp16 & 0x1) { + bridge->flags |= PCI_BUS_IO32; + res->flags |= PCI_RES_IO32; + } + + res->start = (tmp & 0xffff) << 16 | + (tmp16 & 0xf0) << 8; + res->end = 1 + ((tmp & 0xffff0000) | + (tmp16 & 0xf000) | + 0xfff); + if (res->end <= res->start) { + /* Window disabled */ + res->end = res->start = 0; + } + res->size = res->end - res->start; + } + + /* MEM Window - optional */ + res = &bridge->dev.resources[BRIDGE_RES_MEM]; + res->bar = BRIDGE_RES_MEM; + PCI_CFG_R32(pcidev, 0x24, &tmp); + if (tmp != 0) { + bridge->flags |= PCI_BUS_MEM; + res->flags = PCI_RES_MEM; + res->start = (tmp & 0xfff0) << 16; + res->end = 1 + ((tmp & 0xfff00000) | + 0xfffff); + if (res->end <= res->start) { + /* Window disabled */ + res->end = res->start = 0; + } + res->size = res->end - res->start; + } + + /* Scan Secondary Bus */ + pci_read_devs(bridge); + + /* Only 2 BARs for Bridges */ + maxbars = 2; + } else { + /* Devices have subsytem device and vendor ID */ + PCI_CFG_R16(pcidev, PCI_SUBSYSTEM_VENDOR_ID, + &dev->subvendor); + PCI_CFG_R16(pcidev, PCI_SUBSYSTEM_ID, + &dev->subdevice); + + /* Normal PCI Device has max 6 BARs */ + maxbars = 6; + } + + /* Probe BARs */ + for (i = 0; i < maxbars; i++) + pci_read_bar(dev, i); + pci_read_bar(dev, DEV_RES_ROM); + + /* Get System Interrupt/Vector for device. + * 0 means no-IRQ + */ + PCI_CFG_R8(pcidev, PCI_INTERRUPT_LINE, &dev->sysirq); + + /* Stop if not a multi-function device */ + if (func == 0) { + pci_cfg_r8(pcidev, PCI_HEADER_TYPE, &header); + if ((header & PCI_MULTI_FUNCTION) == 0) + break; + } + } + } + + if (bus->num == 0) + bus->sord = max_sord; +} + +int pci_config_read(void) +{ + pci_system_type = PCI_SYSTEM_HOST; + + /* Find all devices and buses */ + pci_hb.flags = PCI_BUS_IO|PCI_BUS_MEMIO|PCI_BUS_MEM; + pci_hb.dev.flags = PCI_DEV_BRIDGE; + pci_read_devs(&pci_hb); + pci_bus_cnt = pci_hb.sord + 1; + if (pci_hb.devs == NULL) + return 0; + + return 0; +} diff --git a/cpukit/libpci/pci_cfg_static.c b/cpukit/libpci/pci_cfg_static.c new file mode 100644 index 0000000000..ff4277cb21 --- /dev/null +++ b/cpukit/libpci/pci_cfg_static.c @@ -0,0 +1,158 @@ +/* PCI (Static) Configuration Library + * + * COPYRIGHT (c) 2010-2011. + * Aeroflex Gaisler Research + * + * 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. + * + * 2011-02-11, Daniel Hellstrom <daniel@gaisler.com> + * created + * + * The Host Bridge bus must be declared by user. It contains the static + * configuration used to setup the devices/functions. + */ + +/* Configure headers */ +#define PCI_CFG_STATIC_LIB + +#include <stdlib.h> +#include <pci.h> +#include <pci/access.h> +#include <pci/cfg.h> + +#define PCI_CFG_R8(dev, args...) pci_cfg_r8(dev, args) +#define PCI_CFG_R16(dev, args...) pci_cfg_r16(dev, args) +#define PCI_CFG_R32(dev, args...) pci_cfg_r32(dev, args) +#define PCI_CFG_W8(dev, args...) pci_cfg_w8(dev, args) +#define PCI_CFG_W16(dev, args...) pci_cfg_w16(dev, args) +#define PCI_CFG_W32(dev, args...) pci_cfg_w32(dev, args) + +/* Number of buses */ +extern int pci_bus_cnt; + +/* Enumrate one bus if device is a bridge, and all it's subordinate buses */ +static int pci_init_dev(struct pci_dev *dev, void *unused) +{ + uint32_t tmp; + uint16_t tmp16, cmd; + struct pci_bus *bridge; + int maxbars, i, romofs; + pci_dev_t pcidev = dev->busdevfun; + struct pci_res *res; + + /* Init Device */ + + /* Set command to reset values, it disables bus + * mastering and address responses. + */ + PCI_CFG_W16(pcidev, PCI_COMMAND, 0); + + /* Clear any already set status bits */ + PCI_CFG_W16(pcidev, PCI_STATUS, 0xf900); + + /* Set latency timer to 64 */ + PCI_CFG_W8(pcidev, PCI_LATENCY_TIMER, 64); + + /* Set System IRQ of PIN */ + PCI_CFG_W8(pcidev, PCI_INTERRUPT_LINE, dev->sysirq); + + cmd = dev->command; + + if ((dev->flags & PCI_DEV_BRIDGE) == 0) { + /* Disable Cardbus CIS Pointer */ + PCI_CFG_W32(pcidev, PCI_CARDBUS_CIS, 0); + + romofs = PCI_ROM_ADDRESS; + maxbars = 6; + } else { + /* Init Bridge */ + + /* Configure bridge (no support for 64-bit) */ + PCI_CFG_W32(pcidev, PCI_PREF_BASE_UPPER32, 0); + PCI_CFG_W32(pcidev, PCI_PREF_LIMIT_UPPER32, 0); + + bridge = (struct pci_bus *)dev; + tmp = (64 << 24) | (bridge->sord << 16) | + (bridge->num << 8) | bridge->pri; + PCI_CFG_W32(pcidev, PCI_PRIMARY_BUS, tmp); + + /*** Setup I/O Bridge Window ***/ + res = &dev->resources[BRIDGE_RES_IO]; + if (res->size > 0) { + tmp16 = ((res->end-1) & 0x0000f000) | + ((res->start & 0x0000f000) >> 8); + tmp = ((res->end-1) & 0xffff0000) | (res->start >> 16); + cmd |= PCI_COMMAND_IO; + } else { + tmp16 = 0x00ff; + tmp = 0; + } + /* I/O Limit and Base */ + PCI_CFG_W16(pcidev, PCI_IO_BASE, tmp16); + PCI_CFG_W32(pcidev, PCI_IO_BASE_UPPER16, tmp); + + /*** Setup MEMIO Bridge Window ***/ + res = &dev->resources[BRIDGE_RES_MEMIO]; + if (res->size > 0) { + tmp = ((res->end-1) & 0xffff0000) | + (res->start >> 16); + cmd |= PCI_COMMAND_MEMORY; + } else { + tmp = 0x0000ffff; + } + /* MEMIO Limit and Base */ + PCI_CFG_W32(pcidev, PCI_MEMORY_BASE, tmp); + + /*** Setup MEM Bridge Window ***/ + res = &dev->resources[BRIDGE_RES_MEM]; + if (res->size > 0) { + tmp = ((res->end-1) & 0xffff0000) | + (res->start >> 16); + cmd |= PCI_COMMAND_MEMORY; + } else { + tmp = 0x0000ffff; + } + /* MEM Limit and Base */ + PCI_CFG_W32(pcidev, PCI_PREF_MEMORY_BASE, tmp); + /* 64-bit space not supported */ + PCI_CFG_W32(pcidev, PCI_PREF_BASE_UPPER32, 0); + PCI_CFG_W32(pcidev, PCI_PREF_LIMIT_UPPER32, 0); + + cmd |= PCI_COMMAND_MASTER; + romofs = PCI_ROM_ADDRESS1; + maxbars = 2; + } + + /* Init BARs */ + for (i = 0; i < maxbars; i++) { + res = &dev->resources[i]; + if (res->flags & PCI_RES_TYPE_MASK) { + PCI_CFG_W32(pcidev, PCI_BASE_ADDRESS_0 + 4*i, + res->start); + if ((res->flags & PCI_RES_TYPE_MASK) == PCI_RES_IO) + cmd |= PCI_COMMAND_IO; + else + cmd |= PCI_COMMAND_MEMORY; + } + } + res = &dev->resources[DEV_RES_ROM]; + if (res->flags & PCI_RES_TYPE_MASK) { + PCI_CFG_W32(pcidev, romofs, res->start|PCI_ROM_ADDRESS_ENABLE); + cmd |= PCI_COMMAND_MEMORY; + } + PCI_CFG_W16(pcidev, PCI_COMMAND, cmd); + + return 0; +} + +/* Assume that user has defined static setup array in pci_hb */ +int pci_config_static(void) +{ + pci_bus_cnt = pci_hb.sord + 1; + pci_system_type = PCI_SYSTEM_HOST; + + /* Init all PCI devices according to depth-first search algorithm */ + return pci_for_each_dev(pci_init_dev, NULL); +} diff --git a/cpukit/libpci/pci_find.c b/cpukit/libpci/pci_find.c new file mode 100644 index 0000000000..bdf711b828 --- /dev/null +++ b/cpukit/libpci/pci_find.c @@ -0,0 +1,55 @@ +/* PCI Help function, Find a PCI device by VENDOR/DEVICE ID + * + * COPYRIGHT (c) 2010-2011. + * Aeroflex Gaisler Research + * + * 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. + * + * 2011-02-12, Daniel Hellstrom <daniel@gaisler.com> + * new implementation based on pci_for_each() to reduce footprint + */ + +#include <pci.h> +#include <pci/access.h> + +struct compare_info { + int index; + uint16_t vendor; + uint16_t device; +}; + +static int compare_dev_id(pci_dev_t pcidev, void *arg) +{ + struct compare_info *info = arg; + uint16_t vid, did; + + pci_cfg_r16(pcidev, PCI_VENDOR_ID, &vid); + pci_cfg_r16(pcidev, PCI_DEVICE_ID, &did); + if ((vid != info->vendor) || (did != info->device)) + return 0; + if (info->index-- == 0) + return pcidev; + else + return 0; +} + +/* Find a Device in PCI configuration space */ +int pci_find(uint16_t ven, uint16_t dev, int index, pci_dev_t *pdev) +{ + struct compare_info info; + int result; + + info.index = index; + info.vendor = ven; + info.device = dev; + + result = pci_for_each(compare_dev_id, &info); + if (pdev) + *pdev = (pci_dev_t)result; + if (result == 0) + return -1; + else + return 0; +} diff --git a/cpukit/libpci/pci_find_dev.c b/cpukit/libpci/pci_find_dev.c new file mode 100644 index 0000000000..f74a126a1e --- /dev/null +++ b/cpukit/libpci/pci_find_dev.c @@ -0,0 +1,52 @@ +/* PCI Help function, Find a PCI device by VENDOR/DEVICE ID + * + * COPYRIGHT (c) 2010-2011. + * Aeroflex Gaisler Research + * + * 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. + * + * 2011-02-12, Daniel Hellstrom <daniel@gaisler.com> + * new implementation based on pci_for_each() to reduce footprint + */ + +#include <pci.h> +#include <pci/cfg.h> + +struct compare_info { + int index; + uint16_t vendor; + uint16_t device; +}; + +static int compare_dev_id(struct pci_dev *dev, void *arg) +{ + struct compare_info *info = arg; + + if ((dev->vendor != info->vendor) || (dev->device != info->device)) + return 0; + if (info->index-- == 0) + return (int)dev; + else + return 0; +} + +/* Find a Device in PCI device tree located in RAM */ +int pci_find_dev(uint16_t ven, uint16_t dev, int index, struct pci_dev **ppdev) +{ + struct compare_info info; + int result; + + info.index = index; + info.vendor = ven; + info.device = dev; + + result = pci_for_each_dev(compare_dev_id, &info); + if (ppdev) + *ppdev = (struct pci_dev *)result; + if (result == 0) + return -1; + else + return 0; +} diff --git a/cpukit/libpci/pci_for_each.c b/cpukit/libpci/pci_for_each.c new file mode 100644 index 0000000000..195eb17d7c --- /dev/null +++ b/cpukit/libpci/pci_for_each.c @@ -0,0 +1,68 @@ +/* PCI Help Function, Iterate over all PCI devices + * + * COPYRIGHT (c) 2010-2011. + * Aeroflex Gaisler Research + * + * 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. + * + * 2011-02-11, Daniel Hellstrom <daniel@gaisler.com> + * created from pci_bus.c + */ + +#include <pci.h> +#include <pci/access.h> + +/*#define DEBUG*/ + +#ifdef DEBUG +#include <rtems/bspIo.h> +#define DBG(args...) printk(args) +#else +#define DBG(args...) +#endif + +int pci_for_each(int (*func)(pci_dev_t, void*), void *arg) +{ + uint32_t id; + uint8_t hd; + int bus, dev, fun, result, fail; + int maxbus = pci_bus_count(); + pci_dev_t pcidev; + + for (bus = 0; bus < maxbus ; bus++) { + dev = 0; + if (bus == 0) + dev = 1; /* Skip PCI host bridge */ + for (; dev < PCI_MAX_DEVICES; dev++) { + pcidev = PCI_DEV(bus, dev, 0); + + for (fun = 0; fun < PCI_MAX_FUNCTIONS; fun++, pcidev++) { + fail = pci_cfg_r32(pcidev, PCI_VENDOR_ID, &id); + if (fail || (0xffffffff == id) || (0 == id)) { + if (fun == 0) + break; + else + continue; + } + + DBG("pcibus_for_each: found 0x%08lx at" + " %d/%d/%d\n", id, bus, dev, fun); + result = func(pcidev, arg); + if (result != 0) + return result; /* Stopped */ + + /* Stop if not a multi-function device */ + if (fun == 0) { + pci_cfg_r8(pcidev, PCI_HEADER_TYPE, + &hd); + if ((hd & PCI_MULTI_FUNCTION) == 0) + break; + } + } + } + } + + return 0; /* scanned all */ +} diff --git a/cpukit/libpci/pci_for_each_child.c b/cpukit/libpci/pci_for_each_child.c new file mode 100644 index 0000000000..5730601132 --- /dev/null +++ b/cpukit/libpci/pci_for_each_child.c @@ -0,0 +1,31 @@ +#include <pci/cfg.h> + +/* Iterate over all PCI devices on a bus (not child buses) and call func(), + * iteration is stopped if a non-zero value is returned by func(). + * + * search options: 0 (no child buses), 1 (depth first), 2 (breadth first) + */ +int pci_for_each_child( + struct pci_bus *bus, + int (*func)(struct pci_dev *, void *arg), + void *arg, + int search) +{ + struct pci_dev *dev = bus->devs; + int ret; + + while (dev) { + ret = func(dev, arg); + if (ret) + return ret; + if (search == SEARCH_DEPTH && (dev->flags & PCI_DEV_BRIDGE)) { + ret = pci_for_each_child((struct pci_bus *)dev, + func, arg, search); + if (ret) + return ret; + } + dev = dev->next; + } + + return 0; +} diff --git a/cpukit/libpci/pci_for_each_dev.c b/cpukit/libpci/pci_for_each_dev.c new file mode 100644 index 0000000000..d17f14d416 --- /dev/null +++ b/cpukit/libpci/pci_for_each_dev.c @@ -0,0 +1,8 @@ +#include <pci/cfg.h> + +int pci_for_each_dev( + int (*func)(struct pci_dev *, void *arg), + void *arg) +{ + return pci_for_each_child(&pci_hb, func, arg, SEARCH_DEPTH); +} diff --git a/cpukit/libpci/pci_get_dev.c b/cpukit/libpci/pci_get_dev.c new file mode 100644 index 0000000000..8be3b07135 --- /dev/null +++ b/cpukit/libpci/pci_get_dev.c @@ -0,0 +1,39 @@ +/* PCI Help function, Find a PCI device by BUS|SLOT|FUNCTION + * + * COPYRIGHT (c) 2010-2011. + * Aeroflex Gaisler Research + * + * 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. + * + * 2011-07-04, Daniel Hellstrom <daniel@gaisler.com> + * Created + */ + +#include <pci.h> +#include <pci/cfg.h> + +static int compare_dev_pcidev(struct pci_dev *dev, void *arg) +{ + pci_dev_t pcidev = (unsigned)arg; + + if (dev->busdevfun == pcidev) + return (int)dev; + else + return 0; +} + +/* Get a Device in PCI device tree located in RAM by PCI BUS|SLOT|FUNCTION */ +int pci_get_dev(pci_dev_t pcidev, struct pci_dev **ppdev) +{ + int result; + + result = pci_for_each_dev(compare_dev_pcidev, (void *)(unsigned)pcidev); + if (ppdev) + *ppdev = (struct pci_dev *)result; + if (result == 0) + return -1; + else + return 0; +} diff --git a/cpukit/libpci/pci_irq.c b/cpukit/libpci/pci_irq.c new file mode 100644 index 0000000000..8d2a9b3466 --- /dev/null +++ b/cpukit/libpci/pci_irq.c @@ -0,0 +1,22 @@ +/* PCI IRQ Library + * + * COPYRIGHT (c) 2010-2011. + * Aeroflex Gaisler Research + * + * 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. + * + * 2011-02-11, Daniel Hellstrom <daniel@gaisler.com> + * created + */ + +#include <pci.h> +#include <pci/access.h> + +int pci_dev_irq(pci_dev_t dev) +{ + uint8_t irq_line; + pci_cfg_r8(dev, PCI_INTERRUPT_LINE, &irq_line); + return irq_line; +} diff --git a/cpukit/libpci/pci_print.c b/cpukit/libpci/pci_print.c new file mode 100644 index 0000000000..6eaf3b4fae --- /dev/null +++ b/cpukit/libpci/pci_print.c @@ -0,0 +1,197 @@ +/* PCI Print Current Configuration To Terminal + * + * COPYRIGHT (c) 2010-2011. + * Aeroflex Gaisler Research + * + * 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. + * + * 2011-02-11, Daniel Hellstrom <daniel@gaisler.com> + * created from old code + */ + +#include <stdio.h> +#include <pci.h> +#include <pci/access.h> + +/* PCI Access Library shortcuts */ +#define PCI_CFG_R8(dev, args...) pci_cfg_r8(dev, args) +#define PCI_CFG_R16(dev, args...) pci_cfg_r16(dev, args) +#define PCI_CFG_R32(dev, args...) pci_cfg_r32(dev, args) +#define PCI_CFG_W8(dev, args...) pci_cfg_w8(dev, args) +#define PCI_CFG_W16(dev, args...) pci_cfg_w16(dev, args) +#define PCI_CFG_W32(dev, args...) pci_cfg_w32(dev, args) + +void pci_print_dev(pci_dev_t dev) +{ + int maxbars, pos, romadrs; + uint32_t tmp, tmp2, id; + uint16_t irq; + uint8_t irq_pin; + char *str, *str2; + uint32_t base, limit; + + maxbars = 6; + romadrs = 0x30; + str = ""; + PCI_CFG_R32(dev, PCI_CLASS_REVISION, &tmp); + tmp >>= 16; + if (tmp == PCI_CLASS_BRIDGE_PCI) { + maxbars = 2; + romadrs = 0x38; + str = "(BRIDGE)"; + } + + PCI_CFG_R32(dev, PCI_VENDOR_ID, &id); + printf("\nBus %x Slot %x function: %x [0x%x] %s\n", + PCI_DEV_EXPAND(dev), dev, str); + printf("\tVendor id: 0x%lx, device id: 0x%lx\n", + id & 0xffff, id >> 16); + if (maxbars == 2) { + PCI_CFG_R32(dev, PCI_PRIMARY_BUS, &tmp); + printf("\tPrimary: %lx Secondary: %lx Subordinate: %lx\n", + tmp & 0xff, (tmp >> 8) & 0xff, (tmp >> 16) & 0xff); + } + + PCI_CFG_R16(dev, PCI_INTERRUPT_LINE, &irq); + irq_pin = irq >> 8; + if ((irq_pin > 0) && (irq_pin < 5)) + printf("\tIRQ INT%c# LINE: %d\n", + (irq_pin - 1) + 'A', (irq & 0xff)); + + /* Print standard BARs */ + for (pos = 0; pos < maxbars; pos++) { + PCI_CFG_R32(dev, PCI_BASE_ADDRESS_0 + pos*4, &tmp); + PCI_CFG_W32(dev, PCI_BASE_ADDRESS_0 + pos*4, 0xffffffff); + PCI_CFG_R32(dev, PCI_BASE_ADDRESS_0 + pos*4, &tmp2); + PCI_CFG_W32(dev, PCI_BASE_ADDRESS_0 + pos*4, tmp); + + if (tmp2 != 0 && tmp2 != 0xffffffff && ((tmp2 & 0x1) || + ((tmp2 & 0x6) == 0))) { + uint32_t mask = ~0xf; + if ((tmp2 & 0x1) == 1) { + /* I/O Bar */ + mask = ~3; + tmp2 = tmp2 | 0xffffff00; + } + tmp2 &= mask; + tmp2 = ~tmp2+1; /* Size of BAR */ + if (tmp2 < 0x1000) { + str = "B"; + } else if (tmp2 < 0x100000) { + str = "kB"; + tmp2 = tmp2 / 1024; + } else { + str = "MB"; + tmp2 = tmp2 / (1024*1024); + } + printf("\tBAR %d: %lx [%lu%s]\n", pos, tmp, tmp2, str); + } + } + + /* Print ROM BARs */ + PCI_CFG_R32(dev, romadrs, &tmp); + PCI_CFG_W32(dev, romadrs, 0xffffffff); + PCI_CFG_R32(dev, romadrs, &tmp2); + PCI_CFG_W32(dev, romadrs, tmp); + if (tmp2 & 1) { + /* ROM BAR available */ + tmp2 &= PCI_ROM_ADDRESS_MASK; + tmp2 = (~tmp2 + 1); /* Size of BAR */ + if (tmp2 < 0x1000) { + str = "B"; + } else if (tmp2 < 0x100000) { + str = "kB"; + tmp2 = tmp2 / 1024; + } else { + str = "MB"; + tmp2 = tmp2 / (1024*1024); + } + str2 = tmp & 1 ? "ENABLED" : "DISABLED"; + printf("\tROM: %08lx [%lu%s] (%s)\n", + tmp, tmp2, str, str2); + } + + /* Print Bridge addresses */ + if (maxbars == 2) { + tmp = 0; + PCI_CFG_R32(dev, 0x1C, &tmp); + if (tmp != 0) { + base = (tmp & 0x00f0) << 8; + limit = (tmp & 0xf000) | 0xfff; + PCI_CFG_R32(dev, 0x30, &tmp); + base |= (tmp & 0xffff) << 16; + limit |= (tmp & 0xffff0000); + str = "ENABLED"; + if (limit < base) + str = "DISABLED"; + printf("\tI/O: BASE: 0x%08lx, LIMIT: 0x%08lx (%s)\n", + base, limit, str); + } + + PCI_CFG_R32(dev, 0x20, &tmp); + if (tmp != 0) { + base = (tmp & 0xfff0) << 16; + limit = (tmp & 0xfff00000) | 0xfffff; + str = "ENABLED"; + if (limit < base) + str = "DISABLED"; + printf("\tMEMIO: BASE: 0x%08lx, LIMIT: 0x%08lx (%s)\n", + base, limit, str); + } + + PCI_CFG_R32(dev, 0x24, &tmp); + if (tmp != 0) { + base = (tmp & 0xfff0) << 16; + limit = (tmp & 0xfff00000) | 0xfffff; + str = "ENABLED"; + if (limit < base) + str = "DISABLED"; + printf("\tMEM: BASE: 0x%08lx, LIMIT: 0x%08lx (%s)\n", + base, limit, str); + } + } + printf("\n"); +} + +void pci_print_device(int bus, int slot, int function) +{ + pci_print_dev(PCI_DEV(bus, slot, function)); +} + +void pci_print(void) +{ + int fail, bus, slot, func; + pci_dev_t dev; + uint8_t header; + uint32_t id; + + printf("\nPCI devices found and configured:\n"); + for (bus = 0; bus < pci_bus_count(); bus++) { + slot = 0; + if (bus == 0) + slot = 1; + for (; slot < PCI_MAX_DEVICES; slot++) { + + for (func=0; func < PCI_MAX_FUNCTIONS; func++) { + + dev = PCI_DEV(bus, slot, func); + fail = PCI_CFG_R32(dev, PCI_VENDOR_ID, &id); + + if (!fail && id != PCI_INVALID_VENDORDEVICEID && id != 0) { + pci_print_dev(dev); + + /* Stop if not a multi-function device */ + if (func == 0) { + PCI_CFG_R8(dev, PCI_HEADER_TYPE, &header); + if ((header & PCI_MULTI_FUNCTION) == 0) + break; + } + } else if (func == 0) + break; + } + } + } + printf("\n"); +} diff --git a/cpukit/libpci/preinstall.am b/cpukit/libpci/preinstall.am new file mode 100644 index 0000000000..6f378ee3df --- /dev/null +++ b/cpukit/libpci/preinstall.am @@ -0,0 +1,74 @@ +## Automatically generated by ampolish3 - Do not edit + +if AMPOLISH3 +$(srcdir)/preinstall.am: Makefile.am + $(AMPOLISH3) $(srcdir)/Makefile.am > $(srcdir)/preinstall.am +endif + +PREINSTALL_DIRS = +DISTCLEANFILES = $(PREINSTALL_DIRS) + +all-am: $(PREINSTALL_FILES) + +PREINSTALL_FILES = +CLEANFILES = $(PREINSTALL_FILES) + +$(PROJECT_INCLUDE)/$(dirstamp): + @$(MKDIR_P) $(PROJECT_INCLUDE) + @: > $(PROJECT_INCLUDE)/$(dirstamp) +PREINSTALL_DIRS += $(PROJECT_INCLUDE)/$(dirstamp) + +$(PROJECT_INCLUDE)/pci.h: pci.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/pci.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/pci.h + +$(PROJECT_INCLUDE)/pci/$(dirstamp): + @$(MKDIR_P) $(PROJECT_INCLUDE)/pci + @: > $(PROJECT_INCLUDE)/pci/$(dirstamp) +PREINSTALL_DIRS += $(PROJECT_INCLUDE)/pci/$(dirstamp) + +$(PROJECT_INCLUDE)/pci/access.h: pci/access.h $(PROJECT_INCLUDE)/pci/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/pci/access.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/pci/access.h + +$(PROJECT_INCLUDE)/pci/cfg.h: pci/cfg.h $(PROJECT_INCLUDE)/pci/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/pci/cfg.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/pci/cfg.h + +$(PROJECT_INCLUDE)/pci/cfg_auto.h: pci/cfg_auto.h $(PROJECT_INCLUDE)/pci/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/pci/cfg_auto.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/pci/cfg_auto.h + +$(PROJECT_INCLUDE)/pci/cfg_static.h: pci/cfg_static.h $(PROJECT_INCLUDE)/pci/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/pci/cfg_static.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/pci/cfg_static.h + +$(PROJECT_INCLUDE)/pci/cfg_peripheral.h: pci/cfg_peripheral.h $(PROJECT_INCLUDE)/pci/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/pci/cfg_peripheral.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/pci/cfg_peripheral.h + +$(PROJECT_INCLUDE)/pci/cfg_read.h: pci/cfg_read.h $(PROJECT_INCLUDE)/pci/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/pci/cfg_read.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/pci/cfg_read.h + +$(PROJECT_INCLUDE)/pci/ids.h: pci/ids.h $(PROJECT_INCLUDE)/pci/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/pci/ids.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/pci/ids.h + +$(PROJECT_INCLUDE)/pci/ids_extra.h: pci/ids_extra.h $(PROJECT_INCLUDE)/pci/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/pci/ids_extra.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/pci/ids_extra.h + +$(PROJECT_INCLUDE)/pci/irq.h: pci/irq.h $(PROJECT_INCLUDE)/pci/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/pci/irq.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/pci/irq.h + +$(PROJECT_INCLUDE)/drvmgr/$(dirstamp): + @$(MKDIR_P) $(PROJECT_INCLUDE)/drvmgr + @: > $(PROJECT_INCLUDE)/drvmgr/$(dirstamp) +PREINSTALL_DIRS += $(PROJECT_INCLUDE)/drvmgr/$(dirstamp) + +$(PROJECT_INCLUDE)/drvmgr/pci_bus.h: pci_bus.h $(PROJECT_INCLUDE)/drvmgr/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/drvmgr/pci_bus.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/drvmgr/pci_bus.h + diff --git a/cpukit/preinstall.am b/cpukit/preinstall.am index b430f793c2..bf691c575a 100644 --- a/cpukit/preinstall.am +++ b/cpukit/preinstall.am @@ -376,3 +376,20 @@ $(PROJECT_INCLUDE)/rtems/fsmount.h: libmisc/fsmount/fsmount.h $(PROJECT_INCLUDE) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/fsmount.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/fsmount.h +$(PROJECT_INCLUDE)/drvmgr/$(dirstamp): + @$(MKDIR_P) $(PROJECT_INCLUDE)/drvmgr + @: > $(PROJECT_INCLUDE)/drvmgr/$(dirstamp) +PREINSTALL_DIRS += $(PROJECT_INCLUDE)/drvmgr/$(dirstamp) + +$(PROJECT_INCLUDE)/drvmgr/drvmgr.h: libdrvmgr/drvmgr.h $(PROJECT_INCLUDE)/drvmgr/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/drvmgr/drvmgr.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/drvmgr/drvmgr.h + +$(PROJECT_INCLUDE)/drvmgr/drvmgr_confdefs.h: libdrvmgr/drvmgr_confdefs.h $(PROJECT_INCLUDE)/drvmgr/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/drvmgr/drvmgr_confdefs.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/drvmgr/drvmgr_confdefs.h + +$(PROJECT_INCLUDE)/drvmgr/drvmgr_list.h: libdrvmgr/drvmgr_list.h $(PROJECT_INCLUDE)/drvmgr/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/drvmgr/drvmgr_list.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/drvmgr/drvmgr_list.h + diff --git a/cpukit/sapi/include/confdefs.h b/cpukit/sapi/include/confdefs.h index 929f72643c..6d2509d671 100644 --- a/cpukit/sapi/include/confdefs.h +++ b/cpukit/sapi/include/confdefs.h @@ -2234,6 +2234,52 @@ rtems_fs_init_functions_t rtems_fs_init_helper = #endif #endif +/* + * Select PCI Configuration Library + */ +#ifdef RTEMS_PCI_CONFIG_LIB + #ifdef CONFIGURE_INIT + #define PCI_LIB_NONE 0 + #define PCI_LIB_AUTO 1 + #define PCI_LIB_STATIC 2 + #define PCI_LIB_READ 3 + #define PCI_LIB_PERIPHERAL 4 + #if CONFIGURE_PCI_LIB == PCI_LIB_AUTO + #define PCI_CFG_AUTO_LIB + #include <pci/cfg.h> + struct pci_bus pci_hb; + #define PCI_LIB_INIT pci_config_auto + #define PCI_LIB_CONFIG pci_config_auto_register + #elif CONFIGURE_PCI_LIB == PCI_LIB_STATIC + #define PCI_CFG_STATIC_LIB + #include <pci/cfg.h> + #define PCI_LIB_INIT pci_config_static + #define PCI_LIB_CONFIG NULL + /* Let user define PCI configuration (struct pci_bus pci_hb) */ + #elif CONFIGURE_PCI_LIB == PCI_LIB_READ + #define PCI_CFG_READ_LIB + #include <pci/cfg.h> + #define PCI_LIB_INIT pci_config_read + #define PCI_LIB_CONFIG NULL + struct pci_bus pci_hb; + #elif CONFIGURE_PCI_LIB == PCI_LIB_PERIPHERAL + #define PCI_LIB_INIT pci_config_peripheral + #define PCI_LIB_CONFIG NULL + /* Let user define PCI configuration (struct pci_bus pci_hb) */ + #elif CONFIGURE_PCI_LIB == PCI_LIB_NONE + #define PCI_LIB_INIT NULL + #define PCI_LIB_CONFIG NULL + /* No PCI Configuration at all, user can use/debug access routines */ + #else + #error NO PCI LIBRARY DEFINED + #endif + + const int pci_config_lib_type = CONFIGURE_PCI_LIB; + int (*pci_config_lib_init)(void) = PCI_LIB_INIT; + void (*pci_config_lib_register)(void *config) = PCI_LIB_CONFIG; + #endif +#endif + #ifdef __cplusplus } #endif diff --git a/cpukit/sapi/src/exinit.c b/cpukit/sapi/src/exinit.c index b0574f3b34..286f58884d 100644 --- a/cpukit/sapi/src/exinit.c +++ b/cpukit/sapi/src/exinit.c @@ -58,6 +58,9 @@ #ifdef RTEMS_ITRON_API #include <rtems/itron/itronapi.h> #endif +#ifdef RTEMS_DRVMGR_STARTUP + #include <drvmgr/drvmgr.h> +#endif Objects_Information *_Internal_Objects[ OBJECTS_INTERNAL_CLASSES_LAST + 1 ]; @@ -91,6 +94,10 @@ void rtems_initialize_data_structures(void) _System_state_Handler_initialization( FALSE ); #endif + #if defined(RTEMS_MULTIPROCESSING) + _Objects_MP_Handler_early_initialization(); + #endif + /* * Initialize any target architecture specific support as early as possible */ @@ -146,6 +153,10 @@ void rtems_initialize_data_structures(void) _IO_Manager_initialization(); + #ifdef RTEMS_DRVMGR_STARTUP + _DRV_Manager_initialization(); + #endif + #ifdef RTEMS_POSIX_API _POSIX_API_Initialize(); #endif @@ -193,8 +204,60 @@ void rtems_initialize_device_drivers(void) * NOTE: The MPCI may be build upon a device driver. */ + #ifdef RTEMS_DRVMGR_STARTUP + /* BSPs has already registered their "root bus" driver in the + * bsp_predriver hook or so. + * + * Init Drivers to Level 1, constraints: + * - Interrupts and system clock timer does not work. + * - malloc() work, however other memory services may not + * have been initialized yet. + * - initializes most basic stuff + * + * Typical setup in Level 1: + * - Find most devices in system, do PCI scan and configuration. + * - Reset hardware if needed. + * - Install IRQ driver + * - Install Timer driver + * - Install console driver and debug printk() + * - Install extra memory. + */ + _DRV_Manager_init_level(1); + #endif + + /* Initialize I/O drivers. + * + * Driver Manager note: + * All drivers may not be registered yet. Drivers will dynamically + * be initialized when registered in level 2,3 and 4. + */ _IO_Initialize_all_drivers(); + #ifdef RTEMS_DRVMGR_STARTUP + /* Init Drivers to Level 2, constraints: + * - Interrupts can be registered and enabled. + * - System Clock is running + * - Console may be used. + * + * This is typically where drivers are initialized + * for the first time. + */ + _DRV_Manager_init_level(2); + + /* Init Drivers to Level 3 + * + * This is typically where normal drivers are initialized + * for the second time, they may depend on other drivers + * API inited in level 2 + */ + _DRV_Manager_init_level(3); + + /* Init Drivers to Level 4, + * Init drivers that depend on services initialized in Level 3 + */ + _DRV_Manager_init_level(4); + #endif + #if defined(RTEMS_MULTIPROCESSING) if ( _System_state_Is_multiprocessing ) { _MPCI_Initialization(); diff --git a/cpukit/sapi/src/io.c b/cpukit/sapi/src/io.c index 65cb43d568..eb72f69b3b 100644 --- a/cpukit/sapi/src/io.c +++ b/cpukit/sapi/src/io.c @@ -24,6 +24,8 @@ #include <string.h> +int _IO_Manager_drivers_inititalized = 0; + /* * _IO_Manager_initialization * @@ -97,6 +99,8 @@ void _IO_Initialize_all_drivers( void ) { rtems_device_major_number major; + _IO_Manager_drivers_inititalized = 1; + for ( major=0 ; major < _IO_Number_of_drivers ; major ++ ) (void) rtems_io_initialize( major, 0, NULL ); } diff --git a/cpukit/sapi/src/ioregisterdriver.c b/cpukit/sapi/src/ioregisterdriver.c index 30d10eb808..6e505a6a14 100644 --- a/cpukit/sapi/src/ioregisterdriver.c +++ b/cpukit/sapi/src/ioregisterdriver.c @@ -28,6 +28,8 @@ #include <rtems/rtems/intr.h> #include <rtems/score/thread.h> +extern int _IO_Manager_drivers_inititalized; + static inline bool rtems_io_is_empty_table( const rtems_driver_address_table *table ) @@ -111,5 +113,15 @@ rtems_status_code rtems_io_register_driver( _Thread_Enable_dispatch(); - return rtems_io_initialize( major, 0, NULL ); + if ( _IO_Manager_drivers_inititalized ) { + /* Other drivers have already been initialized, we initialize + * the driver directly. + */ + return rtems_io_initialize( major, 0, NULL ); + } else { + /* The driver will be initialized together with all other drivers + * in a later stage by _IO_Initialize_all_drivers(). + */ + return RTEMS_SUCCESSFUL; + } } diff --git a/cpukit/score/cpu/sparc/cpu_asm.S b/cpukit/score/cpu/sparc/cpu_asm.S index e5ecc4c084..56e98b1fbe 100644 --- a/cpukit/score/cpu/sparc/cpu_asm.S +++ b/cpukit/score/cpu/sparc/cpu_asm.S @@ -56,7 +56,13 @@ SYM(_CPU_Context_save_fp): or %l1, %lo(SPARC_PSR_EF_MASK), %l1 or %l0, %l1, %l0 mov %l0, %psr ! **** ENABLE FLOAT ACCESS **** - nop; nop; nop; ! Need three nops before EF is + nop; nop; nop; ! Need three nops before EF is +#ifdef SPARC_DYNAMIC_FPU_DETECTION + mov %psr, %l0 ! **** check whether fpu present **** + andcc %l0,%l1,%g0 + beq 1f + nop +#endif ld [%i0], %l0 ! active due to pipeline delay!!! std %f0, [%l0 + FO_F1_OFFSET] std %f2, [%l0 + F2_F3_OFFSET] @@ -75,7 +81,7 @@ SYM(_CPU_Context_save_fp): std %f28, [%l0 + F28_F29_OFFSET] std %f30, [%l0 + F3O_F31_OFFSET] st %fsr, [%l0 + FSR_OFFSET] - ret +1: ret restore /* @@ -105,7 +111,13 @@ SYM(_CPU_Context_restore_fp): or %l1, %lo(SPARC_PSR_EF_MASK), %l1 or %l0, %l1, %l0 mov %l0, %psr ! **** ENABLE FLOAT ACCESS **** - nop; nop; nop; ! Need three nops before EF is + nop; nop; nop; ! Need three nops before EF is +#ifdef SPARC_DYNAMIC_FPU_DETECTION + mov %psr, %l0 ! **** check whether fpu present **** + andcc %l0,%l1,%g0 + beq 1f + nop +#endif ld [%i0], %l0 ! active due to pipeline delay!!! ldd [%l0 + FO_F1_OFFSET], %f0 ldd [%l0 + F2_F3_OFFSET], %f2 @@ -124,7 +136,7 @@ SYM(_CPU_Context_restore_fp): ldd [%l0 + F28_F29_OFFSET], %f28 ldd [%l0 + F3O_F31_OFFSET], %f30 ld [%l0 + FSR_OFFSET], %fsr - ret +1: ret restore #endif /* SPARC_HAS_FPU */ @@ -616,9 +628,8 @@ dont_fix_pil2: ! o1 = 2nd arg = address of the ISF ! WAS LOADED WHEN ISF WAS SAVED!!! - mov %l3, %o0 ! o0 = 1st arg = vector number call %g4, 0 - nop ! delay slot + mov %l3, %o0 ! o0 = 1st arg = vector number /* * Redisable traps so we can finish up the interrupt processing. diff --git a/cpukit/score/cpu/sparc/rtems/score/sparc.h b/cpukit/score/cpu/sparc/rtems/score/sparc.h index f13470e0ac..a145209139 100644 --- a/cpukit/score/cpu/sparc/rtems/score/sparc.h +++ b/cpukit/score/cpu/sparc/rtems/score/sparc.h @@ -64,12 +64,20 @@ extern "C" { #define SPARC_NUMBER_OF_REGISTER_WINDOWS 8 /* + * Compile the SPARC kernel with runtime FPU detection. This is used + * by RTEMS kernels built without FPU but still need to be able to + * run tasks with FPU enabled. This way the kernel is SPARC (v7/v8) + * with or without FPU compatible. + */ + +#define SPARC_DYNAMIC_FPU_DETECTION 1 + +/* * This should be determined based on some soft float derived * cpp predefine but gcc does not currently give us that information. */ - -#if defined(_SOFT_FLOAT) +#if !defined(SPARC_DYNAMIC_FPU_DETECTION) && defined(_SOFT_FLOAT) #define SPARC_HAS_FPU 0 #else #define SPARC_HAS_FPU 1 diff --git a/cpukit/wrapup/Makefile.am b/cpukit/wrapup/Makefile.am index 9c60f8eee3..abca02a2bd 100644 --- a/cpukit/wrapup/Makefile.am +++ b/cpukit/wrapup/Makefile.am @@ -61,6 +61,9 @@ TMP_LIBS += ../libmisc/libuuid.a TMP_LIBS += ../libi2c/libi2c.a +TMP_LIBS += ../libpci/libpci.a +TMP_LIBS += ../libdrvmgr/libdrvmgr.a + if LIBNETWORKING TMP_LIBS += ../libnetworking/libnetworking.a TMP_LIBS += ../libnetworking/libc.a diff --git a/doc/ada_user/Makefile.am b/doc/ada_user/Makefile.am index 822bf0700c..5f1e5b44fd 100644 --- a/doc/ada_user/Makefile.am +++ b/doc/ada_user/Makefile.am @@ -19,6 +19,7 @@ COMMON_FILES += \ $(top_builddir)/user/event.texi $(top_builddir)/user/fatal.texi \ $(top_builddir)/user/glossary.texi $(top_builddir)/user/init.texi \ $(top_builddir)/user/intr.texi $(top_builddir)/user/io.texi \ + $(top_builddir)/user/libpci.texi \ $(top_builddir)/user/mp.texi $(top_builddir)/user/msg.texi \ $(top_builddir)/user/overview.texi $(top_builddir)/user/part.texi \ $(top_builddir)/user/preface.texi $(top_builddir)/user/region.texi \ diff --git a/doc/ada_user/ada_user.texi b/doc/ada_user/ada_user.texi index 0e73505caa..c7b59d8c28 100644 --- a/doc/ada_user/ada_user.texi +++ b/doc/ada_user/ada_user.texi @@ -108,6 +108,7 @@ @include user/userext.texi @include user/conf.texi @include user/mp.texi +@include user/libpci.texi @include user/stackchk.texi @include user/cpuuse.texi @include user/object.texi @@ -148,6 +149,7 @@ This is the online version of the RTEMS Ada User's Guide. * User Extensions Manager:: * Configuring a System:: * Multiprocessing Manager:: +* PCI Library:: * Stack Bounds Checker:: * CPU Usage Statistics:: * Object Services:: diff --git a/doc/develenv/direct.t b/doc/develenv/direct.t index fc0a824efc..78049552c9 100644 --- a/doc/develenv/direct.t +++ b/doc/develenv/direct.t @@ -455,6 +455,9 @@ TCP/IP stack to RTEMS. This directory contains the port of the FreeBSD RPC/XDR source to RTEMS. +@item $@{RTEMS_ROOT@}/cpukit/libpci/ +This directory contains RTEMS PCI Library. + @item $@{RTEMS_ROOT@}/cpukit/posix/ This directory contains the RTEMS implementation of the threading portions of the POSIX API. diff --git a/doc/redo.sh b/doc/redo.sh new file mode 100644 index 0000000000..f0bf4e79f2 --- /dev/null +++ b/doc/redo.sh @@ -0,0 +1,9 @@ +#!/bin/bash +make clean +make distclean +../bootstrap +./configure --enable-maintainer-mode +cd tools; make +cd .. +make info +make all diff --git a/doc/user/Makefile.am b/doc/user/Makefile.am index 47c29b003b..fe5b3dea74 100644 --- a/doc/user/Makefile.am +++ b/doc/user/Makefile.am @@ -17,7 +17,7 @@ GENERATED_FILES = overview.texi concepts.texi datatypes.texi init.texi \ task.texi intr.texi clock.texi timer.texi sem.texi msg.texi event.texi \ signal.texi part.texi region.texi dpmem.texi io.texi fatal.texi \ schedule.texi rtmon.texi barrier.texi bsp.texi userext.texi conf.texi \ - mp.texi stackchk.texi cpuuse.texi object.texi chains.texi + mp.texi libpci.texi stackchk.texi cpuuse.texi object.texi chains.texi COMMON_FILES += $(top_srcdir)/common/cpright.texi @@ -164,10 +164,15 @@ conf.texi: conf.t mp.texi: mp.t $(BMENU2) -p "Configuring a System Sizing the RTEMS RAM Workspace" \ -u "Top" \ + -n "PCI Library" < $< > $@ + +libpci.texi: libpci.t + $(BMENU2) -p "Multiprocessing Manager MULTIPROCESSING_ANNOUNCE - Announce the arrival of a packet" \ + -u "Top" \ -n "Stack Bounds Checker" < $< > $@ stackchk.texi: stackchk.t - $(BMENU2) -p "Multiprocessing Manager MULTIPROCESSING_ANNOUNCE - Announce the arrival of a packet" \ + $(BMENU2) -p "PCI Library PCI Shell command" \ -u "Top" \ -n "CPU Usage Statistics" < $< > $@ @@ -187,7 +192,7 @@ chains.texi: chains.t -n "Directive Status Codes" < $< > $@ EXTRA_DIST = bsp.t clock.t chains.t concepts.t cpuuse.t datatypes.t conf.t \ - dpmem.t event.t fatal.t init.t intr.t io.t mp.t msg.t overview.t \ + dpmem.t event.t fatal.t init.t intr.t io.t libpci.t mp.t msg.t overview.t \ part.t region.t rtmon.t sem.t schedule.t signal.t stackchk.t \ task.t timer.t userext.t $(TXT_FILES) $(PNG_FILES) $(EPS_IMAGES) \ $(noinst_DATA) diff --git a/doc/user/c_user.texi b/doc/user/c_user.texi index eaaa419567..3eabe7e4ed 100644 --- a/doc/user/c_user.texi +++ b/doc/user/c_user.texi @@ -107,6 +107,7 @@ @include userext.texi @include conf.texi @include mp.texi +@include libpci.texi @include stackchk.texi @include cpuuse.texi @include object.texi @@ -147,6 +148,7 @@ This is the online version of the RTEMS C User's Guide. * User Extensions Manager:: * Configuring a System:: * Multiprocessing Manager:: +* PCI Library:: * Stack Bounds Checker:: * CPU Usage Statistics:: * Object Services:: diff --git a/doc/user/conf.t b/doc/user/conf.t index a47ae317a6..56886df36b 100644 --- a/doc/user/conf.t +++ b/doc/user/conf.t @@ -926,6 +926,56 @@ implicitly uses the Ada run-time. @end itemize +@subsection PCI Library + +This section defines the system configuration paramters supported +by @code{rtems/confdefs.h} related to configuring the PCI Library +for RTEMS. + +The PCI Library startup behaviour can be configured in four diffent +ways depending on how @code{CONFIGURE_PCI_CONFIG_LIB} is defined: + +@itemize @bullet +@findex PCI_LIB_AUTO +@item @code{PCI_LIB_AUTO} is used to enable the PCI auto configuration +software. PCI will be automatically probed, PCI buses enumerated, all +devices and bridges will be initialized using Plug & Play software +routines. The PCI device tree will be populated based on the PCI devices +found in the system, PCI devices will be configured by allocating address +region resources automatically in PCI space according to the BSP or host +bridge driver set up. + +@findex PCI_LIB_READ +@item @code{PCI_LIB_READ} is used to enable the PCI read configuration +software. The current PCI configuration is read to create the RAM +representation (the PCI device tree) of the PCI devices present. PCI devices +are assumed to already have been initialized and PCI buses enumrated, it is +therefore required that a BIOS or a boot loader has set up configuration space +prior to booting into RTEMS. + +@findex PCI_LIB_STATIC +@item @code{PCI_LIB_STATIC} is used to enable the PCI static configuration +software. The user provides a PCI tree with information how all PCI devices +are to be configured at compile time by linking in a custom +@code{struct pci_bus pci_hb} tree. The static PCI library will not probe PCI +for devices, instead it will assume that all devices defined by the user is +present, it will enumerate the PCI buses and configure all PCI devices in +static configuration accordingly. Since probe and allocation software is not +needed the startup is faster, have smaller footprint and does not require +dynamic memory allocation. + +@findex PCI_LIB_PERIPHERAL +@item @code{PCI_LIB_PERIPHERAL} is used to enable the PCI peripheral +configuration. It is similar to @code{PCI_LIB_STATIC}, but is will never write +the configuration to the PCI devices since PCI peripherals are not allowed to +access PCI configuration space. + +@end itemize + +Note that selecting PCI_LIB_STATIC or PCI_LIB_PERIPHERAL but not defining +@code{pci_hb} will reuslt in link errors. Note also that in these modes +Plug & Play is not performed. + @section Configuration Table @cindex Configuration Table diff --git a/doc/user/libpci.t b/doc/user/libpci.t new file mode 100644 index 0000000000..1eaca42018 --- /dev/null +++ b/doc/user/libpci.t @@ -0,0 +1,409 @@ +@c +@c COPYRIGHT (c) 2011 +@c Aeroflex Gaisler AB +@c All rights reserved. +@c +@c $Id: libpci.t,v v.vv xxxx/yy/zz xx:yy:zz ? Exp $ +@c + +@chapter PCI Library + +@cindex libpci + +@section Introduction + +The Peripheral Component Interconnect (PCI) bus is a very common computer +bus architecture that is found in almost every PC today. The PCI bus is +normally located at the motherboard where some PCI devices are soldered +directly onto the PCB and expansion slots allows the user to add custom +devices easily. There is a wide range of PCI hardware available implementing +all sorts of interfaces and functions. + +This section describes the PCI Library available in RTEMS used to access the +PCI bus in a portable way across computer architectures supported by RTEMS. + +The PCI Library aims to be compatible with PCI 2.3 with a couple of +limitations, for example there is no support for hot-plugging, 64-bit +memory space and cardbus bridges. + +In order to support different architectures and with small foot-print embedded +systems in mind the PCI Library offers four different configuration options +listed below. It is selected during compile time by defining the appropriate +macros in confdefs.h. It is also possible to enable NONE (No Configuration) +which can be used for debuging PCI access functions. +@itemize @bullet +@item Auto Configuration (do Plug & Play) +@item Read Configuration (read BIOS or boot loader configuration) +@item Static Configuration (write user defined configuration) +@item Peripheral Configuration (no access to cfg-space) +@end itemize + +@section Background + +The PCI bus is constructed in a way where on-board devices and devices +in expansion slots can be automatically found (probed) and configured +using Plug & Play completely implemented in software. The bus is set up once +during boot up. The Plug & Play information can be read and written from +PCI configuration space. A PCI device is identified in configuration space by +a unique bus, slot and function number. Each PCI slot can have up to 8 +functions and interface to another PCI sub-bus by implementing a PCI-to-PCI +bridge according to the PCI Bridge Architecture specification. + +Using the unique [bus:slot:func] any device can be configured regardless how PCI +is currently set up as long as all PCI buses are enumerated correctly. The +enumration is done during probing, all bridges are given a bus numbers in +order for the bridges to respond to accesses from both directions. The PCI +library can assign address ranges to which a PCI device should respond using +Plug & Play technique or a static user defined configuration. After the +configuration has been performed the PCI device drivers can find devices by +the read-only PCI Class type, Vendor ID and Device ID information found in +configuration space for each device. + +In some systems there is a boot loader or BIOS which have already configured +all PCI devices, but on embedded targets it is quite common that there is no +BIOS or boot loader, thus RTEMS must configure the PCI bus. Only the PCI host +may do configuration space access, the host driver or BSP is responsible to +translate the [bus:slot:func] into a valid PCI configuration space access. + +If the target is not a host, but a peripheral, configuration space can not be +accessed, the peripheral is set up by the host during start up. In complex +embedded PCI systems the peripheral may need to access other PCI boards than +then host. In such systems a custom (static) configuration of both the host +and peripheral may be a convenient solution. + +The PCI bus defines four interrupt signals INTA#..INTD#. The interrupt signals +must be mapped into a system interrupt/vector, it is up to the BSP or host +driver to know the mapping, however the BIOS or boot loader may use the +8-bit read/write "Interrupt Line" register to pass the knowledge along to the +OS. + + + The PCI standard +defines and recommends that the backplane route the interupt lines in a +systematic way, however in + +@subsection Software Components + +The PCI library is located in cpukit/libpci, it consists of different parts: +@itemize @bullet +@item PCI Host bridge driver interface +@item Configuration routines +@item Access (Configuration, I/O and Memory space) routines +@item Interrupt routines (implemented by BSP) +@item Print routines +@item Static/peripheral configuration creation +@item PCI shell command +@end itemize + +@subsection PCI Configuration + +During start up the PCI bus must be configured in order for host and peripherals +to access one another using Memory or I/O accesses and that interrupts are +properly handled. Three different spaces are defined and mapped separately: +@enumerate +@item I/O space (IO) +@item non-prefetchable Memory space (MEMIO) +@item prefetchable Memory space (MEM) +@end enumerate + +Regions of the same type (I/O or Memory) may not overlap which is guaranteed +by the software. MEM regions may be mapped into MEMIO regions, but MEMIO +regions can not be mapped into MEM, for that could lead to prefetching of +registers. The interrupt pin which a board is driving can be read out from +PCI configuration space, however it is up to software to know how interrupt +signals are routed between PCI-to-PCI bridges and how PCI INT[A..D]# pins are +mapped to system IRQ. In systems where previous software (boot loader or BIOS) +has already set up this the configuration overwritten or simply read out. + +In order to support different configuration methods the following configuration +libraries are available can selectable by the user: +@itemize @bullet +@item Auto Configuration (run Plug & Play software) +@item Read Configuration (relies on a boot loader or BIOS) +@item Static Configuration (write user defined setup, no Plug & Play) +@item Peripheral Configuration (user defined setup, no access to configuration space) +@end itemize + +A host driver can be made to support all three configuration methods, or any +combination. It may be defined by the BSP which approach is used. + +The configuration software is called from the PCI driver (pci_config_init()). + +Regardless of configuration method a PCI device tree is created in RAM during +initialization, the tree can be accessed to find devices and resources without +accessing configuration space later on. The user is responsible to create the +device tree at compile time when using the static/peripheral method. + + +@subsubsection RTEMS Configuration selection + +The active configuration method can be selected at compile time in the same +way as other project parameters by including rtems/confdefs.h and setting +@itemize @bullet +@item CONFIGURE_INIT +@item RTEMS_PCI_CONFIG_LIB +@item CONFIGURE_PCI_LIB = PCI_LIB_(AUTO,STATIC,READ,PERIPHERAL) +@end itemize + +See the RTEMS configuration section how to setup the PCI library. + + +@subsubsection Auto Configuration + +The auto configuration software enumerate PCI buses and initializes all PCI +devices found using Plug & Play. The auto configuration software requires +that a configuration setup has been registered by the driver or BSP in order +to setup the I/O and Memory regions at the correct address ranges. PCI +interrupt pins can optionally be routed over PCI-to-PCI bridges and mapped +to a system interrupt number. Resources are sorted by size and required +alignment, unused "dead" space may be created when PCI bridges are present +due to the PCI bridge window size does not equal the alignment, to cope with +that resources are reordered to fit smaller BARs into the dead space to minimize +the PCI space required. If a BAR or ROM register can not be allocated a PCI +address region (due to too few resources available) the register will be given +the value of pci_invalid_address which defaults to 0. + +The auto configuration routines support: +@itemize @bullet +@item PCI 2.3 +@item Little and big endian PCI bus +@item one I/O 16 or 32-bit range (IO) +@item memory space (MEMIO) +@item prefetchable memory space (MEM), if not present MEM will be mapped into + MEMIO +@item multiple PCI buses - PCI-to-PCI bridges +@item standard BARs, PCI-to-PCI bridge BARs, ROM BARs +@item Interrupt routing over bridges +@item Interrupt pin to system interrupt mapping +@end itemize + +Not supported: +@itemize @bullet +@item hot-pluggable devices +@item Cardbus bridges +@item 64-bit memory space +@item 16-bit and 32-bit I/O address ranges at the same time +@end itemize + +In PCI 2.3 there may exist I/O BARs that must be located at the low 64kBytes +address range, in order to support this the host driver or BSP must make sure +that I/O addresses region is within this region. + + +@subsubsection Read Configuration + +When a BIOS or boot loader already has setup the PCI bus the configuration can +be read directly from the PCI resource registers and buses are already +enumerated, this is a much simpler approach than configuring PCI ourselves. The +PCI device tree is automatically created based on the current configuration and +devices present. After initialization is done there is no difference between +the auto or read configuration approaches. + + +@subsubsection Static Configuration + +To support custom configurations and small-footprint PCI systems, the user may +provide the PCI device tree which contains the current configuration. The +PCI buses are enumerated and all resources are written to PCI devices during +initialization. When this approach is selected PCI boards must be located at +the same slots every time and devices can not be removed or added, Plug & Play +is not performed. Boards of the same type may of course be exchanged. + +The user can create a configuration by calling pci_cfg_print() on a running +system that has had PCI setup by the auto or read configuration routines, it +can be called from the PCI shell command. The user must provide the PCI device +tree named pci_hb. + + +@subsubsection Peripheral Configuration + +On systems where a peripheral PCI device needs to access other PCI devices than +the host the peripheral configuration approach may be handy. Most PCI devices +answers on the PCI host's requests and start DMA accesses into the Hosts memory, +however in some complex systems PCI devices may want to access other devices +on the same bus or at another PCI bus. + +A PCI peripheral is not allowed to do PCI configuration cycles, which means that +is must either rely on the host to give it the addresses it needs, or that the +addresses are predefined. + +This configuration approach is very similar to the static option, however the +configuration is never written to PCI bus, instead it is only used for drivers +to find PCI devices and resources using the same PCI API as for the host + + +@subsection PCI Access + +The PCI access routines are low-level routines provided for drivers, +configuration software, etc. in order to access different regions in a way +not dependent upon the host driver, BSP or platform. +@itemize @bullet +@item PCI configuration space +@item PCI I/O space +@item Registers over PCI memory space +@item Translate PCI address into CPU accessible address and vice verse +@end itemize + +By using the access routines drivers can be made portable over different +architectures. The access routines take the architecture endianness into +consideration and let the host driver or BSP implement I/O space and +configuration space access. + +Some non-standard hardware may also define the PCI bus big-endian, for example +the LEON2 AT697 PCI host bridge and some LEON3 systems may be configured that +way. It is up to the BSP to set the appropriate PCI endianness on compile time +(BSP_PCI_BIG_ENDIAN) in order for inline macros to be correctly defined. +Another possibility is to use the function pointers defined by the access +layer to implement drivers that support "run-time endianness detection". + + +@subsubsection Configuration space + +Configuration space is accessed using the routines listed below. The +pci_dev_t type is used to specify a specific PCI bus, device and function. It +is up to the host driver or BSP to create a valid access to the requested +PCI slot. Requests made to slots that is not supported by hardware should +result in PCISTS_MSTABRT and/or data must be ignored (writes) or 0xffffffff +is always returned (reads). + +@example + /* Configuration Space Access Read Routines */ + extern int pci_cfg_r8(pci_dev_t dev, int ofs, uint8_t *data); + extern int pci_cfg_r16(pci_dev_t dev, int ofs, uint16_t *data); + extern int pci_cfg_r32(pci_dev_t dev, int ofs, uint32_t *data); + + /* Configuration Space Access Write Routines */ + extern int pci_cfg_w8(pci_dev_t dev, int ofs, uint8_t data); + extern int pci_cfg_w16(pci_dev_t dev, int ofs, uint16_t data); + extern int pci_cfg_w32(pci_dev_t dev, int ofs, uint32_t data); +@end example + + +@subsubsection I/O space + +The BSP or driver provide special routines in order to access I/O space. Some +architectures have a special instruction accessing I/O space, others have it +mapped into a "PCI I/O window" in the standard address space accessed by the +CPU. The window size may vary and must be taken into consideration by the +host driver. The below routines must be used to access I/O space. The address +given to the functions is not the PCI I/O addresses, the caller must have +translated PCI I/O addresses (available in the PCI BARs) into a BSP or host +driver custom address, see @ref{Access functions} how addresses are +translated. + +@example +/* Read a register over PCI I/O Space */ +extern uint8_t pci_io_r8(uint32_t adr); +extern uint16_t pci_io_r16(uint32_t adr); +extern uint32_t pci_io_r32(uint32_t adr); + +/* Write a register over PCI I/O Space */ +extern void pci_io_w8(uint32_t adr, uint8_t data); +extern void pci_io_w16(uint32_t adr, uint16_t data); +extern void pci_io_w32(uint32_t adr, uint32_t data); +@end example + + +@subsubsection Registers over Memory space + +PCI host bridge hardware normally swap data accesses into the endianness of the +host architecture in order to lower the load of the CPU, peripherals can do DMA +without swapping. However, the host controller can not separate a standard +memory access from a memory access to a register, registers may be mapped into +memory space. This leads to register content being swapped, which must be +swapped back. The below routines makes it possible to access registers over PCI +memory space in a portable way on different architectures, the BSP or +architecture must provide necessary functions in order to implement this. + +@example + static inline uint16_t pci_ld_le16(volatile uint16_t *addr); + static inline void pci_st_le16(volatile uint16_t *addr, uint16_t val); + static inline uint32_t pci_ld_le32(volatile uint32_t *addr); + static inline void pci_st_le32(volatile uint32_t *addr, uint32_t val); + static inline uint16_t pci_ld_be16(volatile uint16_t *addr); + static inline void pci_st_be16(volatile uint16_t *addr, uint16_t val); + static inline uint32_t pci_ld_be32(volatile uint32_t *addr); + static inline void pci_st_be32(volatile uint32_t *addr, uint32_t val); +@end example + +In order to support non-standard big-endian PCI bus the above pci_* functions +is required, pci_ld_le16 != ld_le16 on big endian PCI buses. + + +@subsubsection Access functions + +The PCI Access Library can provide device drivers with function pointers +executing the above Configuration, I/O and Memory space accesses. The +functions have the same arguments and return values as the as the above +functions. + +The pci_access_func() function defined below can be used to get a function +pointer of a specific access type. + +@example + /* Get Read/Write function for accessing a register over PCI Memory Space + * (non-inline functions). + * + * Arguments + * wr 0(Read), 1(Write) + * size 1(Byte), 2(Word), 4(Double Word) + * func Where function pointer will be stored + * endian PCI_LITTLE_ENDIAN or PCI_BIG_ENDIAN + * type 1(I/O), 3(REG over MEM), 4(CFG) + * + * Return + * 0 Found function + * others No such function defined by host driver or BSP + */ + int pci_access_func(int wr, int size, void **func, int endian, int type); +@end example + +PCI devices drivers may be written to support run-time detection of endianess, +this is mosly for debugging or for development systems. When the product is +finally deployed macros switch to using the inline functions instead which +have been configured for the correct endianness. + + +@subsubsection PCI address translation + +When PCI addresses, both I/O and memory space, is not mapped 1:1 address +translation before access is needed. If drivers read the PCI resources directly +using configuration space routines or in the device tree, the addresses given +are PCI addresses. The below functions can be used to translate PCI addresses +into CPU accessible addresses or vise versa, translation may be different for +different PCI spaces/regions. + +@example + /* Translate PCI address into CPU accessible address */ + static inline int pci_pci2cpu(uint32_t *address, int type); + + /* Translate CPU accessible address into PCI address (for DMA) */ + static inline int pci_cpu2pci(uint32_t *address, int type); +@end example + + +@subsection PCI Interrupt + +The PCI specification defines four different interrupt lines INTA#..INTD#, +the interrupts are low level sensitive which make it possible to support +multiple interrupt sources on the same interrupt line. Since the lines are +level sensitive the interrupt sources must be acknowledged before clearing the +interrupt contoller, or the interrupt controller must be masked. The BSP must +provide a routine for clearing/acknowledging the interrupt controller, it is +up to the interrupt service routine to acknowledge the interrupt source. + +The PCI Library relies on the BSP for implementing shared interrupt handling +through the BSP_PCI_shared_interrupt_* functions/macros, they must be defined +when including bsp.h. + +PCI device drivers may use the pci_interrupt_ routines in order to call the +BSP specific functions in a platform independent way. The PCI interrupt +interface has been made similar to the RTEMS IRQ extension so that a BSP can +use the standard RTEMS interrupt functions directly. + + +@subsection PCI Shell command + +The RTEMS shell have a PCI command 'pci' which makes it possible to read/write +configuration space, print the current PCI configuration and print out a +configuration C-file for the static or peripheral library. diff --git a/testsuites/mptests/mp06/task1.c b/testsuites/mptests/mp06/task1.c index 8e9fd71b25..976cb85286 100644 --- a/testsuites/mptests/mp06/task1.c +++ b/testsuites/mptests/mp06/task1.c @@ -162,6 +162,7 @@ rtems_task Test_task( if ( Multiprocessing_configuration.node == 2 ) { /* Flush events */ + rtems_task_wake_after( 100 ); puts( "Flushing RTEMS_EVENT_16" ); (void) rtems_event_receive(RTEMS_EVENT_16, RTEMS_NO_WAIT, 0, &event_out); diff --git a/testsuites/psxtests/psxualarm/init.c b/testsuites/psxtests/psxualarm/init.c index 82a4d8d251..3f278d4177 100644 --- a/testsuites/psxtests/psxualarm/init.c +++ b/testsuites/psxtests/psxualarm/init.c @@ -26,12 +26,14 @@ void Signal_handler( ) { Signal_count++; +#if 0 printf( "Signal: %d caught by 0x%" PRIxpthread_t " (%d)\n", signo, pthread_self(), Signal_count ); +#endif Signal_occurred = 1; } diff --git a/testsuites/sptests/sp20/task1.c b/testsuites/sptests/sp20/task1.c index 680cba9f5f..4189659d27 100644 --- a/testsuites/sptests/sp20/task1.c +++ b/testsuites/sptests/sp20/task1.c @@ -26,7 +26,7 @@ #define TA6_ITERATIONS 10 #define TA6_PERIOD_FACTOR 10 -uint32_t Periods[7] = { 0, 2, 2, 2, 2, 100, 0 }; +uint32_t Periods[7] = { 0, 20, 20, 20, 20, 1000, 0 }; uint32_t Iterations[7] = { 0, 50, 50, 50, 50, 1, TA6_ITERATIONS }; rtems_task_priority Priorities[7] = { 0, 1, 1, 3, 4, 5, 1 }; @@ -69,7 +69,7 @@ rtems_task Task_1_through_6( case 4: while ( FOREVER ) { status = rtems_rate_monotonic_period( rmid, Periods[ argument ] ); - directive_failed( status, "rtems_rate_monotonic_period" ); + directive_failed( status, "rtems_rate_monotonic_period 0 of TAN" ); Count.count[ argument ]++; } |