]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
LoongArch: Add PIO for early access before ACPI PCI root register
authorHuacai Chen <chenhuacai@loongson.cn>
Thu, 25 Jun 2026 05:03:47 +0000 (13:03 +0800)
committerHuacai Chen <chenhuacai@loongson.cn>
Thu, 25 Jun 2026 05:03:47 +0000 (13:03 +0800)
For ACPI system we suppose the ISA/LPC PIO range is registered together
with PCI root bridge. But the fact is there may be some early access to
the ISA/LPC PIO range before ACPI PCI root register (most of them are
due to abnormal BIOS). Unconditionally register the ISA/LPC PIO range
usually causes ACPI PCI root register fail because of the address range
confliction. So we add a pair of helpers: acpi_add_early_pio() to add
PIO for early access, and acpi_remove_early_pio() to remove PIO before
PCI root register. Since acpi_remove_early_pio() may be called multiple
times, we add an acpi_pio flag to ensure PIO be removed only once.

Cc: <stable@vger.kernel.org>
Tested-by: Yuanzhen Gan <elysia-best@simplelinux.cn.eu.org>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
arch/loongarch/include/asm/acpi.h
arch/loongarch/kernel/acpi.c
arch/loongarch/kernel/setup.c
arch/loongarch/pci/acpi.c

index eda9d4d0a493c125127ee194a04ae565b663acaa..c05168aedcaa2d9a32173a9f295dbd9027f1ebe5 100644 (file)
@@ -38,6 +38,8 @@ static inline bool acpi_has_cpu_in_madt(void)
 extern struct list_head acpi_wakeup_device_list;
 extern struct acpi_madt_core_pic acpi_core_pic[MAX_CORE_PIC];
 
+extern void acpi_add_early_pio(void);
+extern void acpi_remove_early_pio(void);
 extern int __init parse_acpi_topology(void);
 
 #endif /* !CONFIG_ACPI */
index 058f0dbe8e8f97c6add3da9ce995c53a46071015..8f650c9ffecdecdac6bcb324123e222bd04dbcf2 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/memblock.h>
 #include <linux/of_fdt.h>
 #include <linux/serial_core.h>
+#include <linux/vmalloc.h>
 #include <asm/io.h>
 #include <asm/numa.h>
 #include <asm/loongson.h>
@@ -59,6 +60,33 @@ void __iomem *acpi_os_ioremap(acpi_physical_address phys, acpi_size size)
                return ioremap_cache(phys, size);
 }
 
+#define PIO_BASE (unsigned long)PCI_IOBASE
+#define PIO_SIZE ALIGN(ISA_IOSIZE, PAGE_SIZE)
+
+static bool acpi_pio;
+
+/* Add PIO for early access */
+void acpi_add_early_pio(void)
+{
+       if (!acpi_disabled) {
+               acpi_pio = true;
+               vmap_page_range(PIO_BASE, PIO_BASE + PIO_SIZE,
+                               LOONGSON_LIO_BASE, pgprot_device(PAGE_KERNEL));
+       }
+}
+
+/* Remove PIO for PCI register */
+void acpi_remove_early_pio(void)
+{
+       if (!acpi_pio)
+               return;
+
+       if (!acpi_disabled) {
+               acpi_pio = false;
+               vunmap_range(PIO_BASE, PIO_BASE + PIO_SIZE);
+       }
+}
+
 #ifdef CONFIG_SMP
 static int set_processor_mask(u32 id, u32 pass)
 {
index 369262117c63e43c086a61027227f683652e4b08..eaebb52bd36edb03ae978834c21de44086c877bb 100644 (file)
@@ -502,6 +502,8 @@ static __init int arch_reserve_pio_range(void)
 {
        struct device_node *np;
 
+       acpi_add_early_pio();
+
        for_each_node_by_name(np, "isa") {
                struct of_range range;
                struct of_range_parser parser;
index b02698a338eefeb2c8342bd7fda416dba67af45d..ccbcea61fcd9c42a41b4f22b9a91f865746640cb 100644 (file)
@@ -65,6 +65,8 @@ static int acpi_prepare_root_resources(struct acpi_pci_root_info *ci)
        struct resource_entry *entry, *tmp;
        struct acpi_device *device = ci->bridge;
 
+       acpi_remove_early_pio();
+
        status = acpi_pci_probe_root_resources(ci);
        if (status > 0) {
                acpi_evaluate_integer(device->handle, "PCIH", NULL, &pci_h);