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