sb/intel/common: Refactor _PRT generation to support GSI-based tables
Newer Intel SoCs also support _PRT tables, but they route PCI devices to
more than just PIRQs, and statically specify IRQs instead of using link
devices. Extend/refactor intel_acpi_gen_def_acpi_pirq to support this
additional use case.
Signed-off-by: Tim Wawrzynczak <twawrzynczak@chromium.org>
Change-Id: Ica420a3d12fd1d64c8fe6e4b326fd779b3f10868
Reviewed-on: https://review.coreboot.org/c/coreboot/+/50857
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Angel Pons <th3fanbus@gmail.com>
Reviewed-by: Nico Huber <nico.h@gmx.de>
diff --git a/src/southbridge/intel/bd82x6x/lpc.c b/src/southbridge/intel/bd82x6x/lpc.c
index 4ba8863..43d0e04 100644
--- a/src/southbridge/intel/bd82x6x/lpc.c
+++ b/src/southbridge/intel/bd82x6x/lpc.c
@@ -21,6 +21,7 @@
#include <southbridge/intel/common/pciehp.h>
#include <southbridge/intel/common/acpi_pirq_gen.h>
#include <southbridge/intel/common/pmutil.h>
+#include <southbridge/intel/common/rcba_pirq.h>
#include <southbridge/intel/common/rtc.h>
#include <southbridge/intel/common/spi.h>
#include <types.h>
diff --git a/src/southbridge/intel/common/acpi_pirq_gen.c b/src/southbridge/intel/common/acpi_pirq_gen.c
index 18def9b..852ab85 100644
--- a/src/southbridge/intel/common/acpi_pirq_gen.c
+++ b/src/southbridge/intel/common/acpi_pirq_gen.c
@@ -2,101 +2,78 @@
#include <acpi/acpigen.h>
#include <acpi/acpigen_pci.h>
-#include <console/console.h>
#include <device/pci_def.h>
#include <device/pci_ops.h>
#include <string.h>
#include "acpi_pirq_gen.h"
-enum emit_type {
- EMIT_APIC,
- EMIT_PICM,
-};
-
-static int create_pirq_matrix(char matrix[32][4])
+static void gen_apic_route(const struct slot_pin_irq_map *pin_irq_map,
+ unsigned int map_count)
{
- struct device *dev;
- int num_devs = 0;
+ for (unsigned int i = 0; i < map_count; i++)
+ /*
+ * The reason for subtracting PCI_INT_A from the pin given is
+ * that PCI defines pins as 1-4, and _PRT uses 0-3.
+ */
+ acpigen_write_PRT_GSI_entry(pin_irq_map[i].slot,
+ pin_irq_map[i].pin - PCI_INT_A,
+ pin_irq_map[i].apic_gsi);
+}
- for (dev = pcidev_on_root(0, 0); dev; dev = dev->sibling) {
- u8 pci_dev;
- u8 int_pin;
-
- pci_dev = PCI_SLOT(dev->path.pci.devfn);
- int_pin = pci_read_config8(dev, PCI_INTERRUPT_PIN);
-
- if (int_pin == PCI_INT_NONE || int_pin > PCI_INT_D ||
- matrix[pci_dev][int_pin - PCI_INT_A]
- != PIRQ_NONE)
+static void gen_pic_route(const struct slot_pin_irq_map *pin_irq_map,
+ unsigned int map_count,
+ const struct pic_pirq_map *pirq_map)
+{
+ for (unsigned int i = 0; i < map_count; i++) {
+ enum pirq pirq = pin_irq_map[i].pic_pirq;
+ unsigned int pin = pin_irq_map[i].pin - PCI_INT_A;
+ if (pirq == PIRQ_INVALID)
continue;
- matrix[pci_dev][int_pin - PCI_INT_A] =
- intel_common_map_pirq(dev, int_pin);
- printk(BIOS_SPEW, "ACPI_PIRQ_GEN: %s: pin=%d pirq=%d\n",
- dev_path(dev), int_pin - PCI_INT_A,
- matrix[pci_dev][int_pin - PCI_INT_A] - PIRQ_A);
- num_devs++;
- }
- return num_devs;
-}
-
-static void gen_pirq_route(const enum emit_type emit, const char *lpcb_path,
- char pci_int_mapping[32][4])
-{
- int pci_dev, int_pin;
- char buffer[DEVICE_PATH_MAX];
- char pirq;
-
- for (pci_dev = 0; pci_dev < 32; pci_dev++) {
- for (int_pin = 0; int_pin < 4; int_pin++) {
- pirq = pci_int_mapping[pci_dev][int_pin];
- if (pirq == PIRQ_NONE)
- continue;
-
- if (emit == EMIT_APIC) {
- const unsigned int gsi = 16 + pirq - PIRQ_A;
- acpigen_write_PRT_GSI_entry(pci_dev, int_pin, gsi);
- } else {
- snprintf(buffer, sizeof(buffer),
- "%s.LNK%c",
- lpcb_path, 'A' + pirq - PIRQ_A);
- acpigen_write_PRT_source_entry(pci_dev, int_pin, buffer, 0);
- }
- }
+ if (pirq_map->type == PIRQ_GSI)
+ acpigen_write_PRT_GSI_entry(pin_irq_map[i].slot,
+ pin,
+ pirq_map->gsi[pirq]);
+ else
+ acpigen_write_PRT_source_entry(pin_irq_map[i].slot,
+ pin,
+ pirq_map->source_path[pirq],
+ 0);
}
}
-void intel_acpi_gen_def_acpi_pirq(const struct device *dev)
+void intel_write_pci0_PRT(const struct slot_pin_irq_map *pin_irq_map,
+ unsigned int map_count,
+ const struct pic_pirq_map *pirq_map)
{
- const char *lpcb_path = acpi_device_path(dev);
- char pci_int_mapping[32][4];
- int num_devs;
-
- printk(BIOS_DEBUG, "Generating ACPI PIRQ entries\n");
-
- if (!lpcb_path) {
- printk(BIOS_ERR, "ACPI_PIRQ_GEN: Missing LPCB ACPI path\n");
- return;
- }
-
- memset(pci_int_mapping, 0, sizeof(pci_int_mapping));
- num_devs = create_pirq_matrix(pci_int_mapping);
-
+ /* \_SB.PCI0._PRT */
acpigen_write_scope("\\_SB.PCI0");
acpigen_write_method("_PRT", 0);
acpigen_write_if();
acpigen_emit_namestring("PICM");
acpigen_emit_byte(RETURN_OP);
- acpigen_write_package(num_devs);
- gen_pirq_route(EMIT_APIC, lpcb_path, pci_int_mapping);
+ acpigen_write_package(map_count);
+ gen_apic_route(pin_irq_map, map_count);
acpigen_pop_len(); /* package */
acpigen_write_else();
acpigen_emit_byte(RETURN_OP);
- acpigen_write_package(num_devs);
- gen_pirq_route(EMIT_PICM, lpcb_path, pci_int_mapping);
+ acpigen_write_package(map_count);
+ gen_pic_route(pin_irq_map, map_count, pirq_map);
acpigen_pop_len(); /* package */
acpigen_pop_len(); /* else PICM */
acpigen_pop_len(); /* _PRT */
acpigen_pop_len(); /* \_SB */
}
+
+bool is_slot_pin_assigned(const struct slot_pin_irq_map *pin_irq_map,
+ unsigned int map_count, unsigned int slot,
+ enum pci_pin pin)
+{
+ for (size_t i = 0; i < map_count; i++) {
+ if (pin_irq_map[i].slot == slot && pin_irq_map[i].pin == pin)
+ return true;
+ }
+
+ return false;
+}
diff --git a/src/southbridge/intel/common/acpi_pirq_gen.h b/src/southbridge/intel/common/acpi_pirq_gen.h
index 3fc6b77..83476d1 100644
--- a/src/southbridge/intel/common/acpi_pirq_gen.h
+++ b/src/southbridge/intel/common/acpi_pirq_gen.h
@@ -3,16 +3,20 @@
#ifndef INTEL_COMMON_ACPI_PIRQ_GEN_H
#define INTEL_COMMON_ACPI_PIRQ_GEN_H
+#include <device/device.h>
+
+#define MAX_SLOTS 32
+
enum pci_pin {
PCI_INT_NONE = 0,
PCI_INT_A,
PCI_INT_B,
PCI_INT_C,
PCI_INT_D,
+ PCI_INT_MAX = PCI_INT_D,
};
enum pirq {
- PIRQ_NONE = 0,
PIRQ_A,
PIRQ_B,
PIRQ_C,
@@ -21,10 +25,63 @@
PIRQ_F,
PIRQ_G,
PIRQ_H,
+ PIRQ_COUNT,
+ PIRQ_INVALID = 0xff,
};
-void intel_acpi_gen_def_acpi_pirq(const struct device *dev);
-enum pirq intel_common_map_pirq(const struct device *dev,
- const enum pci_pin pci_pin);
+/*
+ * This struct represents an assignment of slot/pin -> IRQ. Some chipsets may
+ * want to provide both PIC-mode and APIC-mode IRQs (e.g. selected using PICM
+ * set by the OS), therefore a field for each of a PIRQ for PIC-mode and a
+ * GSI for APIC-mode are provided.
+ *
+ * For APIC mode, only GSIs are supported (`acpi_gsi`).
+ *
+ * For PIC mode, if the pirq_map_type is PIRQ_GSI, then `pic_pirq` is used as an
+ * index into `struct pic_pirq_map.gsi`, or for SOURCE_PATH, `pic_pirq` indexes
+ * into `struct pic_pirq_map.source_path` to pick the path to the LNKx device.
+ *
+ * The reasoning for this structure is related to older vs. newer Intel
+ * platforms; older platforms supported routing of PCI IRQs to a PIRQ
+ * only. Newer platforms support routing IRQs to either a PIRQ or (for some PCI
+ * devices) a non-PIRQ GSI.
+ */
+struct slot_pin_irq_map {
+ unsigned int slot;
+ enum pci_pin pin;
+ /* PIRQ # for PIC mode */
+ unsigned int pic_pirq;
+ /* GSI # for APIC mode */
+ unsigned int apic_gsi;
+};
+
+enum pirq_map_type {
+ PIRQ_GSI,
+ PIRQ_SOURCE_PATH,
+};
+
+/*
+ * A PIRQ can be either be statically assigned a GSI or OSPM can use the Methods
+ * on the ACPI device (source_path) to assign IRQs at runtime.
+ */
+struct pic_pirq_map {
+ enum pirq_map_type type;
+ union {
+ unsigned int gsi[PIRQ_COUNT];
+ char source_path[PIRQ_COUNT][DEVICE_PATH_MAX];
+ };
+};
+
+/*
+ * Generate an ACPI _PRT table by providing PIRQ and/or GSI information for each
+ * slot/pin combination, and optionally providing paths to LNKx devices that can
+ * provide IRQs in PIC mode.
+ */
+void intel_write_pci0_PRT(const struct slot_pin_irq_map *pin_irq_map,
+ unsigned int map_count,
+ const struct pic_pirq_map *pirq_map);
+
+bool is_slot_pin_assigned(const struct slot_pin_irq_map *pin_irq_map,
+ unsigned int map_count, unsigned int slot, unsigned int pin);
#endif
diff --git a/src/southbridge/intel/common/rcba_pirq.c b/src/southbridge/intel/common/rcba_pirq.c
index 42e4edc..1037231 100644
--- a/src/southbridge/intel/common/rcba_pirq.c
+++ b/src/southbridge/intel/common/rcba_pirq.c
@@ -1,8 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0-only */
+#include <acpi/acpi_device.h>
#include <console/console.h>
#include <device/device.h>
#include <device/pci.h>
+#include <stdlib.h>
#include <southbridge/intel/common/acpi_pirq_gen.h>
#include <southbridge/intel/common/rcba_pirq.h>
#include <southbridge/intel/common/rcba.h>
@@ -15,7 +17,7 @@
D26IR, D27IR, D28IR, D29IR, D30IR, D31IR,
};
-enum pirq intel_common_map_pirq(const struct device *dev, const enum pci_pin pci_pin)
+static enum pirq map_pirq(const struct device *dev, const enum pci_pin pci_pin)
{
u8 slot = PCI_SLOT(dev->path.pci.devfn);
u8 shift = 4 * (pci_pin - PCI_INT_A);
@@ -25,18 +27,66 @@
if (pci_pin < PCI_INT_A || pci_pin > PCI_INT_D) {
printk(BIOS_ERR, "ACPI_PIRQ_GEN: Slot %d PCI pin %d out of bounds\n",
slot, pci_pin);
- return PIRQ_NONE;
+ return PIRQ_INVALID;
}
/* Slot 24 should not exist and has no D24IR but better be safe here */
if (slot < MIN_SLOT || slot > MAX_SLOT || slot == 24) {
/* non-PCH devices use 1:1 mapping. */
- return (enum pirq)pci_pin;
+ return (enum pirq)(pci_pin - PCI_INT_A);
}
reg = pirq_dir_route_reg[slot - MIN_SLOT];
pirq = (RCBA16(reg) >> shift) & 0x7;
- return (enum pirq)(PIRQ_A + pirq);
+ return (enum pirq)pirq;
+}
+
+void intel_acpi_gen_def_acpi_pirq(const struct device *lpc)
+{
+ struct slot_pin_irq_map *pin_irq_map;
+ const char *lpcb_path = acpi_device_path(lpc);
+ struct pic_pirq_map pirq_map = {0};
+ unsigned int map_count = 0;
+ int i;
+
+ if (!lpcb_path) {
+ printk(BIOS_ERR, "ACPI_PIRQ_GEN: Missing LPCB ACPI path\n");
+ return;
+ }
+
+ printk(BIOS_DEBUG, "Generating ACPI PIRQ entries\n");
+
+ pin_irq_map = calloc(sizeof(struct slot_pin_irq_map), MAX_SLOTS * PCI_INT_MAX);
+ pirq_map.type = PIRQ_SOURCE_PATH;
+ for (i = 0; i < PIRQ_COUNT; i++)
+ snprintf(pirq_map.source_path[i], sizeof(pirq_map.source_path[i]),
+ "%s.LNK%c", lpcb_path, 'A' + i);
+
+ for (struct device *dev = pcidev_on_root(0, 0); dev; dev = dev->sibling) {
+ const u8 pci_dev = PCI_SLOT(dev->path.pci.devfn);
+ const u8 int_pin = pci_read_config8(dev, PCI_INTERRUPT_PIN);
+
+ if (int_pin < PCI_INT_A || int_pin > PCI_INT_D)
+ continue;
+
+ if (is_slot_pin_assigned(pin_irq_map, map_count, pci_dev, int_pin))
+ continue;
+
+ enum pirq pirq = map_pirq(dev, int_pin);
+ pin_irq_map[map_count].slot = pci_dev;
+ pin_irq_map[map_count].pin = (enum pci_pin)int_pin;
+ pin_irq_map[map_count].pic_pirq = pirq;
+ /* PIRQs are mapped to GSIs starting at 16 */
+ pin_irq_map[map_count].apic_gsi = 16 + (unsigned int)pirq;
+ printk(BIOS_SPEW, "ACPI_PIRQ_GEN: %s: pin=%d pirq=%d\n",
+ dev_path(dev), int_pin - PCI_INT_A,
+ pin_irq_map[map_count].pic_pirq);
+ map_count++;
+ }
+
+ intel_write_pci0_PRT(pin_irq_map, map_count, &pirq_map);
+
+ free(pin_irq_map);
}
diff --git a/src/southbridge/intel/common/rcba_pirq.h b/src/southbridge/intel/common/rcba_pirq.h
index 34d864c..eb6d0b8 100644
--- a/src/southbridge/intel/common/rcba_pirq.h
+++ b/src/southbridge/intel/common/rcba_pirq.h
@@ -3,6 +3,8 @@
#ifndef SOUTHBRIDGE_INTEL_COMMON_RCBA_PIRQ_H
#define SOUTHBRIDGE_INTEL_COMMON_RCBA_PIRQ_H
+#include <device/device.h>
+
/*
* The DnnIR registers use common RCBA offsets across these chipsets:
* bd82x6x, i82801, i89xx, ibexpeak, lynxpoint
@@ -23,4 +25,7 @@
#define D20IR 0x3160 /* 16bit */
#define D19IR 0x3168 /* 16bit */
+/* Generate an ACPI _PRT table for chipsets that use PIRQs exclusively */
+void intel_acpi_gen_def_acpi_pirq(const struct device *dev);
+
#endif /* SOUTHBRIDGE_INTEL_COMMON_RCBA_PIRQ_H */
diff --git a/src/southbridge/intel/i82801gx/lpc.c b/src/southbridge/intel/i82801gx/lpc.c
index 08019ac..9c3b0e9 100644
--- a/src/southbridge/intel/i82801gx/lpc.c
+++ b/src/southbridge/intel/i82801gx/lpc.c
@@ -16,6 +16,7 @@
#include <acpi/acpigen.h>
#include <arch/smp/mpspec.h>
#include <southbridge/intel/common/acpi_pirq_gen.h>
+#include <southbridge/intel/common/rcba_pirq.h>
#include <southbridge/intel/common/hpet.h>
#include <southbridge/intel/common/pmbase.h>
#include <southbridge/intel/common/spi.h>
diff --git a/src/southbridge/intel/i82801ix/lpc.c b/src/southbridge/intel/i82801ix/lpc.c
index 667d81d..32d4efc 100644
--- a/src/southbridge/intel/i82801ix/lpc.c
+++ b/src/southbridge/intel/i82801ix/lpc.c
@@ -19,6 +19,7 @@
#include <southbridge/intel/common/pciehp.h>
#include <southbridge/intel/common/pmutil.h>
#include <southbridge/intel/common/acpi_pirq_gen.h>
+#include <southbridge/intel/common/rcba_pirq.h>
#define NMI_OFF 0
diff --git a/src/southbridge/intel/i82801jx/lpc.c b/src/southbridge/intel/i82801jx/lpc.c
index f70636d..55b4746 100644
--- a/src/southbridge/intel/i82801jx/lpc.c
+++ b/src/southbridge/intel/i82801jx/lpc.c
@@ -20,6 +20,7 @@
#include <southbridge/intel/common/pciehp.h>
#include <southbridge/intel/common/pmutil.h>
#include <southbridge/intel/common/acpi_pirq_gen.h>
+#include <southbridge/intel/common/rcba_pirq.h>
#define NMI_OFF 0
diff --git a/src/southbridge/intel/ibexpeak/lpc.c b/src/southbridge/intel/ibexpeak/lpc.c
index ae3233c..6fe44c6 100644
--- a/src/southbridge/intel/ibexpeak/lpc.c
+++ b/src/southbridge/intel/ibexpeak/lpc.c
@@ -21,6 +21,7 @@
#include <southbridge/intel/common/pciehp.h>
#include <southbridge/intel/common/acpi_pirq_gen.h>
#include <southbridge/intel/common/spi.h>
+#include <southbridge/intel/common/rcba_pirq.h>
#define NMI_OFF 0
diff --git a/src/southbridge/intel/lynxpoint/lpc.c b/src/southbridge/intel/lynxpoint/lpc.c
index 71e4d74..8d9b451 100644
--- a/src/southbridge/intel/lynxpoint/lpc.c
+++ b/src/southbridge/intel/lynxpoint/lpc.c
@@ -17,6 +17,7 @@
#include "pch.h"
#include <acpi/acpigen.h>
#include <southbridge/intel/common/acpi_pirq_gen.h>
+#include <southbridge/intel/common/rcba_pirq.h>
#include <southbridge/intel/common/rtc.h>
#include <southbridge/intel/common/spi.h>
#include <types.h>