xref: /haiku/src/system/kernel/arch/x86/timers/x86_apic.cpp (revision 9760dcae2038d47442f4658c2575844c6cf92c40)
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/arch_apic.h>
15 
16 #include <arch/cpu.h>
17 #include <arch/smp.h>
18 
19 #include "apic.h"
20 
21 
22 /* Method Prototypes */
23 static int apic_get_prio(void);
24 static status_t apic_set_hardware_timer(bigtime_t relativeTimeout);
25 static status_t apic_clear_hardware_timer(void);
26 static status_t apic_init(struct kernel_args *args);
27 
28 static void *sApicPtr = NULL;
29 static uint32 sApicTicsPerSec = 0;
30 
31 extern bool gUsingIOAPIC;
32 
33 struct timer_info gAPICTimer = {
34 	"APIC",
35 	&apic_get_prio,
36 	&apic_set_hardware_timer,
37 	&apic_clear_hardware_timer,
38 	&apic_init
39 };
40 
41 
42 static int
43 apic_get_prio(void)
44 {
45 	return 2;
46 }
47 
48 
49 static uint32
50 _apic_read(uint32 offset)
51 {
52 	return *(volatile uint32 *)((char *)sApicPtr + offset);
53 }
54 
55 
56 static void
57 _apic_write(uint32 offset, uint32 data)
58 {
59 	*(volatile uint32 *)((char *)sApicPtr + offset) = data;
60 }
61 
62 
63 static int32
64 apic_timer_interrupt(void *data)
65 {
66 	// if we are not using the IO APIC we need to acknowledge the
67 	// interrupt ourselfs
68 	if (!gUsingIOAPIC)
69 		_apic_write(APIC_EOI, 0);
70 
71 	return timer_interrupt();
72 }
73 
74 
75 #define MIN_TIMEOUT 1
76 
77 static status_t
78 apic_set_hardware_timer(bigtime_t relativeTimeout)
79 {
80 	cpu_status state;
81 	uint32 config;
82 	uint32 ticks;
83 
84 	if (sApicPtr == NULL)
85 		return B_ERROR;
86 
87 	if (relativeTimeout < MIN_TIMEOUT)
88 		relativeTimeout = MIN_TIMEOUT;
89 
90 	// calculation should be ok, since it's going to be 64-bit
91 	ticks = ((relativeTimeout * sApicTicsPerSec) / 1000000);
92 
93 	state = disable_interrupts();
94 
95 	config = _apic_read(APIC_LVT_TIMER) | APIC_LVT_MASKED; // mask the timer
96 	_apic_write(APIC_LVT_TIMER, config);
97 
98 	_apic_write(APIC_INITIAL_TIMER_COUNT, 0); // zero out the timer
99 
100 	config = _apic_read(APIC_LVT_TIMER) & ~APIC_LVT_MASKED; // unmask the timer
101 	_apic_write(APIC_LVT_TIMER, config);
102 
103 	//TRACE_TIMER(("arch_smp_set_apic_timer: config 0x%lx, timeout %Ld, tics/sec %lu, tics %lu\n",
104 	//	config, relativeTimeout, sApicTicsPerSec, ticks));
105 
106 	_apic_write(APIC_INITIAL_TIMER_COUNT, ticks); // start it up
107 
108 	restore_interrupts(state);
109 
110 	return B_OK;
111 }
112 
113 
114 static status_t
115 apic_clear_hardware_timer(void)
116 {
117 	cpu_status state;
118 	uint32 config;
119 
120 	if (sApicPtr == NULL)
121 		return B_ERROR;
122 
123 	state = disable_interrupts();
124 
125 	config = _apic_read(APIC_LVT_TIMER) | APIC_LVT_MASKED; // mask the timer
126 	_apic_write(APIC_LVT_TIMER, config);
127 
128 	_apic_write(APIC_INITIAL_TIMER_COUNT, 0); // zero out the timer
129 
130 	restore_interrupts(state);
131 	return B_OK;
132 }
133 
134 
135 static status_t
136 apic_init(struct kernel_args *args)
137 {
138 	/* If we're in this method, arch_smp called the special init function.
139 	   Therefore, if we got here with sApicPtr NULL, there is no APIC! */
140 	if (sApicPtr == NULL)
141 		return B_ERROR;
142 
143 	return B_OK;
144 }
145 
146 
147 status_t
148 apic_init_timer(struct kernel_args *args, int32 cpu)
149 {
150 	uint32 config;
151 
152 	if (args->arch_args.apic == NULL) {
153 		return B_ERROR;
154 	}
155 
156 	/* This is in place of apic_preinit; if we're not already initialized,
157 	   register the interrupt handler and set the pointers */
158 	if (sApicPtr == NULL) {
159 		sApicPtr = (void *)args->arch_args.apic;
160 		sApicTicsPerSec = args->arch_args.apic_time_cv_factor;
161 		install_io_interrupt_handler(0xfb - ARCH_INTERRUPT_BASE, &apic_timer_interrupt, NULL, B_NO_LOCK_VECTOR);
162 	}
163 
164 	/* setup timer */
165 	config = _apic_read(APIC_LVT_TIMER) & APIC_LVT_TIMER_MASK;
166 	config |= 0xfb | APIC_LVT_MASKED; // vector 0xfb, timer masked
167 	_apic_write(APIC_LVT_TIMER, config);
168 
169 	_apic_write(APIC_INITIAL_TIMER_COUNT, 0); // zero out the clock
170 
171 	config = _apic_read(APIC_TIMER_DIVIDE_CONFIG) & 0xfffffff0;
172 	config |= APIC_TIMER_DIVIDE_CONFIG_1; // clock division by 1
173 	_apic_write(APIC_TIMER_DIVIDE_CONFIG, config);
174 
175 	return B_OK;
176 }
177