1/**
2 * @file op_apic.c
3 *
4 * APIC setup etc. routines
5 *
6 * @remark Copyright 2002 OProfile authors
7 * @remark Read the file COPYING
8 *
9 * @author John Levon
10 * @author Philippe Elie
11 * @author Dave Jones
12 * @author Graydon Hoare
13 */
14
15#include <linux/mm.h>
16#include <linux/init.h>
17#include <linux/config.h>
18#include <asm/io.h>
19
20#include "oprofile.h"
21#include "op_msr.h"
22#include "op_apic.h"
23
24/* used to save/restore original kernel nmi */
25static struct gate_struct kernel_nmi;
26static ulong lvtpc_masked;
27
28/* this masking code is unsafe and nasty but might deal with the small
29 * race when installing the NMI entry into the IDT.
30 */
31static void mask_lvtpc(void * e)
32{
33	u32 v = apic_read(APIC_LVTPC);
34	lvtpc_masked = v & APIC_LVT_MASKED;
35	apic_write(APIC_LVTPC, v | APIC_LVT_MASKED);
36}
37
38static void unmask_lvtpc(void * e)
39{
40	if (!lvtpc_masked)
41		apic_write(APIC_LVTPC, apic_read(APIC_LVTPC) & ~APIC_LVT_MASKED);
42}
43
44
45void install_nmi(void)
46{
47	struct _descr descr;
48
49	/* NMI handler is at idt_table[IDT_VECTOR_NUMBER]            */
50	/* see Intel Vol.3 Figure 5-2, interrupt gate                */
51
52	smp_call_function(mask_lvtpc, NULL, 0, 1);
53	mask_lvtpc(NULL);
54
55	store_idt(descr);
56	kernel_nmi = descr.base[NMI_VECTOR_NUM];
57	SET_NMI_GATE;
58
59	smp_call_function(unmask_lvtpc, NULL, 0, 1);
60	unmask_lvtpc(NULL);
61}
62
63void restore_nmi(void)
64{
65	struct _descr descr;
66
67	smp_call_function(mask_lvtpc, NULL, 0, 1);
68	mask_lvtpc(NULL);
69
70	store_idt(descr);
71	descr.base[NMI_VECTOR_NUM] = kernel_nmi;
72
73	smp_call_function(unmask_lvtpc, NULL, 0, 1);
74	unmask_lvtpc(NULL);
75}
76
77
78/* ---------------- APIC setup ------------------ */
79static uint saved_lvtpc[NR_CPUS];
80
81void __init lvtpc_apic_setup(void * dummy)
82{
83	uint val;
84
85	/* set up LVTPC as we need it */
86	/* IA32 V3, Figure 7.8 */
87	val = apic_read(APIC_LVTPC);
88	saved_lvtpc[op_cpu_id()] = val;
89	/* allow PC overflow interrupts */
90	val &= ~APIC_LVT_MASKED;
91	/* set delivery to NMI */
92	val = SET_APIC_DELIVERY_MODE(val, APIC_MODE_NMI);
93	apic_write(APIC_LVTPC, val);
94}
95
96/* not safe to mark as __exit since used from __init code */
97void lvtpc_apic_restore(void * dummy)
98{
99	/* restoring APIC_LVTPC can trigger an apic error because the delivery
100	 * mode and vector nr combination can be illegal. That's by design: on
101	 * power on apic lvt contain a zero vector nr which are legal only for
102	 * NMI delivrey mode. So inhibit apic err before restoring lvtpc
103	 */
104	uint v = apic_read(APIC_LVTERR);
105	apic_write(APIC_LVTERR, v | APIC_LVT_MASKED);
106	apic_write(APIC_LVTPC, saved_lvtpc[op_cpu_id()]);
107	apic_write(APIC_LVTERR, v);
108}
109
110static int __init enable_apic(void)
111{
112	uint msr_low, msr_high;
113	uint val;
114
115	/* enable local APIC via MSR. Forgetting this is a fun way to
116	 * lock the box. But we have to hope this is allowed if the APIC
117	 * has already been enabled.
118	 *
119	 * IA32 V3, 7.4.2 */
120	rdmsr(MSR_IA32_APICBASE, msr_low, msr_high);
121	if ((msr_low & (1 << 11)) == 0)
122		wrmsr(MSR_IA32_APICBASE, msr_low | (1 << 11), msr_high);
123
124	/* even if the apic is up we must check for a good APIC */
125
126	/* IA32 V3, 7.4.15 */
127	val = apic_read(APIC_LVR);
128	if (!APIC_INTEGRATED(GET_APIC_VERSION(val)))
129		goto not_local_apic;
130
131	/* LVT0,LVT1,LVTT,LVTPC */
132	if (GET_APIC_MAXLVT(apic_read(APIC_LVR)) < 4)
133		goto not_local_apic;
134
135	/* IA32 V3, 7.4.14.1 */
136	val = apic_read(APIC_SPIV);
137	if (!(val & APIC_SPIV_APIC_ENABLED))
138		apic_write(APIC_SPIV, val | APIC_SPIV_APIC_ENABLED);
139
140	return !!(val & APIC_SPIV_APIC_ENABLED);
141
142not_local_apic:
143	/* disable the apic only if it was disabled */
144	if ((msr_low & (1 << 11)) == 0)
145		wrmsr(MSR_IA32_APICBASE, msr_low & ~(1 << 11), msr_high);
146
147	printk(KERN_ERR "oprofile: no suitable local APIC. Falling back to RTC mode.\n");
148	return -ENODEV;
149}
150
151static void __init do_apic_setup(void)
152{
153	uint val;
154
155	local_irq_disable();
156
157	val = APIC_LVT_LEVEL_TRIGGER;
158	val = SET_APIC_DELIVERY_MODE(val, APIC_MODE_EXINT);
159	apic_write(APIC_LVT0, val);
160
161	/* edge triggered, IA 7.4.11 */
162	val = SET_APIC_DELIVERY_MODE(0, APIC_MODE_NMI);
163	apic_write(APIC_LVT1, val);
164
165	/* clear error register */
166	/* IA32 V3, 7.4.17 */
167	/* PHE must be cleared after unmasking by a back-to-back write,
168	 * but it is probably ok because we mask only, the ESR is not
169	 * updated is this a real problem ? */
170	apic_write(APIC_ESR, 0);
171
172	/* mask error interrupt */
173	/* IA32 V3, Figure 7.8 */
174	val = apic_read(APIC_LVTERR);
175	val |= APIC_LVT_MASKED;
176	apic_write(APIC_LVTERR, val);
177
178	/* setup timer vector */
179	/* IA32 V3, 7.4.8 */
180	apic_write(APIC_LVTT, APIC_SEND_PENDING | 0x31);
181
182	/* Divide configuration register */
183	/* PHE the apic clock is based on the FSB. This should only
184	 * changed with a calibration method.  */
185	val = APIC_TDR_DIV_1;
186	apic_write(APIC_TDCR, val);
187
188	local_irq_enable();
189}
190
191/* does the CPU have a local APIC ? */
192static int __init check_cpu_ok(void)
193{
194	if (sysctl.cpu_type != CPU_PPRO &&
195		sysctl.cpu_type != CPU_PII &&
196		sysctl.cpu_type != CPU_PIII &&
197		sysctl.cpu_type != CPU_ATHLON &&
198		sysctl.cpu_type != CPU_HAMMER &&
199		sysctl.cpu_type != CPU_P4 &&
200		sysctl.cpu_type != CPU_P4_HT2)
201		return 0;
202
203	return 1;
204}
205
206int __init apic_setup(void)
207{
208	u32 val;
209
210	if (!check_cpu_ok())
211		goto nodev;
212
213	fixmap_setup();
214
215	switch (enable_apic()) {
216		case 0:
217			do_apic_setup();
218			val = apic_read(APIC_ESR);
219			printk(KERN_INFO "oprofile: enabled local APIC. Err code %.08x\n", val);
220			break;
221		case 1:
222			printk(KERN_INFO "oprofile: APIC was already enabled\n");
223			break;
224		default:
225			goto nodev;
226	}
227
228	lvtpc_apic_setup(NULL);
229	return 0;
230nodev:
231	printk(KERN_WARNING "Your CPU does not have a local APIC, e.g. "
232	       "mobile P6. Falling back to RTC mode.\n");
233	return -ENODEV;
234}
235
236void apic_restore(void)
237{
238	fixmap_restore();
239}
240