Fixed binary search: no more infinite loops when vendor is unknown.
[tangerine.git] / arch / ppc-sam440 / timer / lowlevel.c
blob3ffbe3c1020c445aff6f1c2e1faf5fb2afd885e5
1 #include <aros/debug.h>
2 #include <asm/amcc440.h>
3 #include <asm/io.h>
4 #include "../kernel/syscall.h"
6 #include <aros/asmcall.h>
7 #include <hardware/intbits.h>
8 #include <exec/execbase.h>
10 #include <inttypes.h>
11 #include <proto/exec.h>
12 #include <proto/timer.h>
14 #include "lowlevel.h"
16 uint32_t tbc_expected;
17 uint32_t tbc_achieved;
18 int32_t corr;
20 inline uint32_t __attribute__((const)) tick2usec(uint32_t tick)
22 uint32_t retval;
23 uint64_t tmp = ((uint64_t)tick) * 64424510;
25 retval = (tmp + 0x7fffffff) >> 32;
27 return retval;
30 inline uint32_t __attribute__((const)) usec2tick(uint32_t usec)
32 uint32_t retval;
33 uint64_t tmp = ((uint64_t)usec) * 286331150203ULL;
35 retval = (tmp + 0x7fffffff) >> 32;
37 return retval;
40 void EClockUpdate(struct TimerBase *TimerBase)
42 uint32_t time;
43 uint32_t diff;
44 int show = 0;
46 time = inl(GPT0_TBC);
47 diff = (time - TimerBase->tb_prev_tick);
48 TimerBase->tb_prev_tick = time;
50 TimerBase->tb_ticks_total += diff;
52 TimerBase->tb_ticks_sec += diff;
53 TimerBase->tb_ticks_elapsed += diff;
55 if (TimerBase->tb_ticks_sec >= 66666666)
57 TimerBase->tb_ticks_sec -= 66666666;
58 TimerBase->tb_CurrentTime.tv_secs++;
59 //show = 1;
62 if (TimerBase->tb_ticks_elapsed >= 66666666)
64 TimerBase->tb_ticks_elapsed -= 66666666;
65 TimerBase->tb_Elapsed.tv_secs++;
68 TimerBase->tb_Elapsed.tv_micro = tick2usec(TimerBase->tb_ticks_elapsed);
69 TimerBase->tb_CurrentTime.tv_micro = tick2usec(TimerBase->tb_ticks_sec);
71 // if (show)
72 // bug("[timer] CurrentTime: %d:%06d\n", TimerBase->tb_CurrentTime.tv_secs, TimerBase->tb_CurrentTime.tv_micro);
73 //bug("[timer] %08x %08x %d \n", tbc_expected, tbc_achieved, corr);
76 void EClockSet(struct TimerBase *TimerBase)
78 TimerBase->tb_ticks_sec = usec2tick(TimerBase->tb_CurrentTime.tv_micro);
81 void TimerSetup(struct TimerBase *TimerBase, uint32_t waste)
83 int32_t delay = 1333333; /* 50Hz in worst case */
84 struct timeval time;
85 struct timerequest *tr;
86 uint32_t current_time;
88 tr = (struct timerequest *)GetHead(&TimerBase->tb_Lists[TL_WAITVBL]);
90 if (tr)
92 time.tv_micro = tr->tr_time.tv_micro;
93 time.tv_secs = tr->tr_time.tv_secs;
95 SubTime(&time, &TimerBase->tb_CurrentTime);
97 if ((LONG)time.tv_secs < 0)
99 delay = 0;
101 else if (time.tv_secs == 0)
103 if (time.tv_micro < 20000)
105 if (delay > usec2tick(time.tv_micro))
106 delay = usec2tick(time.tv_micro);
111 tr = (struct timerequest *)GetHead(&TimerBase->tb_Lists[TL_VBLANK]);
113 if (tr)
115 time.tv_micro = tr->tr_time.tv_micro;
116 time.tv_secs = tr->tr_time.tv_secs;
118 SubTime(&time, &TimerBase->tb_Elapsed);
120 if ((LONG)time.tv_secs < 0)
122 delay = 0;
124 else if (time.tv_secs == 0)
126 if (time.tv_micro < 20000)
128 if (delay > usec2tick(time.tv_micro))
129 delay = usec2tick(time.tv_micro);
134 current_time = inl(GPT0_TBC);
135 delay -= current_time - waste + corr;
137 if (delay < 100) delay = 100;
139 tbc_expected = current_time + delay;
141 outl(delay, GPT0_DCT0);
145 This is a slightly faster version of AddTime() that we use here. It
146 also saves the function call overhead...
148 #define FastAddTime(d, s)\
149 (d)->tv_micro += (s)->tv_micro;\
150 (d)->tv_secs += (s)->tv_secs;\
151 if((d)->tv_micro > 999999) {\
152 (d)->tv_secs++;\
153 (d)->tv_micro -= 1000000;\
156 BOOL timer_addToWaitList(struct TimerBase *, struct MinList *, struct timerequest *);
158 void GPTHandler(struct TimerBase *TimerBase, struct ExecBase *SysBase)
160 uint32_t startup_time = inl(GPT0_TBC);
162 if (inl(GPT0_DCIS) & GPT0_DCIS_DCIS)
164 struct timerequest *tr, *next;
165 outl(GPT0_DCIS_DCIS, GPT0_DCIS);
166 EClockUpdate(TimerBase);
168 tbc_achieved = startup_time;
170 /* Timer errors bigger than approx 15 microseconds shouldn't be taken into account */
171 if (tbc_achieved > tbc_expected && (tbc_achieved - tbc_expected) < 1000)
173 corr = ((int32_t)(tbc_achieved - tbc_expected))-1;
178 Go through the "wait for x seconds" list and return requests
179 that have completed. A completed request is one whose time
180 is less than that of the elapsed time.
182 ForeachNodeSafe(&TimerBase->tb_Lists[TL_VBLANK], tr, next)
184 if(CmpTime(&TimerBase->tb_Elapsed, &tr->tr_time) <= 0)
186 if (tr == &TimerBase->tb_vblank_timerequest)
188 struct IntVector *iv = &SysBase->IntVects[INTB_VERTB];
190 /* VBlank Emu */
192 if (iv->iv_Code)
194 AROS_UFC5(void, iv->iv_Code,
195 AROS_UFCA(ULONG, 0, D1),
196 AROS_UFCA(ULONG, 0, A0),
197 AROS_UFCA(APTR, iv->iv_Data, A1),
198 AROS_UFCA(APTR, iv->iv_Code, A5),
199 AROS_UFCA(struct ExecBase *, SysBase, A6)
202 /* Automatically requeue/reactivate request */
204 Remove((struct Node *)tr);
205 tr->tr_node.io_Command = TR_ADDREQUEST;
206 tr->tr_time.tv_secs = 0;
207 tr->tr_time.tv_micro = 1000000 / SysBase->VBlankFrequency;
208 AddTime(&tr->tr_time, &TimerBase->tb_Elapsed);
210 timer_addToWaitList(TimerBase, &TimerBase->tb_Lists[TL_VBLANK], tr);
212 if (--SysBase->Elapsed == 0)
214 SysBase->SysFlags |= 0x2000;
215 SysBase->AttnResched |= 0x80;
218 else
220 /* This request has finished */
221 Remove((struct Node *)tr);
222 tr->tr_time.tv_secs = tr->tr_time.tv_micro = 0;
223 tr->tr_node.io_Error = 0;
224 ReplyMsg((struct Message *)tr);
227 else
230 The first request hasn't finished, as all requests are in
231 order, we don't bother searching through the remaining
233 break;
238 The other this is the "wait until a specified time". Here a request
239 is complete if the time we are waiting for is before the current time.
241 ForeachNodeSafe(&TimerBase->tb_Lists[TL_WAITVBL], tr, next)
243 if(CmpTime(&TimerBase->tb_CurrentTime, &tr->tr_time) <= 0)
245 /* This request has finished */
246 Remove((struct Node *)tr);
247 tr->tr_time.tv_secs = tr->tr_time.tv_micro = 0;
248 tr->tr_node.io_Error = 0;
249 ReplyMsg((struct Message *)tr);
251 else
254 The first request hasn't finished, as all requests are in
255 order, we don't bother searching through the remaining
257 break;
260 TimerSetup(TimerBase, startup_time);