This is the mail archive of the newlib@sourceware.org mailing list for the newlib project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[patch] SPU timer


Hi,

SPU code that enables interrupts must place its interrupt handler at fixed 
address 0x0. This patch virtualizes interrupt handling and adds clock/timer 
services using the decrement register. Any comments or ok to apply?

newlib/ChangeLog:

2008-06-07  Ken Werner  <ken.werner@de.ibm.com>

	* 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.

Ken

Index: src/newlib/libc/machine/spu/spu_clock_svcs.c
===================================================================
--- /dev/null
+++ src/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 <spu_timer.h>
+#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;
+}
Index: src/newlib/libc/machine/spu/spu_timer_flih.S
===================================================================
--- /dev/null
+++ src/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
Index: src/newlib/libc/machine/spu/spu_timer_internal.h
===================================================================
--- /dev/null
+++ src/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 <spu_intrinsics.h>
+#include <spu_mfcio.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#ifdef SPU_TIMER_DEBUG
+#include <stdio.h>
+#include <assert.h>
+#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 1 microsecond (14.3 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
Index: src/newlib/libc/machine/spu/spu_timer_slih.c
===================================================================
--- /dev/null
+++ src/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;
+}
Index: src/newlib/libc/machine/spu/spu_timer_slih_reg.c
===================================================================
--- /dev/null
+++ src/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 <spu_intrinsics.h>
+#include <spu_timer.h>
+
+#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);
+    }
+}
Index: src/newlib/libc/machine/spu/spu_timer_svcs.c
===================================================================
--- /dev/null
+++ src/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 <spu_timer.h>
+#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;
+}
Index: src/newlib/libc/machine/spu/Makefile.am
===================================================================
--- src.orig/newlib/libc/machine/spu/Makefile.am
+++ src/newlib/libc/machine/spu/Makefile.am
@@ -21,7 +21,9 @@ lib_a_SOURCES = setjmp.S assert.c cleare
 	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)
Index: src/newlib/libc/machine/spu/Makefile.in
===================================================================
--- src.orig/newlib/libc/machine/spu/Makefile.in
+++ src/newlib/libc/machine/spu/Makefile.in
@@ -74,7 +74,11 @@ DIST_COMMON = $(srcdir)/../../../../conf
 	$(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-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 cleare
 	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)
Index: src/newlib/libc/machine/spu/include/spu_timer.h
===================================================================
--- /dev/null
+++ src/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 <stdint.h>
+
+/* 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
Index: src/newlib/libc/machine/spu/spu_clock_stop.c
===================================================================
--- /dev/null
+++ src/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 <spu_timer.h>
+#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;
+}
Index: src/newlib/libc/machine/spu/spu_timer_free.c
===================================================================
--- /dev/null
+++ src/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 <spu_timer.h>
+#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;
+}
Index: src/newlib/libc/machine/spu/spu_timer_stop.c
===================================================================
--- /dev/null
+++ src/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 <spu_timer.h>
+#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;
+}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]