xref: /haiku/src/system/kernel/arch/x86/timers/x86_apic.cpp (revision 52f7c9389475e19fc21487b38064b4390eeb6fea)
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 
18 #include "apic_timer.h"
19 
20 
21 //#define TRACE_APIC
22 #ifdef TRACE_APIC
23 #	define TRACE(x...) dprintf("apic: " x)
24 #else
25 #	define TRACE(x...) ;
26 #endif
27 
28 
29 /* Method Prototypes */
30 static int apic_timer_get_priority();
31 static status_t apic_timer_set_hardware_timer(bigtime_t relativeTimeout);
32 static status_t apic_timer_clear_hardware_timer();
33 static status_t apic_timer_init(struct kernel_args *args);
34 
35 static uint32 sApicTicsPerSec = 0;
36 
37 struct timer_info gAPICTimer = {
38 	"APIC",
39 	&apic_timer_get_priority,
40 	&apic_timer_set_hardware_timer,
41 	&apic_timer_clear_hardware_timer,
42 	&apic_timer_init
43 };
44 
45 
46 static int
47 apic_timer_get_priority()
48 {
49 	return 3;
50 }
51 
52 
53 static int32
54 apic_timer_interrupt(void *data)
55 {
56 	return timer_interrupt();
57 }
58 
59 
60 #define MIN_TIMEOUT 1
61 
62 static status_t
63 apic_timer_set_hardware_timer(bigtime_t relativeTimeout)
64 {
65 	if (relativeTimeout < MIN_TIMEOUT)
66 		relativeTimeout = MIN_TIMEOUT;
67 
68 	// calculation should be ok, since it's going to be 64-bit
69 	uint32 ticks = ((relativeTimeout * sApicTicsPerSec) / 1000000);
70 
71 	cpu_status state = disable_interrupts();
72 
73 	uint32 config = apic_lvt_timer() | APIC_LVT_MASKED; // mask the timer
74 	apic_set_lvt_timer(config);
75 
76 	apic_set_lvt_initial_timer_count(0); // zero out the timer
77 
78 	config = apic_lvt_timer() & ~APIC_LVT_MASKED; // unmask the timer
79 	apic_set_lvt_timer(config);
80 
81 	TRACE("arch_smp_set_apic_timer: config 0x%" B_PRIx32 ", timeout %" B_PRIdBIGTIME
82 		", tics/sec %" B_PRIu32 ", tics %" B_PRId32 "\n", config, relativeTimeout,
83 		sApicTicsPerSec, ticks);
84 
85 	apic_set_lvt_initial_timer_count(ticks); // start it up
86 
87 	restore_interrupts(state);
88 
89 	return B_OK;
90 }
91 
92 
93 static status_t
94 apic_timer_clear_hardware_timer()
95 {
96 	cpu_status state = disable_interrupts();
97 
98 	uint32 config = apic_lvt_timer() | APIC_LVT_MASKED;
99 		// mask the timer
100 	apic_set_lvt_timer(config);
101 
102 	apic_set_lvt_initial_timer_count(0); // zero out the timer
103 
104 	restore_interrupts(state);
105 	return B_OK;
106 }
107 
108 
109 static status_t
110 apic_timer_init(struct kernel_args *args)
111 {
112 	if (!apic_available())
113 		return B_ERROR;
114 
115 	sApicTicsPerSec = args->arch_args.apic_time_cv_factor;
116 
117 	reserve_io_interrupt_vectors(1, 0xfb - ARCH_INTERRUPT_BASE,
118 		INTERRUPT_TYPE_LOCAL_IRQ);
119 	install_io_interrupt_handler(0xfb - ARCH_INTERRUPT_BASE,
120 		&apic_timer_interrupt, NULL, B_NO_LOCK_VECTOR);
121 
122 	return B_OK;
123 }
124 
125 
126 status_t
127 apic_timer_per_cpu_init(struct kernel_args *args, int32 cpu)
128 {
129 	/* setup timer */
130 	uint32 config = apic_lvt_timer() & APIC_LVT_TIMER_MASK;
131 	config |= 0xfb | APIC_LVT_MASKED; // vector 0xfb, timer masked
132 	apic_set_lvt_timer(config);
133 
134 	apic_set_lvt_initial_timer_count(0); // zero out the clock
135 
136 	config = apic_lvt_timer_divide_config() & 0xfffffff0;
137 	config |= APIC_TIMER_DIVIDE_CONFIG_1; // clock division by 1
138 	apic_set_lvt_timer_divide_config(config);
139 	return B_OK;
140 }
141