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 */
30static int apic_timer_get_priority();
31static status_t apic_timer_set_hardware_timer(bigtime_t relativeTimeout);
32static status_t apic_timer_clear_hardware_timer();
33static status_t apic_timer_init(struct kernel_args *args);
34
35static uint32 sApicTicsPerSec = 0;
36
37struct 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
46static int
47apic_timer_get_priority()
48{
49	return 3;
50}
51
52
53static int32
54apic_timer_interrupt(void *data)
55{
56	return timer_interrupt();
57}
58
59
60#define MIN_TIMEOUT 1
61
62static status_t
63apic_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
93static status_t
94apic_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
109static status_t
110apic_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
126status_t
127apic_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