Welcome to mirror list, hosted at ThFree Co, Russian Federation.

cygwin.com/git/newlib-cygwin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'newlib/libc/machine/spu/spu_timer_slih.c')
-rw-r--r--newlib/libc/machine/spu/spu_timer_slih.c221
1 files changed, 221 insertions, 0 deletions
diff --git a/newlib/libc/machine/spu/spu_timer_slih.c b/newlib/libc/machine/spu/spu_timer_slih.c
new file mode 100644
index 000000000..93cfa5ea2
--- /dev/null
+++ b/newlib/libc/machine/spu/spu_timer_slih.c
@@ -0,0 +1,221 @@
+/*
+(C) Copyright IBM Corp. 2008
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+* Neither the name of IBM nor the names of its contributors may be
+used to endorse or promote products derived from this software without
+specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* Second Level Interrupt handler and related services for SPU timers. */
+#include "spu_timer_internal.h"
+/* Resets decrementer to the specified value. Also updates software timebase
+ to account for the time between the last decrementer reset and now. There
+ are two cases:
+ * Called by application to start a new timer.
+ * Called by spu_clock to active the next timer.
+ In both cases, the amount of time is the current interval timeout minus the
+ current decrementer value. */
+void
+__reset_spu_decr (int val)
+{
+
+ /* The interrupt occurs when the msb goes from 0 to 1 or when the decrementer
+ goes from 0 to -1. To be precisely accurate we should set the timer to
+ the intverval -1, unless the interval passed in is 0 in which case it
+ should be left at 0. */
+ int enable_val = (__likely (val)) ? val - 1 : 0;
+
+ /* Decrementer must be stopped before writing it - minimize the time
+ stopped. */
+ unsigned mask = __disable_spu_decr ();
+
+ /* Perform tb correction before resettting the decrementer. the corrected
+ value is the current timeout value minus the current decrementer value.
+ Occasionally the read returns 0 - a second read will clear this
+ condition. */
+ int decval0 = spu_readch (SPU_RdDec);
+ int decval = spu_readch (SPU_RdDec);
+ /* Restart decrementer with next timeout val. */
+ __enable_spu_decr (enable_val, mask);
+
+ /* Update the timebase values before enabling for interrupts. */
+ __spu_tb_val += __spu_tb_timeout - decval;
+ __spu_tb_timeout = enable_val;
+}
+
+/* Update software timebase and timeout value for the 'next to expire' timer.
+ Called when starting a new timer so the timer list will have timeouts
+ relative to the current time. */
+static inline void
+__update_spu_tb_val (void)
+{
+ int elapsed = __spu_tb_timeout - spu_readch (SPU_RdDec);
+#ifdef SPU_TIMER_DEBUG
+ if (elapsed < 0)
+ ABORT ();
+#endif
+ __spu_tb_val += elapsed;
+
+ /* Adjust the timeout for the timer next to expire. Note this could cause
+ the timeout to go negative, if it was just about to expire when we called
+ spu_timer_start. This is OK, since this can happen any time interrupts
+ are disabled. We just schedule an immediate timeout in this case. */
+ if (__spu_timers_active)
+ {
+ __spu_timers_active->tmout -= elapsed;
+ if (__spu_timers_active->tmout < 0)
+ __spu_timers_active->tmout = 0;
+ }
+}
+
+/* Add an allocated timer to the active list. The active list is sorted by
+ timeout value. The timer at the head of the list is the timer that will
+ expire next. The rest of the timers have a timeout value that is relative
+ to the timer ahead of it on the list. This relative value is determined
+ here, when the timer is added to the active list. When its position in the
+ list is found, the timer's timeout value is set to its interval minus the
+ sum of all the timeout values ahead of it. The timeout value for the timer
+ following the newly added timer is then adjusted to a new relative value. If
+ the newly added timer is at the head of the list, the decrementer is reset.
+ This function is called by SLIH to restart multiple timers (reset == 0) or
+ by spu_timer_start() to start a single timer (reset == 1). */
+void
+__spu_timer_start (int id, int reset)
+{
+ spu_timer_t *t;
+ spu_timer_t **pn;
+ spu_timer_t *start = &__spu_timers[id];
+ unsigned tmout_time = 0;
+ unsigned my_intvl = start->intvl;
+ unsigned was_enabled = spu_readch (SPU_RdMachStat) & 0x1;
+
+ spu_idisable ();
+
+ t = __spu_timers_active;
+ pn = &__spu_timers_active;
+
+ /* If the active list is empty, just add the timer with the timeout set to
+ the interval. Otherwise find the place in the list for the timer, setting
+ its timeout to its interval minus the sum of timeouts ahead of it. */
+ start->state = SPU_TIMER_ACTIVE;
+ if (__likely (!t))
+ {
+ __spu_timers_active = start;
+ start->next = NULL;
+ start->tmout = my_intvl;
+ }
+ else
+ {
+
+ /* Update swtb and timeout val of the next timer, so all times are
+ relative to now. */
+ if (reset)
+ __update_spu_tb_val ();
+
+ while (t && (my_intvl >= (tmout_time + t->tmout)))
+ {
+ tmout_time += t->tmout;
+ pn = &t->next;;
+ t = t->next;
+ }
+ start->next = t;
+ start->tmout = my_intvl - tmout_time;
+ *pn = start;
+
+ /* Adjust timeout for timer after us. */
+ if (t)
+ t->tmout -= start->tmout;
+ }
+
+ if (reset && (__spu_timers_active == start))
+ __reset_spu_decr (__spu_timers_active->tmout);
+
+ if (__unlikely (was_enabled))
+ spu_ienable ();
+}
+
+/* SLIH for decrementer. Manages software timebase and timers.
+ Called by SPU FLIH. Assumes decrementer is still running
+ (event not yet acknowledeged). */
+unsigned int
+spu_clock_slih (unsigned status)
+{
+ int decr_reset_val;
+ spu_timer_t *active, *handled;
+ unsigned was_enabled = spu_readch (SPU_RdMachStat) & 0x1;
+
+ status &= ~MFC_DECREMENTER_EVENT;
+
+ spu_idisable ();
+
+ /* The decrementer has now expired. The decrementer event was acknowledged
+ in the FLIH but not disabled. The decrementer will continue to run while
+ we're running the clock/timer handler. The software clock keeps running,
+ and accounts for all the time spent running handlers. Add the current
+ timeout to the software timebase and set the timeout to DECR_MAX. This
+ allows the "clock read" code to continue to work while we're in here, and
+ gives us the most possible time to finish before another underflow. */
+ __spu_tb_val += __spu_tb_timeout;
+ __spu_tb_timeout = DECR_MAX;
+
+ /* For all timers that have the current timeout value, move them from the
+ active list to the handled list and call their handlers. Note that the
+ handled/stopped lists may be manipulated by the handlers if they wish to
+ stop/free the timers. Note that only the first expired timer will reflect
+ the real timeout value; the rest of the timers that had the same timeout
+ value will have a relative value of zero. */
+ if (__spu_timers_active)
+ {
+ __spu_timers_active->tmout = 0;
+ while ((active = __spu_timers_active)
+ && (active->tmout <= TIMER_INTERVAL_WINDOW))
+ {
+ __spu_timers_active = active->next;
+ active->next = __spu_timers_handled;
+ __spu_timers_handled = active;
+ active->state = SPU_TIMER_HANDLED;
+ (*active->func) (active->id);
+ }
+ }
+
+ /* put the handled timers back on the list and restart decrementer. */
+ while ((handled = __spu_timers_handled) != NULL)
+ {
+ __spu_timers_handled = handled->next;
+ __spu_timer_start (handled->id, 0);
+ }
+
+ /* Reset the decrementer before returning. If we have any active timers, we
+ set it to the timeout value for the timer at the head of the list, else
+ the default clock value. */
+ decr_reset_val = __spu_timers_active ? __spu_timers_active->tmout : CLOCK_START_VALUE;
+
+ __reset_spu_decr (decr_reset_val);
+
+ if (__likely (was_enabled))
+ spu_ienable ();
+
+ return status;
+}