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:
parent
d26cdb3ea3
commit
f62c49474f
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 @@ 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
|
||||
|
|
|
@ -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 @@ 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);
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue