blob: f952d0a104bfc5f1119eabdaceca9787f7022789 [file] [log] [blame]
Arthur Heymans92a3b672023-06-22 21:30:58 +02001/* SPDX-License-Identifier: GPL-2.0-only */
2
3#include <acpi/acpi.h>
4#include <arch/ioapic.h>
5#include <arch/smp/mpspec.h>
6#include <commonlib/sort.h>
7#include <cpu/cpu.h>
8
9static int acpi_create_madt_lapic(acpi_madt_lapic_t *lapic, u8 cpu, u8 apic)
10{
11 lapic->type = LOCAL_APIC; /* Local APIC structure */
12 lapic->length = sizeof(acpi_madt_lapic_t);
13 lapic->flags = (1 << 0); /* Processor/LAPIC enabled */
14 lapic->processor_id = cpu;
15 lapic->apic_id = apic;
16
17 return lapic->length;
18}
19
20static int acpi_create_madt_lx2apic(acpi_madt_lx2apic_t *lapic, u32 cpu, u32 apic)
21{
22 lapic->type = LOCAL_X2APIC; /* Local APIC structure */
23 lapic->reserved = 0;
24 lapic->length = sizeof(acpi_madt_lx2apic_t);
25 lapic->flags = (1 << 0); /* Processor/LAPIC enabled */
26 lapic->processor_id = cpu;
27 lapic->x2apic_id = apic;
28
29 return lapic->length;
30}
31
32unsigned long acpi_create_madt_one_lapic(unsigned long current, u32 index, u32 lapic_id)
33{
34 if (lapic_id <= ACPI_MADT_MAX_LAPIC_ID)
35 current += acpi_create_madt_lapic((acpi_madt_lapic_t *)current, index,
36 lapic_id);
37 else
38 current += acpi_create_madt_lx2apic((acpi_madt_lx2apic_t *)current, index,
39 lapic_id);
40
41 return current;
42}
43
44/* Increase if necessary. Currently all x86 CPUs only have 2 SMP threads */
45#define MAX_THREAD_ID 1
46/*
47 * From ACPI 6.4 spec:
48 * "The advent of multi-threaded processors yielded multiple logical processors
49 * executing on common processor hardware. ACPI defines logical processors in
50 * an identical manner as physical processors. To ensure that non
51 * multi-threading aware OSPM implementations realize optimal performance on
52 * platforms containing multi-threaded processors, two guidelines should be
53 * followed. The first is the same as above, that is, OSPM should initialize
54 * processors in the order that they appear in the MADT. The second is that
55 * platform firmware should list the first logical processor of each of the
56 * individual multi-threaded processors in the MADT before listing any of the
57 * second logical processors. This approach should be used for all successive
58 * logical processors."
59 */
60static unsigned long acpi_create_madt_lapics(unsigned long current)
61{
62 struct device *cpu;
63 int index, apic_ids[CONFIG_MAX_CPUS] = {0}, num_cpus = 0, sort_start = 0;
64 for (unsigned int thread_id = 0; thread_id <= MAX_THREAD_ID; thread_id++) {
65 for (cpu = all_devices; cpu; cpu = cpu->next) {
66 if (!is_enabled_cpu(cpu))
67 continue;
68 if (num_cpus >= ARRAY_SIZE(apic_ids))
69 break;
70 if (cpu->path.apic.thread_id != thread_id)
71 continue;
72 apic_ids[num_cpus++] = cpu->path.apic.apic_id;
73 }
74 bubblesort(&apic_ids[sort_start], num_cpus - sort_start, NUM_ASCENDING);
75 sort_start = num_cpus;
76 }
77 for (index = 0; index < num_cpus; index++)
78 current = acpi_create_madt_one_lapic(current, index, apic_ids[index]);
79
80 return current;
81}
82
83static int acpi_create_madt_ioapic(acpi_madt_ioapic_t *ioapic, u8 id, u32 addr,
84 u32 gsi_base)
85{
86 ioapic->type = IO_APIC; /* I/O APIC structure */
87 ioapic->length = sizeof(acpi_madt_ioapic_t);
88 ioapic->reserved = 0x00;
89 ioapic->gsi_base = gsi_base;
90 ioapic->ioapic_id = id;
91 ioapic->ioapic_addr = addr;
92
93 return ioapic->length;
94}
95
96/* For a system with multiple I/O APICs it's required that the one potentially
97 routing i8259 via ExtNMI delivery calls this first to get GSI #0. */
98int acpi_create_madt_ioapic_from_hw(acpi_madt_ioapic_t *ioapic, u32 addr)
99{
100 static u32 gsi_base;
101 u32 my_base;
102 u8 id = get_ioapic_id((void *)(uintptr_t)addr);
103 u8 count = ioapic_get_max_vectors((void *)(uintptr_t)addr);
104
105 my_base = gsi_base;
106 gsi_base += count;
107 return acpi_create_madt_ioapic(ioapic, id, addr, my_base);
108}
109
110static int acpi_create_madt_irqoverride(acpi_madt_irqoverride_t *irqoverride,
111 u8 bus, u8 source, u32 gsirq, u16 flags)
112{
113 irqoverride->type = IRQ_SOURCE_OVERRIDE; /* Interrupt source override */
114 irqoverride->length = sizeof(acpi_madt_irqoverride_t);
115 irqoverride->bus = bus;
116 irqoverride->source = source;
117 irqoverride->gsirq = gsirq;
118 irqoverride->flags = flags;
119
120 return irqoverride->length;
121}
122
123static int acpi_create_madt_sci_override(acpi_madt_irqoverride_t *irqoverride)
124{
125 u8 gsi, irq, flags;
126
127 ioapic_get_sci_pin(&gsi, &irq, &flags);
128
129 if (!CONFIG(ACPI_HAVE_PCAT_8259))
130 irq = gsi;
131
132 irqoverride->type = IRQ_SOURCE_OVERRIDE; /* Interrupt source override */
133 irqoverride->length = sizeof(acpi_madt_irqoverride_t);
134 irqoverride->bus = MP_BUS_ISA;
135 irqoverride->source = irq;
136 irqoverride->gsirq = gsi;
137 irqoverride->flags = flags;
138
139 return irqoverride->length;
140}
141
Arthur Heymanscd46e5f2023-06-22 21:34:16 +0200142static unsigned long acpi_create_madt_ioapic_gsi0_default(unsigned long current)
Arthur Heymans92a3b672023-06-22 21:30:58 +0200143{
144 current += acpi_create_madt_ioapic_from_hw((acpi_madt_ioapic_t *)current, IO_APIC_ADDR);
145
146 current += acpi_create_madt_irqoverride((void *)current, MP_BUS_ISA, 0, 2,
147 MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH);
148
149 current += acpi_create_madt_sci_override((void *)current);
150
151 return current;
152}
153
154static int acpi_create_madt_lapic_nmi(acpi_madt_lapic_nmi_t *lapic_nmi, u8 cpu,
155 u16 flags, u8 lint)
156{
157 lapic_nmi->type = LOCAL_APIC_NMI; /* Local APIC NMI structure */
158 lapic_nmi->length = sizeof(acpi_madt_lapic_nmi_t);
159 lapic_nmi->flags = flags;
160 lapic_nmi->processor_id = cpu;
161 lapic_nmi->lint = lint;
162
163 return lapic_nmi->length;
164}
165
166static int acpi_create_madt_lx2apic_nmi(acpi_madt_lx2apic_nmi_t *lapic_nmi, u32 cpu,
167 u16 flags, u8 lint)
168{
169 lapic_nmi->type = LOCAL_X2APIC_NMI; /* Local APIC NMI structure */
170 lapic_nmi->length = sizeof(acpi_madt_lx2apic_nmi_t);
171 lapic_nmi->flags = flags;
172 lapic_nmi->processor_id = cpu;
173 lapic_nmi->lint = lint;
174 lapic_nmi->reserved[0] = 0;
175 lapic_nmi->reserved[1] = 0;
176 lapic_nmi->reserved[2] = 0;
177
178 return lapic_nmi->length;
179}
180
181unsigned long acpi_create_madt_lapic_nmis(unsigned long current)
182{
183 const u16 flags = MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH;
184
185 /* 1: LINT1 connect to NMI */
186 /* create all subtables for processors */
187 current += acpi_create_madt_lapic_nmi((acpi_madt_lapic_nmi_t *)current,
188 ACPI_MADT_LAPIC_NMI_ALL_PROCESSORS, flags, 1);
189
190 if (!CONFIG(XAPIC_ONLY))
191 current += acpi_create_madt_lx2apic_nmi((acpi_madt_lx2apic_nmi_t *)current,
192 ACPI_MADT_LX2APIC_NMI_ALL_PROCESSORS, flags, 1);
193
194 return current;
195}
196
Arthur Heymanscd46e5f2023-06-22 21:34:16 +0200197static unsigned long acpi_create_madt_lapics_with_nmis(unsigned long current)
Arthur Heymans92a3b672023-06-22 21:30:58 +0200198{
199 current = acpi_create_madt_lapics(current);
200 current = acpi_create_madt_lapic_nmis(current);
201 return current;
202}
203
204int acpi_create_srat_lapic(acpi_srat_lapic_t *lapic, u8 node, u8 apic)
205{
206 memset((void *)lapic, 0, sizeof(acpi_srat_lapic_t));
207
208 lapic->type = 0; /* Processor local APIC/SAPIC affinity structure */
209 lapic->length = sizeof(acpi_srat_lapic_t);
210 lapic->flags = (1 << 0); /* Enabled (the use of this structure). */
211 lapic->proximity_domain_7_0 = node;
212 /* TODO: proximity_domain_31_8, local SAPIC EID, clock domain. */
213 lapic->apic_id = apic;
214
215 return lapic->length;
216}
217
218int acpi_create_srat_x2apic(acpi_srat_x2apic_t *x2apic, u32 node, u32 apic)
219{
220 memset((void *)x2apic, 0, sizeof(acpi_srat_x2apic_t));
221
222 x2apic->type = 2; /* Processor x2APIC structure */
223 x2apic->length = sizeof(acpi_srat_x2apic_t);
224 x2apic->flags = (1 << 0); /* Enabled (the use of this structure). */
225 x2apic->proximity_domain = node;
226 x2apic->x2apic_id = apic;
227
228 return x2apic->length;
229}
Arthur Heymanscd46e5f2023-06-22 21:34:16 +0200230
231unsigned long acpi_arch_fill_madt(acpi_madt_t *madt, unsigned long current)
232{
233 madt->lapic_addr = cpu_get_lapic_addr();
234
235 if (CONFIG(ACPI_HAVE_PCAT_8259))
236 madt->flags |= 1;
237
238 if (CONFIG(ACPI_COMMON_MADT_LAPIC))
239 current = acpi_create_madt_lapics_with_nmis(current);
240
241 if (CONFIG(ACPI_COMMON_MADT_IOAPIC))
242 current = acpi_create_madt_ioapic_gsi0_default(current);
243
244 return current;
245}