Change timer 0 from mode 4 to mode 0 to avoid hangs with VirtualBox 4.0.0.
[AROS.git] / arch / i386-pc / timer / ticks.c
blob0827bff502b476d47a8d3c953be32ff7c6eb548a
1 //#include DEBUG 1
3 //#include <aros/debug.h>
4 #include <exec/types.h>
5 #include <proto/exec.h>
6 #include <asm/io.h>
8 #include <proto/timer.h>
10 #include "ticks.h"
12 const ULONG TIMER_RPROK = 3599597124UL;
14 ULONG tick2usec(ULONG tick)
16 ULONG ret, rest;
17 asm volatile("mull %3":"=d"(ret),"=a"(rest):"a"(TIMER_RPROK),"m"(tick));
18 ret+=rest>>31;
19 return ret;
22 ULONG usec2tick(ULONG usec)
24 ULONG ret;
26 // gcc 4.3.1 with -O2: Following doesn't work properly, probably because it
27 // doesn't tell about edx trashing.
29 // asm volatile("movl $0,%%eax; divl %2":"=a"(ret):"d"(usec),"m"(TIMER_RPROK));
31 asm volatile("movl $0,%%eax; divl %2":"=a"(ret),"+d"(usec):"m"(TIMER_RPROK));
32 return ret;
35 void EClockUpdate(struct TimerBase *TimerBase)
37 ULONG time, diff;
39 Disable();
41 outb((inb(0x61) & 0xfd) | 1, 0x61); /* Enable the timer (set GATE on) */
42 /* Latch the current time value */
44 outb(0x80, 0x43);
45 /* Read out current 16-bit time */
46 time = inb(0x42) >> 1;
47 time += inb(0x42) << 7;
49 diff = (TimerBase->tb_prev_tick - time);
51 if (time > TimerBase->tb_prev_tick)
52 diff += 0x8000;
54 TimerBase->tb_prev_tick = time;
56 TimerBase->tb_ticks_total += diff;
57 TimerBase->tb_ticks_sec += diff;
58 TimerBase->tb_ticks_elapsed += diff;
60 if (TimerBase->tb_ticks_sec >= 1193180) {
61 TimerBase->tb_ticks_sec -= 1193180;
62 TimerBase->tb_CurrentTime.tv_secs++;
65 if (TimerBase->tb_ticks_elapsed >= 1193180) {
66 TimerBase->tb_ticks_elapsed -= 1193180;
67 TimerBase->tb_Elapsed.tv_secs++;
70 TimerBase->tb_Elapsed.tv_micro = tick2usec(TimerBase->tb_ticks_elapsed);
71 TimerBase->tb_CurrentTime.tv_micro = tick2usec(TimerBase->tb_ticks_sec);
73 Enable();
76 void EClockSet(struct TimerBase *TimerBase)
78 ULONG time;
80 TimerBase->tb_ticks_sec = usec2tick(TimerBase->tb_CurrentTime.tv_micro);
81 TimerBase->tb_ticks_total = TimerBase->tb_ticks_sec
82 + (UQUAD)TimerBase->tb_CurrentTime.tv_secs * 1193180;
84 /* Latch the current time value */
85 outb(0x80, 0x43);
86 /* Read out current 16-bit time */
87 time = inb(0x42) >> 1;
88 time += inb(0x42) << 7;
89 outb((inb(0x61) & 0xfd) | 1, 0x61); /* Enable the timer (set GATE on) */
91 TimerBase->tb_prev_tick = time;
94 void Timer0Setup(struct TimerBase *TimerBase)
96 struct timeval time;
97 ULONG delay = 23864;
98 struct timerequest *tr;
100 tr = (struct timerequest *)GetHead(&TimerBase->tb_Lists[TL_WAITVBL]);
102 if (tr)
104 time.tv_micro = tr->tr_time.tv_micro;
105 time.tv_secs = tr->tr_time.tv_secs;
107 EClockUpdate(TimerBase);
108 SubTime(&time, &TimerBase->tb_CurrentTime);
110 if ((LONG)time.tv_secs < 0)
112 delay = 0;
114 else if (time.tv_secs == 0)
116 if (time.tv_micro < 20000)
118 delay = usec2tick(time.tv_micro);
123 tr = (struct timerequest *)GetHead(&TimerBase->tb_Lists[TL_VBLANK]);
125 if (tr)
127 time.tv_micro = tr->tr_time.tv_micro;
128 time.tv_secs = tr->tr_time.tv_secs;
130 EClockUpdate(TimerBase);
131 SubTime(&time, &TimerBase->tb_Elapsed);
133 if ((LONG)time.tv_secs < 0)
135 delay = 0;
137 else if (time.tv_secs == 0)
139 if (time.tv_micro < 20000)
141 if (delay > usec2tick(time.tv_micro))
142 delay = usec2tick(time.tv_micro);
147 if (delay < 2) delay = 2;
149 outb((inb(0x61) & 0xfd) | 1, 0x61); /* Enable the timer (set GATE on) */
150 outb(0x30, 0x43); /* Binary, mode 0, LSB&MSB */
151 outb(delay & 0xff, 0x40);
152 outb(delay >> 8, 0x40);