1/*
2 * Copyright 2010, Michael Lotz, mmlr@mlotz.ch. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Copyright 2002-2005, Axel D��rfler, axeld@pinc-software.de. All rights reserved.
6 * Distributed under the terms of the MIT License.
7 *
8 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
9 * Distributed under the terms of the NewOS License.
10 */
11
12#include <arch/x86/apic.h>
13#include <arch/x86/msi.h>
14
15#include <debug.h>
16#include <vm/vm.h>
17
18#include "timers/apic_timer.h"
19
20
21static void *sLocalAPIC = NULL;
22
23
24bool
25apic_available()
26{
27	return sLocalAPIC != NULL;
28}
29
30
31uint32
32apic_read(uint32 offset)
33{
34	return *(volatile uint32 *)((char *)sLocalAPIC + offset);
35}
36
37
38void
39apic_write(uint32 offset, uint32 data)
40{
41	*(volatile uint32 *)((char *)sLocalAPIC + offset) = data;
42}
43
44
45uint32
46apic_local_id()
47{
48	return (apic_read(APIC_ID) & 0xffffffff) >> 24;
49}
50
51
52void
53apic_end_of_interrupt()
54{
55	apic_write(APIC_EOI, 0);
56}
57
58
59void
60apic_disable_local_ints()
61{
62	// just clear them out completely
63	apic_write(APIC_LVT_LINT0, APIC_LVT_MASKED);
64	apic_write(APIC_LVT_LINT1, APIC_LVT_MASKED);
65}
66
67
68status_t
69apic_init(kernel_args *args)
70{
71	if (args->arch_args.apic == NULL)
72		return B_NO_INIT;
73
74	sLocalAPIC = args->arch_args.apic;
75	dprintf("mapping local apic at %p\n", sLocalAPIC);
76	if (vm_map_physical_memory(B_SYSTEM_TEAM, "local apic", &sLocalAPIC,
77			B_EXACT_ADDRESS, B_PAGE_SIZE,
78			B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
79			args->arch_args.apic_phys, true) < 0) {
80		panic("mapping the local apic failed");
81		return B_ERROR;
82	}
83
84	return B_OK;
85}
86
87
88status_t
89apic_per_cpu_init(kernel_args *args, int32 cpu)
90{
91	dprintf("setting up apic for CPU %" B_PRId32 ": apic id %" B_PRIu32 ", "
92		"version %" B_PRIu32 "\n", cpu, apic_local_id(),
93		apic_read(APIC_VERSION));
94
95	/* set spurious interrupt vector to 0xff */
96	uint32 config = apic_read(APIC_SPURIOUS_INTR_VECTOR) & 0xffffff00;
97	config |= APIC_ENABLE | 0xff;
98	apic_write(APIC_SPURIOUS_INTR_VECTOR, config);
99
100	// don't touch the LINT0/1 configuration in virtual wire mode
101	// ToDo: implement support for other modes...
102#if 0
103	if (cpu == 0) {
104		/* setup LINT0 as ExtINT */
105		config = (apic_read(APIC_LINT0) & 0xffff00ff);
106		config |= APIC_LVT_DM_ExtINT | APIC_LVT_IIPP | APIC_LVT_TM;
107		apic_write(APIC_LINT0, config);
108
109		/* setup LINT1 as NMI */
110		config = (apic_read(APIC_LINT1) & 0xffff00ff);
111		config |= APIC_LVT_DM_NMI | APIC_LVT_IIPP;
112		apic_write(APIC_LINT1, config);
113	}
114	if (cpu > 0) {
115		dprintf("LINT0: %p\n", (void *)apic_read(APIC_LINT0));
116		dprintf("LINT1: %p\n", (void *)apic_read(APIC_LINT1));
117
118		/* disable LINT0/1 */
119		config = apic_read(APIC_LINT0);
120		apic_write(APIC_LINT0, config | APIC_LVT_MASKED);
121
122		config = apic_read(APIC_LINT1);
123		apic_write(APIC_LINT1, config | APIC_LVT_MASKED);
124	} else {
125		dprintf("0: LINT0: %p\n", (void *)apic_read(APIC_LINT0));
126		dprintf("0: LINT1: %p\n", (void *)apic_read(APIC_LINT1));
127	}
128#endif
129
130	apic_timer_per_cpu_init(args, cpu);
131
132	/* setup error vector to 0xfe */
133	config = (apic_read(APIC_LVT_ERROR) & 0xffffff00) | 0xfe;
134	apic_write(APIC_LVT_ERROR, config);
135
136	/* accept all interrupts */
137	config = apic_read(APIC_TASK_PRIORITY) & 0xffffff00;
138	apic_write(APIC_TASK_PRIORITY, config);
139
140	config = apic_read(APIC_SPURIOUS_INTR_VECTOR);
141	apic_end_of_interrupt();
142
143	return B_OK;
144}
145