From f62c49474fd6739558e308df0603350dd73b516e Mon Sep 17 00:00:00 2001 From: Tim Wawrzynczak Date: Fri, 26 Feb 2021 10:30:52 -0700 Subject: 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 Change-Id: Ica420a3d12fd1d64c8fe6e4b326fd779b3f10868 Reviewed-on: https://review.coreboot.org/c/coreboot/+/50857 Tested-by: build bot (Jenkins) Reviewed-by: Angel Pons Reviewed-by: Nico Huber --- src/southbridge/intel/bd82x6x/lpc.c | 1 + src/southbridge/intel/common/acpi_pirq_gen.c | 117 +++++++++++---------------- src/southbridge/intel/common/acpi_pirq_gen.h | 65 ++++++++++++++- src/southbridge/intel/common/rcba_pirq.c | 58 ++++++++++++- src/southbridge/intel/common/rcba_pirq.h | 5 ++ src/southbridge/intel/i82801gx/lpc.c | 1 + src/southbridge/intel/i82801ix/lpc.c | 1 + src/southbridge/intel/i82801jx/lpc.c | 1 + src/southbridge/intel/ibexpeak/lpc.c | 1 + src/southbridge/intel/lynxpoint/lpc.c | 1 + 10 files changed, 173 insertions(+), 78 deletions(-) (limited to 'src') diff --git a/src/southbridge/intel/bd82x6x/lpc.c b/src/southbridge/intel/bd82x6x/lpc.c index 4ba88637fb..43d0e04a3c 100644 --- a/src/southbridge/intel/bd82x6x/lpc.c +++ b/src/southbridge/intel/bd82x6x/lpc.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include diff --git a/src/southbridge/intel/common/acpi_pirq_gen.c b/src/southbridge/intel/common/acpi_pirq_gen.c index 18def9b4af..852ab85fcb 100644 --- a/src/southbridge/intel/common/acpi_pirq_gen.c +++ b/src/southbridge/intel/common/acpi_pirq_gen.c @@ -2,101 +2,78 @@ #include #include -#include #include #include #include #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 (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) - 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; + 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); } -static void gen_pirq_route(const enum emit_type emit, const char *lpcb_path, - char pci_int_mapping[32][4]) +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) { - 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; + 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; - 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 3fc6b77b4a..83476d16ca 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 + +#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 @@ enum pirq { 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 42e4edc18a..1037231c34 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 #include #include #include +#include #include #include #include @@ -15,7 +17,7 @@ static const u32 pirq_dir_route_reg[MAX_SLOT - MIN_SLOT + 1] = { 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 @@ enum pirq intel_common_map_pirq(const struct device *dev, const enum pci_pin pci 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 34d864cdd3..eb6d0b8291 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 + /* * 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 08019acd9b..9c3b0e9fb7 100644 --- a/src/southbridge/intel/i82801gx/lpc.c +++ b/src/southbridge/intel/i82801gx/lpc.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include diff --git a/src/southbridge/intel/i82801ix/lpc.c b/src/southbridge/intel/i82801ix/lpc.c index 667d81dbc7..32d4efc6c9 100644 --- a/src/southbridge/intel/i82801ix/lpc.c +++ b/src/southbridge/intel/i82801ix/lpc.c @@ -19,6 +19,7 @@ #include #include #include +#include #define NMI_OFF 0 diff --git a/src/southbridge/intel/i82801jx/lpc.c b/src/southbridge/intel/i82801jx/lpc.c index f70636dde6..55b4746c28 100644 --- a/src/southbridge/intel/i82801jx/lpc.c +++ b/src/southbridge/intel/i82801jx/lpc.c @@ -20,6 +20,7 @@ #include #include #include +#include #define NMI_OFF 0 diff --git a/src/southbridge/intel/ibexpeak/lpc.c b/src/southbridge/intel/ibexpeak/lpc.c index ae3233c55c..6fe44c6f14 100644 --- a/src/southbridge/intel/ibexpeak/lpc.c +++ b/src/southbridge/intel/ibexpeak/lpc.c @@ -21,6 +21,7 @@ #include #include #include +#include #define NMI_OFF 0 diff --git a/src/southbridge/intel/lynxpoint/lpc.c b/src/southbridge/intel/lynxpoint/lpc.c index 71e4d74602..8d9b4510d3 100644 --- a/src/southbridge/intel/lynxpoint/lpc.c +++ b/src/southbridge/intel/lynxpoint/lpc.c @@ -17,6 +17,7 @@ #include "pch.h" #include #include +#include #include #include #include -- cgit v1.2.3