[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