blob: 8b0642f1494016b9f5726877e3b497b5b7ce3006 [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>
6#include <device/pci_def.h>
Tim Wawrzynczak81bcce92021-06-23 21:21:51 -06007#include <device/pci_ops.h>
Tim Wawrzynczakb59980b2021-02-04 17:05:30 -07008#include <intelblocks/gpio.h>
9#include <intelblocks/irq.h>
Tim Wawrzynczakc657ab92021-03-01 16:53:22 -070010#include <intelblocks/lpc_lib.h>
Tim Wawrzynczak81bcce92021-06-23 21:21:51 -060011#include <soc/pci_devs.h>
Tim Wawrzynczakb59980b2021-02-04 17:05:30 -070012#include <southbridge/intel/common/acpi_pirq_gen.h>
13#include <stdlib.h>
Tim Wawrzynczakb59980b2021-02-04 17:05:30 -070014#include <types.h>
15
16#define MIN_SHARED_IRQ 16
17#define MAX_SHARED_IRQ 23
18#define TOTAL_SHARED_IRQ (MAX_SHARED_IRQ - MIN_SHARED_IRQ + 1)
19#define MAX_IRQS 120
20
21#define IDX2PIN(i) (enum pci_pin)((i) + PCI_INT_A)
22#define PIN2IDX(p) (size_t)((p) - PCI_INT_A)
Tim Wawrzynczakb59980b2021-02-04 17:05:30 -070023
24struct pin_info {
25 enum pin_state {
26 FREE_PIN,
27 SHARED_IRQ_PIN,
28 UNIQUE_IRQ_PIN,
29 } pin_state;
30 unsigned int usage_count;
31 unsigned int irq;
32};
33
34static unsigned int irq_share_count[TOTAL_SHARED_IRQ];
35
36/*
37 * Assign PCI IRQs & pins according to controller rules.
38 *
39 * This information is provided to the FSP in order for it to do the
40 * programming; this is required because the FSP is also responsible for
41 * enabling some PCI devices so they will show up on their respective PCI
42 * buses. The FSP & PCH BIOS Specification contain rules for how certain IPs
43 * require their interrupt pin and interrupt line to be programmed.
44 *
45 * IOAPIC IRQs are used for PCI devices & GPIOs. The GPIO IRQs are fixed in
46 * hardware (the IRQ field is RO), and often start at 24, which means
47 * conflicts with PCI devices (if using the default FSP configuration) are very
48 * possible.
49 *
50 * These are the rules:
51 * 1) One entry per slot/function
52 * 2) Functions using PIRQs must use IOxAPIC IRQs 16-23
53 * 3) Single-function devices must use INTA
54 * 4) Each slot must have consistent INTx<->PIRQy mappings
55 * 5) Some functions have special interrupt pin requirements (FIXED_INT_ANY_PIRQ)
56 * 6) PCI Express RPs must be assigned in a special way (FIXED_INT_PIRQ)
57 * 7) Some functions require a unique IRQ number (mostly LPSS devices, DIRECT_IRQ)
58 * 8) PCI functions must avoid sharing an IRQ with a GPIO pad which routes its
59 * IRQ through IO-APIC.
60 */
61
62static int find_free_unique_irq(void)
63{
64 static unsigned int next_irq = MAX_SHARED_IRQ + 1;
65
66 while (next_irq < MAX_IRQS && gpio_routes_ioapic_irq(next_irq))
67 ++next_irq;
68
69 if (next_irq == MAX_IRQS)
70 return INVALID_IRQ;
71
72 return next_irq++;
73}
74
75static enum pci_pin find_free_pin(const struct pin_info pin_info[PCI_INT_MAX])
76{
77 for (size_t pin_idx = 0; pin_idx < PCI_INT_MAX; pin_idx++) {
78 if (pin_info[pin_idx].pin_state == FREE_PIN)
79 return IDX2PIN(pin_idx);
80 }
81
82 return PCI_INT_NONE;
83}
84
85static enum pci_pin find_shareable_pin(const struct pin_info pin_info[PCI_INT_MAX])
86{
87 unsigned int least_shared = 255;
88 int least_index = -1;
89
90 for (size_t pin_idx = 0; pin_idx < PCI_INT_MAX; pin_idx++) {
91 if (pin_info[pin_idx].pin_state == SHARED_IRQ_PIN &&
92 pin_info[pin_idx].usage_count < least_shared) {
93 least_shared = pin_info[pin_idx].usage_count;
94 least_index = pin_idx;
95 }
96 }
97
98 if (least_index < 0)
99 return PCI_INT_NONE;
100
101 return IDX2PIN(least_index);
102}
103
104static enum pirq find_global_least_used_pirq(void)
105{
106 unsigned int least_shared = 255;
107 int least_index = -1;
108
109 for (size_t i = 0; i < TOTAL_SHARED_IRQ; i++) {
110 if (irq_share_count[i] < least_shared) {
111 least_shared = irq_share_count[i];
112 least_index = i;
113 }
114 }
115
116 if (least_index >= 0)
117 return (enum pirq)least_index + PIRQ_A;
118
119 return PIRQ_INVALID;
120}
121
122
123static int pirq_to_irq(enum pirq pirq)
124{
125 return pirq_idx(pirq) + MIN_SHARED_IRQ;
126}
127
128static bool assign_pirq(struct pin_info pin_info[PCI_INT_MAX], enum pci_pin pin, enum pirq pirq)
129{
130 if (pirq < PIRQ_A || pirq > PIRQ_H) {
Julius Wernere9665952022-01-21 17:06:20 -0800131 printk(BIOS_ERR, "Invalid pirq constraint %u\n", pirq);
Tim Wawrzynczakb59980b2021-02-04 17:05:30 -0700132 return false;
133 }
134
135 const int irq = pirq_to_irq(pirq);
136 pin_info[PIN2IDX(pin)].irq = irq;
137 irq_share_count[pirq_idx(pirq)]++;
138 return true;
139}
140
141static bool assign_pin(enum pci_pin pin, unsigned int fn, enum pin_state state,
142 struct pin_info *pin_info,
143 enum pci_pin fn_pin_map[MAX_FNS])
144{
145 if (pin < PCI_INT_A || pin > PCI_INT_D) {
Julius Wernere9665952022-01-21 17:06:20 -0800146 printk(BIOS_ERR, "Invalid pin constraint %u\n", pin);
Tim Wawrzynczakb59980b2021-02-04 17:05:30 -0700147 return false;
148 }
149
150 const size_t pin_idx = PIN2IDX(pin);
151 pin_info[pin_idx].pin_state = state;
152 pin_info[pin_idx].usage_count++;
153 fn_pin_map[fn] = pin;
154
155 return true;
156}
157
158static bool assign_fixed_pins(const struct slot_irq_constraints *constraints,
159 struct pin_info *pin_info, enum pci_pin fn_pin_map[MAX_FNS])
160{
161 for (size_t i = 0; i < MAX_FNS; i++) {
162 const enum pci_pin fixed_int_pin = constraints->fns[i].fixed_int_pin;
163 if (fixed_int_pin == PCI_INT_NONE)
164 continue;
165
166 if (!assign_pin(fixed_int_pin, i, SHARED_IRQ_PIN, pin_info, fn_pin_map))
167 return false;
168 }
169
170 return true;
171}
172
173static bool assign_fixed_pirqs(const struct slot_irq_constraints *constraints,
174 struct pin_info *pin_info, enum pci_pin fn_pin_map[MAX_FNS])
175{
176 for (size_t i = 0; i < MAX_FNS; i++) {
177 const enum pirq fixed_pirq = constraints->fns[i].fixed_pirq;
178 if (fixed_pirq == PIRQ_INVALID)
179 continue;
180
181 /* A constraint with a fixed pirq is assumed to also have a
182 fixed pin */
183 const enum pci_pin pin = fn_pin_map[i];
184 if (pin == PCI_INT_NONE) {
Julius Wernere9665952022-01-21 17:06:20 -0800185 printk(BIOS_ERR, "Slot %u, pirq %u, no pin for function %zu\n",
Tim Wawrzynczakb59980b2021-02-04 17:05:30 -0700186 constraints->slot, fixed_pirq, i);
187 return false;
188 }
189
190 if (!assign_pirq(pin_info, pin, fixed_pirq))
191 return false;
192 }
193
194 return true;
195}
196
197static bool assign_direct_irqs(const struct slot_irq_constraints *constraints,
198 struct pin_info *pin_info, enum pci_pin fn_pin_map[MAX_FNS])
199{
200 for (size_t i = 0; i < MAX_FNS; i++) {
201 if (constraints->fns[i].irq_route != IRQ_DIRECT)
202 continue;
203
204 enum pci_pin pin = find_free_pin(pin_info);
205 if (pin == PCI_INT_NONE)
206 return false;
207
208 if (!assign_pin(pin, i, UNIQUE_IRQ_PIN, pin_info, fn_pin_map))
209 return false;
210
211 const int irq = find_free_unique_irq();
212 if (irq == INVALID_IRQ) {
Julius Wernere9665952022-01-21 17:06:20 -0800213 printk(BIOS_ERR, "No free unique IRQs found\n");
Tim Wawrzynczakb59980b2021-02-04 17:05:30 -0700214 return false;
215 }
216
217 const size_t pin_idx = PIN2IDX(pin);
218 pin_info[pin_idx].irq = irq;
219 }
220
221 return true;
222}
223
224static bool assign_shareable_pins(const struct slot_irq_constraints *constraints,
225 struct pin_info *pin_info, enum pci_pin fn_pin_map[MAX_FNS])
226{
227 for (size_t i = 0; i < MAX_FNS; i++) {
228 if (constraints->fns[i].irq_route != IRQ_PIRQ)
229 continue;
230
231 if (fn_pin_map[i] == PCI_INT_NONE) {
232 enum pci_pin pin = find_free_pin(pin_info);
233 if (pin == PCI_INT_NONE) {
234 pin = find_shareable_pin(pin_info);
235
236 if (pin == PCI_INT_NONE) {
Julius Wernere9665952022-01-21 17:06:20 -0800237 printk(BIOS_ERR, "No shareable pins found\n");
Tim Wawrzynczakb59980b2021-02-04 17:05:30 -0700238 return false;
239 }
240 }
241
242 if (!assign_pin(pin, i, SHARED_IRQ_PIN, pin_info, fn_pin_map))
243 return false;
244 }
245 }
246
247 return true;
248}
249
250static bool assign_pirqs(struct pin_info pin_info[PCI_INT_MAX])
251{
252 for (size_t pin_idx = 0; pin_idx < PCI_INT_MAX; pin_idx++) {
253 if (pin_info[pin_idx].pin_state != SHARED_IRQ_PIN || pin_info[pin_idx].irq != 0)
254 continue;
255
256 enum pirq pirq = find_global_least_used_pirq();
257 if (pirq == PIRQ_INVALID)
258 return false;
259
260 if (!assign_pirq(pin_info, IDX2PIN(pin_idx), pirq))
261 return false;
262 }
263
264 return true;
265}
266
267static void add_entry(struct pci_irq_entry **head, pci_devfn_t devfn, enum pci_pin pin,
268 unsigned int irq)
269{
270 struct pci_irq_entry *entry = malloc(sizeof(*entry));
271 struct pci_irq_entry **tmp = head;
272
273 entry->devfn = devfn;
274 entry->pin = pin;
275 entry->irq = irq;
276 entry->next = NULL;
277
278 while (*tmp)
279 tmp = &(*tmp)->next;
280
281 *tmp = entry;
282}
283
284static void add_slot_entries(struct pci_irq_entry **head, unsigned int slot,
285 struct pin_info pin_info[PCI_INT_MAX],
286 const enum pci_pin fn_pin_map[MAX_FNS])
287{
288 for (size_t fn = 0; fn < MAX_FNS; fn++) {
289 if (fn_pin_map[fn] == PCI_INT_NONE)
290 continue;
291
292 const size_t pin_idx = PIN2IDX(fn_pin_map[fn]);
293 add_entry(head, PCI_DEVFN(slot, fn), fn_pin_map[fn], pin_info[pin_idx].irq);
294 }
295}
296
297static bool assign_slot(struct pci_irq_entry **head,
298 const struct slot_irq_constraints *constraints)
299{
300 struct pin_info pin_info[PCI_INT_MAX] = {0};
301 enum pci_pin fn_pin_map[MAX_FNS] = {0};
302
303 /* The order in which pins are assigned is important in that strict constraints must
304 * be resolved first. This means fixed_int_pin -> fixed_pirq -> direct route ->
305 * shared pins -> shared pirqs
306 */
307 if (!assign_fixed_pins(constraints, pin_info, fn_pin_map))
308 return false;
309
310 if (!assign_fixed_pirqs(constraints, pin_info, fn_pin_map))
311 return false;
312
313 if (!assign_direct_irqs(constraints, pin_info, fn_pin_map))
314 return false;
315
316 if (!assign_shareable_pins(constraints, pin_info, fn_pin_map))
317 return false;
318
319 if (!assign_pirqs(pin_info))
320 return false;
321
322 add_slot_entries(head, constraints->slot, pin_info, fn_pin_map);
323 return true;
324}
325
Tim Wawrzynczake9ee9192021-06-24 11:40:45 -0600326static struct pci_irq_entry *cached_entries;
Tim Wawrzynczakb59980b2021-02-04 17:05:30 -0700327
Tim Wawrzynczake9ee9192021-06-24 11:40:45 -0600328bool assign_pci_irqs(const struct slot_irq_constraints *constraints, size_t num_slots)
329{
Tim Wawrzynczakb59980b2021-02-04 17:05:30 -0700330 for (size_t i = 0; i < num_slots; i++) {
Tim Wawrzynczake9ee9192021-06-24 11:40:45 -0600331 if (!assign_slot(&cached_entries, &constraints[i]))
332 return false;
Tim Wawrzynczakb59980b2021-02-04 17:05:30 -0700333 }
334
Tim Wawrzynczake9ee9192021-06-24 11:40:45 -0600335 const struct pci_irq_entry *entry = cached_entries;
Tim Wawrzynczakb59980b2021-02-04 17:05:30 -0700336 while (entry) {
337 printk(BIOS_INFO, "PCI %2X.%X, %s, using IRQ #%d\n",
338 PCI_SLOT(entry->devfn), PCI_FUNC(entry->devfn),
339 pin_to_str(entry->pin), entry->irq);
340
341 entry = entry->next;
342 }
343
Tim Wawrzynczake9ee9192021-06-24 11:40:45 -0600344 return true;
345}
346
347const struct pci_irq_entry *get_cached_pci_irqs(void)
348{
349 return cached_entries;
Tim Wawrzynczakb59980b2021-02-04 17:05:30 -0700350}
Tim Wawrzynczakc657ab92021-03-01 16:53:22 -0700351
352static enum pirq irq_to_pirq(unsigned int irq)
353{
354 if (irq >= MIN_SHARED_IRQ && irq <= MAX_SHARED_IRQ)
355 return (enum pirq)(irq - MIN_SHARED_IRQ + PIRQ_A);
356 else
357 /*
358 * Unknown if devices that require unique IRQs will
359 * even work in legacy PIC mode, given they cannot map
360 * to a PIRQ, therefore skip adding an entry.
361 */
362 return PIRQ_INVALID;
363}
364
Tim Wawrzynczake9ee9192021-06-24 11:40:45 -0600365bool generate_pin_irq_map(void)
Tim Wawrzynczakc657ab92021-03-01 16:53:22 -0700366{
367 struct slot_pin_irq_map *pin_irq_map;
368 const uint8_t *legacy_pirq_routing;
369 struct pic_pirq_map pirq_map = {0};
370 size_t map_count = 0;
371 size_t pirq_routes;
372 size_t i;
373
Tim Wawrzynczake9ee9192021-06-24 11:40:45 -0600374 if (!cached_entries)
375 return false;
376
Tim Wawrzynczakc657ab92021-03-01 16:53:22 -0700377 pin_irq_map = calloc(MAX_SLOTS, sizeof(struct slot_pin_irq_map) * PCI_INT_MAX);
378
379 pirq_map.type = PIRQ_GSI;
380 legacy_pirq_routing = lpc_get_pic_pirq_routing(&pirq_routes);
381 for (i = 0; i < PIRQ_COUNT && i < pirq_routes; i++)
382 pirq_map.gsi[i] = legacy_pirq_routing[i];
383
Tim Wawrzynczake9ee9192021-06-24 11:40:45 -0600384 const struct pci_irq_entry *entry = cached_entries;
Tim Wawrzynczakc657ab92021-03-01 16:53:22 -0700385 while (entry) {
386 const unsigned int slot = PCI_SLOT(entry->devfn);
387
388 if (is_slot_pin_assigned(pin_irq_map, map_count, slot, entry->pin)) {
389 entry = entry->next;
390 continue;
391 }
392
393 pin_irq_map[map_count].slot = slot;
394 pin_irq_map[map_count].pin = entry->pin;
395 pin_irq_map[map_count].apic_gsi = entry->irq;
396 pin_irq_map[map_count].pic_pirq = irq_to_pirq(entry->irq);
397 map_count++;
398 entry = entry->next;
399 }
400
401 intel_write_pci0_PRT(pin_irq_map, map_count, &pirq_map);
402 free(pin_irq_map);
Tim Wawrzynczake9ee9192021-06-24 11:40:45 -0600403
404 return true;
Tim Wawrzynczakc657ab92021-03-01 16:53:22 -0700405}
Tim Wawrzynczak81bcce92021-06-23 21:21:51 -0600406
Tim Wawrzynczake9ee9192021-06-24 11:40:45 -0600407bool irq_program_non_pch(void)
Tim Wawrzynczak81bcce92021-06-23 21:21:51 -0600408{
Tim Wawrzynczake9ee9192021-06-24 11:40:45 -0600409 const struct pci_irq_entry *entry = cached_entries;
410
411 if (!entry)
412 return false;
413
414 while (entry) {
415 if (PCI_SLOT(entry->devfn) >= MIN_PCH_SLOT) {
416 entry = entry->next;
Tim Wawrzynczak81bcce92021-06-23 21:21:51 -0600417 continue;
418 }
419
Tim Wawrzynczak251d4052021-07-09 10:21:49 -0600420 if (entry->irq) {
421 pci_devfn_t dev = PCI_DEV(0, PCI_SLOT(entry->devfn),
422 PCI_FUNC(entry->devfn));
423 pci_s_write_config8(dev, PCI_INTERRUPT_LINE, entry->irq);
424 pci_s_write_config8(dev, PCI_INTERRUPT_PIN, (uint8_t)entry->pin);
425 }
Tim Wawrzynczak81bcce92021-06-23 21:21:51 -0600426
Tim Wawrzynczake9ee9192021-06-24 11:40:45 -0600427 entry = entry->next;
Tim Wawrzynczak81bcce92021-06-23 21:21:51 -0600428 }
Tim Wawrzynczake9ee9192021-06-24 11:40:45 -0600429
430 return true;
Tim Wawrzynczak81bcce92021-06-23 21:21:51 -0600431}
Tim Wawrzynczak61005c82021-06-24 11:48:27 -0600432
433int get_pci_devfn_irq(unsigned int devfn)
434{
435 const struct pci_irq_entry *entry = cached_entries;
436
437 while (entry) {
438 if (entry->devfn == devfn)
439 return entry->irq;
440
441 entry = entry->next;
442 }
443
444 return INVALID_IRQ;
445}