xref: /haiku/src/system/kernel/arch/x86/timers/x86_apic.cpp (revision 38c7ed7c476e4a985fee9129c8150a85237d362e)
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_priority();
24 static status_t apic_set_hardware_timer(bigtime_t relativeTimeout);
25 static status_t apic_clear_hardware_timer();
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_priority,
36 	&apic_set_hardware_timer,
37 	&apic_clear_hardware_timer,
38 	&apic_init
39 };
40 
41 
42 static int
43 apic_get_priority()
44 {
45 	return 3;
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 	if (sApicPtr == NULL)
81 		return B_ERROR;
82 
83 	if (relativeTimeout < MIN_TIMEOUT)
84 		relativeTimeout = MIN_TIMEOUT;
85 
86 	// calculation should be ok, since it's going to be 64-bit
87 	uint32 ticks = ((relativeTimeout * sApicTicsPerSec) / 1000000);
88 
89 	cpu_status state = disable_interrupts();
90 
91 	uint32 config = _apic_read(APIC_LVT_TIMER) | APIC_LVT_MASKED; // 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 	config = _apic_read(APIC_LVT_TIMER) & ~APIC_LVT_MASKED; // unmask the timer
97 	_apic_write(APIC_LVT_TIMER, config);
98 
99 	//TRACE_TIMER(("arch_smp_set_apic_timer: config 0x%lx, timeout %Ld, tics/sec %lu, tics %lu\n",
100 	//	config, relativeTimeout, sApicTicsPerSec, ticks));
101 
102 	_apic_write(APIC_INITIAL_TIMER_COUNT, ticks); // start it up
103 
104 	restore_interrupts(state);
105 
106 	return B_OK;
107 }
108 
109 
110 static status_t
111 apic_clear_hardware_timer()
112 {
113 	if (sApicPtr == NULL)
114 		return B_ERROR;
115 
116 	cpu_status state = disable_interrupts();
117 
118 	uint32 config = _apic_read(APIC_LVT_TIMER) | APIC_LVT_MASKED;
119 		// mask the timer
120 	_apic_write(APIC_LVT_TIMER, config);
121 
122 	_apic_write(APIC_INITIAL_TIMER_COUNT, 0); // zero out the timer
123 
124 	restore_interrupts(state);
125 	return B_OK;
126 }
127 
128 
129 static status_t
130 apic_init(struct kernel_args *args)
131 {
132 	/* If we're in this method, arch_smp called the special init function.
133 	   Therefore, if we got here with sApicPtr NULL, there is no APIC! */
134 	if (sApicPtr == NULL)
135 		return B_ERROR;
136 
137 	return B_OK;
138 }
139 
140 
141 status_t
142 apic_init_timer(struct kernel_args *args, int32 cpu)
143 {
144 	if (args->arch_args.apic == NULL)
145 		return B_ERROR;
146 
147 	/* This is in place of apic_preinit; if we're not already initialized,
148 	   register the interrupt handler and set the pointers */
149 	if (sApicPtr == NULL) {
150 		sApicPtr = (void *)args->arch_args.apic;
151 		sApicTicsPerSec = args->arch_args.apic_time_cv_factor;
152 		install_io_interrupt_handler(0xfb - ARCH_INTERRUPT_BASE,
153 			&apic_timer_interrupt, NULL, B_NO_LOCK_VECTOR);
154 	}
155 
156 	/* setup timer */
157 	uint32 config = _apic_read(APIC_LVT_TIMER) & APIC_LVT_TIMER_MASK;
158 	config |= 0xfb | APIC_LVT_MASKED; // vector 0xfb, timer masked
159 	_apic_write(APIC_LVT_TIMER, config);
160 
161 	_apic_write(APIC_INITIAL_TIMER_COUNT, 0); // zero out the clock
162 
163 	config = _apic_read(APIC_TIMER_DIVIDE_CONFIG) & 0xfffffff0;
164 	config |= APIC_TIMER_DIVIDE_CONFIG_1; // clock division by 1
165 	_apic_write(APIC_TIMER_DIVIDE_CONFIG, config);
166 
167 	return B_OK;
168 }
169