1 /* SPDX-License-Identifier: GPL-2.0-only */
4 #include <device/device.h>
5 #include <device/mmio.h>
6 #include <arch/ioapic.h>
7 #include <console/console.h>
8 #include <cpu/x86/lapic.h>
12 #define ALL (0xff << 24)
14 #define INT_DISABLED (1 << 16)
15 #define INT_ENABLED (0 << 16)
16 #define TRIGGER_EDGE (0 << 15)
17 #define TRIGGER_LEVEL (1 << 15)
18 #define POLARITY_HIGH (0 << 13)
19 #define POLARITY_LOW (1 << 13)
20 #define PHYSICAL_DEST (0 << 11)
21 #define LOGICAL_DEST (1 << 11)
22 #define ExtINT (7 << 8)
27 static u32
io_apic_read(uintptr_t ioapic_base
, u32 reg
)
29 write32p(ioapic_base
, reg
);
30 return read32p(ioapic_base
+ 0x10);
33 static void io_apic_write(uintptr_t ioapic_base
, u32 reg
, u32 value
)
35 write32p(ioapic_base
, reg
);
36 write32p(ioapic_base
+ 0x10, value
);
39 static void write_vector(uintptr_t ioapic_base
, u8 vector
, u32 high
, u32 low
)
41 io_apic_write(ioapic_base
, vector
* 2 + 0x10, low
);
42 io_apic_write(ioapic_base
, vector
* 2 + 0x11, high
);
44 printk(BIOS_SPEW
, "IOAPIC: vector 0x%02x value 0x%08x 0x%08x\n",
48 /* Bits 23-16 of register 0x01 specify the maximum redirection entry, which
49 * is the number of interrupts minus 1. */
50 unsigned int ioapic_get_max_vectors(uintptr_t ioapic_base
)
55 reg
= io_apic_read(ioapic_base
, 0x01);
56 count
= (reg
>> 16) & 0xff;
62 printk(BIOS_DEBUG
, "IOAPIC: %d interrupts\n", count
);
66 /* Set maximum number of redirection entries (MRE). It is write-once register
67 * for some chipsets, and a negative mre_count will lock it to the number
68 * of vectors read from the register. */
69 void ioapic_set_max_vectors(uintptr_t ioapic_base
, int mre_count
)
74 reg
= io_apic_read(ioapic_base
, 0x01);
75 count
= (reg
>> 16) & 0xff;
77 count
= mre_count
- 1;
80 io_apic_write(ioapic_base
, 0x01, reg
);
83 void ioapic_lock_max_vectors(uintptr_t ioapic_base
)
85 ioapic_set_max_vectors(ioapic_base
, -1);
88 static void clear_vectors(uintptr_t ioapic_base
, u8 first
, u8 last
)
93 printk(BIOS_DEBUG
, "IOAPIC: Clearing IOAPIC at %" PRIxPTR
"\n", ioapic_base
);
98 for (i
= first
; i
<= last
; i
++)
99 write_vector(ioapic_base
, i
, high
, low
);
101 if (io_apic_read(ioapic_base
, 0x10) == 0xffffffff) {
102 printk(BIOS_WARNING
, "IOAPIC not responding.\n");
107 static void route_i8259_irq0(uintptr_t ioapic_base
)
109 u32 bsp_lapicid
= lapicid();
112 ASSERT(bsp_lapicid
< 255);
114 printk(BIOS_DEBUG
, "IOAPIC: Bootstrap Processor Local APIC = 0x%02x\n",
117 /* Enable Virtual Wire Mode. Should this be LOGICAL_DEST instead? */
118 low
= INT_ENABLED
| TRIGGER_EDGE
| POLARITY_HIGH
| PHYSICAL_DEST
| ExtINT
;
119 high
= bsp_lapicid
<< (56 - 32);
121 write_vector(ioapic_base
, 0, high
, low
);
123 if (io_apic_read(ioapic_base
, 0x10) == 0xffffffff) {
124 printk(BIOS_WARNING
, "IOAPIC not responding.\n");
129 static void set_ioapic_id(uintptr_t ioapic_base
, u8 ioapic_id
)
133 printk(BIOS_DEBUG
, "IOAPIC: Initializing IOAPIC at %" PRIxPTR
"\n",
135 printk(BIOS_DEBUG
, "IOAPIC: ID = 0x%02x\n", ioapic_id
);
137 io_apic_write(ioapic_base
, 0x00,
138 (io_apic_read(ioapic_base
, 0x00) & 0xf0ffffff) | (ioapic_id
<< 24));
140 printk(BIOS_SPEW
, "IOAPIC: Dumping registers\n");
141 for (i
= 0; i
< 3; i
++)
142 printk(BIOS_SPEW
, " reg 0x%04x: 0x%08x\n", i
,
143 io_apic_read(ioapic_base
, i
));
146 u8
get_ioapic_id(uintptr_t ioapic_base
)
149 * According to 82093AA I/O ADVANCED PROGRAMMABLE INTERRUPT CONTROLLER (IOAPIC)
150 * only 4 bits (24:27) are used for the ID. In practice the upper bits are either
151 * always 0 or used for larger IDs.
153 return (io_apic_read(ioapic_base
, 0x00) >> 24) & 0xff;
156 u8
get_ioapic_version(uintptr_t ioapic_base
)
158 return io_apic_read(ioapic_base
, 0x01) & 0xff;
161 void ioapic_set_boot_config(uintptr_t ioapic_base
, bool irq_on_fsb
)
165 * For the Pentium 4 and above APICs deliver their interrupts
166 * on the front side bus, enable that.
168 printk(BIOS_DEBUG
, "IOAPIC: Enabling interrupts on FSB\n");
169 io_apic_write(ioapic_base
, 0x03,
170 io_apic_read(ioapic_base
, 0x03) | (1 << 0));
173 "IOAPIC: Enabling interrupts on APIC serial bus\n");
174 io_apic_write(ioapic_base
, 0x03, 0);
179 * Create a new IOAPIC device under the given device.
181 * @param parent The parent device. A PCI domain or PCI device (in case of PCI IOAPIC).
182 * @param ioapic_base The IOAPIC base address
183 * @param gsi_base Platform specific GSI base
184 * @return Pointer to the device struct.
186 struct device
*ioapic_create_dev(struct device
*parent
,
187 const uintptr_t ioapic_base
,
190 struct device_path path
= {0};
196 struct bus
*bus
= alloc_bus(parent
);
201 register_new_ioapic_gsi0(ioapic_base
);
203 register_new_ioapic(ioapic_base
);
205 path
.type
= DEVICE_PATH_IOAPIC
;
206 path
.ioapic
.ioapic_id
= get_ioapic_id(ioapic_base
);
207 path
.ioapic
.addr
= ioapic_base
;
208 path
.ioapic
.gsi_base
= gsi_base
;
210 dev
= alloc_dev(bus
, &path
);
216 void setup_ioapic(uintptr_t ioapic_base
, u8 ioapic_id
)
218 set_ioapic_id(ioapic_base
, ioapic_id
);
219 clear_vectors(ioapic_base
, 0, ioapic_get_max_vectors(ioapic_base
) - 1);
220 route_i8259_irq0(ioapic_base
);
223 void register_new_ioapic_gsi0(uintptr_t ioapic_base
)
225 setup_ioapic(ioapic_base
, 0);
228 void register_new_ioapic(uintptr_t ioapic_base
)
232 set_ioapic_id(ioapic_base
, ioapic_id
);
233 clear_vectors(ioapic_base
, 0, ioapic_get_max_vectors(ioapic_base
) - 1);