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