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]

Re: [patch] SPU timer


On Monday 09 June 2008 12:12, Kazunori Asayama wrote:
> The time base frequency depends on platform. E.g., 14.318MHz on Cell
> blades, while 79.8MHz on PS3. Does it work correctly for all Cell targets?

Thanks for reviewing. The comment is misleading here and i changed it. The SPU
timer uses ticks not seconds. The hardware has technically one decrement
register. The decrementer frequency varies on different platforms (as listed
in spu_timer.h):
 QS20       14318000
 QS21/QS22  26666666
 PS3        79800000

This patch abstracts the decrementer and allows the user to register multiple
timers. If two (or more) timers expire nearly at the same it wouldn't make
sense to setup the decrementer again since it would have been expired already.
Therefore code calls all handlers whose time is smaller than the
TIMER_INTERVAL_WINDOW. The exact size of that window (in wall clock units)
doesn't really matter. If it's too big or too small the precision suffers.
The TIMER_INTERVAL_WINDOW is specified in ticks since the user 
specifies the timer inteval.

Ken

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.

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 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
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]