acf02fdba4c6e49c11c00e47c854c3c37b2f7a8b
[monolithium.git] / kernel / src / timer.c
1 /*
2  * timer.c
3  *
4  * Copyright (C) 2013 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as
8  * published by the Free Software Foundation, either version 3 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <timer.h>
21 #include <thread.h>
22
23 volatile qword_t total_ticks = 0;
24
25 void timer_irq(registers_t *regs, byte_t irq_num)
26 {
27     total_ticks++;
28
29     if (scheduler_enabled)
30     {
31         scheduler(regs);
32         get_current_thread()->running_ticks++;
33     }
34 }
35
36 sysret_t syscall_get_milliseconds(void)
37 {
38     return total_ticks;
39 }
40
41 sysret_t syscall_get_nanoseconds(void)
42 {
43     critical_t critical;
44     enter_critical(&critical);
45
46     qword_t result = total_ticks * 1000000ULL;
47
48     cpu_write_port_byte(TIMER_CMD_PORT, 0x00);
49     word_t count = cpu_read_port_byte(TIMER_CHANNEL_PORT(0));
50     count |= cpu_read_port_byte(TIMER_CHANNEL_PORT(0)) << 8;
51     count = -count;
52
53     result += (count * 1000ULL) / (qword_t)TIMER_BASE_FREQUENCY;
54
55     leave_critical(&critical);
56     return result;
57 }
58
59 void timer_init(void)
60 {
61     dword_t value = TIMER_BASE_FREQUENCY / TIMER_FREQUENCY;
62     dword_t remainder = TIMER_BASE_FREQUENCY - value * TIMER_FREQUENCY;
63
64     if ((remainder * 2) > TIMER_FREQUENCY) value++;
65     if (value == 0) value = 1;
66     if (value >= 65536) value = 0;
67
68     cpu_write_port_byte(TIMER_CMD_PORT, TIMER_RATE_GENERATOR(0));
69     cpu_write_port_byte(TIMER_CHANNEL_PORT(0), value & 0xFF);
70     cpu_write_port_byte(TIMER_CHANNEL_PORT(0), (value >> 8) & 0xFF);
71     register_irq_handler(TIMER_IRQ, &timer_irq, FALSE);
72 }