xref: /haiku/src/system/kernel/arch/x86/timers/x86_apic.cpp (revision e2932f63b00ba76ab3769a9c217754a4d03868ca)
1 /*
2  * Copyright 2008, Dustin Howett, dustin.howett@gmail.com. All rights reserved.
3  * Copyright 2002-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
4  * Distributed under the terms of the MIT License.
5  *
6  * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
7  * Distributed under the terms of the NewOS License.
8  */
9 
10 #include <timer.h>
11 #include <arch/x86/timer.h>
12 
13 #include <int.h>
14 #include <arch/x86/apic.h>
15 
16 #include <arch/cpu.h>
17 #include <arch/smp.h>
18 
19 #include "apic_timer.h"
20 
21 
22 /* Method Prototypes */
23 static int apic_timer_get_priority();
24 static status_t apic_timer_set_hardware_timer(bigtime_t relativeTimeout);
25 static status_t apic_timer_clear_hardware_timer();
26 static status_t apic_timer_init(struct kernel_args *args);
27 
28 static uint32 sApicTicsPerSec = 0;
29 
30 struct timer_info gAPICTimer = {
31 	"APIC",
32 	&apic_timer_get_priority,
33 	&apic_timer_set_hardware_timer,
34 	&apic_timer_clear_hardware_timer,
35 	&apic_timer_init
36 };
37 
38 
39 static int
40 apic_timer_get_priority()
41 {
42 	return 3;
43 }
44 
45 
46 static int32
47 apic_timer_interrupt(void *data)
48 {
49 	return timer_interrupt();
50 }
51 
52 
53 #define MIN_TIMEOUT 1
54 
55 static status_t
56 apic_timer_set_hardware_timer(bigtime_t relativeTimeout)
57 {
58 	if (relativeTimeout < MIN_TIMEOUT)
59 		relativeTimeout = MIN_TIMEOUT;
60 
61 	// calculation should be ok, since it's going to be 64-bit
62 	uint32 ticks = ((relativeTimeout * sApicTicsPerSec) / 1000000);
63 
64 	cpu_status state = disable_interrupts();
65 
66 	uint32 config = apic_read(APIC_LVT_TIMER) | APIC_LVT_MASKED; // mask the timer
67 	apic_write(APIC_LVT_TIMER, config);
68 
69 	apic_write(APIC_INITIAL_TIMER_COUNT, 0); // zero out the timer
70 
71 	config = apic_read(APIC_LVT_TIMER) & ~APIC_LVT_MASKED; // unmask the timer
72 	apic_write(APIC_LVT_TIMER, config);
73 
74 	//TRACE_TIMER(("arch_smp_set_apic_timer: config 0x%lx, timeout %Ld, tics/sec %lu, tics %lu\n",
75 	//	config, relativeTimeout, sApicTicsPerSec, ticks));
76 
77 	apic_write(APIC_INITIAL_TIMER_COUNT, ticks); // start it up
78 
79 	restore_interrupts(state);
80 
81 	return B_OK;
82 }
83 
84 
85 static status_t
86 apic_timer_clear_hardware_timer()
87 {
88 	cpu_status state = disable_interrupts();
89 
90 	uint32 config = apic_read(APIC_LVT_TIMER) | APIC_LVT_MASKED;
91 		// mask the timer
92 	apic_write(APIC_LVT_TIMER, config);
93 
94 	apic_write(APIC_INITIAL_TIMER_COUNT, 0); // zero out the timer
95 
96 	restore_interrupts(state);
97 	return B_OK;
98 }
99 
100 
101 static status_t
102 apic_timer_init(struct kernel_args *args)
103 {
104 	if (!apic_available())
105 		return B_ERROR;
106 
107 	sApicTicsPerSec = args->arch_args.apic_time_cv_factor;
108 
109 	reserve_io_interrupt_vectors(1, 0xfb - ARCH_INTERRUPT_BASE);
110 	install_io_interrupt_handler(0xfb - ARCH_INTERRUPT_BASE,
111 		&apic_timer_interrupt, NULL, B_NO_LOCK_VECTOR);
112 
113 	return B_OK;
114 }
115 
116 
117 status_t
118 apic_timer_per_cpu_init(struct kernel_args *args, int32 cpu)
119 {
120 	/* setup timer */
121 	uint32 config = apic_read(APIC_LVT_TIMER) & APIC_LVT_TIMER_MASK;
122 	config |= 0xfb | APIC_LVT_MASKED; // vector 0xfb, timer masked
123 	apic_write(APIC_LVT_TIMER, config);
124 
125 	apic_write(APIC_INITIAL_TIMER_COUNT, 0); // zero out the clock
126 
127 	config = apic_read(APIC_TIMER_DIVIDE_CONFIG) & 0xfffffff0;
128 	config |= APIC_TIMER_DIVIDE_CONFIG_1; // clock division by 1
129 	apic_write(APIC_TIMER_DIVIDE_CONFIG, config);
130 	return B_OK;
131 }
132