aboutsummaryrefslogtreecommitdiff
path: root/arch/x86/cpu/lapic.c
blob: fbea2d157288ef93f5a6e594dc7bec01e3401674 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/*
 * From coreboot file of same name
 *
 * Copyright (C) 2008-2009 coresystems GmbH
 * Copyright (C) 2014 Google, Inc
 *
 * SPDX-License-Identifier:	GPL-2.0
 */

#include <common.h>
#include <asm/io.h>
#include <asm/lapic.h>
#include <asm/msr.h>
#include <asm/msr-index.h>
#include <asm/post.h>

unsigned long lapic_read(unsigned long reg)
{
	return readl(LAPIC_DEFAULT_BASE + reg);
}

#define xchg(ptr, v)	((__typeof__(*(ptr)))__xchg((unsigned long)(v), (ptr), \
						    sizeof(*(ptr))))

struct __xchg_dummy	{ unsigned long a[100]; };
#define __xg(x)		((struct __xchg_dummy *)(x))

/*
 * Note: no "lock" prefix even on SMP. xchg always implies lock anyway.
 *
 * Note 2: xchg has side effect, so that attribute volatile is necessary,
 *         but generally the primitive is invalid, *ptr is output argument.
 */
static inline unsigned long __xchg(unsigned long x, volatile void *ptr,
				   int size)
{
	switch (size) {
	case 1:
		__asm__ __volatile__("xchgb %b0,%1"
			: "=q" (x)
			: "m" (*__xg(ptr)), "0" (x)
			: "memory");
		break;
	case 2:
		__asm__ __volatile__("xchgw %w0,%1"
			: "=r" (x)
			: "m" (*__xg(ptr)), "0" (x)
			: "memory");
		break;
	case 4:
		__asm__ __volatile__("xchgl %0,%1"
			: "=r" (x)
			: "m" (*__xg(ptr)), "0" (x)
			: "memory");
		break;
	}

	return x;
}

void lapic_write(unsigned long reg, unsigned long v)
{
	(void)xchg((volatile unsigned long *)(LAPIC_DEFAULT_BASE + reg), v);
}

void enable_lapic(void)
{
	if (!IS_ENABLED(CONFIG_INTEL_QUARK)) {
		msr_t msr;

		msr = msr_read(MSR_IA32_APICBASE);
		msr.hi &= 0xffffff00;
		msr.lo |= MSR_IA32_APICBASE_ENABLE;
		msr.lo &= ~MSR_IA32_APICBASE_BASE;
		msr.lo |= LAPIC_DEFAULT_BASE;
		msr_write(MSR_IA32_APICBASE, msr);
	}
}

void disable_lapic(void)
{
	if (!IS_ENABLED(CONFIG_INTEL_QUARK)) {
		msr_t msr;

		msr = msr_read(MSR_IA32_APICBASE);
		msr.lo &= ~MSR_IA32_APICBASE_ENABLE;
		msr_write(MSR_IA32_APICBASE, msr);
	}
}

unsigned long lapicid(void)
{
	return lapic_read(LAPIC_ID) >> 24;
}

static void lapic_wait_icr_idle(void)
{
	do { } while (lapic_read(LAPIC_ICR) & LAPIC_ICR_BUSY);
}

int lapic_remote_read(int apicid, int reg, unsigned long *pvalue)
{
	int timeout;
	unsigned long status;
	int result;

	lapic_wait_icr_idle();
	lapic_write(LAPIC_ICR2, SET_LAPIC_DEST_FIELD(apicid));
	lapic_write(LAPIC_ICR, LAPIC_DM_REMRD | (reg >> 4));

	timeout = 0;
	do {
		status = lapic_read(LAPIC_ICR) & LAPIC_ICR_RR_MASK;
	} while (status == LAPIC_ICR_RR_INPROG && timeout++ < 1000);

	result = -1;
	if (status == LAPIC_ICR_RR_VALID) {
		*pvalue = lapic_read(LAPIC_RRR);
		result = 0;
	}

	return result;
}

void lapic_setup(void)
{
	/* Only Pentium Pro and later have those MSR stuff */
	debug("Setting up local apic: ");

	/* Enable the local apic */
	enable_lapic();

	/* Set Task Priority to 'accept all' */
	lapic_write(LAPIC_TASKPRI,
		    lapic_read(LAPIC_TASKPRI) & ~LAPIC_TPRI_MASK);

	/* Put the local apic in virtual wire mode */
	lapic_write(LAPIC_SPIV, (lapic_read(LAPIC_SPIV) &
		    ~(LAPIC_VECTOR_MASK)) | LAPIC_SPIV_ENABLE);
	lapic_write(LAPIC_LVT0, (lapic_read(LAPIC_LVT0) &
		    ~(LAPIC_LVT_MASKED | LAPIC_LVT_LEVEL_TRIGGER |
		    LAPIC_LVT_REMOTE_IRR | LAPIC_INPUT_POLARITY |
		    LAPIC_SEND_PENDING | LAPIC_LVT_RESERVED_1 |
		    LAPIC_DELIVERY_MODE_MASK)) |
		    (LAPIC_LVT_REMOTE_IRR | LAPIC_SEND_PENDING |
		    LAPIC_DELIVERY_MODE_EXTINT));
	lapic_write(LAPIC_LVT1, (lapic_read(LAPIC_LVT1) &
		    ~(LAPIC_LVT_MASKED | LAPIC_LVT_LEVEL_TRIGGER |
		    LAPIC_LVT_REMOTE_IRR | LAPIC_INPUT_POLARITY |
		    LAPIC_SEND_PENDING | LAPIC_LVT_RESERVED_1 |
		    LAPIC_DELIVERY_MODE_MASK)) |
		    (LAPIC_LVT_REMOTE_IRR | LAPIC_SEND_PENDING |
		    LAPIC_DELIVERY_MODE_NMI));

	debug("apic_id: 0x%02lx, ", lapicid());

	debug("done.\n");
	post_code(POST_LAPIC);
}