[patch] SPU timer

Jeff Johnston jjohnstn@redhat.com
Wed Jun 11 22:16:00 GMT 2008


Patch applied.

-- Jeff J.

Ken Werner wrote:
> 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;
> +}
>   



More information about the Newlib mailing list