1 /*
2  * Copyright (c) 1997-1998, 2000-2002, 2004, 2006-2008, 2011-2015 Wind River
3  * Systems, Inc.
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #define DT_DRV_COMPAT intel_ioapic
9 
10 /**
11  * @file
12  * @brief Intel IO APIC/xAPIC driver
13  *
14  * This module is a driver for the IO APIC/xAPIC (Advanced Programmable
15  * Interrupt Controller) for P6 (PentiumPro, II, III) family processors
16  * and P7 (Pentium4) family processors.  The IO APIC/xAPIC is included
17  * in the Intel's system chip set, such as ICH2.  Software intervention
18  * may be required to enable the IO APIC/xAPIC in some chip sets.
19  * The 8259A interrupt controller is intended for use in a uni-processor
20  * system, IO APIC can be used in either a uni-processor or multi-processor
21  * system.  The IO APIC handles interrupts very differently than the 8259A.
22  * Briefly, these differences are:
23  *  - Method of Interrupt Transmission. The IO APIC transmits interrupts
24  *    through a 3-wire bus and interrupts are handled without the need for
25  *    the processor to run an interrupt acknowledge cycle.
26  *  - Interrupt Priority. The priority of interrupts in the IO APIC is
27  *    independent of the interrupt number.  For example, interrupt 10 can
28  *    be given a higher priority than interrupt 3.
29  *  - More Interrupts. The IO APIC supports a total of 24 interrupts.
30  *
31  * The IO APIC unit consists of a set of interrupt input signals, a 24-entry
32  * by 64-bit Interrupt Redirection Table, programmable registers, and a message
33  * unit for sending and receiving APIC messages over the APIC bus or the
34  * Front-Side (system) bus.  IO devices inject interrupts into the system by
35  * asserting one of the interrupt lines to the IO APIC.  The IO APIC selects the
36  * corresponding entry in the Redirection Table and uses the information in that
37  * entry to format an interrupt request message.  Each entry in the Redirection
38  * Table can be individually programmed to indicate edge/level sensitive interrupt
39  * signals, the interrupt vector and priority, the destination processor, and how
40  * the processor is selected (statically and dynamically).  The information in
41  * the table is used to transmit a message to other APIC units (via the APIC bus
42  * or the Front-Side (system) bus).  IO APIC is used in the Symmetric IO Mode.
43  * The base address of IO APIC is determined in loapic_init() and stored in the
44  * global variable ioApicBase and ioApicData.
45  * The lower 32 bit value of the redirection table entries for IRQ 0
46  * to 15 are edge triggered positive high, and for IRQ 16 to 23 are level
47  * triggered positive low.
48  *
49  * This implementation doesn't support multiple IO APICs.
50  *
51  * INCLUDE FILES: ioapic.h loapic.h
52  *
53  */
54 
55 #include <kernel.h>
56 #include <arch/cpu.h>
57 
58 #include <toolchain.h>
59 #include <linker/sections.h>
60 #include <device.h>
61 #include <string.h>
62 
63 #include <drivers/interrupt_controller/ioapic.h> /* public API declarations */
64 #include <drivers/interrupt_controller/loapic.h> /* public API declarations and registers */
65 #include "intc_ioapic_priv.h"
66 
67 DEVICE_MMIO_TOPLEVEL_STATIC(ioapic_regs, DT_DRV_INST(0));
68 
69 #define IOAPIC_REG DEVICE_MMIO_TOPLEVEL_GET(ioapic_regs)
70 
71 /*
72  * Destination field (bits[56:63]) defines a set of processors, which is
73  * used to be compared with local LDR to determine which local APICs accept
74  * the interrupt.
75  *
76  * XAPIC: in logical destination mode and flat model (determined by DFR).
77  * LDR bits[24:31] can accommodate up to 8 logical APIC IDs.
78  *
79  * X2APIC: in logical destination mode and cluster model.
80  * In this case, LDR is read-only to system software and supports up to 16
81  * logical IDs. (Cluster ID: don't care to IO APIC).
82  *
83  * In either case, regardless how many CPUs in the system, 0xff implies that
84  * it's intended to deliver to all possible 8 local APICs.
85  */
86 #define DEFAULT_RTE_DEST	(0xFF << 24)
87 
88 static __pinned_bss uint32_t ioapic_rtes;
89 
90 #ifdef CONFIG_PM_DEVICE
91 #include <pm/device.h>
92 
93 #define BITS_PER_IRQ  4
94 #define IOAPIC_BITFIELD_HI_LO	0
95 #define IOAPIC_BITFIELD_LVL_EDGE 1
96 #define IOAPIC_BITFIELD_ENBL_DSBL 2
97 #define IOAPIC_BITFIELD_DELIV_MODE 3
98 
99 #define BIT_POS_FOR_IRQ_OPTION(irq, option) ((irq) * BITS_PER_IRQ + (option))
100 
101 /* Allocating up to 256 irq bits bufffer for RTEs, RTEs are dynamically found
102  * so let's just assume the maximum, it's only 128 bytes in total.
103  */
104 #define SUSPEND_BITS_REQD (ROUND_UP((256 * BITS_PER_IRQ), 32))
105 
106 __pinned_bss
107 uint32_t ioapic_suspend_buf[SUSPEND_BITS_REQD / 32] = {0};
108 #endif
109 
110 static uint32_t __IoApicGet(int32_t offset);
111 static void __IoApicSet(int32_t offset, uint32_t value);
112 static void ioApicRedSetHi(unsigned int irq, uint32_t upper32);
113 static void ioApicRedSetLo(unsigned int irq, uint32_t lower32);
114 static uint32_t ioApicRedGetLo(unsigned int irq);
115 static void IoApicRedUpdateLo(unsigned int irq, uint32_t value,
116 					uint32_t mask);
117 
118 /*
119  * The functions irq_enable() and irq_disable() are implemented in the
120  * interrupt controller driver due to the IRQ virtualization imposed by
121  * the x86 architecture.
122  */
123 
124 /**
125  *
126  * @brief Initialize the IO APIC or xAPIC
127  *
128  * This routine initializes the IO APIC or xAPIC.
129  *
130  * @return N/A
131  */
132 __boot_func
ioapic_init(const struct device * unused)133 int ioapic_init(const struct device *unused)
134 {
135 	ARG_UNUSED(unused);
136 
137 	DEVICE_MMIO_TOPLEVEL_MAP(ioapic_regs, K_MEM_CACHE_NONE);
138 
139 	/* Reading MRE: this will give the number of RTEs available */
140 	ioapic_rtes = ((__IoApicGet(IOAPIC_VERS) &
141 			IOAPIC_MRE_MASK) >> IOAPIC_MRE_POS) + 1;
142 
143 #ifdef CONFIG_IOAPIC_MASK_RTE
144 	int32_t ix;	/* redirection table index */
145 	uint32_t rteValue; /* value to copy into redirection table entry */
146 
147 	rteValue = IOAPIC_EDGE | IOAPIC_HIGH | IOAPIC_FIXED | IOAPIC_INT_MASK |
148 		   IOAPIC_LOGICAL | 0 /* dummy vector */;
149 
150 	for (ix = 0; ix < ioapic_rtes; ix++) {
151 		ioApicRedSetHi(ix, DEFAULT_RTE_DEST);
152 		ioApicRedSetLo(ix, rteValue);
153 	}
154 #endif
155 	return 0;
156 }
157 
158 __pinned_func
z_ioapic_num_rtes(void)159 uint32_t z_ioapic_num_rtes(void)
160 {
161 	return ioapic_rtes;
162 }
163 
164 /**
165  *
166  * @brief Enable a specified APIC interrupt input line
167  *
168  * This routine enables a specified APIC interrupt input line.
169  * @param irq IRQ number to enable
170  *
171  * @return N/A
172  */
173 __pinned_func
z_ioapic_irq_enable(unsigned int irq)174 void z_ioapic_irq_enable(unsigned int irq)
175 {
176 	IoApicRedUpdateLo(irq, 0, IOAPIC_INT_MASK);
177 }
178 
179 /**
180  *
181  * @brief Disable a specified APIC interrupt input line
182  *
183  * This routine disables a specified APIC interrupt input line.
184  * @param irq IRQ number to disable
185  *
186  * @return N/A
187  */
188 __pinned_func
z_ioapic_irq_disable(unsigned int irq)189 void z_ioapic_irq_disable(unsigned int irq)
190 {
191 	IoApicRedUpdateLo(irq, IOAPIC_INT_MASK, IOAPIC_INT_MASK);
192 }
193 
194 
195 #ifdef CONFIG_PM_DEVICE
196 
197 __pinned_func
store_flags(unsigned int irq,uint32_t flags)198 void store_flags(unsigned int irq, uint32_t flags)
199 {
200 	/* Currently only the following four flags are modified */
201 	if (flags & IOAPIC_LOW) {
202 		sys_bitfield_set_bit((mem_addr_t) ioapic_suspend_buf,
203 			BIT_POS_FOR_IRQ_OPTION(irq, IOAPIC_BITFIELD_HI_LO));
204 	}
205 
206 	if (flags & IOAPIC_LEVEL) {
207 		sys_bitfield_set_bit((mem_addr_t) ioapic_suspend_buf,
208 			BIT_POS_FOR_IRQ_OPTION(irq, IOAPIC_BITFIELD_LVL_EDGE));
209 	}
210 
211 	if (flags & IOAPIC_INT_MASK) {
212 		sys_bitfield_set_bit((mem_addr_t) ioapic_suspend_buf,
213 			BIT_POS_FOR_IRQ_OPTION(irq, IOAPIC_BITFIELD_ENBL_DSBL));
214 	}
215 
216 	/*
217 	 * We support lowest priority and fixed mode only, so only one bit
218 	 * needs to be saved.
219 	 */
220 	if (flags & IOAPIC_LOWEST) {
221 		sys_bitfield_set_bit((mem_addr_t) ioapic_suspend_buf,
222 			BIT_POS_FOR_IRQ_OPTION(irq, IOAPIC_BITFIELD_DELIV_MODE));
223 	}
224 }
225 
226 __pinned_func
restore_flags(unsigned int irq)227 uint32_t restore_flags(unsigned int irq)
228 {
229 	uint32_t flags = 0U;
230 
231 	if (sys_bitfield_test_bit((mem_addr_t) ioapic_suspend_buf,
232 		BIT_POS_FOR_IRQ_OPTION(irq, IOAPIC_BITFIELD_HI_LO))) {
233 		flags |= IOAPIC_LOW;
234 	}
235 
236 	if (sys_bitfield_test_bit((mem_addr_t) ioapic_suspend_buf,
237 		BIT_POS_FOR_IRQ_OPTION(irq, IOAPIC_BITFIELD_LVL_EDGE))) {
238 		flags |= IOAPIC_LEVEL;
239 	}
240 
241 	if (sys_bitfield_test_bit((mem_addr_t) ioapic_suspend_buf,
242 		BIT_POS_FOR_IRQ_OPTION(irq, IOAPIC_BITFIELD_ENBL_DSBL))) {
243 		flags |= IOAPIC_INT_MASK;
244 	}
245 
246 	if (sys_bitfield_test_bit((mem_addr_t) ioapic_suspend_buf,
247 		BIT_POS_FOR_IRQ_OPTION(irq, IOAPIC_BITFIELD_DELIV_MODE))) {
248 		flags |= IOAPIC_LOWEST;
249 	}
250 
251 	return flags;
252 }
253 
254 
255 __pinned_func
ioapic_suspend(const struct device * port)256 int ioapic_suspend(const struct device *port)
257 {
258 	int irq;
259 	uint32_t rte_lo;
260 
261 	ARG_UNUSED(port);
262 	(void)memset(ioapic_suspend_buf, 0, (SUSPEND_BITS_REQD >> 3));
263 	for (irq = 0; irq < ioapic_rtes; irq++) {
264 		/*
265 		 * The following check is to figure out the registered
266 		 * IRQ lines, so as to limit ourselves to saving the
267 		 *  flags for them only.
268 		 */
269 		if (_irq_to_interrupt_vector[irq]) {
270 			rte_lo = ioApicRedGetLo(irq);
271 			store_flags(irq, rte_lo);
272 		}
273 	}
274 	return 0;
275 }
276 
277 __pinned_func
ioapic_resume_from_suspend(const struct device * port)278 int ioapic_resume_from_suspend(const struct device *port)
279 {
280 	int irq;
281 	uint32_t flags;
282 	uint32_t rteValue;
283 
284 	ARG_UNUSED(port);
285 
286 	for (irq = 0; irq < ioapic_rtes; irq++) {
287 		if (_irq_to_interrupt_vector[irq]) {
288 			/* Get the saved flags */
289 			flags = restore_flags(irq);
290 			/* Appending the flags that are never modified */
291 			flags = flags | IOAPIC_LOGICAL;
292 
293 			rteValue = (_irq_to_interrupt_vector[irq] &
294 					IOAPIC_VEC_MASK) | flags;
295 		} else {
296 			/* Initialize the other RTEs to sane values */
297 			rteValue = IOAPIC_EDGE | IOAPIC_HIGH |
298 				IOAPIC_FIXED | IOAPIC_INT_MASK |
299 				IOAPIC_LOGICAL | 0 ; /* dummy vector*/
300 		}
301 		ioApicRedSetHi(irq, DEFAULT_RTE_DEST);
302 		ioApicRedSetLo(irq, rteValue);
303 	}
304 	return 0;
305 }
306 
307 /*
308 * Implements the driver control management functionality
309 * the *context may include IN data or/and OUT data
310 */
311 __pinned_func
ioapic_device_ctrl(const struct device * dev,enum pm_device_action action)312 static int ioapic_device_ctrl(const struct device *dev,
313 			      enum pm_device_action action)
314 {
315 	int ret = 0;
316 
317 	switch (action) {
318 	case PM_DEVICE_ACTION_RESUME:
319 		ret = ioapic_resume_from_suspend(dev);
320 		break;
321 	case PM_DEVICE_ACTION_SUSPEND:
322 		ret = ioapic_suspend(dev);
323 		break;
324 	default:
325 		ret = -ENOTSUP;
326 	}
327 
328 	return ret;
329 }
330 
331 #endif  /*CONFIG_PM_DEVICE*/
332 
333 /**
334  *
335  * @brief Programs the interrupt redirection table
336  *
337  * This routine sets up the redirection table entry for the specified IRQ
338  * @param irq Virtualized IRQ
339  * @param vector Vector number
340  * @param flags Interrupt flags
341  *
342  * @return N/A
343  */
344 __boot_func
z_ioapic_irq_set(unsigned int irq,unsigned int vector,uint32_t flags)345 void z_ioapic_irq_set(unsigned int irq, unsigned int vector, uint32_t flags)
346 {
347 	uint32_t rteValue;   /* value to copy into redirection table entry */
348 
349 	/* the delivery mode is determined by the flags passed from drivers */
350 	rteValue = IOAPIC_INT_MASK | IOAPIC_LOGICAL |
351 		   (vector & IOAPIC_VEC_MASK) | flags;
352 	ioApicRedSetHi(irq, DEFAULT_RTE_DEST);
353 	ioApicRedSetLo(irq, rteValue);
354 }
355 
356 /**
357  *
358  * @brief Program interrupt vector for specified irq
359  *
360  * The routine writes the interrupt vector in the Interrupt Redirection
361  * Table for specified irq number
362  *
363  * @param irq Interrupt number
364  * @param vector Vector number
365  * @return N/A
366  */
367 __boot_func
z_ioapic_int_vec_set(unsigned int irq,unsigned int vector)368 void z_ioapic_int_vec_set(unsigned int irq, unsigned int vector)
369 {
370 	IoApicRedUpdateLo(irq, vector, IOAPIC_VEC_MASK);
371 }
372 
373 /**
374  *
375  * @brief Read a 32 bit IO APIC register
376  *
377  * This routine reads the specified IO APIC register using indirect addressing.
378  * @param offset Register offset (8 bits)
379  *
380  * @return register value
381  */
382 __pinned_func
__IoApicGet(int32_t offset)383 static uint32_t __IoApicGet(int32_t offset)
384 {
385 	uint32_t value; /* value */
386 	unsigned int key;	/* interrupt lock level */
387 
388 	/* lock interrupts to ensure indirect addressing works "atomically" */
389 
390 	key = irq_lock();
391 
392 	*((volatile uint32_t *) (IOAPIC_REG + IOAPIC_IND)) = (char)offset;
393 	value = *((volatile uint32_t *)(IOAPIC_REG + IOAPIC_DATA));
394 
395 	irq_unlock(key);
396 
397 	return value;
398 }
399 
400 /**
401  *
402  * @brief Write a 32 bit IO APIC register
403  *
404  * This routine writes the specified IO APIC register using indirect addressing.
405  *
406  * @param offset Register offset (8 bits)
407  * @param value Value to set the register
408  * @return N/A
409  */
410 __pinned_func
__IoApicSet(int32_t offset,uint32_t value)411 static void __IoApicSet(int32_t offset, uint32_t value)
412 {
413 	unsigned int key; /* interrupt lock level */
414 
415 	/* lock interrupts to ensure indirect addressing works "atomically" */
416 
417 	key = irq_lock();
418 
419 	*(volatile uint32_t *)(IOAPIC_REG + IOAPIC_IND) = (char)offset;
420 	*((volatile uint32_t *)(IOAPIC_REG + IOAPIC_DATA)) = value;
421 
422 	irq_unlock(key);
423 }
424 
425 /**
426  *
427  * @brief Get low 32 bits of Redirection Table entry
428  *
429  * This routine reads the low-order 32 bits of a Redirection Table entry.
430  *
431  * @param irq INTIN number
432  * @return 32 low-order bits
433  */
434 __pinned_func
ioApicRedGetLo(unsigned int irq)435 static uint32_t ioApicRedGetLo(unsigned int irq)
436 {
437 	int32_t offset = IOAPIC_REDTBL + (irq << 1); /* register offset */
438 
439 	return __IoApicGet(offset);
440 }
441 
442 /**
443  *
444  * @brief Set low 32 bits of Redirection Table entry
445  *
446  * This routine writes the low-order 32 bits of a Redirection Table entry.
447  *
448  * @param irq INTIN number
449  * @param lower32 Value to be written
450  * @return N/A
451  */
452 __pinned_func
ioApicRedSetLo(unsigned int irq,uint32_t lower32)453 static void ioApicRedSetLo(unsigned int irq, uint32_t lower32)
454 {
455 	int32_t offset = IOAPIC_REDTBL + (irq << 1); /* register offset */
456 
457 	__IoApicSet(offset, lower32);
458 }
459 
460 /**
461  *
462  * @brief Set high 32 bits of Redirection Table entry
463  *
464  * This routine writes the high-order 32 bits of a Redirection Table entry.
465  *
466  * @param irq INTIN number
467  * @param upper32 Value to be written
468  * @return N/A
469  */
470 __pinned_func
ioApicRedSetHi(unsigned int irq,uint32_t upper32)471 static void ioApicRedSetHi(unsigned int irq, uint32_t upper32)
472 {
473 	int32_t offset = IOAPIC_REDTBL + (irq << 1) + 1; /* register offset */
474 
475 	__IoApicSet(offset, upper32);
476 }
477 
478 /**
479  *
480  * @brief Modify low 32 bits of Redirection Table entry
481  *
482  * This routine modifies selected portions of the low-order 32 bits of a
483  * Redirection Table entry, as indicated by the associate bit mask.
484  *
485  * @param irq INTIN number
486  * @param value Value to be written
487  * @param mask  Mask of bits to be modified
488  * @return N/A
489  */
490 __pinned_func
IoApicRedUpdateLo(unsigned int irq,uint32_t value,uint32_t mask)491 static void IoApicRedUpdateLo(unsigned int irq,
492 				uint32_t value,
493 				uint32_t mask)
494 {
495 	ioApicRedSetLo(irq, (ioApicRedGetLo(irq) & ~mask) | (value & mask));
496 }
497 
498 
499 #ifdef CONFIG_PM_DEVICE
500 SYS_DEVICE_DEFINE("ioapic", ioapic_init, ioapic_device_ctrl, PRE_KERNEL_1,
501 		  CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
502 #else
503 SYS_INIT(ioapic_init, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
504 #endif
505