1259698Sdim/*
2259698Sdim * Copyright 2008, Dustin Howett, dustin.howett@gmail.com. All rights reserved.
3259698Sdim * Copyright 2002-2005, Axel D��rfler, axeld@pinc-software.de. All rights reserved.
4259698Sdim * Distributed under the terms of the MIT License.
5259698Sdim *
6259698Sdim * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
7259698Sdim * Distributed under the terms of the NewOS License.
8259698Sdim */
9259698Sdim
10259698Sdim#include <timer.h>
11259698Sdim#include <arch/x86/timer.h>
12259698Sdim
13259698Sdim#include <int.h>
14259698Sdim#include <arch/x86/apic.h>
15259698Sdim
16259698Sdim#include <arch/cpu.h>
17259698Sdim
18259698Sdim#include "apic_timer.h"
19259698Sdim
20259698Sdim
21259698Sdim//#define TRACE_APIC
22259698Sdim#ifdef TRACE_APIC
23259698Sdim#	define TRACE(x...) dprintf("apic: " x)
24259698Sdim#else
25259698Sdim#	define TRACE(x...) ;
26259698Sdim#endif
27259698Sdim
28259698Sdim
29259698Sdim/* Method Prototypes */
30259698Sdimstatic int apic_timer_get_priority();
31259698Sdimstatic status_t apic_timer_set_hardware_timer(bigtime_t relativeTimeout);
32259698Sdimstatic status_t apic_timer_clear_hardware_timer();
33259698Sdimstatic status_t apic_timer_init(struct kernel_args *args);
34259698Sdim
35259698Sdimstatic uint32 sApicTicsPerSec = 0;
36259698Sdim
37261991Sdimstruct timer_info gAPICTimer = {
38259698Sdim	"APIC",
39259698Sdim	&apic_timer_get_priority,
40259698Sdim	&apic_timer_set_hardware_timer,
41259698Sdim	&apic_timer_clear_hardware_timer,
42259698Sdim	&apic_timer_init
43259698Sdim};
44259698Sdim
45259698Sdim
46259698Sdimstatic int
47259698Sdimapic_timer_get_priority()
48259698Sdim{
49259698Sdim	return 3;
50259698Sdim}
51259698Sdim
52259698Sdim
53259698Sdimstatic int32
54259698Sdimapic_timer_interrupt(void *data)
55259698Sdim{
56259698Sdim	return timer_interrupt();
57259698Sdim}
58259698Sdim
59259698Sdim
60259698Sdim#define MIN_TIMEOUT 1
61259698Sdim
62259698Sdimstatic status_t
63259698Sdimapic_timer_set_hardware_timer(bigtime_t relativeTimeout)
64259698Sdim{
65259698Sdim	if (relativeTimeout < MIN_TIMEOUT)
66259698Sdim		relativeTimeout = MIN_TIMEOUT;
67259698Sdim
68259698Sdim	// calculation should be ok, since it's going to be 64-bit
69259698Sdim	uint32 ticks = ((relativeTimeout * sApicTicsPerSec) / 1000000);
70259698Sdim
71259698Sdim	cpu_status state = disable_interrupts();
72259698Sdim
73259698Sdim	uint32 config = apic_lvt_timer() | APIC_LVT_MASKED; // mask the timer
74259698Sdim	apic_set_lvt_timer(config);
75259698Sdim
76259698Sdim	apic_set_lvt_initial_timer_count(0); // zero out the timer
77259698Sdim
78259698Sdim	config = apic_lvt_timer() & ~APIC_LVT_MASKED; // unmask the timer
79259698Sdim	apic_set_lvt_timer(config);
80259698Sdim
81259698Sdim	TRACE("arch_smp_set_apic_timer: config 0x%" B_PRIx32 ", timeout %" B_PRIdBIGTIME
82259698Sdim		", tics/sec %" B_PRIu32 ", tics %" B_PRId32 "\n", config, relativeTimeout,
83259698Sdim		sApicTicsPerSec, ticks);
84259698Sdim
85259698Sdim	apic_set_lvt_initial_timer_count(ticks); // start it up
86259698Sdim
87259698Sdim	restore_interrupts(state);
88259698Sdim
89259698Sdim	return B_OK;
90259698Sdim}
91259698Sdim
92259698Sdim
93259698Sdimstatic status_t
94259698Sdimapic_timer_clear_hardware_timer()
95259698Sdim{
96259698Sdim	cpu_status state = disable_interrupts();
97259698Sdim
98259698Sdim	uint32 config = apic_lvt_timer() | APIC_LVT_MASKED;
99259698Sdim		// mask the timer
100259698Sdim	apic_set_lvt_timer(config);
101259698Sdim
102259698Sdim	apic_set_lvt_initial_timer_count(0); // zero out the timer
103259698Sdim
104259698Sdim	restore_interrupts(state);
105259698Sdim	return B_OK;
106259698Sdim}
107259698Sdim
108259698Sdim
109259698Sdimstatic status_t
110259698Sdimapic_timer_init(struct kernel_args *args)
111259698Sdim{
112259698Sdim	if (!apic_available())
113259698Sdim		return B_ERROR;
114259698Sdim
115259698Sdim	sApicTicsPerSec = args->arch_args.apic_time_cv_factor;
116259698Sdim
117259698Sdim	reserve_io_interrupt_vectors(1, 0xfb - ARCH_INTERRUPT_BASE,
118259698Sdim		INTERRUPT_TYPE_LOCAL_IRQ);
119259698Sdim	install_io_interrupt_handler(0xfb - ARCH_INTERRUPT_BASE,
120259698Sdim		&apic_timer_interrupt, NULL, B_NO_LOCK_VECTOR);
121259698Sdim
122259698Sdim	return B_OK;
123259698Sdim}
124259698Sdim
125259698Sdim
126259698Sdimstatus_t
127259698Sdimapic_timer_per_cpu_init(struct kernel_args *args, int32 cpu)
128259698Sdim{
129259698Sdim	/* setup timer */
130259698Sdim	uint32 config = apic_lvt_timer() & APIC_LVT_TIMER_MASK;
131259698Sdim	config |= 0xfb | APIC_LVT_MASKED; // vector 0xfb, timer masked
132259698Sdim	apic_set_lvt_timer(config);
133259698Sdim
134259698Sdim	apic_set_lvt_initial_timer_count(0); // zero out the clock
135259698Sdim
136259698Sdim	config = apic_lvt_timer_divide_config() & 0xfffffff0;
137259698Sdim	config |= APIC_TIMER_DIVIDE_CONFIG_1; // clock division by 1
138259698Sdim	apic_set_lvt_timer_divide_config(config);
139259698Sdim	return B_OK;
140259698Sdim}
141259698Sdim