From a0496b5e4f665e627005ed4fbc721f9dc52396b0 Mon Sep 17 00:00:00 2001 From: Jeff Johnston Date: Tue, 10 Jun 2008 02:13:22 +0000 Subject: [PATCH] 2008-06-09 Ken Werner * libc/machine/spu/Makefile.am: Add new files. * libc/machine/spu/Makefile.in: Likewise. * libc/machine/spu/include/spu_timer.h: New file to add timer support using interrupts. * libc/machine/spu/spu_clock_stop.c: Likewise. * libc/machine/spu/spu_clock_svcs.c: Likewise. * libc/machine/spu/spu_timer_flih.S: Likewise. * libc/machine/spu/spu_timer_free.c: Likewise. * libc/machine/spu/spu_timer_internal.h: Likewise. * libc/machine/spu/spu_timer_slih.c: Likewise. * libc/machine/spu/spu_timer_slih_reg.c: Likewise. * libc/machine/spu/spu_timer_stop.c: Likewise. * libc/machine/spu/spu_timer_svcs.c: Likewise. --- newlib/ChangeLog | 16 ++ newlib/libc/machine/spu/Makefile.am | 4 +- newlib/libc/machine/spu/Makefile.in | 77 ++++++- newlib/libc/machine/spu/include/spu_timer.h | 84 +++++++ newlib/libc/machine/spu/spu_clock_stop.c | 67 ++++++ newlib/libc/machine/spu/spu_clock_svcs.c | 93 ++++++++ newlib/libc/machine/spu/spu_timer_flih.S | 152 +++++++++++++ newlib/libc/machine/spu/spu_timer_free.c | 86 ++++++++ newlib/libc/machine/spu/spu_timer_internal.h | 140 ++++++++++++ newlib/libc/machine/spu/spu_timer_slih.c | 221 +++++++++++++++++++ newlib/libc/machine/spu/spu_timer_slih_reg.c | 72 ++++++ newlib/libc/machine/spu/spu_timer_stop.c | 101 +++++++++ newlib/libc/machine/spu/spu_timer_svcs.c | 115 ++++++++++ 13 files changed, 1217 insertions(+), 11 deletions(-) create mode 100644 newlib/libc/machine/spu/include/spu_timer.h create mode 100644 newlib/libc/machine/spu/spu_clock_stop.c create mode 100644 newlib/libc/machine/spu/spu_clock_svcs.c create mode 100644 newlib/libc/machine/spu/spu_timer_flih.S create mode 100644 newlib/libc/machine/spu/spu_timer_free.c create mode 100644 newlib/libc/machine/spu/spu_timer_internal.h create mode 100644 newlib/libc/machine/spu/spu_timer_slih.c create mode 100644 newlib/libc/machine/spu/spu_timer_slih_reg.c create mode 100644 newlib/libc/machine/spu/spu_timer_stop.c create mode 100644 newlib/libc/machine/spu/spu_timer_svcs.c diff --git a/newlib/ChangeLog b/newlib/ChangeLog index 13a4bb3c9..e68a1f4b6 100644 --- a/newlib/ChangeLog +++ b/newlib/ChangeLog @@ -1,3 +1,19 @@ +2008-06-09 Ken Werner + + * libc/machine/spu/Makefile.am: Add new files. + * libc/machine/spu/Makefile.in: Likewise. + * libc/machine/spu/include/spu_timer.h: New file to add timer support + using interrupts. + * libc/machine/spu/spu_clock_stop.c: Likewise. + * libc/machine/spu/spu_clock_svcs.c: Likewise. + * libc/machine/spu/spu_timer_flih.S: Likewise. + * libc/machine/spu/spu_timer_free.c: Likewise. + * libc/machine/spu/spu_timer_internal.h: Likewise. + * libc/machine/spu/spu_timer_slih.c: Likewise. + * libc/machine/spu/spu_timer_slih_reg.c: Likewise. + * libc/machine/spu/spu_timer_stop.c: Likewise. + * libc/machine/spu/spu_timer_svcs.c: Likewise. + 2008-06-09 Ken Werner * libc/machine/spu/strcat.c: Return value fixed. diff --git a/newlib/libc/machine/spu/Makefile.am b/newlib/libc/machine/spu/Makefile.am index bd66094c5..fe4798e29 100644 --- a/newlib/libc/machine/spu/Makefile.am +++ b/newlib/libc/machine/spu/Makefile.am @@ -21,7 +21,9 @@ lib_a_SOURCES = setjmp.S assert.c clearerr.c creat.c fclose.c feof.c \ tmpnam.c ungetc.c usleep.c vfiprintf.c vfiscanf.c vfprintf.c \ vfscanf.c viprintf.c viscanf.c vprintf.c vscanf.c vsiprintf.c \ vsiscanf.c vsniprintf.c vsnprintf.c vsprintf.c vsscanf.c \ - stack_reg_va.S + stack_reg_va.S spu_clock_svcs.c spu_clock_stop.c spu_timer_flih.S \ + spu_timer_slih.c spu_timer_slih_reg.c spu_timer_svcs.c \ + spu_timer_stop.c spu_timer_free.c lib_a_CCASFLAGS = $(AM_CCASFLAGS) lib_a_CFLAGS = $(AM_CFLAGS) diff --git a/newlib/libc/machine/spu/Makefile.in b/newlib/libc/machine/spu/Makefile.in index 73464d5d9..941f010fc 100644 --- a/newlib/libc/machine/spu/Makefile.in +++ b/newlib/libc/machine/spu/Makefile.in @@ -74,7 +74,11 @@ DIST_COMMON = $(srcdir)/../../../../config.guess \ $(srcdir)/../../../../compile $(srcdir)/../../../../compile \ $(srcdir)/../../../../compile $(srcdir)/../../../../compile \ $(srcdir)/../../../../compile $(srcdir)/../../../../compile \ - $(srcdir)/../../../../compile $(srcdir)/../../../../compile + $(srcdir)/../../../../compile $(srcdir)/../../../../compile \ + $(srcdir)/../../../../compile $(srcdir)/../../../../compile \ + $(srcdir)/../../../../compile $(srcdir)/../../../../compile \ + $(srcdir)/../../../../compile $(srcdir)/../../../../compile \ + $(srcdir)/../../../../compile subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/../../../acinclude.m4 \ @@ -131,7 +135,12 @@ am_lib_a_OBJECTS = lib_a-setjmp.$(OBJEXT) lib_a-assert.$(OBJEXT) \ lib_a-vscanf.$(OBJEXT) lib_a-vsiprintf.$(OBJEXT) \ lib_a-vsiscanf.$(OBJEXT) lib_a-vsniprintf.$(OBJEXT) \ lib_a-vsnprintf.$(OBJEXT) lib_a-vsprintf.$(OBJEXT) \ - lib_a-vsscanf.$(OBJEXT) lib_a-stack_reg_va.$(OBJEXT) + lib_a-vsscanf.$(OBJEXT) lib_a-stack_reg_va.$(OBJEXT) \ + lib_a-spu_clock_svcs.$(OBJEXT) lib_a-spu_clock_stop.$(OBJEXT) \ + lib_a-spu_timer_flih.$(OBJEXT) lib_a-spu_timer_slih.$(OBJEXT) \ + lib_a-spu_timer_slih_reg.$(OBJEXT) \ + lib_a-spu_timer_svcs.$(OBJEXT) lib_a-spu_timer_stop.$(OBJEXT) \ + lib_a-spu_timer_free.$(OBJEXT) lib_a_OBJECTS = $(am_lib_a_OBJECTS) DEFAULT_INCLUDES = -I. -I$(srcdir) depcomp = @@ -205,6 +214,11 @@ STRIP = @STRIP@ USE_LIBTOOL_FALSE = @USE_LIBTOOL_FALSE@ USE_LIBTOOL_TRUE = @USE_LIBTOOL_TRUE@ VERSION = @VERSION@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_AS = @ac_ct_AS@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_READELF = @ac_ct_READELF@ +ac_ct_STRIP = @ac_ct_STRIP@ aext = @aext@ am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ @@ -220,23 +234,18 @@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ datadir = @datadir@ -datarootdir = @datarootdir@ -docdir = @docdir@ -dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ -htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ libm_machine_dir = @libm_machine_dir@ -localedir = @localedir@ localstatedir = @localstatedir@ lpfx = @lpfx@ machine_dir = @machine_dir@ @@ -245,10 +254,8 @@ mkdir_p = @mkdir_p@ newlib_basedir = @newlib_basedir@ oext = @oext@ oldincludedir = @oldincludedir@ -pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ -psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sys_dir = @sys_dir@ @@ -271,7 +278,9 @@ lib_a_SOURCES = setjmp.S assert.c clearerr.c creat.c fclose.c feof.c \ tmpnam.c ungetc.c usleep.c vfiprintf.c vfiscanf.c vfprintf.c \ vfscanf.c viprintf.c viscanf.c vprintf.c vscanf.c vsiprintf.c \ vsiscanf.c vsniprintf.c vsnprintf.c vsprintf.c vsscanf.c \ - stack_reg_va.S + stack_reg_va.S spu_clock_svcs.c spu_clock_stop.c spu_timer_flih.S \ + spu_timer_slih.c spu_timer_slih_reg.c spu_timer_svcs.c \ + spu_timer_stop.c spu_timer_free.c lib_a_CCASFLAGS = $(AM_CCASFLAGS) lib_a_CFLAGS = $(AM_CFLAGS) @@ -430,6 +439,12 @@ lib_a-stack_reg_va.o: stack_reg_va.S lib_a-stack_reg_va.obj: stack_reg_va.S $(CCAS) $(lib_a_CCASFLAGS) $(CCASFLAGS) -c -o lib_a-stack_reg_va.obj `if test -f 'stack_reg_va.S'; then $(CYGPATH_W) 'stack_reg_va.S'; else $(CYGPATH_W) '$(srcdir)/stack_reg_va.S'; fi` +lib_a-spu_timer_flih.o: spu_timer_flih.S + $(CCAS) $(lib_a_CCASFLAGS) $(CCASFLAGS) -c -o lib_a-spu_timer_flih.o `test -f 'spu_timer_flih.S' || echo '$(srcdir)/'`spu_timer_flih.S + +lib_a-spu_timer_flih.obj: spu_timer_flih.S + $(CCAS) $(lib_a_CCASFLAGS) $(CCASFLAGS) -c -o lib_a-spu_timer_flih.obj `if test -f 'spu_timer_flih.S'; then $(CYGPATH_W) 'spu_timer_flih.S'; else $(CYGPATH_W) '$(srcdir)/spu_timer_flih.S'; fi` + .c.o: $(COMPILE) -c $< @@ -855,6 +870,48 @@ lib_a-vsscanf.o: vsscanf.c lib_a-vsscanf.obj: vsscanf.c $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-vsscanf.obj `if test -f 'vsscanf.c'; then $(CYGPATH_W) 'vsscanf.c'; else $(CYGPATH_W) '$(srcdir)/vsscanf.c'; fi` + +lib_a-spu_clock_svcs.o: spu_clock_svcs.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-spu_clock_svcs.o `test -f 'spu_clock_svcs.c' || echo '$(srcdir)/'`spu_clock_svcs.c + +lib_a-spu_clock_svcs.obj: spu_clock_svcs.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-spu_clock_svcs.obj `if test -f 'spu_clock_svcs.c'; then $(CYGPATH_W) 'spu_clock_svcs.c'; else $(CYGPATH_W) '$(srcdir)/spu_clock_svcs.c'; fi` + +lib_a-spu_clock_stop.o: spu_clock_stop.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-spu_clock_stop.o `test -f 'spu_clock_stop.c' || echo '$(srcdir)/'`spu_clock_stop.c + +lib_a-spu_clock_stop.obj: spu_clock_stop.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-spu_clock_stop.obj `if test -f 'spu_clock_stop.c'; then $(CYGPATH_W) 'spu_clock_stop.c'; else $(CYGPATH_W) '$(srcdir)/spu_clock_stop.c'; fi` + +lib_a-spu_timer_slih.o: spu_timer_slih.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-spu_timer_slih.o `test -f 'spu_timer_slih.c' || echo '$(srcdir)/'`spu_timer_slih.c + +lib_a-spu_timer_slih.obj: spu_timer_slih.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-spu_timer_slih.obj `if test -f 'spu_timer_slih.c'; then $(CYGPATH_W) 'spu_timer_slih.c'; else $(CYGPATH_W) '$(srcdir)/spu_timer_slih.c'; fi` + +lib_a-spu_timer_slih_reg.o: spu_timer_slih_reg.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-spu_timer_slih_reg.o `test -f 'spu_timer_slih_reg.c' || echo '$(srcdir)/'`spu_timer_slih_reg.c + +lib_a-spu_timer_slih_reg.obj: spu_timer_slih_reg.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-spu_timer_slih_reg.obj `if test -f 'spu_timer_slih_reg.c'; then $(CYGPATH_W) 'spu_timer_slih_reg.c'; else $(CYGPATH_W) '$(srcdir)/spu_timer_slih_reg.c'; fi` + +lib_a-spu_timer_svcs.o: spu_timer_svcs.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-spu_timer_svcs.o `test -f 'spu_timer_svcs.c' || echo '$(srcdir)/'`spu_timer_svcs.c + +lib_a-spu_timer_svcs.obj: spu_timer_svcs.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-spu_timer_svcs.obj `if test -f 'spu_timer_svcs.c'; then $(CYGPATH_W) 'spu_timer_svcs.c'; else $(CYGPATH_W) '$(srcdir)/spu_timer_svcs.c'; fi` + +lib_a-spu_timer_stop.o: spu_timer_stop.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-spu_timer_stop.o `test -f 'spu_timer_stop.c' || echo '$(srcdir)/'`spu_timer_stop.c + +lib_a-spu_timer_stop.obj: spu_timer_stop.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-spu_timer_stop.obj `if test -f 'spu_timer_stop.c'; then $(CYGPATH_W) 'spu_timer_stop.c'; else $(CYGPATH_W) '$(srcdir)/spu_timer_stop.c'; fi` + +lib_a-spu_timer_free.o: spu_timer_free.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-spu_timer_free.o `test -f 'spu_timer_free.c' || echo '$(srcdir)/'`spu_timer_free.c + +lib_a-spu_timer_free.obj: spu_timer_free.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-spu_timer_free.obj `if test -f 'spu_timer_free.c'; then $(CYGPATH_W) 'spu_timer_free.c'; else $(CYGPATH_W) '$(srcdir)/spu_timer_free.c'; fi` uninstall-info-am: ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) diff --git a/newlib/libc/machine/spu/include/spu_timer.h b/newlib/libc/machine/spu/include/spu_timer.h new file mode 100644 index 000000000..ea10ce57d --- /dev/null +++ b/newlib/libc/machine/spu/include/spu_timer.h @@ -0,0 +1,84 @@ +/* +(C) Copyright IBM Corp. 2008 + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. +* Neither the name of IBM nor the names of its contributors may be +used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _SPU_TIMER_H_ +#define _SPU_TIMER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* Clock services. */ +extern void spu_clock_start (void); +extern int spu_clock_stop (void); +extern uint64_t spu_clock_read (void); + +/* Timer services. */ +extern int spu_timer_alloc (int interval, void (*func) (int)); +extern int spu_timer_free (int id); +extern int spu_timer_start (int id); +extern int spu_timer_stop (int id); + +/* Interrupt services. */ +extern void spu_slih_register (unsigned event_mask, + unsigned (*slih) (unsigned)); +extern unsigned spu_clock_slih (unsigned event_mask); + +/* Number of supported timers. */ +#define SPU_TIMER_NTIMERS 4 + +/* Recommended minimun spu timer interval time from (cat /proc/cpuinfo) + * QS20 100/14318000 = 6.98 usec + * QS21/QS22 100/26666666 = 3.75 usec + * PS3 100/79800000 = 1.25 usec */ +#define SPU_TIMER_MIN_INTERVAL 100 + +/* Clock error codes. */ +#define SPU_CLOCK_ERR_NOT_RUNNING -2 +#define SPU_CLOCK_ERR_STILL_RUNNING -3 +#define SPU_CLOCK_ERR_TIMERS_ACTIVE -4 + +/* Timer error codes. */ +#define SPU_TIMER_ERR_INVALID_PARM -10 +#define SPU_TIMER_ERR_NONE_FREE -11 +#define SPU_TIMER_ERR_INVALID_ID -12 +#define SPU_TIMER_ERR_ACTIVE -13 +#define SPU_TIMER_ERR_NOT_ACTIVE -14 +#define SPU_TIMER_ERR_NOCLOCK -15 +#define SPU_TIMER_ERR_FREE -16 +#define SPU_TIMER_ERR_NOT_STOPPED -17 + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/newlib/libc/machine/spu/spu_clock_stop.c b/newlib/libc/machine/spu/spu_clock_stop.c new file mode 100644 index 000000000..da31b42c6 --- /dev/null +++ b/newlib/libc/machine/spu/spu_clock_stop.c @@ -0,0 +1,67 @@ +/* +(C) Copyright IBM Corp. 2008 + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. +* Neither the name of IBM nor the names of its contributors may be +used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +/* SPU clock stop library service. */ +#include +#include "spu_timer_internal.h" + +/* Stops the SPU clock: + * decrements clock start count + * when count is zero, disables the decrementer event and stops the + decrementer + Returns 0 on success and <0 on failure: + * SPU_CLOCK_ERR_NOT_RUNNING - clock was already off + * SPU_CLOCK_ERR_TIMERS_ACTIVE - active timers exist + * SPU_CLOCK_ERR_STILL_RUNNING - start count was decremented but clock was + not stopped */ +int +spu_clock_stop (void) +{ + if (__spu_clock_startcnt == 0) + return SPU_CLOCK_ERR_NOT_RUNNING; + + if (__spu_clock_startcnt == 1 && (__spu_timers_active || __spu_timers_handled)) + return SPU_CLOCK_ERR_TIMERS_ACTIVE; + + /* Don't stop clock if the clock is still in use. */ + if (--__spu_clock_startcnt != 0) + return SPU_CLOCK_ERR_STILL_RUNNING; + + /* Clock stopped, stop decrementer. */ + __disable_spu_decr (); + + /* Clock is enabled on clock start - restore to original state (saved at start). */ + if (__likely (!__spu_clock_state_was_enabled)) + { + spu_idisable (); + } + + return 0; +} diff --git a/newlib/libc/machine/spu/spu_clock_svcs.c b/newlib/libc/machine/spu/spu_clock_svcs.c new file mode 100644 index 000000000..6cc2af4a9 --- /dev/null +++ b/newlib/libc/machine/spu/spu_clock_svcs.c @@ -0,0 +1,93 @@ +/* +(C) Copyright IBM Corp. 2008 + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. +* Neither the name of IBM nor the names of its contributors may be +used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +/* SPU clock start and read library services. */ +#include +#include "spu_timer_internal.h" + +/* The software managed timebase value. */ +volatile uint64_t __spu_tb_val __attribute__ ((aligned (16))); + +/* Timeout value of the current interval. */ +volatile int __spu_tb_timeout __attribute__ ((aligned (16))); + +/* Clock start count (clock is running if >0). */ +volatile unsigned __spu_clock_startcnt __attribute__ ((aligned (16))); + +/* Saved interrupt state from clock_start. */ +volatile unsigned __spu_clock_state_was_enabled; + +/* Initializes the software managed timebase, enables the decrementer event, + starts the decrementer and enables interrupts. Must be called before + clock or timer services can be used. Should only be called by base app/lib + code (not from an interrupt/timer handler). + Returns with interrupts ENABLED. */ +void +spu_clock_start (void) +{ + /* Increment clock start and return if it was already running. */ + if (++__spu_clock_startcnt > 1) + return; + + __spu_clock_state_was_enabled = spu_readch (SPU_RdMachStat) & 0x1; + + spu_idisable (); + __spu_tb_timeout = CLOCK_START_VALUE; + __spu_tb_val = 0; + + /* Disable, write, enable the decrementer. */ + __enable_spu_decr (__spu_tb_timeout, __disable_spu_decr ()); + + spu_ienable (); + + return; +} + +/* Returns a monotonically increasing, 64-bit counter, in timebase units, + relative to the last call to spu_clock_start(). */ +uint64_t +spu_clock_read (void) +{ + int64_t time; + unsigned was_enabled; + + /* Return 0 if clock is off. */ + if (__spu_clock_startcnt == 0) + return 0LL; + + was_enabled = spu_readch (SPU_RdMachStat) & 0x1; + spu_idisable (); + + time = __spu_tb_val + (__spu_tb_timeout - spu_readch (SPU_RdDec)); + + if (__likely (was_enabled)) + spu_ienable (); + return time; +} diff --git a/newlib/libc/machine/spu/spu_timer_flih.S b/newlib/libc/machine/spu/spu_timer_flih.S new file mode 100644 index 000000000..63f5f074c --- /dev/null +++ b/newlib/libc/machine/spu/spu_timer_flih.S @@ -0,0 +1,152 @@ +/* +(C) Copyright IBM Corp. 2008 + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. +* Neither the name of IBM nor the names of its contributors may be +used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +/* First-level interrupt handler. */ + +/* The following two convenience macros assist in the coding of the + saving and restoring the volatile register starting from register + 2 up to register 79. + + saveregs first, last Saves registers from first to the last. + restoreregs first, last Restores registers from last down to first. + + Note: first must be less than or equal to last. */ + +.macro saveregs first, last + stqd $\first, -(STACK_SKIP+\first)*16($SP) +.if \last-\first + saveregs "(\first+1)",\last +.endif +.endm + + +.macro restoreregs first, last + lqd $\last, (82-\last)*16($SP) +.if \last-\first + restoreregs \first,"(\last-1)" +.endif +.endm + + .section .interrupt,"ax" + .align 3 + .type spu_flih, @function +spu_flih: + /* Adjust the stack pointer to skip the maximum register save area + (STACK_SKIP quadword registers) in case an interrupt occurred while + executing a leaf function that used the stack area without actually + allocating its own stack frame. */ + .set STACK_SKIP, 125 + + /* Save the current link register on a new stack frame for the + normal spu_flih() version of this file. */ + stqd $0, -(STACK_SKIP+80)*16($SP) + stqd $SP, -(STACK_SKIP+82)*16($SP) /* Save back chain pointer. */ + + saveregs 2, 39 + + il $2, -(STACK_SKIP+82)*16 /* Stack frame size. */ + rdch $3, $SPU_RdEventStat /* Read event status. */ + + rdch $6, $SPU_RdEventMask /* Read event mask. */ + hbrp /* Open a slot for instruction prefetch. */ + + saveregs 40,59 + + clz $4, $3 /* Get first slih index. */ + stqd $6, -(STACK_SKIP+1)*16($SP) /* Save event mask on stack. */ + + saveregs 60, 67 + + /* Do not disable/ack the decrementer event here. + The timer library manages this and expects it + to be enabled upon entry to the SLIH. */ + il $7, 0x20 + andc $5, $3, $7 + andc $7, $6, $5 /* Clear event bits. */ + saveregs 68, 69 + + wrch $SPU_WrEventAck, $3 /* Ack events(s) - include decrementer event. */ + wrch $SPU_WrEventMask, $7 /* Disable event(s) - exclude decrementer event. */ + + saveregs 70, 79 + + a $SP, $SP, $2 /* Instantiate flih stack frame. */ +next_event: + /* Fetch and dispatch the event handler for the first non-zero event. The + dispatch handler is indexed into the __spu_slih_handlers array using the + count of zero off the event status as an index. */ + ila $5, __spu_slih_handlers /* Slih array offset. */ + + shli $4, $4, 2 /* Slih entry offset. */ + lqx $5, $4, $5 /* Load slih address. */ + rotqby $5, $5, $4 /* Rotate to word 0. */ + bisl $0, $5 /* Branch to slih. */ + + clz $4, $3 /* Get next slih index. */ + brnz $3, next_event + + + lqd $2, 81*16($SP) /* Read event mask from stack. */ + + restoreregs 40, 79 + + wrch $SPU_WrEventMask, $2 /* Restore event mask. */ + hbrp /* Open a slot for instruction pre-fetch. */ + + restoreregs 2, 39 + + /* Restore the link register from the new stack frame for the + normal spu_flih() version of this file. */ + lqd $0, 2*16($SP) + + lqd $SP, 0*16($SP) /* restore stack pointer from back chain ptr. */ + + irete /* Return from interrupt and re-enable interrupts. */ + .size spu_flih, .-spu_flih +/* spu_slih_handlers[] + Here we initialize 33 default event handlers. The first entry in this array + corresponds to the event handler for the event associated with bit 0 of + Channel 0 (External Event Status). The 32nd entry in this array corresponds + to bit 31 of Channel 0 (DMA Tag Status Update Event). The 33rd entry in + this array is a special case entry to handle "phantom events" which occur + when the channel count for Channel 0 is 1, causing an asynchronous SPU + interrupt, but the value returned for a read of Channel 0 is 0. The index + calculated into this array by spu_flih() for this case is 32, hence the + 33rd entry. */ +.data + .align 4 + .extern __spu_default_slih + .global __spu_slih_handlers + .type __spu_slih_handlers, @object +__spu_slih_handlers: + .rept 33 + .long __spu_default_slih + .endr + .size __spu_slih_handlers, .-__spu_slih_handlers diff --git a/newlib/libc/machine/spu/spu_timer_free.c b/newlib/libc/machine/spu/spu_timer_free.c new file mode 100644 index 000000000..9fb3e0cda --- /dev/null +++ b/newlib/libc/machine/spu/spu_timer_free.c @@ -0,0 +1,86 @@ +/* +(C) Copyright IBM Corp. 2008 + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. +* Neither the name of IBM nor the names of its contributors may be +used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +/* SPU timer free library service. */ +#include +#include "spu_timer_internal.h" + + +/* Frees an allocated timer. The timer must be in the stopped state for this + to succeed. Maybe be called: + * after allocated, before it's started + * after it's been explicitly stopped + Returns 0 on success, timer sucessfully deallocated. Returns <0 on failure + * SPU_TIMER_INVALID_ID - id out of range + * SPU_TIMER_ERR_FREE - id in free state + * SPU_TIMER_ERR_ACTIVE - id in handled or active state */ +int +spu_timer_free (int id) +{ + spu_timer_t *t, **pn; + unsigned was_enabled; + + if (id < 0 || id >= SPU_TIMER_NTIMERS) + return SPU_TIMER_ERR_INVALID_ID; + + if (__spu_timers[id].state == SPU_TIMER_STOPPED) + { + + was_enabled = spu_readch (SPU_RdMachStat) & 0x1; + spu_idisable (); + + t = __spu_timers_stopped; + pn = &__spu_timers_stopped; + + while (t && (t->id != id)) + { + pn = &t->next; + t = t->next; + } +#ifdef SPU_TIMER_DEBUG + if (!t) + ABORT (); +#endif + *pn = t->next; + + /* Add timer back to free list (mask). */ + __spu_timers_avail |= (1 << (id)); + __spu_timers[id].state = SPU_TIMER_FREE; + + if (__likely (was_enabled)) + spu_ienable (); + + return 0; + } + + /* Handle invalid states. */ + return (__spu_timers[id].state == SPU_TIMER_FREE) ? + SPU_TIMER_ERR_FREE : SPU_TIMER_ERR_ACTIVE; +} diff --git a/newlib/libc/machine/spu/spu_timer_internal.h b/newlib/libc/machine/spu/spu_timer_internal.h new file mode 100644 index 000000000..a438561b9 --- /dev/null +++ b/newlib/libc/machine/spu/spu_timer_internal.h @@ -0,0 +1,140 @@ +/* +(C) Copyright IBM Corp. 2008 + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. +* Neither the name of IBM nor the names of its contributors may be +used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +/* Internal definitions for SPU timer library. */ +#ifndef _SPU_TIMER_INTERNAL_H_ +#define _SPU_TIMER_INTERNAL_H_ + +#include +#include +#include +#include + +#ifdef SPU_TIMER_DEBUG +#include +#include +#endif + +/* The timer state tells which list its on. */ +typedef enum spu_timer_state +{ + SPU_TIMER_FREE = 0, + SPU_TIMER_ACTIVE = 1, + SPU_TIMER_HANDLED = 2, + SPU_TIMER_STOPPED = 3 +} spu_timer_state_t; + +typedef struct spu_timer +{ + int tmout __attribute__ ((aligned (16))); /* Time until expiration (tb). */ + int intvl __attribute__ ((aligned (16))); /* Interval. */ + int id __attribute__ ((aligned (16))); + spu_timer_state_t state __attribute__ ((aligned (16))); + void (*func) (int) __attribute__ ((aligned (16))); /* Handler. */ + struct spu_timer *next __attribute__ ((aligned (16))); +} spu_timer_t; + + +/* Max decrementer value. */ +#define DECR_MAX 0xFFFFFFFFU + + /* Arbitrary non-triggering value. */ +#define CLOCK_START_VALUE 0x7FFFFFFF + +#define MIN_INTVL 1 +#define MAX_INTVL INT_MAX + +/* Timers within 15 tics will expire together. */ +#define TIMER_INTERVAL_WINDOW 15 + +/* Disables the decrementer and returns the saved event mask for a subsequent + call to __enable_spu_decr. The decrementer interrupt is acknowledged in the + flih when the event is received, but is required also as part of the + procedure to stop the decrementer. */ +static inline unsigned +__disable_spu_decr (void) +{ + unsigned mask = spu_readch (SPU_RdEventMask); + spu_writech (SPU_WrEventMask, mask & ~MFC_DECREMENTER_EVENT); + spu_writech (SPU_WrEventAck, MFC_DECREMENTER_EVENT); + spu_sync_c (); + return mask; +} + +/* Writes and enables the decrementer, along with the given event mask. */ +static inline void +__enable_spu_decr (int val, unsigned mask) +{ + spu_writech (SPU_WrDec, (val)); + spu_writech (SPU_WrEventMask, mask | MFC_DECREMENTER_EVENT); + spu_sync_c (); +} + +/* These are shared between modules but are not inlined, to save space. */ +extern void __spu_timer_start (int id, int reset); +extern void __reset_spu_decr (int val); + +/* The timers. */ +extern spu_timer_t __spu_timers[]; + +/* Active timer list. */ +extern spu_timer_t *__spu_timers_active; + +/* Stopped (allocated) timer list. */ +extern spu_timer_t *__spu_timers_stopped; + +/* List of timers being handled. */ +extern spu_timer_t *__spu_timers_handled; + +/* Bitmask of available timers. */ +extern unsigned __spu_timers_avail; + +/* The software managed timebase value. */ +extern volatile uint64_t __spu_tb_val; + +/* Timeout value of the current interval. */ +extern volatile int __spu_tb_timeout; + +/* Clock start count (clock is running if >0). */ +extern volatile unsigned __spu_clock_startcnt; + +/* Saved interrupt state from clock_start. */ +extern volatile unsigned __spu_clock_state_was_enabled; + +#define __likely(_c) __builtin_expect((_c), 1) +#define __unlikely(_c) __builtin_expect((_c), 0) + +#define ABORT() \ +{\ + fprintf(stderr, "Internal error, aborting: %s:%d\n", __FILE__, __LINE__);\ + assert(0);\ +} + +#endif diff --git a/newlib/libc/machine/spu/spu_timer_slih.c b/newlib/libc/machine/spu/spu_timer_slih.c new file mode 100644 index 000000000..93cfa5ea2 --- /dev/null +++ b/newlib/libc/machine/spu/spu_timer_slih.c @@ -0,0 +1,221 @@ +/* +(C) Copyright IBM Corp. 2008 + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. +* Neither the name of IBM nor the names of its contributors may be +used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +/* Second Level Interrupt handler and related services for SPU timers. */ +#include "spu_timer_internal.h" +/* Resets decrementer to the specified value. Also updates software timebase + to account for the time between the last decrementer reset and now. There + are two cases: + * Called by application to start a new timer. + * Called by spu_clock to active the next timer. + In both cases, the amount of time is the current interval timeout minus the + current decrementer value. */ +void +__reset_spu_decr (int val) +{ + + /* The interrupt occurs when the msb goes from 0 to 1 or when the decrementer + goes from 0 to -1. To be precisely accurate we should set the timer to + the intverval -1, unless the interval passed in is 0 in which case it + should be left at 0. */ + int enable_val = (__likely (val)) ? val - 1 : 0; + + /* Decrementer must be stopped before writing it - minimize the time + stopped. */ + unsigned mask = __disable_spu_decr (); + + /* Perform tb correction before resettting the decrementer. the corrected + value is the current timeout value minus the current decrementer value. + Occasionally the read returns 0 - a second read will clear this + condition. */ + int decval0 = spu_readch (SPU_RdDec); + int decval = spu_readch (SPU_RdDec); + /* Restart decrementer with next timeout val. */ + __enable_spu_decr (enable_val, mask); + + /* Update the timebase values before enabling for interrupts. */ + __spu_tb_val += __spu_tb_timeout - decval; + __spu_tb_timeout = enable_val; +} + +/* Update software timebase and timeout value for the 'next to expire' timer. + Called when starting a new timer so the timer list will have timeouts + relative to the current time. */ +static inline void +__update_spu_tb_val (void) +{ + int elapsed = __spu_tb_timeout - spu_readch (SPU_RdDec); +#ifdef SPU_TIMER_DEBUG + if (elapsed < 0) + ABORT (); +#endif + __spu_tb_val += elapsed; + + /* Adjust the timeout for the timer next to expire. Note this could cause + the timeout to go negative, if it was just about to expire when we called + spu_timer_start. This is OK, since this can happen any time interrupts + are disabled. We just schedule an immediate timeout in this case. */ + if (__spu_timers_active) + { + __spu_timers_active->tmout -= elapsed; + if (__spu_timers_active->tmout < 0) + __spu_timers_active->tmout = 0; + } +} + +/* Add an allocated timer to the active list. The active list is sorted by + timeout value. The timer at the head of the list is the timer that will + expire next. The rest of the timers have a timeout value that is relative + to the timer ahead of it on the list. This relative value is determined + here, when the timer is added to the active list. When its position in the + list is found, the timer's timeout value is set to its interval minus the + sum of all the timeout values ahead of it. The timeout value for the timer + following the newly added timer is then adjusted to a new relative value. If + the newly added timer is at the head of the list, the decrementer is reset. + This function is called by SLIH to restart multiple timers (reset == 0) or + by spu_timer_start() to start a single timer (reset == 1). */ +void +__spu_timer_start (int id, int reset) +{ + spu_timer_t *t; + spu_timer_t **pn; + spu_timer_t *start = &__spu_timers[id]; + unsigned tmout_time = 0; + unsigned my_intvl = start->intvl; + unsigned was_enabled = spu_readch (SPU_RdMachStat) & 0x1; + + spu_idisable (); + + t = __spu_timers_active; + pn = &__spu_timers_active; + + /* If the active list is empty, just add the timer with the timeout set to + the interval. Otherwise find the place in the list for the timer, setting + its timeout to its interval minus the sum of timeouts ahead of it. */ + start->state = SPU_TIMER_ACTIVE; + if (__likely (!t)) + { + __spu_timers_active = start; + start->next = NULL; + start->tmout = my_intvl; + } + else + { + + /* Update swtb and timeout val of the next timer, so all times are + relative to now. */ + if (reset) + __update_spu_tb_val (); + + while (t && (my_intvl >= (tmout_time + t->tmout))) + { + tmout_time += t->tmout; + pn = &t->next;; + t = t->next; + } + start->next = t; + start->tmout = my_intvl - tmout_time; + *pn = start; + + /* Adjust timeout for timer after us. */ + if (t) + t->tmout -= start->tmout; + } + + if (reset && (__spu_timers_active == start)) + __reset_spu_decr (__spu_timers_active->tmout); + + if (__unlikely (was_enabled)) + spu_ienable (); +} + +/* SLIH for decrementer. Manages software timebase and timers. + Called by SPU FLIH. Assumes decrementer is still running + (event not yet acknowledeged). */ +unsigned int +spu_clock_slih (unsigned status) +{ + int decr_reset_val; + spu_timer_t *active, *handled; + unsigned was_enabled = spu_readch (SPU_RdMachStat) & 0x1; + + status &= ~MFC_DECREMENTER_EVENT; + + spu_idisable (); + + /* The decrementer has now expired. The decrementer event was acknowledged + in the FLIH but not disabled. The decrementer will continue to run while + we're running the clock/timer handler. The software clock keeps running, + and accounts for all the time spent running handlers. Add the current + timeout to the software timebase and set the timeout to DECR_MAX. This + allows the "clock read" code to continue to work while we're in here, and + gives us the most possible time to finish before another underflow. */ + __spu_tb_val += __spu_tb_timeout; + __spu_tb_timeout = DECR_MAX; + + /* For all timers that have the current timeout value, move them from the + active list to the handled list and call their handlers. Note that the + handled/stopped lists may be manipulated by the handlers if they wish to + stop/free the timers. Note that only the first expired timer will reflect + the real timeout value; the rest of the timers that had the same timeout + value will have a relative value of zero. */ + if (__spu_timers_active) + { + __spu_timers_active->tmout = 0; + while ((active = __spu_timers_active) + && (active->tmout <= TIMER_INTERVAL_WINDOW)) + { + __spu_timers_active = active->next; + active->next = __spu_timers_handled; + __spu_timers_handled = active; + active->state = SPU_TIMER_HANDLED; + (*active->func) (active->id); + } + } + + /* put the handled timers back on the list and restart decrementer. */ + while ((handled = __spu_timers_handled) != NULL) + { + __spu_timers_handled = handled->next; + __spu_timer_start (handled->id, 0); + } + + /* Reset the decrementer before returning. If we have any active timers, we + set it to the timeout value for the timer at the head of the list, else + the default clock value. */ + decr_reset_val = __spu_timers_active ? __spu_timers_active->tmout : CLOCK_START_VALUE; + + __reset_spu_decr (decr_reset_val); + + if (__likely (was_enabled)) + spu_ienable (); + + return status; +} diff --git a/newlib/libc/machine/spu/spu_timer_slih_reg.c b/newlib/libc/machine/spu/spu_timer_slih_reg.c new file mode 100644 index 000000000..cd1e86fbb --- /dev/null +++ b/newlib/libc/machine/spu/spu_timer_slih_reg.c @@ -0,0 +1,72 @@ +/* +(C) Copyright IBM Corp. 2008 + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. +* Neither the name of IBM nor the names of its contributors may be +used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + + +/* Services for SLIH registration. */ +#include +#include + +#define SPU_EVENT_ID(_mask) \ + (spu_extract(spu_cntlz(spu_promote(_mask, 0)), 0)) +typedef unsigned (*spu_slih_t) (unsigned); + +extern spu_slih_t __spu_slih_handlers[]; + +/* This function is called whenever an event occurs for which no second level + event handler was registered. The default event handler does nothing and + zeros the most significant event bit indicating that the event was processed + (when in reality, it was discarded). */ +unsigned +__spu_default_slih (unsigned events) +{ + unsigned int mse; + + mse = 0x80000000 >> SPU_EVENT_ID (events); + events &= ~mse; + + return (events); +} + +/* Registers a SPU second level interrupt handler for the events specified by + mask. The event mask consists of a set of bits corresponding to the event + status bits (see channel 0 description). A mask containing multiple 1 bits + will set the second level event handler for each of the events. */ +void +spu_slih_register (unsigned mask, spu_slih_t func) +{ + unsigned int id; + + while (mask) + { + id = SPU_EVENT_ID (mask); + __spu_slih_handlers[id] = (func) ? func : __spu_default_slih; + mask &= ~(0x80000000 >> id); + } +} diff --git a/newlib/libc/machine/spu/spu_timer_stop.c b/newlib/libc/machine/spu/spu_timer_stop.c new file mode 100644 index 000000000..94286c914 --- /dev/null +++ b/newlib/libc/machine/spu/spu_timer_stop.c @@ -0,0 +1,101 @@ +/* +(C) Copyright IBM Corp. 2008 + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. +* Neither the name of IBM nor the names of its contributors may be +used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +/* SPU timer stop library service. */ +#include +#include "spu_timer_internal.h" + +/* Stop a timer. Moves it from either the active or handled list to the + stopped list. Returns 0 on sucess, timer was successfully stopped. + Returns <0 - Failure: + * SPU_TIMER_ERR_NOT_ACTIVE - timer was not active + * SPU_TIMER_ERR_INVALID_ID - invalid timer id + * SPU_TIMER_ERR_NOCLOCK - spu clock is not running */ +int +spu_timer_stop (int id) +{ + spu_timer_t *t, **pn; + unsigned was_enabled; + + if (id < 0 || id >= SPU_TIMER_NTIMERS) + return SPU_TIMER_ERR_INVALID_ID; + + if (__spu_clock_startcnt == 0) + return SPU_TIMER_ERR_NOCLOCK; + + + /* Free or stopped states. */ + if (__spu_timers[id].state == SPU_TIMER_ACTIVE || + __spu_timers[id].state == SPU_TIMER_HANDLED) + { + was_enabled = spu_readch (SPU_RdMachStat) & 0x1; + spu_idisable (); + + /* Timer is on either active list or handled list. */ + t = (__spu_timers[id].state == SPU_TIMER_ACTIVE) + ? __spu_timers_active : __spu_timers_handled; + + pn = (__spu_timers[id].state == SPU_TIMER_ACTIVE) + ? &__spu_timers_active : &__spu_timers_handled; + + while (t && t->id != id) + { + pn = &t->next; + t = t->next; + } +#ifdef SPU_TIMER_DEBUG + if (!t) + ABORT (); /* Internal error. */ +#endif + /* Fix timeout of next timer and decrementer if we were at the head of + the active list. */ + if (t->next) + { + t->next->tmout += t->tmout; + if (__spu_timers_active == t) + __reset_spu_decr (t->next->tmout); + } + else + __reset_spu_decr (CLOCK_START_VALUE); + + *pn = t->next; /* Update this elements to pointer. */ + t->next = __spu_timers_stopped; + __spu_timers_stopped = t; + + __spu_timers[id].state = SPU_TIMER_STOPPED; + + if (__likely (was_enabled)) + spu_ienable (); + + return 0; + } + + return SPU_TIMER_ERR_NOT_ACTIVE; +} diff --git a/newlib/libc/machine/spu/spu_timer_svcs.c b/newlib/libc/machine/spu/spu_timer_svcs.c new file mode 100644 index 000000000..8e7013ccd --- /dev/null +++ b/newlib/libc/machine/spu/spu_timer_svcs.c @@ -0,0 +1,115 @@ +/* +(C) Copyright IBM Corp. 2008 + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. +* Neither the name of IBM nor the names of its contributors may be +used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +/* SPU timer start and alloc library services. */ +#include +#include "spu_timer_internal.h" + +/* The timers. */ +spu_timer_t __spu_timers[SPU_TIMER_NTIMERS] __attribute__ ((aligned (16))); + +/* Active timer list. */ +spu_timer_t *__spu_timers_active; + +/* Stopped (allocated) timer list. */ +spu_timer_t *__spu_timers_stopped; + +/* List of timers being handled. */ +spu_timer_t *__spu_timers_handled; + +/* Bitmask of available timers. */ +unsigned __spu_timers_avail = + ((1 << (SPU_TIMER_NTIMERS - 1)) + ((1 << (SPU_TIMER_NTIMERS - 1)) - 1)); + +/* Allocates an SPU interval timer and returns the timer ID. Must be called + before starting a timer. interval specifies the expiration interval in + timebase units. func specifies the function pointer to expiration handler. + Returns the timer ID on success or <0 on Failure: + * SPU_TIMER_ERR_NONE_FREE - no free timers to allocate + * SPU_TIMER_ERR_INVALID_PARM - invalid parm */ +int +spu_timer_alloc (int interval, void (*func) (int)) +{ + unsigned was_enabled; + int id; + if (interval < MIN_INTVL || interval > MAX_INTVL || func == NULL) + return SPU_TIMER_ERR_INVALID_PARM; + + was_enabled = spu_readch (SPU_RdMachStat) & 0x1; + + /* Get id of next available timer. */ + id = spu_extract ((spu_sub ((unsigned) 31, + spu_cntlz (spu_promote + (__spu_timers_avail, 0)))), 0); + + /* No timers avail. */ + if (id == -1) + return SPU_TIMER_ERR_NONE_FREE; + + /* Higher order bits represent lower timer ids. */ + __spu_timers_avail &= ~(1 << (id)); + id = (SPU_TIMER_NTIMERS - 1) - id; + + /* Initialize timer and put it on stopped list. */ + (__spu_timers + id)->func = func; + (__spu_timers + id)->intvl = interval; + (__spu_timers + id)->id = id; + (__spu_timers + id)->state = SPU_TIMER_STOPPED; + + spu_idisable (); + (__spu_timers + id)->next = __spu_timers_stopped; + __spu_timers_stopped = &__spu_timers[id]; + + if (__likely (was_enabled)) + spu_ienable (); + return id; +} + +/* External interface for starting a timer. See description of + __spu_timer_start(). Returns 0 on success and <0 on failure: + * SPU_TIMER_ERR_INVALID_ID - invalid id + * SPU_TIMER_ERR_NOCLOCK - clock is off + * SPU_TIMER_ERR_NOT_STOPPED - timer not in stopped state */ +int +spu_timer_start (int id) +{ + if (id < 0 || id >= SPU_TIMER_NTIMERS) + return SPU_TIMER_ERR_INVALID_ID; + + if (__spu_clock_startcnt == 0) + return SPU_TIMER_ERR_NOCLOCK; + + if (__spu_timers[id].state != SPU_TIMER_STOPPED) + return SPU_TIMER_ERR_NOT_STOPPED; + + __spu_timer_start (id, 1); + + return 0; +}