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 */
22static int apic_timer_get_priority();
23static status_t apic_timer_set_hardware_timer(bigtime_t relativeTimeout);
24static status_t apic_timer_clear_hardware_timer();
25static status_t apic_timer_init(struct kernel_args *args);
26
27static uint32 sApicTicsPerSec = 0;
28
29struct 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
38static int
39apic_timer_get_priority()
40{
41	return 3;
42}
43
44
45static int32
46apic_timer_interrupt(void *data)
47{
48	return timer_interrupt();
49}
50
51
52#define MIN_TIMEOUT 1
53
54static status_t
55apic_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
84static status_t
85apic_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
100static status_t
101apic_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
117status_t
118apic_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