xref: /aosp_15_r20/external/coreboot/src/arch/x86/ioapic.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <assert.h>
4 #include <device/mmio.h>
5 #include <arch/ioapic.h>
6 #include <console/console.h>
7 #include <cpu/x86/lapic.h>
8 #include <inttypes.h>
9 #include <types.h>
10 
11 #define ALL		(0xff << 24)
12 #define NONE		(0)
13 #define INT_DISABLED	(1 << 16)
14 #define INT_ENABLED	(0 << 16)
15 #define TRIGGER_EDGE	(0 << 15)
16 #define TRIGGER_LEVEL	(1 << 15)
17 #define POLARITY_HIGH	(0 << 13)
18 #define POLARITY_LOW	(1 << 13)
19 #define PHYSICAL_DEST	(0 << 11)
20 #define LOGICAL_DEST	(1 << 11)
21 #define ExtINT		(7 << 8)
22 #define NMI		(4 << 8)
23 #define SMI		(2 << 8)
24 #define INT		(1 << 8)
25 
io_apic_read(uintptr_t ioapic_base,u32 reg)26 static u32 io_apic_read(uintptr_t ioapic_base, u32 reg)
27 {
28 	write32p(ioapic_base, reg);
29 	return read32p(ioapic_base + 0x10);
30 }
31 
io_apic_write(uintptr_t ioapic_base,u32 reg,u32 value)32 static void io_apic_write(uintptr_t ioapic_base, u32 reg, u32 value)
33 {
34 	write32p(ioapic_base, reg);
35 	write32p(ioapic_base + 0x10, value);
36 }
37 
write_vector(uintptr_t ioapic_base,u8 vector,u32 high,u32 low)38 static void write_vector(uintptr_t ioapic_base, u8 vector, u32 high, u32 low)
39 {
40 	io_apic_write(ioapic_base, vector * 2 + 0x10, low);
41 	io_apic_write(ioapic_base, vector * 2 + 0x11, high);
42 
43 	printk(BIOS_SPEW, "IOAPIC: vector 0x%02x value 0x%08x 0x%08x\n",
44 	       vector, high, low);
45 }
46 
47 /* Bits 23-16 of register 0x01 specify the maximum redirection entry, which
48  * is the number of interrupts minus 1. */
ioapic_get_max_vectors(uintptr_t ioapic_base)49 unsigned int ioapic_get_max_vectors(uintptr_t ioapic_base)
50 {
51 	u32 reg;
52 	u8 count;
53 
54 	reg = io_apic_read(ioapic_base, 0x01);
55 	count = (reg >> 16) & 0xff;
56 
57 	if (count == 0xff)
58 		count = 23;
59 	count++;
60 
61 	printk(BIOS_DEBUG, "IOAPIC: %d interrupts\n", count);
62 	return count;
63 }
64 
65 /* Set maximum number of redirection entries (MRE). It is write-once register
66  * for some chipsets, and a negative mre_count will lock it to the number
67  * of vectors read from the register. */
ioapic_set_max_vectors(uintptr_t ioapic_base,int mre_count)68 void ioapic_set_max_vectors(uintptr_t ioapic_base, int mre_count)
69 {
70 	u32 reg;
71 	u8 count;
72 
73 	reg = io_apic_read(ioapic_base, 0x01);
74 	count = (reg >> 16) & 0xff;
75 	if (mre_count > 0)
76 		count = mre_count - 1;
77 	reg &= ~(0xff << 16);
78 	reg |= count << 16;
79 	io_apic_write(ioapic_base, 0x01, reg);
80 }
81 
ioapic_lock_max_vectors(uintptr_t ioapic_base)82 void ioapic_lock_max_vectors(uintptr_t ioapic_base)
83 {
84 	ioapic_set_max_vectors(ioapic_base, -1);
85 }
86 
clear_vectors(uintptr_t ioapic_base,u8 first,u8 last)87 static void clear_vectors(uintptr_t ioapic_base, u8 first, u8 last)
88 {
89 	u32 low, high;
90 	u8 i;
91 
92 	printk(BIOS_DEBUG, "IOAPIC: Clearing IOAPIC at %" PRIxPTR "\n", ioapic_base);
93 
94 	low = INT_DISABLED;
95 	high = NONE;
96 
97 	for (i = first; i <= last; i++)
98 		write_vector(ioapic_base, i, high, low);
99 
100 	if (io_apic_read(ioapic_base, 0x10) == 0xffffffff) {
101 		printk(BIOS_WARNING, "IOAPIC not responding.\n");
102 		return;
103 	}
104 }
105 
route_i8259_irq0(uintptr_t ioapic_base)106 static void route_i8259_irq0(uintptr_t ioapic_base)
107 {
108 	u32 bsp_lapicid = lapicid();
109 	u32 low, high;
110 
111 	ASSERT(bsp_lapicid < 255);
112 
113 	printk(BIOS_DEBUG, "IOAPIC: Bootstrap Processor Local APIC = 0x%02x\n",
114 	       bsp_lapicid);
115 
116 	/* Enable Virtual Wire Mode. Should this be LOGICAL_DEST instead? */
117 	low = INT_ENABLED | TRIGGER_EDGE | POLARITY_HIGH | PHYSICAL_DEST | ExtINT;
118 	high = bsp_lapicid << (56 - 32);
119 
120 	write_vector(ioapic_base, 0, high, low);
121 
122 	if (io_apic_read(ioapic_base, 0x10) == 0xffffffff) {
123 		printk(BIOS_WARNING, "IOAPIC not responding.\n");
124 		return;
125 	}
126 }
127 
set_ioapic_id(uintptr_t ioapic_base,u8 ioapic_id)128 static void set_ioapic_id(uintptr_t ioapic_base, u8 ioapic_id)
129 {
130 	int i;
131 
132 	printk(BIOS_DEBUG, "IOAPIC: Initializing IOAPIC at %" PRIxPTR "\n",
133 	       ioapic_base);
134 	printk(BIOS_DEBUG, "IOAPIC: ID = 0x%02x\n", ioapic_id);
135 
136 	io_apic_write(ioapic_base, 0x00,
137 		      (io_apic_read(ioapic_base, 0x00) & 0xf0ffffff) | (ioapic_id << 24));
138 
139 	printk(BIOS_SPEW, "IOAPIC: Dumping registers\n");
140 	for (i = 0; i < 3; i++)
141 		printk(BIOS_SPEW, "  reg 0x%04x: 0x%08x\n", i,
142 		       io_apic_read(ioapic_base, i));
143 }
144 
get_ioapic_id(uintptr_t ioapic_base)145 u8 get_ioapic_id(uintptr_t ioapic_base)
146 {
147 	/*
148 	 * According to 82093AA I/O ADVANCED PROGRAMMABLE INTERRUPT CONTROLLER (IOAPIC)
149 	 * only 4 bits (24:27) are used for the ID. In practice the upper bits are either
150 	 * always 0 or used for larger IDs.
151 	 */
152 	return (io_apic_read(ioapic_base, 0x00) >> 24) & 0xff;
153 }
154 
get_ioapic_version(uintptr_t ioapic_base)155 u8 get_ioapic_version(uintptr_t ioapic_base)
156 {
157 	return io_apic_read(ioapic_base, 0x01) & 0xff;
158 }
159 
ioapic_set_boot_config(uintptr_t ioapic_base,bool irq_on_fsb)160 void ioapic_set_boot_config(uintptr_t ioapic_base, bool irq_on_fsb)
161 {
162 	if (irq_on_fsb) {
163 		/*
164 		 * For the Pentium 4 and above APICs deliver their interrupts
165 		 * on the front side bus, enable that.
166 		 */
167 		printk(BIOS_DEBUG, "IOAPIC: Enabling interrupts on FSB\n");
168 		io_apic_write(ioapic_base, 0x03,
169 			      io_apic_read(ioapic_base, 0x03) | (1 << 0));
170 	} else {
171 		printk(BIOS_DEBUG,
172 			"IOAPIC: Enabling interrupts on APIC serial bus\n");
173 		io_apic_write(ioapic_base, 0x03, 0);
174 	}
175 }
176 
setup_ioapic(uintptr_t ioapic_base,u8 ioapic_id)177 void setup_ioapic(uintptr_t ioapic_base, u8 ioapic_id)
178 {
179 	set_ioapic_id(ioapic_base, ioapic_id);
180 	clear_vectors(ioapic_base, 0, ioapic_get_max_vectors(ioapic_base) - 1);
181 	route_i8259_irq0(ioapic_base);
182 }
183 
register_new_ioapic_gsi0(uintptr_t ioapic_base)184 void register_new_ioapic_gsi0(uintptr_t ioapic_base)
185 {
186 	setup_ioapic(ioapic_base, 0);
187 }
188 
register_new_ioapic(uintptr_t ioapic_base)189 void register_new_ioapic(uintptr_t ioapic_base)
190 {
191 	static u8 ioapic_id;
192 	ioapic_id++;
193 	set_ioapic_id(ioapic_base, ioapic_id);
194 	clear_vectors(ioapic_base, 0, ioapic_get_max_vectors(ioapic_base) - 1);
195 }
196