source: trunk/libs/newlib/src/newlib/libc/machine/spu/spu_timer_slih.c @ 444

Last change on this file since 444 was 444, checked in by satin@…, 6 years ago

add newlib,libalmos-mkh, restructure shared_syscalls.h and mini-libc

File size: 8.2 KB
Line 
1/*
2(C) Copyright IBM Corp. 2008
3
4All rights reserved.
5
6Redistribution and use in source and binary forms, with or without
7modification, are permitted provided that the following conditions are met:
8
9* Redistributions of source code must retain the above copyright notice,
10this list of conditions and the following disclaimer.
11* Redistributions in binary form must reproduce the above copyright
12notice, this list of conditions and the following disclaimer in the
13documentation and/or other materials provided with the distribution.
14* Neither the name of IBM nor the names of its contributors may be
15used to endorse or promote products derived from this software without
16specific prior written permission.
17
18THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28POSSIBILITY OF SUCH DAMAGE.
29*/
30
31/* Second Level Interrupt handler and related services for SPU timers.  */
32#include "spu_timer_internal.h"
33/* Resets decrementer to the specified value. Also updates software timebase
34   to account for the time between the last decrementer reset and now. There
35   are two cases:
36    * Called by application to start a new timer.
37    * Called by spu_clock to active the next timer.
38   In both cases, the amount of time is the current interval timeout minus the
39   current decrementer value.  */
40void
41__reset_spu_decr (int val)
42{
43
44  /* The interrupt occurs when the msb goes from 0 to 1 or when the decrementer
45     goes from 0 to -1.  To be precisely accurate we should set the timer to
46     the intverval -1, unless the interval passed in is 0 in which case it
47     should be left at 0.  */
48  int enable_val = (__likely (val)) ? val - 1 : 0;
49
50  /* Decrementer must be stopped before writing it - minimize the time
51     stopped.  */
52  unsigned mask = __disable_spu_decr ();
53
54  /* Perform tb correction before resettting the decrementer. the corrected
55     value is the current timeout value minus the current decrementer value.
56     Occasionally the read returns 0 - a second read will clear this
57     condition.  */
58  spu_readch (SPU_RdDec);
59  int decval = spu_readch (SPU_RdDec);
60  /* Restart decrementer with next timeout val.  */
61  __enable_spu_decr (enable_val, mask);
62
63  /* Update the timebase values before enabling for interrupts.  */
64  __spu_tb_val += __spu_tb_timeout - decval;
65  __spu_tb_timeout = enable_val;
66}
67
68/* Update software timebase and timeout value for the 'next to expire' timer.
69   Called when starting a new timer so the timer list will have timeouts
70   relative to the current time.  */
71static inline void
72__update_spu_tb_val (void)
73{
74  int elapsed = __spu_tb_timeout - spu_readch (SPU_RdDec);
75#ifdef SPU_TIMER_DEBUG
76  if (elapsed < 0)
77    ABORT ();
78#endif
79  __spu_tb_val += elapsed;
80
81  /* Adjust the timeout for the timer next to expire. Note this could cause
82     the timeout to go negative, if it was just about to expire when we called
83     spu_timer_start.  This is OK, since this can happen any time interrupts
84     are disabled. We just schedule an immediate timeout in this case.  */
85  if (__spu_timers_active)
86    {
87      __spu_timers_active->tmout -= elapsed;
88      if (__spu_timers_active->tmout < 0)
89        __spu_timers_active->tmout = 0;
90    }
91}
92
93/* Add an allocated timer to the active list. The active list is sorted by
94   timeout value. The timer at the head of the list is the timer that will
95   expire next.  The rest of the timers have a timeout value that is relative
96   to the timer ahead of it on the list.  This relative value is determined
97   here, when the timer is added to the active list. When its position in the
98   list is found, the timer's timeout value is set to its interval minus the
99   sum of all the timeout values ahead of it.  The timeout value for the timer
100   following the newly added timer is then adjusted to a new relative value. If
101   the newly added timer is at the head of the list, the decrementer is reset.
102   This function is called by SLIH to restart multiple timers (reset == 0) or
103   by spu_timer_start() to start a single timer (reset == 1).  */
104void
105__spu_timer_start (int id, int reset)
106{
107  spu_timer_t *t;
108  spu_timer_t **pn;
109  spu_timer_t *start = &__spu_timers[id];
110  unsigned tmout_time = 0;
111  unsigned my_intvl = start->intvl;
112  unsigned was_enabled = spu_readch (SPU_RdMachStat) & 0x1;
113
114  spu_idisable ();
115
116  t = __spu_timers_active;
117  pn = &__spu_timers_active;
118
119  /* If the active list is empty, just add the timer with the timeout set to
120     the interval. Otherwise find the place in the list for the timer, setting
121     its timeout to its interval minus the sum of timeouts ahead of it.  */
122  start->state = SPU_TIMER_ACTIVE;
123  if (__likely (!t))
124    {
125      __spu_timers_active = start;
126      start->next = NULL;
127      start->tmout = my_intvl;
128    }
129  else
130    {
131
132      /* Update swtb and timeout val of the next timer, so all times are
133         relative to now.  */
134      if (reset)
135        __update_spu_tb_val ();
136
137      while (t && (my_intvl >= (tmout_time + t->tmout)))
138        {
139          tmout_time += t->tmout;
140          pn = &t->next;;
141          t = t->next;
142        }
143      start->next = t;
144      start->tmout = my_intvl - tmout_time;
145      *pn = start;
146
147      /* Adjust timeout for timer after us.  */
148      if (t)
149        t->tmout -= start->tmout;
150    }
151
152  if (reset && (__spu_timers_active == start))
153    __reset_spu_decr (__spu_timers_active->tmout);
154
155  if (__unlikely (was_enabled))
156    spu_ienable ();
157}
158
159/* SLIH for decrementer.  Manages software timebase and timers.
160   Called by SPU FLIH. Assumes decrementer is still running
161   (event not yet acknowledeged).  */
162unsigned int
163spu_clock_slih (unsigned status)
164{
165  int decr_reset_val;
166  spu_timer_t *active, *handled;
167  unsigned was_enabled = spu_readch (SPU_RdMachStat) & 0x1;
168
169  status &= ~MFC_DECREMENTER_EVENT;
170
171  spu_idisable ();
172
173  /* The decrementer has now expired.  The decrementer event was acknowledged
174     in the FLIH but not disabled. The decrementer will continue to run while
175     we're running the clock/timer handler. The software clock keeps running,
176     and accounts for all the time spent running handlers. Add the current
177     timeout to the software timebase and set the timeout to DECR_MAX. This
178     allows the "clock read" code to continue to work while we're in here, and
179     gives us the most possible time to finish before another underflow.  */
180  __spu_tb_val += __spu_tb_timeout;
181  __spu_tb_timeout = DECR_MAX;
182
183  /* For all timers that have the current timeout value, move them from the
184     active list to the handled list and call their handlers. Note that the
185     handled/stopped lists may be manipulated by the handlers if they wish to
186     stop/free the timers. Note that only the first expired timer will reflect
187     the real timeout value; the rest of the timers that had the same timeout
188     value will have a relative value of zero.  */
189  if (__spu_timers_active)
190    {
191      __spu_timers_active->tmout = 0;
192      while ((active = __spu_timers_active)
193             && (active->tmout <= TIMER_INTERVAL_WINDOW))
194        {
195          __spu_timers_active = active->next;
196          active->next = __spu_timers_handled;
197          __spu_timers_handled = active;
198          active->state = SPU_TIMER_HANDLED;
199          (*active->func) (active->id);
200        }
201    }
202
203  /* put the handled timers back on the list and restart decrementer.  */
204  while ((handled = __spu_timers_handled) != NULL)
205    {
206      __spu_timers_handled = handled->next;
207      __spu_timer_start (handled->id, 0);
208    }
209
210  /* Reset the decrementer before returning. If we have any active timers, we
211     set it to the timeout value for the timer at the head of the list, else
212     the default clock value.  */
213  decr_reset_val = __spu_timers_active ? __spu_timers_active->tmout : CLOCK_START_VALUE;
214
215  __reset_spu_decr (decr_reset_val);
216
217  if (__likely (was_enabled))
218    spu_ienable ();
219
220  return status;
221}
Note: See TracBrowser for help on using the repository browser.