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