blob: 4ffe1387983adfe1bf790253d159045ffe8ebf4c [file] [log] [blame]
Tim Wawrzynczakb59980b2021-02-04 17:05:30 -07001/* SPDX-License-Identifier: GPL-2.0-only */
2
3#include <assert.h>
4#include <console/console.h>
5#include <device/pci.h>
Tim Wawrzynczak81bcce92021-06-23 21:21:51 -06006#include <device/pci_ops.h>
Tim Wawrzynczakb59980b2021-02-04 17:05:30 -07007#include <intelblocks/gpio.h>
8#include <intelblocks/irq.h>
Tim Wawrzynczakc657ab92021-03-01 16:53:22 -07009#include <intelblocks/lpc_lib.h>
Tim Wawrzynczak81bcce92021-06-23 21:21:51 -060010#include <soc/pci_devs.h>
Tim Wawrzynczakb59980b2021-02-04 17:05:30 -070011#include <southbridge/intel/common/acpi_pirq_gen.h>
12#include <stdlib.h>
Tim Wawrzynczakb59980b2021-02-04 17:05:30 -070013#include <types.h>
14
15#define MIN_SHARED_IRQ 16
16#define MAX_SHARED_IRQ 23
17#define TOTAL_SHARED_IRQ (MAX_SHARED_IRQ - MIN_SHARED_IRQ + 1)
18#define MAX_IRQS 120
19
20#define IDX2PIN(i) (enum pci_pin)((i) + PCI_INT_A)
21#define PIN2IDX(p) (size_t)((p) - PCI_INT_A)
Tim Wawrzynczakb59980b2021-02-04 17:05:30 -070022
23struct pin_info {
24 enum pin_state {
25 FREE_PIN,
26 SHARED_IRQ_PIN,
27 UNIQUE_IRQ_PIN,
28 } pin_state;
29 unsigned int usage_count;
30 unsigned int irq;
31};
32
33static unsigned int irq_share_count[TOTAL_SHARED_IRQ];
34
35/*
36 * Assign PCI IRQs & pins according to controller rules.
37 *
38 * This information is provided to the FSP in order for it to do the
39 * programming; this is required because the FSP is also responsible for
40 * enabling some PCI devices so they will show up on their respective PCI
41 * buses. The FSP & PCH BIOS Specification contain rules for how certain IPs
42 * require their interrupt pin and interrupt line to be programmed.
43 *
44 * IOAPIC IRQs are used for PCI devices & GPIOs. The GPIO IRQs are fixed in
45 * hardware (the IRQ field is RO), and often start at 24, which means
46 * conflicts with PCI devices (if using the default FSP configuration) are very
47 * possible.
48 *
49 * These are the rules:
50 * 1) One entry per slot/function
51 * 2) Functions using PIRQs must use IOxAPIC IRQs 16-23
52 * 3) Single-function devices must use INTA
53 * 4) Each slot must have consistent INTx<->PIRQy mappings
54 * 5) Some functions have special interrupt pin requirements (FIXED_INT_ANY_PIRQ)
55 * 6) PCI Express RPs must be assigned in a special way (FIXED_INT_PIRQ)
56 * 7) Some functions require a unique IRQ number (mostly LPSS devices, DIRECT_IRQ)
57 * 8) PCI functions must avoid sharing an IRQ with a GPIO pad which routes its
58 * IRQ through IO-APIC.
59 */
60
61static int find_free_unique_irq(void)
62{
63 static unsigned int next_irq = MAX_SHARED_IRQ + 1;
64
65 while (next_irq < MAX_IRQS && gpio_routes_ioapic_irq(next_irq))
66 ++next_irq;
67
68 if (next_irq == MAX_IRQS)
69 return INVALID_IRQ;
70
71 return next_irq++;
72}
73
74static enum pci_pin find_free_pin(const struct pin_info pin_info[PCI_INT_MAX])
75{
76 for (size_t pin_idx = 0; pin_idx < PCI_INT_MAX; pin_idx++) {
77 if (pin_info[pin_idx].pin_state == FREE_PIN)
78 return IDX2PIN(pin_idx);
79 }
80
81 return PCI_INT_NONE;
82}
83
84static enum pci_pin find_shareable_pin(const struct pin_info pin_info[PCI_INT_MAX])
85{
86 unsigned int least_shared = 255;
87 int least_index = -1;
88
89 for (size_t pin_idx = 0; pin_idx < PCI_INT_MAX; pin_idx++) {
90 if (pin_info[pin_idx].pin_state == SHARED_IRQ_PIN &&
91 pin_info[pin_idx].usage_count < least_shared) {
92 least_shared = pin_info[pin_idx].usage_count;
93 least_index = pin_idx;
94 }
95 }
96
97 if (least_index < 0)
98 return PCI_INT_NONE;
99
100 return IDX2PIN(least_index);
101}
102
103static enum pirq find_global_least_used_pirq(void)
104{
105 unsigned int least_shared = 255;
106 int least_index = -1;
107
108 for (size_t i = 0; i < TOTAL_SHARED_IRQ; i++) {
109 if (irq_share_count[i] < least_shared) {
110 least_shared = irq_share_count[i];
111 least_index = i;
112 }
113 }
114
115 if (least_index >= 0)
116 return (enum pirq)least_index + PIRQ_A;
117
118 return PIRQ_INVALID;
119}
120
121
122static int pirq_to_irq(enum pirq pirq)
123{
124 return pirq_idx(pirq) + MIN_SHARED_IRQ;
125}
126
127static bool assign_pirq(struct pin_info pin_info[PCI_INT_MAX], enum pci_pin pin, enum pirq pirq)
128{
129 if (pirq < PIRQ_A || pirq > PIRQ_H) {
Julius Wernere9665952022-01-21 17:06:20 -0800130 printk(BIOS_ERR, "Invalid pirq constraint %u\n", pirq);
Tim Wawrzynczakb59980b2021-02-04 17:05:30 -0700131 return false;
132 }
133
134 const int irq = pirq_to_irq(pirq);
135 pin_info[PIN2IDX(pin)].irq = irq;
136 irq_share_count[pirq_idx(pirq)]++;
137 return true;
138}
139
140static bool assign_pin(enum pci_pin pin, unsigned int fn, enum pin_state state,
141 struct pin_info *pin_info,
142 enum pci_pin fn_pin_map[MAX_FNS])
143{
144 if (pin < PCI_INT_A || pin > PCI_INT_D) {
Julius Wernere9665952022-01-21 17:06:20 -0800145 printk(BIOS_ERR, "Invalid pin constraint %u\n", pin);
Tim Wawrzynczakb59980b2021-02-04 17:05:30 -0700146 return false;
147 }
148
149 const size_t pin_idx = PIN2IDX(pin);
150 pin_info[pin_idx].pin_state = state;
151 pin_info[pin_idx].usage_count++;
152 fn_pin_map[fn] = pin;
153
154 return true;
155}
156
157static bool assign_fixed_pins(const struct slot_irq_constraints *constraints,
158 struct pin_info *pin_info, enum pci_pin fn_pin_map[MAX_FNS])
159{
160 for (size_t i = 0; i < MAX_FNS; i++) {
161 const enum pci_pin fixed_int_pin = constraints->fns[i].fixed_int_pin;
162 if (fixed_int_pin == PCI_INT_NONE)
163 continue;
164
165 if (!assign_pin(fixed_int_pin, i, SHARED_IRQ_PIN, pin_info, fn_pin_map))
166 return false;
167 }
168
169 return true;
170}
171
172static bool assign_fixed_pirqs(const struct slot_irq_constraints *constraints,
173 struct pin_info *pin_info, enum pci_pin fn_pin_map[MAX_FNS])
174{
175 for (size_t i = 0; i < MAX_FNS; i++) {
176 const enum pirq fixed_pirq = constraints->fns[i].fixed_pirq;
177 if (fixed_pirq == PIRQ_INVALID)
178 continue;
179
180 /* A constraint with a fixed pirq is assumed to also have a
181 fixed pin */
182 const enum pci_pin pin = fn_pin_map[i];
183 if (pin == PCI_INT_NONE) {
Julius Wernere9665952022-01-21 17:06:20 -0800184 printk(BIOS_ERR, "Slot %u, pirq %u, no pin for function %zu\n",
Tim Wawrzynczakb59980b2021-02-04 17:05:30 -0700185 constraints->slot, fixed_pirq, i);
186 return false;
187 }
188
189 if (!assign_pirq(pin_info, pin, fixed_pirq))
190 return false;
191 }
192
193 return true;
194}
195
196static bool assign_direct_irqs(const struct slot_irq_constraints *constraints,
197 struct pin_info *pin_info, enum pci_pin fn_pin_map[MAX_FNS])
198{
199 for (size_t i = 0; i < MAX_FNS; i++) {
200 if (constraints->fns[i].irq_route != IRQ_DIRECT)
201 continue;
202
203 enum pci_pin pin = find_free_pin(pin_info);
204 if (pin == PCI_INT_NONE)
205 return false;
206
207 if (!assign_pin(pin, i, UNIQUE_IRQ_PIN, pin_info, fn_pin_map))
208 return false;
209
210 const int irq = find_free_unique_irq();
211 if (irq == INVALID_IRQ) {
Julius Wernere9665952022-01-21 17:06:20 -0800212 printk(BIOS_ERR, "No free unique IRQs found\n");
Tim Wawrzynczakb59980b2021-02-04 17:05:30 -0700213 return false;
214 }
215
216 const size_t pin_idx = PIN2IDX(pin);
217 pin_info[pin_idx].irq = irq;
218 }
219
220 return true;
221}
222
223static bool assign_shareable_pins(const struct slot_irq_constraints *constraints,
224 struct pin_info *pin_info, enum pci_pin fn_pin_map[MAX_FNS])
225{
226 for (size_t i = 0; i < MAX_FNS; i++) {
227 if (constraints->fns[i].irq_route != IRQ_PIRQ)
228 continue;
229
230 if (fn_pin_map[i] == PCI_INT_NONE) {
231 enum pci_pin pin = find_free_pin(pin_info);
232 if (pin == PCI_INT_NONE) {
233 pin = find_shareable_pin(pin_info);
234
235 if (pin == PCI_INT_NONE) {
Julius Wernere9665952022-01-21 17:06:20 -0800236 printk(BIOS_ERR, "No shareable pins found\n");
Tim Wawrzynczakb59980b2021-02-04 17:05:30 -0700237 return false;
238 }
239 }
240
241 if (!assign_pin(pin, i, SHARED_IRQ_PIN, pin_info, fn_pin_map))
242 return false;
243 }
244 }
245
246 return true;
247}
248
249static bool assign_pirqs(struct pin_info pin_info[PCI_INT_MAX])
250{
251 for (size_t pin_idx = 0; pin_idx < PCI_INT_MAX; pin_idx++) {
252 if (pin_info[pin_idx].pin_state != SHARED_IRQ_PIN || pin_info[pin_idx].irq != 0)
253 continue;
254
255 enum pirq pirq = find_global_least_used_pirq();
256 if (pirq == PIRQ_INVALID)
257 return false;
258
259 if (!assign_pirq(pin_info, IDX2PIN(pin_idx), pirq))
260 return false;
261 }
262
263 return true;
264}
265
266static void add_entry(struct pci_irq_entry **head, pci_devfn_t devfn, enum pci_pin pin,
267 unsigned int irq)
268{
269 struct pci_irq_entry *entry = malloc(sizeof(*entry));
270 struct pci_irq_entry **tmp = head;
271
272 entry->devfn = devfn;
273 entry->pin = pin;
274 entry->irq = irq;
275 entry->next = NULL;
276
277 while (*tmp)
278 tmp = &(*tmp)->next;
279
280 *tmp = entry;
281}
282
283static void add_slot_entries(struct pci_irq_entry **head, unsigned int slot,
284 struct pin_info pin_info[PCI_INT_MAX],
285 const enum pci_pin fn_pin_map[MAX_FNS])
286{
287 for (size_t fn = 0; fn < MAX_FNS; fn++) {
288 if (fn_pin_map[fn] == PCI_INT_NONE)
289 continue;
290
291 const size_t pin_idx = PIN2IDX(fn_pin_map[fn]);
292 add_entry(head, PCI_DEVFN(slot, fn), fn_pin_map[fn], pin_info[pin_idx].irq);
293 }
294}
295
296static bool assign_slot(struct pci_irq_entry **head,
297 const struct slot_irq_constraints *constraints)
298{
299 struct pin_info pin_info[PCI_INT_MAX] = {0};
300 enum pci_pin fn_pin_map[MAX_FNS] = {0};
301
302 /* The order in which pins are assigned is important in that strict constraints must
303 * be resolved first. This means fixed_int_pin -> fixed_pirq -> direct route ->
304 * shared pins -> shared pirqs
305 */
306 if (!assign_fixed_pins(constraints, pin_info, fn_pin_map))
307 return false;
308
309 if (!assign_fixed_pirqs(constraints, pin_info, fn_pin_map))
310 return false;
311
312 if (!assign_direct_irqs(constraints, pin_info, fn_pin_map))
313 return false;
314
315 if (!assign_shareable_pins(constraints, pin_info, fn_pin_map))
316 return false;
317
318 if (!assign_pirqs(pin_info))
319 return false;
320
321 add_slot_entries(head, constraints->slot, pin_info, fn_pin_map);
322 return true;
323}
324
Tim Wawrzynczake9ee9192021-06-24 11:40:45 -0600325static struct pci_irq_entry *cached_entries;
Tim Wawrzynczakb59980b2021-02-04 17:05:30 -0700326
Tim Wawrzynczake9ee9192021-06-24 11:40:45 -0600327bool assign_pci_irqs(const struct slot_irq_constraints *constraints, size_t num_slots)
328{
Tim Wawrzynczakb59980b2021-02-04 17:05:30 -0700329 for (size_t i = 0; i < num_slots; i++) {
Tim Wawrzynczake9ee9192021-06-24 11:40:45 -0600330 if (!assign_slot(&cached_entries, &constraints[i]))
331 return false;
Tim Wawrzynczakb59980b2021-02-04 17:05:30 -0700332 }
333
Tim Wawrzynczake9ee9192021-06-24 11:40:45 -0600334 const struct pci_irq_entry *entry = cached_entries;
Tim Wawrzynczakb59980b2021-02-04 17:05:30 -0700335 while (entry) {
336 printk(BIOS_INFO, "PCI %2X.%X, %s, using IRQ #%d\n",
337 PCI_SLOT(entry->devfn), PCI_FUNC(entry->devfn),
338 pin_to_str(entry->pin), entry->irq);
339
340 entry = entry->next;
341 }
342
Tim Wawrzynczake9ee9192021-06-24 11:40:45 -0600343 return true;
344}
345
346const struct pci_irq_entry *get_cached_pci_irqs(void)
347{
348 return cached_entries;
Tim Wawrzynczakb59980b2021-02-04 17:05:30 -0700349}
Tim Wawrzynczakc657ab92021-03-01 16:53:22 -0700350
351static enum pirq irq_to_pirq(unsigned int irq)
352{
353 if (irq >= MIN_SHARED_IRQ && irq <= MAX_SHARED_IRQ)
354 return (enum pirq)(irq - MIN_SHARED_IRQ + PIRQ_A);
355 else
356 /*
357 * Unknown if devices that require unique IRQs will
358 * even work in legacy PIC mode, given they cannot map
359 * to a PIRQ, therefore skip adding an entry.
360 */
361 return PIRQ_INVALID;
362}
363
Tim Wawrzynczake9ee9192021-06-24 11:40:45 -0600364bool generate_pin_irq_map(void)
Tim Wawrzynczakc657ab92021-03-01 16:53:22 -0700365{
366 struct slot_pin_irq_map *pin_irq_map;
367 const uint8_t *legacy_pirq_routing;
368 struct pic_pirq_map pirq_map = {0};
369 size_t map_count = 0;
370 size_t pirq_routes;
371 size_t i;
372
Tim Wawrzynczake9ee9192021-06-24 11:40:45 -0600373 if (!cached_entries)
374 return false;
375
Tim Wawrzynczakc657ab92021-03-01 16:53:22 -0700376 pin_irq_map = calloc(MAX_SLOTS, sizeof(struct slot_pin_irq_map) * PCI_INT_MAX);
377
378 pirq_map.type = PIRQ_GSI;
379 legacy_pirq_routing = lpc_get_pic_pirq_routing(&pirq_routes);
380 for (i = 0; i < PIRQ_COUNT && i < pirq_routes; i++)
381 pirq_map.gsi[i] = legacy_pirq_routing[i];
382
Tim Wawrzynczake9ee9192021-06-24 11:40:45 -0600383 const struct pci_irq_entry *entry = cached_entries;
Tim Wawrzynczakc657ab92021-03-01 16:53:22 -0700384 while (entry) {
385 const unsigned int slot = PCI_SLOT(entry->devfn);
386
387 if (is_slot_pin_assigned(pin_irq_map, map_count, slot, entry->pin)) {
388 entry = entry->next;
389 continue;
390 }
391
392 pin_irq_map[map_count].slot = slot;
393 pin_irq_map[map_count].pin = entry->pin;
394 pin_irq_map[map_count].apic_gsi = entry->irq;
395 pin_irq_map[map_count].pic_pirq = irq_to_pirq(entry->irq);
396 map_count++;
397 entry = entry->next;
398 }
399
400 intel_write_pci0_PRT(pin_irq_map, map_count, &pirq_map);
401 free(pin_irq_map);
Tim Wawrzynczake9ee9192021-06-24 11:40:45 -0600402
403 return true;
Tim Wawrzynczakc657ab92021-03-01 16:53:22 -0700404}
Tim Wawrzynczak81bcce92021-06-23 21:21:51 -0600405
Kapil Porwal9395cf92022-12-22 23:08:26 +0530406bool __weak is_pch_slot(unsigned int devfn)
407{
408 if (PCI_SLOT(devfn) >= MIN_PCH_SLOT)
409 return true;
410 return false;
411}
412
Tim Wawrzynczake9ee9192021-06-24 11:40:45 -0600413bool irq_program_non_pch(void)
Tim Wawrzynczak81bcce92021-06-23 21:21:51 -0600414{
Tim Wawrzynczake9ee9192021-06-24 11:40:45 -0600415 const struct pci_irq_entry *entry = cached_entries;
416
417 if (!entry)
418 return false;
419
420 while (entry) {
Kapil Porwal9395cf92022-12-22 23:08:26 +0530421 if (is_pch_slot(entry->devfn)) {
Tim Wawrzynczake9ee9192021-06-24 11:40:45 -0600422 entry = entry->next;
Tim Wawrzynczak81bcce92021-06-23 21:21:51 -0600423 continue;
424 }
425
Tim Wawrzynczak251d4052021-07-09 10:21:49 -0600426 if (entry->irq) {
427 pci_devfn_t dev = PCI_DEV(0, PCI_SLOT(entry->devfn),
428 PCI_FUNC(entry->devfn));
429 pci_s_write_config8(dev, PCI_INTERRUPT_LINE, entry->irq);
430 pci_s_write_config8(dev, PCI_INTERRUPT_PIN, (uint8_t)entry->pin);
431 }
Tim Wawrzynczak81bcce92021-06-23 21:21:51 -0600432
Tim Wawrzynczake9ee9192021-06-24 11:40:45 -0600433 entry = entry->next;
Tim Wawrzynczak81bcce92021-06-23 21:21:51 -0600434 }
Tim Wawrzynczake9ee9192021-06-24 11:40:45 -0600435
436 return true;
Tim Wawrzynczak81bcce92021-06-23 21:21:51 -0600437}
Tim Wawrzynczak61005c82021-06-24 11:48:27 -0600438
439int get_pci_devfn_irq(unsigned int devfn)
440{
441 const struct pci_irq_entry *entry = cached_entries;
442
443 while (entry) {
444 if (entry->devfn == devfn)
445 return entry->irq;
446
447 entry = entry->next;
448 }
449
450 return INVALID_IRQ;
451}