/* SPDX-License-Identifier: GPL-2.0-only */ #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]) { 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; } 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) { 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); 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_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_pop_len(); /* package */ acpigen_pop_len(); /* else PICM */ acpigen_pop_len(); /* _PRT */ acpigen_pop_len(); /* \_SB */ }