1 /*
2 * Copyright (c) 1984-2008, 2011-2015 Wind River Systems, Inc.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6
7 #define DT_DRV_COMPAT intel_loapic
8
9 /*
10 * driver for x86 CPU local APIC (as an interrupt controller)
11 */
12
13 #include <zephyr/kernel.h>
14 #include <zephyr/kernel_structs.h>
15 #include <zephyr/arch/cpu.h>
16 #include <zephyr/pm/device.h>
17 #include <zephyr/types.h>
18 #include <string.h>
19 #include <zephyr/sys/__assert.h>
20 #include <zephyr/arch/x86/msr.h>
21
22 #include <zephyr/toolchain.h>
23 #include <zephyr/linker/sections.h>
24 #include <zephyr/drivers/interrupt_controller/loapic.h> /* public API declarations */
25 #include <zephyr/device.h>
26 #include <zephyr/drivers/interrupt_controller/sysapic.h>
27 #include <zephyr/drivers/interrupt_controller/ioapic.h>
28
29 /* Local APIC Version Register Bits */
30
31 #define LOAPIC_VERSION_MASK 0x000000ff /* LO APIC Version mask */
32 #define LOAPIC_MAXLVT_MASK 0x00ff0000 /* LO APIC Max LVT mask */
33 #define LOAPIC_PENTIUM4 0x00000014 /* LO APIC in Pentium4 */
34 #define LOAPIC_LVT_PENTIUM4 5 /* LO APIC LVT - Pentium4 */
35 #define LOAPIC_LVT_P6 4 /* LO APIC LVT - P6 */
36 #define LOAPIC_LVT_P5 3 /* LO APIC LVT - P5 */
37
38 /* Local APIC Vector Table Bits */
39
40 #define LOAPIC_VECTOR 0x000000ff /* vectorNo */
41 #define LOAPIC_MODE 0x00000700 /* delivery mode */
42 #define LOAPIC_FIXED 0x00000000 /* delivery mode: FIXED */
43 #define LOAPIC_SMI 0x00000200 /* delivery mode: SMI */
44 #define LOAPIC_NMI 0x00000400 /* delivery mode: NMI */
45 #define LOAPIC_EXT 0x00000700 /* delivery mode: ExtINT */
46 #define LOAPIC_IDLE 0x00000000 /* delivery status: Idle */
47 #define LOAPIC_PEND 0x00001000 /* delivery status: Pend */
48 #define LOAPIC_HIGH 0x00000000 /* polarity: High */
49 #define LOAPIC_LOW 0x00002000 /* polarity: Low */
50 #define LOAPIC_REMOTE 0x00004000 /* remote IRR */
51 #define LOAPIC_EDGE 0x00000000 /* trigger mode: Edge */
52 #define LOAPIC_LEVEL 0x00008000 /* trigger mode: Level */
53
54 /* Local APIC Spurious-Interrupt Register Bits */
55
56 #define LOAPIC_ENABLE 0x100 /* APIC Enabled */
57 #define LOAPIC_FOCUS_DISABLE 0x200 /* Focus Processor Checking */
58
59 #if CONFIG_LOAPIC_SPURIOUS_VECTOR_ID == -1
60 #define LOAPIC_SPURIOUS_VECTOR_ID (CONFIG_IDT_NUM_VECTORS - 1)
61 #else
62 #define LOAPIC_SPURIOUS_VECTOR_ID CONFIG_LOAPIC_SPURIOUS_VECTOR_ID
63 #endif
64
65 #define LOAPIC_SSPND_BITS_PER_IRQ 1 /* Just the one for enable disable*/
66 #define LOAPIC_SUSPEND_BITS_REQD (ROUND_UP((LOAPIC_IRQ_COUNT * LOAPIC_SSPND_BITS_PER_IRQ), 32))
67 #ifdef CONFIG_PM_DEVICE
68 __pinned_bss
69 uint32_t loapic_suspend_buf[LOAPIC_SUSPEND_BITS_REQD / 32] = {0};
70 #endif
71
72 DEVICE_MMIO_TOPLEVEL(LOAPIC_REGS_STR, DT_DRV_INST(0));
73
74 __pinned_func
send_eoi(void)75 void send_eoi(void)
76 {
77 x86_write_xapic(LOAPIC_EOI, 0);
78 }
79
80 /**
81 * @brief Enable and initialize the local APIC.
82 *
83 * Called from early assembly layer (e.g., crt0.S).
84 */
85 __pinned_func
z_loapic_enable(unsigned char cpu_number)86 void z_loapic_enable(unsigned char cpu_number)
87 {
88 int32_t loApicMaxLvt; /* local APIC Max LVT */
89 DEVICE_MMIO_TOPLEVEL_MAP(LOAPIC_REGS_STR, K_MEM_CACHE_NONE);
90
91 #ifndef CONFIG_X2APIC
92 /*
93 * in xAPIC and flat model, bits 24-31 in LDR (Logical APIC ID) are
94 * bitmap of target logical APIC ID and it supports maximum 8 local
95 * APICs.
96 *
97 * The logical APIC ID could be arbitrarily selected by system software
98 * and is different from local APIC ID in local APIC ID register.
99 *
100 * We choose 0 for BSP, and the index to x86_cpuboot[] for secondary
101 * CPUs.
102 *
103 * in X2APIC, LDR is read-only.
104 */
105 x86_write_xapic(LOAPIC_LDR, 1 << (cpu_number + 24));
106 #endif
107
108 /*
109 * enable the local APIC. note that we use xAPIC mode here, since
110 * x2APIC access is not enabled until the next step (if at all).
111 */
112
113 x86_write_xapic(LOAPIC_SVR,
114 x86_read_xapic(LOAPIC_SVR) | LOAPIC_ENABLE);
115
116 #ifdef CONFIG_X2APIC
117 /*
118 * turn on x2APIC mode. we trust the config option, so
119 * we don't check CPUID to see if x2APIC is supported.
120 */
121
122 uint64_t msr = z_x86_msr_read(X86_APIC_BASE_MSR);
123 msr |= X86_APIC_BASE_MSR_X2APIC;
124 z_x86_msr_write(X86_APIC_BASE_MSR, msr);
125 #endif
126
127 loApicMaxLvt = (x86_read_loapic(LOAPIC_VER) & LOAPIC_MAXLVT_MASK) >> 16;
128
129 /* reset the DFR, TPR, TIMER_CONFIG, and TIMER_ICR */
130
131 #ifndef CONFIG_X2APIC
132 /* Flat model */
133 x86_write_loapic(LOAPIC_DFR, 0xffffffff); /* no DFR in x2APIC mode */
134 #endif
135
136 x86_write_loapic(LOAPIC_TPR, 0x0);
137 x86_write_loapic(LOAPIC_TIMER_CONFIG, 0x0);
138 x86_write_loapic(LOAPIC_TIMER_ICR, 0x0);
139
140 /* program Local Vector Table for the Virtual Wire Mode */
141
142 /* set LINT0: extInt, high-polarity, edge-trigger, not-masked */
143
144 x86_write_loapic(LOAPIC_LINT0, (x86_read_loapic(LOAPIC_LINT0) &
145 ~(LOAPIC_MODE | LOAPIC_LOW |
146 LOAPIC_LEVEL | LOAPIC_LVT_MASKED)) |
147 (LOAPIC_EXT | LOAPIC_HIGH | LOAPIC_EDGE));
148
149 /* set LINT1: NMI, high-polarity, edge-trigger, not-masked */
150
151 x86_write_loapic(LOAPIC_LINT1, (x86_read_loapic(LOAPIC_LINT1) &
152 ~(LOAPIC_MODE | LOAPIC_LOW |
153 LOAPIC_LEVEL | LOAPIC_LVT_MASKED)) |
154 (LOAPIC_NMI | LOAPIC_HIGH | LOAPIC_EDGE));
155
156 /* lock the Local APIC interrupts */
157
158 x86_write_loapic(LOAPIC_TIMER, LOAPIC_LVT_MASKED);
159 x86_write_loapic(LOAPIC_ERROR, LOAPIC_LVT_MASKED);
160
161 if (loApicMaxLvt >= LOAPIC_LVT_P6) {
162 x86_write_loapic(LOAPIC_PMC, LOAPIC_LVT_MASKED);
163 }
164
165 if (loApicMaxLvt >= LOAPIC_LVT_PENTIUM4) {
166 x86_write_loapic(LOAPIC_THERMAL, LOAPIC_LVT_MASKED);
167 }
168
169 #if CONFIG_LOAPIC_SPURIOUS_VECTOR
170 x86_write_loapic(LOAPIC_SVR, (x86_read_loapic(LOAPIC_SVR) & 0xFFFFFF00) |
171 (LOAPIC_SPURIOUS_VECTOR_ID & 0xFF));
172 #endif
173
174 /* discard a pending interrupt if any */
175 x86_write_loapic(LOAPIC_EOI, 0);
176 }
177
178 /**
179 * @brief Dummy initialization function.
180 *
181 * The local APIC is initialized via z_loapic_enable() long before the
182 * kernel runs through its device initializations, so this is unneeded.
183 */
184 __boot_func
loapic_init(const struct device * unused)185 static int loapic_init(const struct device *unused)
186 {
187 ARG_UNUSED(unused);
188 return 0;
189 }
190
191
192 __pinned_func
z_loapic_irq_base(void)193 uint32_t z_loapic_irq_base(void)
194 {
195 return z_ioapic_num_rtes();
196 }
197
198 /**
199 * @brief Set the vector field in the specified RTE
200 *
201 * This associates an IRQ with the desired vector in the IDT.
202 */
203 __boot_func
z_loapic_int_vec_set(unsigned int irq,unsigned int vector)204 void z_loapic_int_vec_set(unsigned int irq, /* IRQ number of the interrupt */
205 unsigned int vector /* vector to copy into the LVT */
206 )
207 {
208 unsigned int oldLevel; /* previous interrupt lock level */
209
210 /*
211 * The following mappings are used:
212 *
213 * IRQ0 -> LOAPIC_TIMER
214 * IRQ1 -> LOAPIC_THERMAL
215 * IRQ2 -> LOAPIC_PMC
216 * IRQ3 -> LOAPIC_LINT0
217 * IRQ4 -> LOAPIC_LINT1
218 * IRQ5 -> LOAPIC_ERROR
219 *
220 * It's assumed that LVTs are spaced by 0x10 bytes
221 */
222
223 /* update the 'vector' bits in the LVT */
224
225 oldLevel = irq_lock();
226 x86_write_loapic(LOAPIC_TIMER + (irq * 0x10),
227 (x86_read_loapic(LOAPIC_TIMER + (irq * 0x10)) &
228 ~LOAPIC_VECTOR) | vector);
229 irq_unlock(oldLevel);
230 }
231
232 /**
233 * @brief Enable an individual LOAPIC interrupt (IRQ)
234 *
235 * @param irq the IRQ number of the interrupt
236 *
237 * This routine clears the interrupt mask bit in the LVT for the specified IRQ
238 */
239 __pinned_func
z_loapic_irq_enable(unsigned int irq)240 void z_loapic_irq_enable(unsigned int irq)
241 {
242 unsigned int oldLevel; /* previous interrupt lock level */
243
244 /*
245 * See the comments in _LoApicLvtVecSet() regarding IRQ to LVT mappings
246 * and ths assumption concerning LVT spacing.
247 */
248
249 /* clear the mask bit in the LVT */
250
251 oldLevel = irq_lock();
252 x86_write_loapic(LOAPIC_TIMER + (irq * 0x10),
253 x86_read_loapic(LOAPIC_TIMER + (irq * 0x10)) &
254 ~LOAPIC_LVT_MASKED);
255 irq_unlock(oldLevel);
256 }
257
258 /**
259 * @brief Disable an individual LOAPIC interrupt (IRQ)
260 *
261 * @param irq the IRQ number of the interrupt
262 *
263 * This routine clears the interrupt mask bit in the LVT for the specified IRQ
264 */
265 __pinned_func
z_loapic_irq_disable(unsigned int irq)266 void z_loapic_irq_disable(unsigned int irq)
267 {
268 unsigned int oldLevel; /* previous interrupt lock level */
269
270 /*
271 * See the comments in _LoApicLvtVecSet() regarding IRQ to LVT mappings
272 * and ths assumption concerning LVT spacing.
273 */
274
275 /* set the mask bit in the LVT */
276
277 oldLevel = irq_lock();
278 x86_write_loapic(LOAPIC_TIMER + (irq * 0x10),
279 x86_read_loapic(LOAPIC_TIMER + (irq * 0x10)) |
280 LOAPIC_LVT_MASKED);
281 irq_unlock(oldLevel);
282 }
283
284
285 /**
286 * @brief Find the currently executing interrupt vector, if any
287 *
288 * This routine finds the vector of the interrupt that is being processed.
289 * The ISR (In-Service Register) register contain the vectors of the interrupts
290 * in service. And the higher vector is the identification of the interrupt
291 * being currently processed.
292 *
293 * This function must be called with interrupts locked in interrupt context.
294 *
295 * ISR registers' offsets:
296 * --------------------
297 * | Offset | bits |
298 * --------------------
299 * | 0100H | 0:31 |
300 * | 0110H | 32:63 |
301 * | 0120H | 64:95 |
302 * | 0130H | 96:127 |
303 * | 0140H | 128:159 |
304 * | 0150H | 160:191 |
305 * | 0160H | 192:223 |
306 * | 0170H | 224:255 |
307 * --------------------
308 *
309 * @return The vector of the interrupt that is currently being processed, or -1
310 * if no IRQ is being serviced.
311 */
312 __pinned_func
z_irq_controller_isr_vector_get(void)313 int z_irq_controller_isr_vector_get(void)
314 {
315 int pReg, block;
316
317 /* Block 0 bits never lit up as these are all exception or reserved
318 * vectors
319 */
320 for (block = 7; likely(block > 0); block--) {
321 pReg = x86_read_loapic(LOAPIC_ISR + (block * 0x10));
322 if (pReg != 0) {
323 return (block * 32) + (find_msb_set(pReg) - 1);
324 }
325
326 }
327 return -1;
328 }
329
330 #ifdef CONFIG_PM_DEVICE
331 __pinned_func
loapic_suspend(const struct device * port)332 static int loapic_suspend(const struct device *port)
333 {
334 volatile uint32_t lvt; /* local vector table entry value */
335 int loapic_irq;
336
337 ARG_UNUSED(port);
338
339 (void)memset(loapic_suspend_buf, 0, (LOAPIC_SUSPEND_BITS_REQD >> 3));
340
341 for (loapic_irq = 0; loapic_irq < LOAPIC_IRQ_COUNT; loapic_irq++) {
342
343 if (_irq_to_interrupt_vector[z_loapic_irq_base() + loapic_irq]) {
344
345 /* Since vector numbers are already present in RAM/ROM,
346 * We save only the mask bits here.
347 */
348 lvt = x86_read_loapic(LOAPIC_TIMER + (loapic_irq * 0x10));
349
350 if ((lvt & LOAPIC_LVT_MASKED) == 0U) {
351 sys_bitfield_set_bit((mem_addr_t)loapic_suspend_buf,
352 loapic_irq);
353 }
354 }
355 }
356
357 return 0;
358 }
359
360 __pinned_func
loapic_resume(const struct device * port)361 int loapic_resume(const struct device *port)
362 {
363 int loapic_irq;
364
365 ARG_UNUSED(port);
366
367 /* Assuming all loapic device registers lose their state, the call to
368 * z_loapic_init(), should bring all the registers to a sane state.
369 */
370 loapic_init(NULL);
371
372 for (loapic_irq = 0; loapic_irq < LOAPIC_IRQ_COUNT; loapic_irq++) {
373
374 if (_irq_to_interrupt_vector[z_loapic_irq_base() + loapic_irq]) {
375 /* Configure vector and enable the required ones*/
376 z_loapic_int_vec_set(loapic_irq,
377 _irq_to_interrupt_vector[z_loapic_irq_base() +
378 loapic_irq]);
379
380 if (sys_bitfield_test_bit((mem_addr_t) loapic_suspend_buf,
381 loapic_irq)) {
382 z_loapic_irq_enable(loapic_irq);
383 }
384 }
385 }
386
387 return 0;
388 }
389
390 /*
391 * Implements the driver control management functionality
392 * the *context may include IN data or/and OUT data
393 */
394 __pinned_func
loapic_pm_action(const struct device * dev,enum pm_device_action action)395 static int loapic_pm_action(const struct device *dev,
396 enum pm_device_action action)
397 {
398 int ret = 0;
399
400 switch (action) {
401 case PM_DEVICE_ACTION_SUSPEND:
402 ret = loapic_suspend(dev);
403 break;
404 case PM_DEVICE_ACTION_RESUME:
405 ret = loapic_resume(dev);
406 break;
407 default:
408 return -ENOTSUP;
409 }
410
411 return ret;
412 }
413 #endif /* CONFIG_PM_DEVICE */
414
415 PM_DEVICE_DT_INST_DEFINE(0, loapic_pm_action);
416
417 DEVICE_DT_INST_DEFINE(0, loapic_init, PM_DEVICE_DT_INST_GET(0), NULL, NULL,
418 PRE_KERNEL_1, CONFIG_INTC_INIT_PRIORITY, NULL);
419
420 #if CONFIG_LOAPIC_SPURIOUS_VECTOR
421 extern void z_loapic_spurious_handler(void);
422
423 NANO_CPU_INT_REGISTER(z_loapic_spurious_handler, NANO_SOFT_IRQ,
424 LOAPIC_SPURIOUS_VECTOR_ID >> 4,
425 LOAPIC_SPURIOUS_VECTOR_ID, 0);
426 #endif
427