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 __pinned_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