detect if the compiler supports -fno-builtin and -fno-builtin-vsnprintf. (NicJA)
[AROS.git] / arch / all-pc / timer / ticks.c
blobcd735485aa70e27df110b4bd8c786f9176bb6c76
1 /*
2 Copyright © 1995-2017, 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>
11 #include <proto/execlock.h>
13 #include "ticks.h"
14 #include "timer_macros.h"
17 * This code uses two channels of the PIT for simplicity:
18 * Channel 0 - sends IRQ 0 on terminal count. We use it as alarm clock.
19 * Channel 2 is used as EClock counter. It counts all the time and is never reloaded.
20 * We initialize it in timer_init.c.
24 * The math magic behind this is:
26 * 1. Theoretical value of microseconds is: tick * 1000000 / tb_eclock_rate.
27 * 2. tb_eclock_rate is constant and equal to 1193180Hz (frequency of PIT's master quartz).
28 * 3. tick2usec() is called much more frequently than usec2tick(). And multiplication is faster than division.
30 * So let's get rid of division:
32 * a) Multiply both divident and divisor of the initial equation by 0x100000000 (4294967296 in decimal). In asm this
33 * can be accomplished simply by using 64-bit math ops and using upper half instead of lower:
34 * usec = (tick * 1000000 * 0x100000000) / (1193180 * 0x100000000) = tick * (1000000 * 0x100000000 / 1193180) / 0x100000000.
35 * b) Calculate the constant part in brackets: 1000000 * 4294967296 / 1193180 = 3599597124.
36 * c) So: usec = tick * 3599597124 / 0x100000000 = (tick * 3599597124) >> 32.
38 * (c) Michal Schulz.
40 static const ULONG TIMER_TUCONV = (ULONG)(1000000 * 0x100000000ULL / 1193180);
42 static inline const ULONG tick2usec(const ULONG tick)
44 return (ULONG)(((UQUAD)tick * TIMER_TUCONV) >> 32);
48 * So let's get rid of division once again:
50 * Theoretical value of ticks is: usec * tb_eclock_rate / 1000000
51 * a) Multiply both divident and divisor of the initial equation by 0x100000000 (4294967296 in decimal). In asm this
52 * can be accomplished simply by using 64-bit math ops and using upper half instead of lower:
53 * tick * 0x100000000 = usec * (1193180 * 0x100000000 / 1000000)
54 * b) Calculate the constant part in brackets:
55 * tick * 0x100000000 = usec * 5124669078
56 * c) Shift off the low bits
57 * tick = (usec * 5124669078) >> 32;
58 * d) BUT! 5124669078 > 1<<32 , so by the communitive property we get:
59 * tick = (usec * (2^32 + (5124669078 - 2^32))) / 2^32
60 * or
61 * tick = ((usec * 2^32)) + (usec * (5124669078 - 2^32))) / 2^32
62 * or
63 * tick = ((usec * 2^32) + (usec * 829701782)) / 2^32
64 * or
65 * tick = ((usec * 2^32 / 2^32) + (usec * 829701782) / 2^32
66 * or
67 * tick = usec + ((usec * 829701782) >> 32);
69 static const ULONG TIMER_UTCONV = (1193180 * 0x100000000ULL / 1000000) - 0x100000000;
71 static inline const ULONG usec2tick(const ULONG usec)
73 return usec + (ULONG)(((UQUAD)usec * TIMER_UTCONV) >> 32);
77 * Calculate difference and add it to our current EClock value.
78 * PIT counters actually count backwards. This is why everything here looks reversed.
80 static void EClockAdd(ULONG time, struct TimerBase *TimerBase)
82 ULONG diff = (TimerBase->tb_prev_tick - time);
84 if (time > TimerBase->tb_prev_tick)
86 /* Handle PIT rollover through 0xFFFF */
87 diff += 0x10000;
90 /* Increment our time counters */
91 TimerBase->tb_ticks_total += diff;
92 INCTIME(TimerBase->tb_CurrentTime, TimerBase->tb_ticks_sec, diff);
93 INCTIME(TimerBase->tb_Elapsed, TimerBase->tb_ticks_elapsed, diff);
96 void EClockUpdate(struct TimerBase *TimerBase)
98 ULONG time;
100 outb(CH0|ACCESS_LATCH, PIT_CONTROL); /* Latch the current time value */
101 time = ch_read(PIT_CH0); /* Read out current 16-bit time */
103 EClockAdd(time, TimerBase); /* Increment our time counters */
104 TimerBase->tb_prev_tick = time; /* Remember last counter value as start of new interval */
107 void EClockSet(struct TimerBase *TimerBase)
109 TimerBase->tb_ticks_sec = usec2tick(TimerBase->tb_CurrentTime.tv_micro);
110 TimerBase->tb_ticks_total = TimerBase->tb_ticks_sec + (UQUAD)TimerBase->tb_CurrentTime.tv_secs * TimerBase->tb_eclock_rate;
112 outb(CH0|ACCESS_LATCH, PIT_CONTROL); /* Latch the current time value */
113 TimerBase->tb_prev_tick = ch_read(PIT_CH0); /* Read out current 16-bit time */
116 void Timer0Setup(struct TimerBase *TimerBase)
118 #if defined(__AROSEXEC_SMP__)
119 struct ExecLockBase *ExecLockBase = TimerBase->tb_ExecLockBase;
120 #endif
121 struct timeval time;
122 ULONG delay = 23864;
123 ULONG old_tick;
124 struct timerequest *tr;
126 #if defined(__AROSEXEC_SMP__)
127 if (ExecLockBase) ObtainLock(TimerBase->tb_ListLock, SPINLOCK_MODE_READ, 0);
128 #endif
129 if ((tr = (struct timerequest *)GetHead(&TimerBase->tb_Lists[TL_MICROHZ])) != NULL)
131 time.tv_micro = tr->tr_time.tv_micro;
132 time.tv_secs = tr->tr_time.tv_secs;
134 EClockUpdate(TimerBase);
135 SUBTIME(&time, &TimerBase->tb_Elapsed);
137 if ((LONG)time.tv_secs < 0)
139 delay = 0;
141 else if (time.tv_secs == 0)
143 if (time.tv_micro < 20000)
145 delay = usec2tick(time.tv_micro);
149 #if defined(__AROSEXEC_SMP__)
150 if (ExecLockBase) ReleaseLock(TimerBase->tb_ListLock, 0);
151 #endif
152 if (delay < 2) delay = 2;
155 * We are going to reload the counter. By this moment, some time has passed after the last EClockUpdate()pdate.
156 * In order to keep up with the precision, we pick up this time here.
158 outb(CH0|ACCESS_LATCH, PIT_CONTROL); /* Latch the current time value */
159 old_tick = ch_read(PIT_CH0); /* Read out current 16-bit time */
161 outb(CH0|ACCESS_FULL|MODE_SW_STROBE, PIT_CONTROL); /* Software strobe mode, 16-bit access */
162 ch_write(delay, PIT_CH0); /* Activate the new delay */
165 * Now, when our new delay is already in progress, we can spend some time
166 * on adding previous value to our time counters.
168 EClockAdd(old_tick, TimerBase);
169 /* tb_prev_tick is used by EClockAdd(), so update it only now */
170 TimerBase->tb_prev_tick = delay;