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