2 Copyright (C) 2008 Mathias Gottschlag
4 Permission is hereby granted, free of charge, to any person obtaining a copy of
5 this software and associated documentation files (the "Software"), to deal in the
6 Software without restriction, including without limitation the rights to use,
7 copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
8 Software, and to permit persons to whom the Software is furnished to do so,
9 subject to the following conditions:
11 The above copyright notice and this permission notice shall be included in all
12 copies or substantial portions of the Software.
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
15 INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
16 PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
17 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
18 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
19 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 #include "ke/interrupts.h"
27 #include "ke/errors.h"
31 // TODO: Make this work without APIC
33 static volatile uint32_t interrupt_count
= 0;
34 static uint32_t ticks_per_usec
= 0;
35 static volatile uint32_t acpi_start
= 0;
37 static uint32_t us_per_tick
= 0;
39 static void keTimerCalibrationHandler(KeCPU
*cpu
, uint8_t intno
)
41 if (interrupt_count
== 0)
43 // Initialize APIC timer
44 keAPICWrite(0x3e0, 0xb);
45 keAPICWrite(0x320, 0x20020);
46 keAPICWrite(0x380, 0xFFFFFFFF);
48 else if (interrupt_count
== 1)
50 acpi_start
= keAPICRead(0x390);
52 else if (interrupt_count
== 2)
54 // Get APIC timer speed
55 ticks_per_usec
= (acpi_start
- keAPICRead(0x390)) / 10000;
60 static void keTimerHandler(KeCPU
*cpu
, uint8_t intno
)
63 KeTimer
*oldtimer
= cpu
->next_timer
;
64 cpu
->next_timer
= cpu
->next_timer
->next
;
69 // Increase current time and restart periodic timer
70 if (oldtimer
->periodic
)
72 cpu
->time
+= oldtimer
->time
;
73 keSetTimer(oldtimer
, oldtimer
->time
, 1);
77 cpu
->time
= oldtimer
->time
;
79 if (cpu
->next_timer
!= oldtimer
)
81 // Start next timer if it wasn't already started
82 if (cpu
->time
> cpu
->next_timer
->targettime
)
84 cpu
->next_timer
->targettime
= cpu
->time
+ 30;
86 keAPICWrite(0x380, ticks_per_usec
* (cpu
->next_timer
->targettime
- cpu
->time
));
87 cpu
->next_timer
->active
= 1;
89 if (oldtimer
->callback
) oldtimer
->callback(oldtimer
);
91 if (oldtimer
== cpu
->sched_timer
)
93 char *cpusign
= (char*)0xC00B8000 + keAPICGetCPU() * 2;
97 // Schedule to another thread here
98 KeThread
*newthread
= keGetSchedulableThread(1);
101 cpu
->currentthread
= newthread
;
105 keLockSpinlock(&cpu
->idlethread
->active
);
106 cpu
->currentthread
= cpu
->idlethread
;
109 keAPICWrite(0xb0, 0);
112 void keInitTimer(uint32_t frequency
)
114 // Calibrate APIC timer
115 keRegisterIRQ(0, keTimerCalibrationHandler
);
116 // Set PIT to 10ms-interval
117 uint16_t divisor
= (uint16_t)(1193180 / 100);
119 outb(0x40, divisor
& 0xFF);
120 outb(0x40, divisor
>> 8);
122 while (interrupt_count
< 3)
124 char *cpusign
= (char*)0xC00B8000 + keAPICGetCPU() * 2;
130 keAPICWrite(0x320, 0x10000);
133 keRegisterIRQ(0x10, keTimerHandler
);
134 us_per_tick
= 1000000 / frequency
;
137 void keInstallTimer(void)
139 keAPICWrite(0x3e0, 0xb);
140 keAPICWrite(0x320, 0x20030);
141 KeCPU
*cpu
= keGetCurrentCPU();
142 cpu
->sched_timer
= keCreateTimer();
143 cpu
->starttime
= keGetUnixTime();
144 keSetTimer(cpu
->sched_timer
, us_per_tick
, 1);
145 // keRegisterIRQ(0, keTimerHandler);
148 KeTimer
*keCreateTimer(void)
150 KeTimer
*timer
= malloc(sizeof(KeTimer
));
151 memset(timer
, 0, sizeof(KeTimer
));
154 int keDestroyTimer(KeTimer
*timer
)
159 int keSetTimer(KeTimer
*timer
, uint64_t time
, int periodic
)
161 KeExecLevel oldlevel
= keSetExecutionLevel(KE_LEVEL_HIGH
);
162 KeCPU
*cpu
= keGetCurrentCPU();
163 if (timer
->queued
) keCancelTimer(timer
);
164 // Store data in the struct
166 uint32_t currenttime
= keGetTime();
169 timer
->targettime
= currenttime
+ time
;
173 timer
->targettime
= time
;
175 timer
->periodic
= periodic
;
176 // Add timer to timer queue
177 if (!cpu
->next_timer
)
179 keAPICWrite(0x380, ticks_per_usec
* (timer
->targettime
- currenttime
));
181 cpu
->next_timer
= timer
;
185 if (cpu
->next_timer
->targettime
> timer
->targettime
)
187 // First timer to expire
188 if (currenttime
>= timer
->targettime
)
190 timer
->targettime
= currenttime
+ 1;
192 keAPICWrite(0x380, ticks_per_usec
* (timer
->targettime
- currenttime
));
193 timer
->next
= cpu
->next_timer
;
194 cpu
->next_timer
= timer
;
199 // Add later in the queue
200 KeTimer
*prev_timer
= cpu
->next_timer
;
201 while (prev_timer
->next
&& (prev_timer
->next
->targettime
< timer
->targettime
))
203 prev_timer
= prev_timer
->next
;
205 timer
->next
= prev_timer
->next
;
206 prev_timer
->next
= timer
;
210 keSetExecutionLevel(oldlevel
);
213 int keCancelTimer(KeTimer
*timer
)
216 return KE_ERROR_UNKNOWN
;
218 int keAdjustTimer(KeTimer
*timer
, uint64_t time
)
220 // TODO: Currently only works for timers on the current CPU!
221 if (!timer
->queued
) return KE_ERROR_UNKNOWN
;
222 if (time
== 0) time
= 1;
224 uint32_t currenttime
= keGetTime();
225 timer
->targettime
= currenttime
+ time
;
226 // TODO: Move back in the queue if needed
227 if (timer
->active
) keAPICWrite(0x380, ticks_per_usec
* (timer
->targettime
- currenttime
));
231 uint64_t keGetTime(void)
233 KeCPU
*cpu
= keGetCurrentCPU();
234 if (!cpu
->next_timer
|| !cpu
->next_timer
->active
) return cpu
->time
;
235 uint32_t tickcount
= keAPICRead(0x390);
236 return cpu
->next_timer
->targettime
- tickcount
/ ticks_per_usec
;
238 uint32_t keGetTimePerTick(void)
243 static uint8_t keReadCMOS(uint8_t index
)
245 uint8_t indexbyte
= inb(0x70);
246 indexbyte
= (indexbyte
& 0x80) | (index
& 0x7F);
247 outb(0x70, indexbyte
);
252 static unsigned int days
[12] = {
257 31 + 28 + 31 + 30 + 31,
258 31 + 28 + 31 + 30 + 31 + 30,
259 31 + 28 + 31 + 30 + 31 + 30 + 31,
260 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
261 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
262 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
263 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
264 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31
267 static int keIsLeapYear(unsigned int year
)
269 if (year
% 400 == 0) return 1;
270 if (year
% 100 == 0) return 0;
271 if (year
% 4 == 0) return 1;
275 uint32_t keGetUnixTime(void)
278 uint32_t seconds
= keReadCMOS(0x0);
279 uint32_t minutes
= keReadCMOS(0x2);
280 uint32_t hours
= keReadCMOS(0x4);
281 uint32_t day
= keReadCMOS(0x7);
282 uint32_t month
= keReadCMOS(0x8);
283 uint32_t year
= keReadCMOS(0x9);
284 uint32_t century
= keReadCMOS(0x32);
286 seconds
= (seconds
& 0xF) + (seconds
>> 4) * 10;
287 minutes
= (minutes
& 0xF) + (minutes
>> 4) * 10;
288 hours
= (hours
& 0xF) + (hours
>> 4) * 10;
289 day
= (day
& 0xF) + (day
>> 4) * 10;
290 month
= (month
& 0xF) + (month
>> 4) * 10;
291 year
= (year
& 0xF) + (year
>> 4) * 10;
292 century
= (century
& 0xF) + (century
>> 4) * 10;
293 year
+= century
* 100;
297 day
+= days
[month
- 2];
298 if ((month
> 2) && keIsLeapYear(year
))
301 int time
= seconds
+ minutes
* 60 + hours
* 3600 + day
* 86400
302 + (year
- 70) * 31536000 + ((year
- 69) / 4) * 86400
303 - ((year
- 1) / 100) * 86400 + ((year
+ 299) / 400) * 86400;