Added hack that moves E-clock timer to another CIA timer if program wants to allocate...
[AROS.git] / arch / m68k-amiga / timer / timer_init.c
blob33173e0ad1fd41929530b126bd47d4bc32efa4bc
1 /*
2 Copyright © 1995-2010, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: Timer startup and device commands
6 */
8 /****************************************************************************************/
12 implementation notes:
14 - CIAA-A: normal timer jobs (microhz/e-clock)
15 - CIAA-B: E-clock counter (can move to CIAB-A or CIAB-B)
16 - vblank interrupt used for vblank timer unit
18 Unit conversions and misuse of tv_sec/tv_usec fields probably looks strange..
21 #define DEBUG 0
22 #include <aros/debug.h>
24 #include <aros/kernel.h>
25 #include <exec/types.h>
26 #include <exec/io.h>
27 #include <exec/errors.h>
28 #include <exec/devices.h>
29 #include <exec/alerts.h>
30 #include <exec/initializers.h>
31 #include <devices/timer.h>
32 #include <hardware/intbits.h>
33 #include <hardware/cia.h>
34 #include <graphics/gfxbase.h>
36 #include <proto/exec.h>
37 #include <proto/kernel.h>
38 #include <proto/timer.h>
39 #include <proto/cia.h>
40 #include <proto/battclock.h>
42 #include <aros/symbolsets.h>
44 #include LC_LIBDEFS_FILE
46 #include <timer_intern.h>
47 #include <timer_platform.h>
49 AROS_INTP(ciab_eclock);
50 AROS_INTP(ciaint_timer);
51 AROS_INTP(cia_vbint);
53 /****************************************************************************************/
55 static void start_eclock(struct TimerBase *tb, UWORD cnt)
57 *tb->tb_eclock_cr = 0x00;
58 SetICR(tb->tb_eclock_res, 1 << tb->tb_eclock_intbit);
59 // start timer in continuous mode
60 *tb->tb_eclock_lo = cnt;
61 *tb->tb_eclock_hi = cnt >> 8;
62 *tb->tb_eclock_cr |= 0x10;
63 *tb->tb_eclock_cr |= 0x01;
65 static void set_eclock(struct TimerBase *tb, WORD intbit)
67 WORD cia = intbit >> 1;
68 intbit &= 1;
69 tb->tb_eclock_cia = (struct CIA*)(cia ? 0xbfd000 : 0xbfe001);
70 tb->tb_eclock_cr = (UBYTE*)tb->tb_eclock_cia + (intbit ? 0xf00 : 0xe00);
71 tb->tb_eclock_lo = (UBYTE*)tb->tb_eclock_cia + (intbit ? 0x600 : 0x400);
72 tb->tb_eclock_hi = tb->tb_eclock_lo + 0x100;
73 tb->tb_eclock_intbit = intbit;
74 tb->tb_eclock_res = tb->tb_cia[cia];
77 static void TimerHook(struct Resource *cia, struct TimerBase *tb, WORD iCRBit)
79 WORD cnt;
80 BOOL intactive;
81 UBYTE hi, lo;
83 if (tb->tb_eclock_res != cia || tb->tb_eclock_intbit != iCRBit)
84 return;
85 /* We are in Disabled state already
86 * Someone wants to allocate our eclock timer
87 * Check if we have any available timers left..
89 D(bug("someone wants our timer %x %d\n", cia, iCRBit));
90 for (cnt = 1; cnt < 4; cnt++) {
91 if (!AddICRVector(tb->tb_cia[cnt >> 1], cnt & 1, &tb->tb_ciaint_eclock))
92 break;
94 if (cnt >= 4) {
95 D(bug("no free timers\n"));
96 return;
98 /* We have one! */
99 D(bug("e-clock timer moved to %d\n", cnt));
100 for (;;) {
101 hi = *tb->tb_eclock_hi;
102 lo = *tb->tb_eclock_lo;
103 if (hi == *tb->tb_eclock_hi)
104 break;
106 intactive = (SetICR(tb->tb_eclock_res, 0) & (1 << tb->tb_eclock_intbit)) != 0;
107 if (intactive) {
108 // re-read, there may have been wrap-around
109 for (;;) {
110 hi = *tb->tb_eclock_hi;
111 lo = *tb->tb_eclock_lo;
112 if (hi == *tb->tb_eclock_hi)
113 break;
116 *tb->tb_eclock_cr = 0x00;
117 RemICRVector(tb->tb_eclock_res, tb->tb_eclock_intbit, &tb->tb_ciaint_eclock);
118 set_eclock (tb, cnt);
119 start_eclock (tb, (hi << 8) | lo);
120 if (intactive)
121 SetICR(tb->tb_eclock_res, 0x80 | (1 << tb->tb_eclock_intbit));
124 static int GM_UNIQUENAME(Init)(LIBBASETYPEPTR LIBBASE)
126 struct Interrupt *inter;
127 struct Interrupt fakeinter;
128 struct BattClockBase *BattClockBase;
129 struct GfxBase *GfxBase;
131 GfxBase = TaggedOpenLibrary(TAGGEDOPEN_GRAPHICS);
133 InitCustom(GfxBase);
135 LIBBASE->tb_eclock_rate = (GfxBase->DisplayFlags & REALLY_PAL) ? 709379 : 715909;
136 LIBBASE->tb_vblank_rate = (GfxBase->DisplayFlags & PAL) ? 50 : 60;
137 LIBBASE->tb_vblank_micros = 1000000 / LIBBASE->tb_vblank_rate;
138 SysBase->ex_EClockFrequency = LIBBASE->tb_eclock_rate;
139 LIBBASE->tb_eclock_micro_mult = (GfxBase->DisplayFlags & REALLY_PAL) ? 92385 : 91542;
140 LIBBASE->tb_micro_eclock_mult = (GfxBase->DisplayFlags & REALLY_PAL) ? 23245 : 23459;
142 CloseLibrary((struct Library*)GfxBase);
144 BattClockBase = OpenResource("battclock.resource");
145 if (BattClockBase)
146 LIBBASE->tb_CurrentTime.tv_secs = ReadBattClock();
148 /* Initialise the lists */
149 NEWLIST(&LIBBASE->tb_Lists[UNIT_VBLANK]);
150 NEWLIST(&LIBBASE->tb_Lists[UNIT_MICROHZ]);
152 inter = &LIBBASE->tb_vbint;
153 inter->is_Code = (APTR)cia_vbint;
154 inter->is_Data = LIBBASE;
155 inter->is_Node.ln_Name = "timer.device VBlank";
156 inter->is_Node.ln_Pri = 20;
157 inter->is_Node.ln_Type = NT_INTERRUPT;
158 AddIntServer(INTB_VERTB, inter);
160 LIBBASE->tb_cia[0] = OpenResource("ciaa.resource");
161 LIBBASE->tb_cia[1] = OpenResource("ciab.resource");
162 if (!LIBBASE->tb_cia[0] || !LIBBASE->tb_cia[1])
163 Alert(AT_DeadEnd | AG_OpenRes | AO_CIARsrc);
164 LIBBASE->tb_eclock_res = LIBBASE->tb_micro_res = LIBBASE->tb_cia[0];
166 /* CIA-A timer A = microhz */
167 LIBBASE->tb_micro_cia = (struct CIA*)0xbfe001;
168 LIBBASE->tb_micro_cr = (UBYTE*)LIBBASE->tb_micro_cia + 0xe00;
169 LIBBASE->tb_micro_lo = (UBYTE*)LIBBASE->tb_micro_cia + 0x400;
170 LIBBASE->tb_micro_hi = (UBYTE*)LIBBASE->tb_micro_cia + 0x500;
171 LIBBASE->tb_micro_intbit = 0;
173 inter = &LIBBASE->tb_ciaint_timer;
174 inter->is_Node.ln_Pri = 0;
175 inter->is_Node.ln_Type = NT_INTERRUPT;
176 inter->is_Node.ln_Name = "timer.device microhz";
177 inter->is_Code = (APTR)ciaint_timer;
178 inter->is_Data = LIBBASE;
180 /* CIA-A timer B = E-Clock */
181 set_eclock (LIBBASE, 1);
183 inter = &LIBBASE->tb_ciaint_eclock;
184 inter->is_Node.ln_Pri = 0;
185 inter->is_Node.ln_Type = NT_INTERRUPT;
186 inter->is_Node.ln_Name = "timer.device eclock";
187 inter->is_Code = (APTR)ciab_eclock;
188 inter->is_Data = LIBBASE;
190 Disable();
192 if (AddICRVector(LIBBASE->tb_micro_res, LIBBASE->tb_micro_intbit, &LIBBASE->tb_ciaint_timer))
193 Alert(AT_DeadEnd | AG_NoMemory | AO_CIARsrc);
194 *LIBBASE->tb_micro_cr = 0x08; // one-shot
195 SetICR(LIBBASE->tb_micro_res, 1 << LIBBASE->tb_micro_intbit);
197 if (AddICRVector(LIBBASE->tb_eclock_res, LIBBASE->tb_eclock_intbit, &LIBBASE->tb_ciaint_eclock))
198 Alert(AT_DeadEnd | AG_NoMemory | AO_CIARsrc);
199 start_eclock (LIBBASE, 0xffff);
201 /* Emulate AOS 2.0+ feature which moves 2.0+ only E-clock timer out of
202 * the way if some other (Usually 1.x) program assumes CIAA-B is always
203 * free and wants to allocate it. This is m68k AROS hack, there is no
204 * official API.
206 fakeinter.is_Code = (APTR)TimerHook;
207 fakeinter.is_Data = LIBBASE;
208 AddICRVector(LIBBASE->tb_cia[0], -1, &fakeinter);
209 AddICRVector(LIBBASE->tb_cia[1], -1, &fakeinter);
211 Enable();
213 D(bug("timer.device init\n"));
215 return TRUE;
218 /****************************************************************************************/
220 static int GM_UNIQUENAME(Open)
222 LIBBASETYPEPTR LIBBASE,
223 struct timerequest *tr,
224 ULONG unitNum,
225 ULONG flags
228 switch(unitNum)
230 case UNIT_VBLANK:
231 case UNIT_WAITUNTIL:
232 case UNIT_MICROHZ:
233 case UNIT_ECLOCK:
234 case UNIT_WAITECLOCK:
235 tr->tr_node.io_Error = 0;
236 tr->tr_node.io_Unit = (struct Unit *)unitNum;
237 tr->tr_node.io_Device = (struct Device *)LIBBASE;
238 break;
240 default:
241 tr->tr_node.io_Error = IOERR_OPENFAIL;
244 return TRUE;
247 /****************************************************************************************/
249 static int GM_UNIQUENAME(Expunge)(LIBBASETYPEPTR LIBBASE)
251 Disable();
252 RemIntServer(INTB_VERTB, &LIBBASE->tb_vbint);
253 RemICRVector(LIBBASE->tb_micro_res, LIBBASE->tb_micro_intbit, &LIBBASE->tb_ciaint_timer);
254 RemICRVector(LIBBASE->tb_eclock_res, LIBBASE->tb_eclock_intbit, &LIBBASE->tb_ciaint_eclock);
255 Enable();
256 return TRUE;
259 /****************************************************************************************/
261 ADD2INITLIB(GM_UNIQUENAME(Init), 0)
262 ADD2OPENDEV(GM_UNIQUENAME(Open), 0)
263 ADD2EXPUNGELIB(GM_UNIQUENAME(Expunge), 0)