cleanup debug
[AROS.git] / arch / all-pc / timer / ticks.c
blobc9277b002516df02f26035a9cf87046bcc153233
1 /*
2 Copyright © 1995-2011, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: Hardware management routines for IBM PC-AT timer
6 Lang: english
7 */
9 #include <asm/io.h>
10 #include <proto/exec.h>
12 #include "ticks.h"
13 #include "timer_macros.h"
16 * This code uses two channels of the PIT for simplicity:
17 * Channel 0 - sends IRQ 0 on terminal count. We use it as alarm clock.
18 * Channel 2 is used as EClock counter. It counts all the time and is never reloaded.
19 * We initialize it in timer_init.c.
23 * The math magic behing this is:
25 * 1. Theoretical value of microseconds is: tick * 1000000 / tb_eclock_rate.
26 * 2. tb_eclock_rate is constant and equal to 1193180Hz (frequency of PIT's master quartz).
27 * 3. tick2usec() is called much more frequently than usec2tick(). And multiplication is faster than division.
29 * So let's get rid of division:
31 * a) Multiply both divident and divisor of the initial equation by 0x100000000 (4294967296 in decimal). In asm this
32 * can be accomplished simply by using 64-bit math ops and using upper half instead of lower:
33 * usec = (tick * 1000000 * 0x100000000) / (1193180 * 0x100000000) = tick * (1000000 * 0x100000000 / 1193180) / 0x100000000.
34 * b) Calculate the constant part in brackets: 1000000 * 4294967296 / 1193180 = 3599597124.
35 * c) So: usec = tick * 3599597124 / 0x100000000 = (tick * 3599597124) >> 32.
37 * (c) Michal Schulz.
39 static const ULONG TIMER_TUCONV = (ULONG)(1000000 * 0x100000000ULL / 1193180);
41 static inline const ULONG tick2usec(const ULONG tick)
43 return (ULONG)(((UQUAD)tick * TIMER_TUCONV) >> 32);
47 * So let's get rid of division once again:
49 * Theoretical value of ticks is: usec * tb_eclock_rate / 1000000
50 * a) Multiply both divident and divisor of the initial equation by 0x100000000 (4294967296 in decimal). In asm this
51 * can be accomplished simply by using 64-bit math ops and using upper half instead of lower:
52 * tick * 0x100000000 = usec * (1193180 * 0x100000000 / 1000000)
53 * b) Calculate the constant part in brackets:
54 * tick * 0x100000000 = usec * 5124669078
55 * c) Shift off the low bits
56 * tick = (usec * 5124669078) >> 32;
57 * d) BUT! 5124669078 > 1<<32 , so by the communitive property we get:
58 * tick = (usec * (2^32 + (5124669078 - 2^32))) / 2^32
59 * or
60 * tick = ((usec * 2^32)) + (usec * (5124669078 - 2^32))) / 2^32
61 * or
62 * tick = ((usec * 2^32) + (usec * 829701782)) / 2^32
63 * or
64 * tick = ((usec * 2^32 / 2^32) + (usec * 829701782) / 2^32
65 * or
66 * tick = usec + ((usec * 829701782) >> 32);
68 static const ULONG TIMER_UTCONV = (1193180 * 0x100000000ULL / 1000000) - 0x100000000;
70 static inline const ULONG usec2tick(const ULONG usec)
72 return usec + (ULONG)(((UQUAD)usec * TIMER_UTCONV) >> 32);
76 * Calculate difference and add it to our current EClock value.
77 * PIT counters actually count backwards. This is why everything here looks reversed.
79 static void EClockAdd(ULONG time, struct TimerBase *TimerBase)
81 ULONG diff = (TimerBase->tb_prev_tick - time);
83 if (time > TimerBase->tb_prev_tick)
85 /* Handle PIT rollover through 0xFFFF */
86 diff += 0x10000;
89 /* Increment our time counters */
90 TimerBase->tb_ticks_total += diff;
91 INCTIME(TimerBase->tb_CurrentTime, TimerBase->tb_ticks_sec, diff);
92 INCTIME(TimerBase->tb_Elapsed, TimerBase->tb_ticks_elapsed, diff);
95 void EClockUpdate(struct TimerBase *TimerBase)
97 ULONG time;
99 outb(CH0|ACCESS_LATCH, PIT_CONTROL); /* Latch the current time value */
100 time = ch_read(PIT_CH0); /* Read out current 16-bit time */
102 EClockAdd(time, TimerBase); /* Increment our time counters */
103 TimerBase->tb_prev_tick = time; /* Remember last counter value as start of new interval */
106 void EClockSet(struct TimerBase *TimerBase)
108 TimerBase->tb_ticks_sec = usec2tick(TimerBase->tb_CurrentTime.tv_micro);
109 TimerBase->tb_ticks_total = TimerBase->tb_ticks_sec + (UQUAD)TimerBase->tb_CurrentTime.tv_secs * TimerBase->tb_eclock_rate;
111 outb(CH0|ACCESS_LATCH, PIT_CONTROL); /* Latch the current time value */
112 TimerBase->tb_prev_tick = ch_read(PIT_CH0); /* Read out current 16-bit time */
115 void Timer0Setup(struct TimerBase *TimerBase)
117 struct timeval time;
118 ULONG delay = 23864;
119 ULONG old_tick;
120 struct timerequest *tr = (struct timerequest *)GetHead(&TimerBase->tb_Lists[TL_MICROHZ]);
122 if (tr)
124 time.tv_micro = tr->tr_time.tv_micro;
125 time.tv_secs = tr->tr_time.tv_secs;
127 EClockUpdate(TimerBase);
128 SUBTIME(&time, &TimerBase->tb_Elapsed);
130 if ((LONG)time.tv_secs < 0)
132 delay = 0;
134 else if (time.tv_secs == 0)
136 if (time.tv_micro < 20000)
138 delay = usec2tick(time.tv_micro);
143 if (delay < 2) delay = 2;
146 * We are going to reload the counter. By this moment, some time has passed after the last EClockUpdate()pdate.
147 * In order to keep up with the precision, we pick up this time here.
149 outb(CH0|ACCESS_LATCH, PIT_CONTROL); /* Latch the current time value */
150 old_tick = ch_read(PIT_CH0); /* Read out current 16-bit time */
152 outb(CH0|ACCESS_FULL|MODE_SW_STROBE, PIT_CONTROL); /* Software strobe mode, 16-bit access */
153 ch_write(delay, PIT_CH0); /* Activate the new delay */
156 * Now, when our new delay is already in progress, we can spend some time
157 * on adding previous value to our time counters.
159 EClockAdd(old_tick, TimerBase);
160 /* tb_prev_tick is used by EClockAdd(), so update it only now */
161 TimerBase->tb_prev_tick = delay;