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>
This commit is contained in:
Tim Wawrzynczak 2021-02-26 10:30:52 -07:00 committed by Nico Huber
parent d26cdb3ea3
commit f62c49474f
10 changed files with 174 additions and 79 deletions

View File

@ -21,6 +21,7 @@
#include <southbridge/intel/common/pciehp.h> #include <southbridge/intel/common/pciehp.h>
#include <southbridge/intel/common/acpi_pirq_gen.h> #include <southbridge/intel/common/acpi_pirq_gen.h>
#include <southbridge/intel/common/pmutil.h> #include <southbridge/intel/common/pmutil.h>
#include <southbridge/intel/common/rcba_pirq.h>
#include <southbridge/intel/common/rtc.h> #include <southbridge/intel/common/rtc.h>
#include <southbridge/intel/common/spi.h> #include <southbridge/intel/common/spi.h>
#include <types.h> #include <types.h>

View File

@ -2,101 +2,78 @@
#include <acpi/acpigen.h> #include <acpi/acpigen.h>
#include <acpi/acpigen_pci.h> #include <acpi/acpigen_pci.h>
#include <console/console.h>
#include <device/pci_def.h> #include <device/pci_def.h>
#include <device/pci_ops.h> #include <device/pci_ops.h>
#include <string.h> #include <string.h>
#include "acpi_pirq_gen.h" #include "acpi_pirq_gen.h"
enum emit_type { static void gen_apic_route(const struct slot_pin_irq_map *pin_irq_map,
EMIT_APIC, unsigned int map_count)
EMIT_PICM,
};
static int create_pirq_matrix(char matrix[32][4])
{ {
struct device *dev; for (unsigned int i = 0; i < map_count; i++)
int num_devs = 0; /*
* 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) { static void gen_pic_route(const struct slot_pin_irq_map *pin_irq_map,
u8 pci_dev; unsigned int map_count,
u8 int_pin; const struct pic_pirq_map *pirq_map)
{
pci_dev = PCI_SLOT(dev->path.pci.devfn); for (unsigned int i = 0; i < map_count; i++) {
int_pin = pci_read_config8(dev, PCI_INTERRUPT_PIN); enum pirq pirq = pin_irq_map[i].pic_pirq;
unsigned int pin = pin_irq_map[i].pin - PCI_INT_A;
if (int_pin == PCI_INT_NONE || int_pin > PCI_INT_D || if (pirq == PIRQ_INVALID)
matrix[pci_dev][int_pin - PCI_INT_A]
!= PIRQ_NONE)
continue; continue;
matrix[pci_dev][int_pin - PCI_INT_A] = if (pirq_map->type == PIRQ_GSI)
intel_common_map_pirq(dev, int_pin); acpigen_write_PRT_GSI_entry(pin_irq_map[i].slot,
printk(BIOS_SPEW, "ACPI_PIRQ_GEN: %s: pin=%d pirq=%d\n", pin,
dev_path(dev), int_pin - PCI_INT_A, pirq_map->gsi[pirq]);
matrix[pci_dev][int_pin - PCI_INT_A] - PIRQ_A); else
num_devs++; acpigen_write_PRT_source_entry(pin_irq_map[i].slot,
} pin,
return num_devs; pirq_map->source_path[pirq],
} 0);
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);
}
}
} }
} }
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); /* \_SB.PCI0._PRT */
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);
acpigen_write_scope("\\_SB.PCI0"); acpigen_write_scope("\\_SB.PCI0");
acpigen_write_method("_PRT", 0); acpigen_write_method("_PRT", 0);
acpigen_write_if(); acpigen_write_if();
acpigen_emit_namestring("PICM"); acpigen_emit_namestring("PICM");
acpigen_emit_byte(RETURN_OP); acpigen_emit_byte(RETURN_OP);
acpigen_write_package(num_devs); acpigen_write_package(map_count);
gen_pirq_route(EMIT_APIC, lpcb_path, pci_int_mapping); gen_apic_route(pin_irq_map, map_count);
acpigen_pop_len(); /* package */ acpigen_pop_len(); /* package */
acpigen_write_else(); acpigen_write_else();
acpigen_emit_byte(RETURN_OP); acpigen_emit_byte(RETURN_OP);
acpigen_write_package(num_devs); acpigen_write_package(map_count);
gen_pirq_route(EMIT_PICM, lpcb_path, pci_int_mapping); gen_pic_route(pin_irq_map, map_count, pirq_map);
acpigen_pop_len(); /* package */ acpigen_pop_len(); /* package */
acpigen_pop_len(); /* else PICM */ acpigen_pop_len(); /* else PICM */
acpigen_pop_len(); /* _PRT */ acpigen_pop_len(); /* _PRT */
acpigen_pop_len(); /* \_SB */ 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;
}

View File

@ -3,16 +3,20 @@
#ifndef INTEL_COMMON_ACPI_PIRQ_GEN_H #ifndef INTEL_COMMON_ACPI_PIRQ_GEN_H
#define INTEL_COMMON_ACPI_PIRQ_GEN_H #define INTEL_COMMON_ACPI_PIRQ_GEN_H
#include <device/device.h>
#define MAX_SLOTS 32
enum pci_pin { enum pci_pin {
PCI_INT_NONE = 0, PCI_INT_NONE = 0,
PCI_INT_A, PCI_INT_A,
PCI_INT_B, PCI_INT_B,
PCI_INT_C, PCI_INT_C,
PCI_INT_D, PCI_INT_D,
PCI_INT_MAX = PCI_INT_D,
}; };
enum pirq { enum pirq {
PIRQ_NONE = 0,
PIRQ_A, PIRQ_A,
PIRQ_B, PIRQ_B,
PIRQ_C, PIRQ_C,
@ -21,10 +25,63 @@ enum pirq {
PIRQ_F, PIRQ_F,
PIRQ_G, PIRQ_G,
PIRQ_H, 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, * This struct represents an assignment of slot/pin -> IRQ. Some chipsets may
const enum pci_pin pci_pin); * 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 #endif

View File

@ -1,8 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
#include <acpi/acpi_device.h>
#include <console/console.h> #include <console/console.h>
#include <device/device.h> #include <device/device.h>
#include <device/pci.h> #include <device/pci.h>
#include <stdlib.h>
#include <southbridge/intel/common/acpi_pirq_gen.h> #include <southbridge/intel/common/acpi_pirq_gen.h>
#include <southbridge/intel/common/rcba_pirq.h> #include <southbridge/intel/common/rcba_pirq.h>
#include <southbridge/intel/common/rcba.h> #include <southbridge/intel/common/rcba.h>
@ -15,7 +17,7 @@ static const u32 pirq_dir_route_reg[MAX_SLOT - MIN_SLOT + 1] = {
D26IR, D27IR, D28IR, D29IR, D30IR, D31IR, 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 slot = PCI_SLOT(dev->path.pci.devfn);
u8 shift = 4 * (pci_pin - PCI_INT_A); 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) { 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", printk(BIOS_ERR, "ACPI_PIRQ_GEN: Slot %d PCI pin %d out of bounds\n",
slot, pci_pin); slot, pci_pin);
return PIRQ_NONE; return PIRQ_INVALID;
} }
/* Slot 24 should not exist and has no D24IR but better be safe here */ /* Slot 24 should not exist and has no D24IR but better be safe here */
if (slot < MIN_SLOT || slot > MAX_SLOT || slot == 24) { if (slot < MIN_SLOT || slot > MAX_SLOT || slot == 24) {
/* non-PCH devices use 1:1 mapping. */ /* 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]; reg = pirq_dir_route_reg[slot - MIN_SLOT];
pirq = (RCBA16(reg) >> shift) & 0x7; 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);
} }

View File

@ -3,6 +3,8 @@
#ifndef SOUTHBRIDGE_INTEL_COMMON_RCBA_PIRQ_H #ifndef SOUTHBRIDGE_INTEL_COMMON_RCBA_PIRQ_H
#define 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: * The DnnIR registers use common RCBA offsets across these chipsets:
* bd82x6x, i82801, i89xx, ibexpeak, lynxpoint * bd82x6x, i82801, i89xx, ibexpeak, lynxpoint
@ -23,4 +25,7 @@
#define D20IR 0x3160 /* 16bit */ #define D20IR 0x3160 /* 16bit */
#define D19IR 0x3168 /* 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 */ #endif /* SOUTHBRIDGE_INTEL_COMMON_RCBA_PIRQ_H */

View File

@ -16,6 +16,7 @@
#include <acpi/acpigen.h> #include <acpi/acpigen.h>
#include <arch/smp/mpspec.h> #include <arch/smp/mpspec.h>
#include <southbridge/intel/common/acpi_pirq_gen.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/hpet.h>
#include <southbridge/intel/common/pmbase.h> #include <southbridge/intel/common/pmbase.h>
#include <southbridge/intel/common/spi.h> #include <southbridge/intel/common/spi.h>

View File

@ -19,6 +19,7 @@
#include <southbridge/intel/common/pciehp.h> #include <southbridge/intel/common/pciehp.h>
#include <southbridge/intel/common/pmutil.h> #include <southbridge/intel/common/pmutil.h>
#include <southbridge/intel/common/acpi_pirq_gen.h> #include <southbridge/intel/common/acpi_pirq_gen.h>
#include <southbridge/intel/common/rcba_pirq.h>
#define NMI_OFF 0 #define NMI_OFF 0

View File

@ -20,6 +20,7 @@
#include <southbridge/intel/common/pciehp.h> #include <southbridge/intel/common/pciehp.h>
#include <southbridge/intel/common/pmutil.h> #include <southbridge/intel/common/pmutil.h>
#include <southbridge/intel/common/acpi_pirq_gen.h> #include <southbridge/intel/common/acpi_pirq_gen.h>
#include <southbridge/intel/common/rcba_pirq.h>
#define NMI_OFF 0 #define NMI_OFF 0

View File

@ -21,6 +21,7 @@
#include <southbridge/intel/common/pciehp.h> #include <southbridge/intel/common/pciehp.h>
#include <southbridge/intel/common/acpi_pirq_gen.h> #include <southbridge/intel/common/acpi_pirq_gen.h>
#include <southbridge/intel/common/spi.h> #include <southbridge/intel/common/spi.h>
#include <southbridge/intel/common/rcba_pirq.h>
#define NMI_OFF 0 #define NMI_OFF 0

View File

@ -17,6 +17,7 @@
#include "pch.h" #include "pch.h"
#include <acpi/acpigen.h> #include <acpi/acpigen.h>
#include <southbridge/intel/common/acpi_pirq_gen.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/rtc.h>
#include <southbridge/intel/common/spi.h> #include <southbridge/intel/common/spi.h>
#include <types.h> #include <types.h>