xref: /haiku/src/system/kernel/arch/x86/timers/x86_apic.cpp (revision 37c7d5d83a2372a6971e383411d5bacbeef0ebdc)
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 	install_io_interrupt_handler(0xfb - ARCH_INTERRUPT_BASE,
109 		&apic_timer_interrupt, NULL, B_NO_LOCK_VECTOR);
110 
111 	return B_OK;
112 }
113 
114 
115 status_t
116 apic_timer_per_cpu_init(struct kernel_args *args, int32 cpu)
117 {
118 	/* setup timer */
119 	uint32 config = apic_read(APIC_LVT_TIMER) & APIC_LVT_TIMER_MASK;
120 	config |= 0xfb | APIC_LVT_MASKED; // vector 0xfb, timer masked
121 	apic_write(APIC_LVT_TIMER, config);
122 
123 	apic_write(APIC_INITIAL_TIMER_COUNT, 0); // zero out the clock
124 
125 	config = apic_read(APIC_TIMER_DIVIDE_CONFIG) & 0xfffffff0;
126 	config |= APIC_TIMER_DIVIDE_CONFIG_1; // clock division by 1
127 	apic_write(APIC_TIMER_DIVIDE_CONFIG, config);
128 	return B_OK;
129 }
130