Merge 'irq/loongarch', 'pci/ctrl/loongson' and 'pci/header-cleanup-immutable'
authorHuacai Chen <chenhuacai@loongson.cn>
Thu, 11 Aug 2022 13:06:14 +0000 (21:06 +0800)
committerHuacai Chen <chenhuacai@loongson.cn>
Thu, 11 Aug 2022 13:06:14 +0000 (21:06 +0800)
LoongArch architecture changes for 5.20 depend on the irqchip and pci
changes to work, so merge them to create a base.

66 files changed:
arch/alpha/include/asm/dma.h
arch/alpha/include/asm/pci.h
arch/arc/include/asm/dma.h
arch/arm/include/asm/dma.h
arch/arm/include/asm/pci.h
arch/arm64/include/asm/pci.h
arch/csky/include/asm/pci.h
arch/ia64/include/asm/dma.h
arch/ia64/include/asm/pci.h
arch/loongarch/Kconfig
arch/loongarch/include/asm/acpi.h
arch/loongarch/include/asm/irq.h
arch/loongarch/kernel/acpi.c
arch/loongarch/kernel/irq.c
arch/loongarch/kernel/time.c
arch/m68k/include/asm/dma.h
arch/m68k/include/asm/pci.h
arch/microblaze/include/asm/dma.h
arch/mips/include/asm/dma.h
arch/mips/include/asm/mach-loongson64/irq.h
arch/mips/include/asm/pci.h
arch/parisc/include/asm/dma.h
arch/parisc/include/asm/pci.h
arch/powerpc/include/asm/dma.h
arch/powerpc/include/asm/pci.h
arch/riscv/include/asm/pci.h
arch/s390/include/asm/dma.h
arch/s390/include/asm/pci.h
arch/sh/include/asm/dma.h
arch/sh/include/asm/pci.h
arch/sparc/include/asm/dma.h
arch/sparc/include/asm/pci.h
arch/um/include/asm/pci.h
arch/x86/include/asm/dma.h
arch/x86/include/asm/pci.h
arch/x86/kernel/cpu/cyrix.c
arch/xtensa/include/asm/dma.h
arch/xtensa/include/asm/pci.h
drivers/acpi/bus.c
drivers/acpi/irq.c
drivers/acpi/pci_mcfg.c
drivers/comedi/drivers/comedi_isadma.c
drivers/irqchip/Kconfig
drivers/irqchip/Makefile
drivers/irqchip/irq-gic-v3.c
drivers/irqchip/irq-gic.c
drivers/irqchip/irq-loongarch-cpu.c [new file with mode: 0644]
drivers/irqchip/irq-loongson-eiointc.c [new file with mode: 0644]
drivers/irqchip/irq-loongson-liointc.c
drivers/irqchip/irq-loongson-pch-lpc.c [new file with mode: 0644]
drivers/irqchip/irq-loongson-pch-msi.c
drivers/irqchip/irq-loongson-pch-pic.c
drivers/pci/controller/Kconfig
drivers/pci/controller/pci-loongson.c
drivers/pci/pci.c
drivers/pci/quirks.c
drivers/pnp/resource.c
include/asm-generic/pci.h
include/asm-generic/pci_iomap.h
include/linux/acpi.h
include/linux/cpuhotplug.h
include/linux/irq.h
include/linux/isa-dma.h [new file with mode: 0644]
include/linux/pci-ecam.h
kernel/irq/generic-chip.c
sound/core/isadma.c

index 28610ea..a04d76b 100644 (file)
@@ -365,13 +365,4 @@ extern void free_dma(unsigned int dmanr);  /* release it again */
 #define KERNEL_HAVE_CHECK_DMA
 extern int check_dma(unsigned int dmanr);
 
-/* From PCI */
-
-#ifdef CONFIG_PCI
-extern int isa_dma_bridge_buggy;
-#else
-#define isa_dma_bridge_buggy   (0)
-#endif
-
-
 #endif /* _ASM_DMA_H */
index cf6bc1e..6312656 100644 (file)
@@ -56,12 +56,6 @@ struct pci_controller {
 
 /* IOMMU controls.  */
 
-/* TODO: integrate with include/asm-generic/pci.h ? */
-static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel)
-{
-       return channel ? 15 : 14;
-}
-
 #define pci_domain_nr(bus) ((struct pci_controller *)(bus)->sysdata)->index
 
 static inline int pci_proc_domain(struct pci_bus *bus)
index 5b744f4..0243102 100644 (file)
@@ -7,10 +7,5 @@
 #define ASM_ARC_DMA_H
 
 #define MAX_DMA_ADDRESS 0xC0000000
-#ifdef CONFIG_PCI
-extern int isa_dma_bridge_buggy;
-#else
-#define isa_dma_bridge_buggy   0
-#endif
 
 #endif
index 45180a2..05f29a7 100644 (file)
@@ -143,10 +143,4 @@ extern int  get_dma_residue(unsigned int chan);
 
 #endif /* CONFIG_ISA_DMA_API */
 
-#ifdef CONFIG_PCI
-extern int isa_dma_bridge_buggy;
-#else
-#define isa_dma_bridge_buggy    (0)
-#endif
-
 #endif /* __ASM_ARM_DMA_H */
index 68e6f25..5916b88 100644 (file)
@@ -22,11 +22,6 @@ static inline int pci_proc_domain(struct pci_bus *bus)
 #define HAVE_PCI_MMAP
 #define ARCH_GENERIC_PCI_MMAP_RESOURCE
 
-static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel)
-{
-       return channel ? 15 : 14;
-}
-
 extern void pcibios_report_status(unsigned int status_mask, int warn);
 
 #endif /* __KERNEL__ */
index b33ca26..016eb6b 100644 (file)
@@ -9,7 +9,6 @@
 #include <asm/io.h>
 
 #define PCIBIOS_MIN_IO         0x1000
-#define PCIBIOS_MIN_MEM                0
 
 /*
  * Set to 1 if the kernel should re-assign all PCI bus numbers
        (pci_has_flag(PCI_REASSIGN_ALL_BUS))
 
 #define arch_can_pci_mmap_wc() 1
-#define ARCH_GENERIC_PCI_MMAP_RESOURCE 1
 
-extern int isa_dma_bridge_buggy;
-
-#ifdef CONFIG_PCI
-static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel)
-{
-       /* no legacy IRQ on arm64 */
-       return -ENODEV;
-}
-
-static inline int pci_proc_domain(struct pci_bus *bus)
-{
-       return 1;
-}
-#endif  /* CONFIG_PCI */
+/* Generic PCI */
+#include <asm-generic/pci.h>
 
 #endif  /* __ASM_PCI_H */
index ebc765b..42724c6 100644 (file)
@@ -9,26 +9,7 @@
 
 #include <asm/io.h>
 
-#define PCIBIOS_MIN_IO         0
-#define PCIBIOS_MIN_MEM                0
-
-/* C-SKY shim does not initialize PCI bus */
-#define pcibios_assign_all_busses() 1
-
-extern int isa_dma_bridge_buggy;
-
-#ifdef CONFIG_PCI
-static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel)
-{
-       /* no legacy IRQ on csky */
-       return -ENODEV;
-}
-
-static inline int pci_proc_domain(struct pci_bus *bus)
-{
-       /* always show the domain in /proc */
-       return 1;
-}
-#endif  /* CONFIG_PCI */
+/* Generic PCI */
+#include <asm-generic/pci.h>
 
 #endif  /* __ASM_CSKY_PCI_H */
index 59625e9..eaed262 100644 (file)
@@ -12,8 +12,6 @@
 
 extern unsigned long MAX_DMA_ADDRESS;
 
-extern int isa_dma_bridge_buggy;
-
 #define free_dma(x)
 
 #endif /* _ASM_IA64_DMA_H */
index 8c163d1..fa8f545 100644 (file)
@@ -63,10 +63,4 @@ static inline int pci_proc_domain(struct pci_bus *bus)
        return (pci_domain_nr(bus) != 0);
 }
 
-#define HAVE_ARCH_PCI_GET_LEGACY_IDE_IRQ
-static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel)
-{
-       return channel ? isa_irq_to_vector(15) : isa_irq_to_vector(14);
-}
-
 #endif /* _ASM_IA64_PCI_H */
index 62b5b07..83fe390 100644 (file)
@@ -2,6 +2,7 @@
 config LOONGARCH
        bool
        default y
+       select ACPI_GENERIC_GSI if ACPI
        select ACPI_SYSTEM_POWER_STATES_SUPPORT if ACPI
        select ARCH_BINFMT_ELF_STATE
        select ARCH_ENABLE_MEMORY_HOTPLUG
index 62044cd..c510821 100644 (file)
@@ -31,6 +31,148 @@ static inline bool acpi_has_cpu_in_madt(void)
 
 extern struct list_head acpi_wakeup_device_list;
 
+/*
+ * Temporary definitions until the core ACPICA code gets updated (see
+ * 1656837932-18257-1-git-send-email-lvjianmin@loongson.cn and its
+ * follow-ups for the "rationale").
+ *
+ * Once the "legal reasons" are cleared and that the code is merged,
+ * this can be dropped entierely.
+ */
+#if (ACPI_CA_VERSION == 0x20220331 && !defined(LOONGARCH_ACPICA_EXT))
+
+#define LOONGARCH_ACPICA_EXT   1
+
+#define        ACPI_MADT_TYPE_CORE_PIC         17
+#define        ACPI_MADT_TYPE_LIO_PIC          18
+#define        ACPI_MADT_TYPE_HT_PIC           19
+#define        ACPI_MADT_TYPE_EIO_PIC          20
+#define        ACPI_MADT_TYPE_MSI_PIC          21
+#define        ACPI_MADT_TYPE_BIO_PIC          22
+#define        ACPI_MADT_TYPE_LPC_PIC          23
+
+/* Values for Version field above */
+
+enum acpi_madt_core_pic_version {
+       ACPI_MADT_CORE_PIC_VERSION_NONE = 0,
+       ACPI_MADT_CORE_PIC_VERSION_V1 = 1,
+       ACPI_MADT_CORE_PIC_VERSION_RESERVED = 2 /* 2 and greater are reserved */
+};
+
+enum acpi_madt_lio_pic_version {
+       ACPI_MADT_LIO_PIC_VERSION_NONE = 0,
+       ACPI_MADT_LIO_PIC_VERSION_V1 = 1,
+       ACPI_MADT_LIO_PIC_VERSION_RESERVED = 2  /* 2 and greater are reserved */
+};
+
+enum acpi_madt_eio_pic_version {
+       ACPI_MADT_EIO_PIC_VERSION_NONE = 0,
+       ACPI_MADT_EIO_PIC_VERSION_V1 = 1,
+       ACPI_MADT_EIO_PIC_VERSION_RESERVED = 2  /* 2 and greater are reserved */
+};
+
+enum acpi_madt_ht_pic_version {
+       ACPI_MADT_HT_PIC_VERSION_NONE = 0,
+       ACPI_MADT_HT_PIC_VERSION_V1 = 1,
+       ACPI_MADT_HT_PIC_VERSION_RESERVED = 2   /* 2 and greater are reserved */
+};
+
+enum acpi_madt_bio_pic_version {
+       ACPI_MADT_BIO_PIC_VERSION_NONE = 0,
+       ACPI_MADT_BIO_PIC_VERSION_V1 = 1,
+       ACPI_MADT_BIO_PIC_VERSION_RESERVED = 2  /* 2 and greater are reserved */
+};
+
+enum acpi_madt_msi_pic_version {
+       ACPI_MADT_MSI_PIC_VERSION_NONE = 0,
+       ACPI_MADT_MSI_PIC_VERSION_V1 = 1,
+       ACPI_MADT_MSI_PIC_VERSION_RESERVED = 2  /* 2 and greater are reserved */
+};
+
+enum acpi_madt_lpc_pic_version {
+       ACPI_MADT_LPC_PIC_VERSION_NONE = 0,
+       ACPI_MADT_LPC_PIC_VERSION_V1 = 1,
+       ACPI_MADT_LPC_PIC_VERSION_RESERVED = 2  /* 2 and greater are reserved */
+};
+
+#pragma pack(1)
+
+/* Core Interrupt Controller */
+
+struct acpi_madt_core_pic {
+       struct acpi_subtable_header header;
+       u8 version;
+       u32 processor_id;
+       u32 core_id;
+       u32 flags;
+};
+
+/* Legacy I/O Interrupt Controller */
+
+struct acpi_madt_lio_pic {
+       struct acpi_subtable_header header;
+       u8 version;
+       u64 address;
+       u16 size;
+       u8 cascade[2];
+       u32 cascade_map[2];
+};
+
+/* Extend I/O Interrupt Controller */
+
+struct acpi_madt_eio_pic {
+       struct acpi_subtable_header header;
+       u8 version;
+       u8 cascade;
+       u8 node;
+       u64 node_map;
+};
+
+/* HT Interrupt Controller */
+
+struct acpi_madt_ht_pic {
+       struct acpi_subtable_header header;
+       u8 version;
+       u64 address;
+       u16 size;
+       u8 cascade[8];
+};
+
+/* Bridge I/O Interrupt Controller */
+
+struct acpi_madt_bio_pic {
+       struct acpi_subtable_header header;
+       u8 version;
+       u64 address;
+       u16 size;
+       u16 id;
+       u16 gsi_base;
+};
+
+/* MSI Interrupt Controller */
+
+struct acpi_madt_msi_pic {
+       struct acpi_subtable_header header;
+       u8 version;
+       u64 msg_address;
+       u32 start;
+       u32 count;
+};
+
+/* LPC Interrupt Controller */
+
+struct acpi_madt_lpc_pic {
+       struct acpi_subtable_header header;
+       u8 version;
+       u64 address;
+       u16 size;
+       u8 cascade;
+};
+
+#pragma pack()
+
+#endif
+
 #endif /* !CONFIG_ACPI */
 
 #define ACPI_TABLE_UPGRADE_MAX_PHYS ARCH_LOW_ADDRESS_LIMIT
index ace3ea6..149b212 100644 (file)
@@ -35,9 +35,6 @@ static inline bool on_irq_stack(int cpu, unsigned long sp)
        return (low <= sp && sp <= high);
 }
 
-int get_ipi_irq(void);
-int get_pmc_irq(void);
-int get_timer_irq(void);
 void spurious_interrupt(void);
 
 #define NR_IRQS_LEGACY 16
@@ -48,6 +45,14 @@ void arch_trigger_cpumask_backtrace(const struct cpumask *mask, bool exclude_sel
 #define MAX_IO_PICS 2
 #define NR_IRQS        (64 + (256 * MAX_IO_PICS))
 
+struct acpi_vector_group {
+       int node;
+       int pci_segment;
+       struct irq_domain *parent;
+};
+extern struct acpi_vector_group pch_group[MAX_IO_PICS];
+extern struct acpi_vector_group msi_group[MAX_IO_PICS];
+
 #define CORES_PER_EIO_NODE     4
 
 #define LOONGSON_CPU_UART0_VEC         10 /* CPU UART0 */
@@ -79,15 +84,6 @@ void arch_trigger_cpumask_backtrace(const struct cpumask *mask, bool exclude_sel
 extern int find_pch_pic(u32 gsi);
 extern int eiointc_get_node(int id);
 
-static inline void eiointc_enable(void)
-{
-       uint64_t misc;
-
-       misc = iocsr_read64(LOONGARCH_IOCSR_MISC_FUNC);
-       misc |= IOCSR_MISC_FUNC_EXT_IOI_EN;
-       iocsr_write64(misc, LOONGARCH_IOCSR_MISC_FUNC);
-}
-
 struct acpi_madt_lio_pic;
 struct acpi_madt_eio_pic;
 struct acpi_madt_ht_pic;
@@ -95,21 +91,29 @@ struct acpi_madt_bio_pic;
 struct acpi_madt_msi_pic;
 struct acpi_madt_lpc_pic;
 
-struct irq_domain *loongarch_cpu_irq_init(void);
-
-struct irq_domain *liointc_acpi_init(struct irq_domain *parent,
+int liointc_acpi_init(struct irq_domain *parent,
                                        struct acpi_madt_lio_pic *acpi_liointc);
-struct irq_domain *eiointc_acpi_init(struct irq_domain *parent,
+int eiointc_acpi_init(struct irq_domain *parent,
                                        struct acpi_madt_eio_pic *acpi_eiointc);
 
 struct irq_domain *htvec_acpi_init(struct irq_domain *parent,
                                        struct acpi_madt_ht_pic *acpi_htvec);
-struct irq_domain *pch_lpc_acpi_init(struct irq_domain *parent,
+int pch_lpc_acpi_init(struct irq_domain *parent,
                                        struct acpi_madt_lpc_pic *acpi_pchlpc);
-struct irq_domain *pch_msi_acpi_init(struct irq_domain *parent,
+#if IS_ENABLED(CONFIG_LOONGSON_PCH_MSI)
+int pch_msi_acpi_init(struct irq_domain *parent,
                                        struct acpi_madt_msi_pic *acpi_pchmsi);
-struct irq_domain *pch_pic_acpi_init(struct irq_domain *parent,
+#else
+static inline int pch_msi_acpi_init(struct irq_domain *parent,
+                                       struct acpi_madt_msi_pic *acpi_pchmsi)
+{
+       return 0;
+}
+#endif
+int pch_pic_acpi_init(struct irq_domain *parent,
                                        struct acpi_madt_bio_pic *acpi_pchpic);
+int find_pch_pic(u32 gsi);
+struct fwnode_handle *get_pch_msi_handle(int pci_segment);
 
 extern struct acpi_madt_lio_pic *acpi_liointc;
 extern struct acpi_madt_eio_pic *acpi_eiointc[MAX_IO_PICS];
@@ -119,11 +123,10 @@ extern struct acpi_madt_lpc_pic *acpi_pchlpc;
 extern struct acpi_madt_msi_pic *acpi_pchmsi[MAX_IO_PICS];
 extern struct acpi_madt_bio_pic *acpi_pchpic[MAX_IO_PICS];
 
-extern struct irq_domain *cpu_domain;
-extern struct irq_domain *liointc_domain;
-extern struct irq_domain *pch_lpc_domain;
-extern struct irq_domain *pch_msi_domain[MAX_IO_PICS];
-extern struct irq_domain *pch_pic_domain[MAX_IO_PICS];
+extern struct fwnode_handle *cpuintc_handle;
+extern struct fwnode_handle *liointc_handle;
+extern struct fwnode_handle *pch_lpc_handle;
+extern struct fwnode_handle *pch_pic_handle[MAX_IO_PICS];
 
 extern irqreturn_t loongson3_ipi_interrupt(int irq, void *dev);
 
index bb729ee..03aa145 100644 (file)
@@ -25,7 +25,6 @@ EXPORT_SYMBOL(acpi_pci_disabled);
 int acpi_strict = 1; /* We have no workarounds on LoongArch */
 int num_processors;
 int disabled_cpus;
-enum acpi_irq_model_id acpi_irq_model = ACPI_IRQ_MODEL_PLATFORM;
 
 u64 acpi_saved_sp;
 
@@ -33,70 +32,6 @@ u64 acpi_saved_sp;
 
 #define PREFIX                 "ACPI: "
 
-int acpi_gsi_to_irq(u32 gsi, unsigned int *irqp)
-{
-       if (irqp != NULL)
-               *irqp = acpi_register_gsi(NULL, gsi, -1, -1);
-       return (*irqp >= 0) ? 0 : -EINVAL;
-}
-EXPORT_SYMBOL_GPL(acpi_gsi_to_irq);
-
-int acpi_isa_irq_to_gsi(unsigned int isa_irq, u32 *gsi)
-{
-       if (gsi)
-               *gsi = isa_irq;
-       return 0;
-}
-
-/*
- * success: return IRQ number (>=0)
- * failure: return < 0
- */
-int acpi_register_gsi(struct device *dev, u32 gsi, int trigger, int polarity)
-{
-       struct irq_fwspec fwspec;
-
-       switch (gsi) {
-       case GSI_MIN_CPU_IRQ ... GSI_MAX_CPU_IRQ:
-               fwspec.fwnode = liointc_domain->fwnode;
-               fwspec.param[0] = gsi - GSI_MIN_CPU_IRQ;
-               fwspec.param_count = 1;
-
-               return irq_create_fwspec_mapping(&fwspec);
-
-       case GSI_MIN_LPC_IRQ ... GSI_MAX_LPC_IRQ:
-               if (!pch_lpc_domain)
-                       return -EINVAL;
-
-               fwspec.fwnode = pch_lpc_domain->fwnode;
-               fwspec.param[0] = gsi - GSI_MIN_LPC_IRQ;
-               fwspec.param[1] = acpi_dev_get_irq_type(trigger, polarity);
-               fwspec.param_count = 2;
-
-               return irq_create_fwspec_mapping(&fwspec);
-
-       case GSI_MIN_PCH_IRQ ... GSI_MAX_PCH_IRQ:
-               if (!pch_pic_domain[0])
-                       return -EINVAL;
-
-               fwspec.fwnode = pch_pic_domain[0]->fwnode;
-               fwspec.param[0] = gsi - GSI_MIN_PCH_IRQ;
-               fwspec.param[1] = IRQ_TYPE_LEVEL_HIGH;
-               fwspec.param_count = 2;
-
-               return irq_create_fwspec_mapping(&fwspec);
-       }
-
-       return -EINVAL;
-}
-EXPORT_SYMBOL_GPL(acpi_register_gsi);
-
-void acpi_unregister_gsi(u32 gsi)
-{
-
-}
-EXPORT_SYMBOL_GPL(acpi_unregister_gsi);
-
 void __init __iomem * __acpi_map_table(unsigned long phys, unsigned long size)
 {
 
index b34b8d7..1ba19c7 100644 (file)
@@ -25,12 +25,8 @@ DEFINE_PER_CPU(unsigned long, irq_stack);
 DEFINE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat);
 EXPORT_PER_CPU_SYMBOL(irq_stat);
 
-struct irq_domain *cpu_domain;
-struct irq_domain *liointc_domain;
-struct irq_domain *pch_lpc_domain;
-struct irq_domain *pch_msi_domain[MAX_IO_PICS];
-struct irq_domain *pch_pic_domain[MAX_IO_PICS];
-
+struct acpi_vector_group pch_group[MAX_IO_PICS];
+struct acpi_vector_group msi_group[MAX_IO_PICS];
 /*
  * 'what should we do if we get a hw irq event on an illegal vector'.
  * each architecture has to answer this themselves.
@@ -56,6 +52,51 @@ int arch_show_interrupts(struct seq_file *p, int prec)
        return 0;
 }
 
+static int __init early_pci_mcfg_parse(struct acpi_table_header *header)
+{
+       struct acpi_table_mcfg *mcfg;
+       struct acpi_mcfg_allocation *mptr;
+       int i, n;
+
+       if (header->length < sizeof(struct acpi_table_mcfg))
+               return -EINVAL;
+
+       n = (header->length - sizeof(struct acpi_table_mcfg)) /
+                                       sizeof(struct acpi_mcfg_allocation);
+       mcfg = (struct acpi_table_mcfg *)header;
+       mptr = (struct acpi_mcfg_allocation *) &mcfg[1];
+
+       for (i = 0; i < n; i++, mptr++) {
+               msi_group[i].pci_segment = mptr->pci_segment;
+               pch_group[i].node = msi_group[i].node = (mptr->address >> 44) & 0xf;
+       }
+
+       return 0;
+}
+
+static void __init init_vec_parent_group(void)
+{
+       int i;
+
+       for (i = 0; i < MAX_IO_PICS; i++) {
+               msi_group[i].pci_segment = -1;
+               msi_group[i].node = -1;
+               pch_group[i].node = -1;
+       }
+
+       acpi_table_parse(ACPI_SIG_MCFG, early_pci_mcfg_parse);
+}
+
+static int __init get_ipi_irq(void)
+{
+       struct irq_domain *d = irq_find_matching_fwnode(cpuintc_handle, DOMAIN_BUS_ANY);
+
+       if (d)
+               return irq_create_mapping(d, EXCCODE_IPI - EXCCODE_INT_START);
+
+       return -EINVAL;
+}
+
 void __init init_IRQ(void)
 {
        int i;
@@ -69,9 +110,12 @@ void __init init_IRQ(void)
        clear_csr_ecfg(ECFG0_IM);
        clear_csr_estat(ESTATF_IP);
 
+       init_vec_parent_group();
        irqchip_init();
 #ifdef CONFIG_SMP
-       ipi_irq = EXCCODE_IPI - EXCCODE_INT_START;
+       ipi_irq = get_ipi_irq();
+       if (ipi_irq < 0)
+               panic("IPI IRQ mapping failed\n");
        irq_set_percpu_devid(ipi_irq);
        r = request_percpu_irq(ipi_irq, loongson3_ipi_interrupt, "IPI", &ipi_dummy_dev);
        if (r < 0)
index fe68238..79dc5ed 100644 (file)
@@ -123,6 +123,16 @@ void sync_counter(void)
        csr_write64(-init_timeval, LOONGARCH_CSR_CNTC);
 }
 
+static int get_timer_irq(void)
+{
+       struct irq_domain *d = irq_find_matching_fwnode(cpuintc_handle, DOMAIN_BUS_ANY);
+
+       if (d)
+               return irq_create_mapping(d, EXCCODE_TIMER - EXCCODE_INT_START);
+
+       return -EINVAL;
+}
+
 int constant_clockevent_init(void)
 {
        unsigned int irq;
@@ -132,7 +142,9 @@ int constant_clockevent_init(void)
        struct clock_event_device *cd;
        static int timer_irq_installed = 0;
 
-       irq = EXCCODE_TIMER - EXCCODE_INT_START;
+       irq = get_timer_irq();
+       if (irq < 0)
+               pr_err("Failed to map irq %d (timer)\n", irq);
 
        cd = &per_cpu(constant_clockevent_device, cpu);
 
index f6c5e0d..1c8d9c5 100644 (file)
@@ -6,10 +6,4 @@
    bootmem allocator (but this should do it for this) */
 #define MAX_DMA_ADDRESS PAGE_OFFSET
 
-#ifdef CONFIG_PCI
-extern int isa_dma_bridge_buggy;
-#else
-#define isa_dma_bridge_buggy    (0)
-#endif
-
 #endif /* _M68K_DMA_H */
index 5a4bc22..ccdfa0d 100644 (file)
@@ -2,8 +2,6 @@
 #ifndef _ASM_M68K_PCI_H
 #define _ASM_M68K_PCI_H
 
-#include <asm-generic/pci.h>
-
 #define        pcibios_assign_all_busses()     1
 
 #define        PCIBIOS_MIN_IO          0x00000100
index f801582..7484c9e 100644 (file)
@@ -9,10 +9,4 @@
 /* Virtual address corresponding to last available physical memory address.  */
 #define MAX_DMA_ADDRESS (CONFIG_KERNEL_START + memory_size - 1)
 
-#ifdef CONFIG_PCI
-extern int isa_dma_bridge_buggy;
-#else
-#define isa_dma_bridge_buggy     (0)
-#endif
-
 #endif /* _ASM_MICROBLAZE_DMA_H */
index be726b9..d6186e6 100644 (file)
@@ -307,12 +307,4 @@ static __inline__ int get_dma_residue(unsigned int dmanr)
 extern int request_dma(unsigned int dmanr, const char * device_id);    /* reserve a DMA channel */
 extern void free_dma(unsigned int dmanr);      /* release it again */
 
-/* From PCI */
-
-#ifdef CONFIG_PCI
-extern int isa_dma_bridge_buggy;
-#else
-#define isa_dma_bridge_buggy   (0)
-#endif
-
 #endif /* _ASM_DMA_H */
index 98ea977..67c15f3 100644 (file)
@@ -7,8 +7,9 @@
 #define NR_MIPS_CPU_IRQS       8
 #define NR_MAX_CHAINED_IRQS    40 /* Chained IRQs means those not directly used by devices */
 #define NR_IRQS                        (NR_IRQS_LEGACY + NR_MIPS_CPU_IRQS + NR_MAX_CHAINED_IRQS + 256)
-
+#define MAX_IO_PICS            1
 #define MIPS_CPU_IRQ_BASE      NR_IRQS_LEGACY
+#define GSI_MIN_CPU_IRQ                0
 
 #include <asm/mach-generic/irq.h>
 
index 9ffc819..3fd6e22 100644 (file)
@@ -139,10 +139,4 @@ static inline int pci_proc_domain(struct pci_bus *bus)
 /* Do platform specific device initialization at pci_enable_device() time */
 extern int pcibios_plat_dev_init(struct pci_dev *dev);
 
-/* Chances are this interrupt is wired PC-style ...  */
-static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel)
-{
-       return channel ? 15 : 14;
-}
-
 #endif /* _ASM_PCI_H */
index eea80ed..9e8c101 100644 (file)
@@ -176,10 +176,4 @@ static __inline__ void set_dma_count(unsigned int dmanr, unsigned int count)
 
 #define free_dma(dmanr)
 
-#ifdef CONFIG_PCI
-extern int isa_dma_bridge_buggy;
-#else
-#define isa_dma_bridge_buggy   (0)
-#endif
-
 #endif /* _ASM_DMA_H */
index f14465b..127ed50 100644 (file)
@@ -162,11 +162,6 @@ extern void pcibios_init_bridge(struct pci_dev *);
 #define PCIBIOS_MIN_IO          0x10
 #define PCIBIOS_MIN_MEM         0x1000 /* NBPG - but pci/setup-res.c dies */
 
-static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel)
-{
-       return channel ? 15 : 14;
-}
-
 #define HAVE_PCI_MMAP
 #define ARCH_GENERIC_PCI_MMAP_RESOURCE
 
index 6161a95..d97c66d 100644 (file)
@@ -340,11 +340,5 @@ extern int request_dma(unsigned int dmanr, const char *device_id);
 /* release it again */
 extern void free_dma(unsigned int dmanr);
 
-#ifdef CONFIG_PCI
-extern int isa_dma_bridge_buggy;
-#else
-#define isa_dma_bridge_buggy   (0)
-#endif
-
 #endif /* __KERNEL__ */
 #endif /* _ASM_POWERPC_DMA_H */
index 915d6ee..f9da506 100644 (file)
@@ -39,7 +39,6 @@
 #define pcibios_assign_all_busses() \
        (pci_has_flag(PCI_REASSIGN_ALL_BUS))
 
-#define HAVE_ARCH_PCI_GET_LEGACY_IDE_IRQ
 static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel)
 {
        if (ppc_md.pci_get_legacy_ide_irq)
index 7fd52a3..6ef4a14 100644 (file)
 
 #include <asm/io.h>
 
-#define PCIBIOS_MIN_IO         0
-#define PCIBIOS_MIN_MEM                0
-
-/* RISC-V shim does not initialize PCI bus */
-#define pcibios_assign_all_busses() 1
-
-#define ARCH_GENERIC_PCI_MMAP_RESOURCE 1
-
-extern int isa_dma_bridge_buggy;
-
-#ifdef CONFIG_PCI
-static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel)
-{
-       /* no legacy IRQ on risc-v */
-       return -ENODEV;
-}
-
-static inline int pci_proc_domain(struct pci_bus *bus)
-{
-       /* always show the domain in /proc */
-       return 1;
-}
-
-#ifdef CONFIG_NUMA
-
+#if defined(CONFIG_PCI) && defined(CONFIG_NUMA)
 static inline int pcibus_to_node(struct pci_bus *bus)
 {
        return dev_to_node(&bus->dev);
@@ -46,8 +22,9 @@ static inline int pcibus_to_node(struct pci_bus *bus)
                                 cpu_all_mask :                         \
                                 cpumask_of_node(pcibus_to_node(bus)))
 #endif
-#endif /* CONFIG_NUMA */
+#endif /* defined(CONFIG_PCI) && defined(CONFIG_NUMA) */
 
-#endif  /* CONFIG_PCI */
+/* Generic PCI */
+#include <asm-generic/pci.h>
 
 #endif  /* _ASM_RISCV_PCI_H */
index 6f26f35..dec1c4c 100644 (file)
  */
 #define MAX_DMA_ADDRESS         0x80000000
 
-#ifdef CONFIG_PCI
-extern int isa_dma_bridge_buggy;
-#else
-#define isa_dma_bridge_buggy   (0)
-#endif
-
 #endif /* _ASM_S390_DMA_H */
index fdb9745..5889ddc 100644 (file)
@@ -6,7 +6,6 @@
 #include <linux/mutex.h>
 #include <linux/iommu.h>
 #include <linux/pci_hotplug.h>
-#include <asm-generic/pci.h>
 #include <asm/pci_clp.h>
 #include <asm/pci_debug.h>
 #include <asm/sclp.h>
index 17d23ae..c8bee3f 100644 (file)
@@ -137,10 +137,4 @@ extern int register_chan_caps(const char *dmac, struct dma_chan_caps *capslist);
 extern int dma_create_sysfs_files(struct dma_channel *, struct dma_info *);
 extern void dma_remove_sysfs_files(struct dma_channel *, struct dma_info *);
 
-#ifdef CONFIG_PCI
-extern int isa_dma_bridge_buggy;
-#else
-#define isa_dma_bridge_buggy   (0)
-#endif
-
 #endif /* __ASM_SH_DMA_H */
index ad22e88..54c3012 100644 (file)
@@ -88,10 +88,4 @@ static inline int pci_proc_domain(struct pci_bus *bus)
        return hose->need_domain_info;
 }
 
-/* Chances are this interrupt is wired PC-style ...  */
-static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel)
-{
-       return channel ? 15 : 14;
-}
-
 #endif /* __ASM_SH_PCI_H */
index 462e7c7..08043f3 100644 (file)
 #define DMA_BURST64      0x40
 #define DMA_BURSTBITS    0x7f
 
-/* From PCI */
-
-#ifdef CONFIG_PCI
-extern int isa_dma_bridge_buggy;
-#else
-#define isa_dma_bridge_buggy   (0)
-#endif
-
 #ifdef CONFIG_SPARC32
 struct device;
 
index 4deddf4..0c58f65 100644 (file)
@@ -40,13 +40,4 @@ static inline int pci_proc_domain(struct pci_bus *bus)
 #define get_pci_unmapped_area get_fb_unmapped_area
 #endif /* CONFIG_SPARC64 */
 
-#if defined(CONFIG_SPARC64) || defined(CONFIG_LEON_PCI)
-static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel)
-{
-       return PCI_IRQ_NONE;
-}
-#else
-#include <asm-generic/pci.h>
-#endif
-
 #endif /* ___ASM_SPARC_PCI_H */
index da13fd5..34fe492 100644 (file)
@@ -4,28 +4,8 @@
 #include <linux/types.h>
 #include <asm/io.h>
 
-#define PCIBIOS_MIN_IO         0
-#define PCIBIOS_MIN_MEM                0
-
-#define pcibios_assign_all_busses() 1
-
-extern int isa_dma_bridge_buggy;
-
-#ifdef CONFIG_PCI
-static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel)
-{
-       /* no legacy IRQs */
-       return -ENODEV;
-}
-#endif
-
-#ifdef CONFIG_PCI_DOMAINS
-static inline int pci_proc_domain(struct pci_bus *bus)
-{
-       /* always show the domain in /proc */
-       return 1;
-}
-#endif  /* CONFIG_PCI */
+/* Generic PCI */
+#include <asm-generic/pci.h>
 
 #ifdef CONFIG_PCI_MSI_IRQ_DOMAIN
 /*
index 8e95aa4..8ae6e0e 100644 (file)
@@ -307,12 +307,4 @@ extern int request_dma(unsigned int dmanr, const char *device_id);
 extern void free_dma(unsigned int dmanr);
 #endif
 
-/* From PCI */
-
-#ifdef CONFIG_PCI
-extern int isa_dma_bridge_buggy;
-#else
-#define isa_dma_bridge_buggy   (0)
-#endif
-
 #endif /* _ASM_X86_DMA_H */
index f3fd592..736793d 100644 (file)
@@ -105,9 +105,6 @@ static inline void early_quirks(void) { }
 
 extern void pci_iommu_alloc(void);
 
-/* generic pci stuff */
-#include <asm-generic/pci.h>
-
 #ifdef CONFIG_NUMA
 /* Returns the node based on pci bus */
 static inline int __pcibus_to_node(const struct pci_bus *bus)
index 7227c15..9651275 100644 (file)
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 #include <linux/bitops.h>
 #include <linux/delay.h>
+#include <linux/isa-dma.h>
 #include <linux/pci.h>
 #include <asm/dma.h>
 #include <linux/io.h>
index bb099a3..1726445 100644 (file)
 extern int request_dma(unsigned int dmanr, const char * device_id);
 extern void free_dma(unsigned int dmanr);
 
-#ifdef CONFIG_PCI
-extern int isa_dma_bridge_buggy;
-#else
-#define isa_dma_bridge_buggy   (0)
-#endif
-
-
 #endif
index 8e2b48a..b56de96 100644 (file)
@@ -43,7 +43,4 @@
 #define ARCH_GENERIC_PCI_MMAP_RESOURCE 1
 #define arch_can_pci_mmap_io()         1
 
-/* Generic PCI */
-#include <asm-generic/pci.h>
-
 #endif /* _XTENSA_PCI_H */
index e2db1bd..0dbb39e 100644 (file)
@@ -1144,6 +1144,9 @@ static int __init acpi_bus_init_irq(void)
        case ACPI_IRQ_MODEL_PLATFORM:
                message = "platform specific model";
                break;
+       case ACPI_IRQ_MODEL_LPIC:
+               message = "LPIC";
+               break;
        default:
                pr_info("Unknown interrupt routing model\n");
                return -ENODEV;
index c68e694..dabe45e 100644 (file)
@@ -12,7 +12,8 @@
 
 enum acpi_irq_model_id acpi_irq_model;
 
-static struct fwnode_handle *acpi_gsi_domain_id;
+static struct fwnode_handle *(*acpi_get_gsi_domain_id)(u32 gsi);
+static u32 (*acpi_gsi_to_irq_fallback)(u32 gsi);
 
 /**
  * acpi_gsi_to_irq() - Retrieve the linux irq number for a given GSI
@@ -26,14 +27,18 @@ static struct fwnode_handle *acpi_gsi_domain_id;
  */
 int acpi_gsi_to_irq(u32 gsi, unsigned int *irq)
 {
-       struct irq_domain *d = irq_find_matching_fwnode(acpi_gsi_domain_id,
-                                                       DOMAIN_BUS_ANY);
+       struct irq_domain *d;
 
+       d = irq_find_matching_fwnode(acpi_get_gsi_domain_id(gsi),
+                                       DOMAIN_BUS_ANY);
        *irq = irq_find_mapping(d, gsi);
        /*
-        * *irq == 0 means no mapping, that should
-        * be reported as a failure
+        * *irq == 0 means no mapping, that should be reported as a
+        * failure, unless there is an arch-specific fallback handler.
         */
+       if (!*irq && acpi_gsi_to_irq_fallback)
+               *irq = acpi_gsi_to_irq_fallback(gsi);
+
        return (*irq > 0) ? 0 : -EINVAL;
 }
 EXPORT_SYMBOL_GPL(acpi_gsi_to_irq);
@@ -53,12 +58,12 @@ int acpi_register_gsi(struct device *dev, u32 gsi, int trigger,
 {
        struct irq_fwspec fwspec;
 
-       if (WARN_ON(!acpi_gsi_domain_id)) {
+       fwspec.fwnode = acpi_get_gsi_domain_id(gsi);
+       if (WARN_ON(!fwspec.fwnode)) {
                pr_warn("GSI: No registered irqchip, giving up\n");
                return -EINVAL;
        }
 
-       fwspec.fwnode = acpi_gsi_domain_id;
        fwspec.param[0] = gsi;
        fwspec.param[1] = acpi_dev_get_irq_type(trigger, polarity);
        fwspec.param_count = 2;
@@ -73,13 +78,14 @@ EXPORT_SYMBOL_GPL(acpi_register_gsi);
  */
 void acpi_unregister_gsi(u32 gsi)
 {
-       struct irq_domain *d = irq_find_matching_fwnode(acpi_gsi_domain_id,
-                                                       DOMAIN_BUS_ANY);
+       struct irq_domain *d;
        int irq;
 
        if (WARN_ON(acpi_irq_model == ACPI_IRQ_MODEL_GIC && gsi < 16))
                return;
 
+       d = irq_find_matching_fwnode(acpi_get_gsi_domain_id(gsi),
+                                    DOMAIN_BUS_ANY);
        irq = irq_find_mapping(d, gsi);
        irq_dispose_mapping(irq);
 }
@@ -97,7 +103,8 @@ EXPORT_SYMBOL_GPL(acpi_unregister_gsi);
  * The referenced device fwhandle or NULL on failure
  */
 static struct fwnode_handle *
-acpi_get_irq_source_fwhandle(const struct acpi_resource_source *source)
+acpi_get_irq_source_fwhandle(const struct acpi_resource_source *source,
+                            u32 gsi)
 {
        struct fwnode_handle *result;
        struct acpi_device *device;
@@ -105,7 +112,7 @@ acpi_get_irq_source_fwhandle(const struct acpi_resource_source *source)
        acpi_status status;
 
        if (!source->string_length)
-               return acpi_gsi_domain_id;
+               return acpi_get_gsi_domain_id(gsi);
 
        status = acpi_get_handle(NULL, source->string_ptr, &handle);
        if (WARN_ON(ACPI_FAILURE(status)))
@@ -194,7 +201,7 @@ static acpi_status acpi_irq_parse_one_cb(struct acpi_resource *ares,
                        ctx->index -= irq->interrupt_count;
                        return AE_OK;
                }
-               fwnode = acpi_gsi_domain_id;
+               fwnode = acpi_get_gsi_domain_id(irq->interrupts[ctx->index]);
                acpi_irq_parse_one_match(fwnode, irq->interrupts[ctx->index],
                                         irq->triggering, irq->polarity,
                                         irq->shareable, ctx);
@@ -207,7 +214,8 @@ static acpi_status acpi_irq_parse_one_cb(struct acpi_resource *ares,
                        ctx->index -= eirq->interrupt_count;
                        return AE_OK;
                }
-               fwnode = acpi_get_irq_source_fwhandle(&eirq->resource_source);
+               fwnode = acpi_get_irq_source_fwhandle(&eirq->resource_source,
+                                                     eirq->interrupts[ctx->index]);
                acpi_irq_parse_one_match(fwnode, eirq->interrupts[ctx->index],
                                         eirq->triggering, eirq->polarity,
                                         eirq->shareable, ctx);
@@ -291,10 +299,20 @@ EXPORT_SYMBOL_GPL(acpi_irq_get);
  *          GSI interrupts
  */
 void __init acpi_set_irq_model(enum acpi_irq_model_id model,
-                              struct fwnode_handle *fwnode)
+                              struct fwnode_handle *(*fn)(u32))
 {
        acpi_irq_model = model;
-       acpi_gsi_domain_id = fwnode;
+       acpi_get_gsi_domain_id = fn;
+}
+
+/**
+ * acpi_set_gsi_to_irq_fallback - Register a GSI transfer
+ * callback to fallback to arch specified implementation.
+ * @fn: arch-specific fallback handler
+ */
+void __init acpi_set_gsi_to_irq_fallback(u32 (*fn)(u32))
+{
+       acpi_gsi_to_irq_fallback = fn;
 }
 
 /**
@@ -312,8 +330,14 @@ struct irq_domain *acpi_irq_create_hierarchy(unsigned int flags,
                                             const struct irq_domain_ops *ops,
                                             void *host_data)
 {
-       struct irq_domain *d = irq_find_matching_fwnode(acpi_gsi_domain_id,
-                                                       DOMAIN_BUS_ANY);
+       struct irq_domain *d;
+
+       /* This only works for the GIC model... */
+       if (acpi_irq_model != ACPI_IRQ_MODEL_GIC)
+               return NULL;
+
+       d = irq_find_matching_fwnode(acpi_get_gsi_domain_id(0),
+                                    DOMAIN_BUS_ANY);
 
        if (!d)
                return NULL;
index 53cab97..860014b 100644 (file)
@@ -41,6 +41,8 @@ struct mcfg_fixup {
 static struct mcfg_fixup mcfg_quirks[] = {
 /*     { OEM_ID, OEM_TABLE_ID, REV, SEGMENT, BUS_RANGE, ops, cfgres }, */
 
+#ifdef CONFIG_ARM64
+
 #define AL_ECAM(table_id, rev, seg, ops) \
        { "AMAZON", table_id, rev, seg, MCFG_BUS_ANY, ops }
 
@@ -169,6 +171,17 @@ static struct mcfg_fixup mcfg_quirks[] = {
        ALTRA_ECAM_QUIRK(1, 13),
        ALTRA_ECAM_QUIRK(1, 14),
        ALTRA_ECAM_QUIRK(1, 15),
+#endif /* ARM64 */
+
+#ifdef CONFIG_LOONGARCH
+#define LOONGSON_ECAM_MCFG(table_id, seg) \
+       { "LOONGS", table_id, 1, seg, MCFG_BUS_ANY, &loongson_pci_ecam_ops }
+
+       LOONGSON_ECAM_MCFG("\0", 0),
+       LOONGSON_ECAM_MCFG("LOONGSON", 0),
+       LOONGSON_ECAM_MCFG("\0", 1),
+       LOONGSON_ECAM_MCFG("LOONGSON", 1),
+#endif /* LOONGARCH */
 };
 
 static char mcfg_oem_id[ACPI_OEM_ID_SIZE];
index 7009824..020b3d1 100644 (file)
@@ -8,7 +8,7 @@
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include <linux/dma-mapping.h>
-#include <asm/dma.h>
+#include <linux/isa-dma.h>
 #include <linux/comedi/comedidev.h>
 #include <linux/comedi/comedi_isadma.h>
 
index bbb11cb..a9cd929 100644 (file)
@@ -546,6 +546,16 @@ config EXYNOS_IRQ_COMBINER
          Say yes here to add support for the IRQ combiner devices embedded
          in Samsung Exynos chips.
 
+config IRQ_LOONGARCH_CPU
+       bool
+       select GENERIC_IRQ_CHIP
+       select IRQ_DOMAIN
+       select GENERIC_IRQ_EFFECTIVE_AFF_MASK
+       help
+         Support for the LoongArch CPU Interrupt Controller. For details of
+         irq chip hierarchy on LoongArch platforms please read the document
+         Documentation/loongarch/irq-chip-model.rst.
+
 config LOONGSON_LIOINTC
        bool "Loongson Local I/O Interrupt Controller"
        depends on MACH_LOONGSON64
@@ -555,6 +565,16 @@ config LOONGSON_LIOINTC
        help
          Support for the Loongson Local I/O Interrupt Controller.
 
+config LOONGSON_EIOINTC
+       bool "Loongson Extend I/O Interrupt Controller"
+       depends on LOONGARCH
+       depends on MACH_LOONGSON64
+       default MACH_LOONGSON64
+       select IRQ_DOMAIN_HIERARCHY
+       select GENERIC_IRQ_CHIP
+       help
+         Support for the Loongson3 Extend I/O Interrupt Vector Controller.
+
 config LOONGSON_HTPIC
        bool "Loongson3 HyperTransport PIC Controller"
        depends on MACH_LOONGSON64 && MIPS
@@ -574,7 +594,7 @@ config LOONGSON_HTVEC
 
 config LOONGSON_PCH_PIC
        bool "Loongson PCH PIC Controller"
-       depends on MACH_LOONGSON64 || COMPILE_TEST
+       depends on MACH_LOONGSON64
        default MACH_LOONGSON64
        select IRQ_DOMAIN_HIERARCHY
        select IRQ_FASTEOI_HIERARCHY_HANDLERS
@@ -583,7 +603,7 @@ config LOONGSON_PCH_PIC
 
 config LOONGSON_PCH_MSI
        bool "Loongson PCH MSI Controller"
-       depends on MACH_LOONGSON64 || COMPILE_TEST
+       depends on MACH_LOONGSON64
        depends on PCI
        default MACH_LOONGSON64
        select IRQ_DOMAIN_HIERARCHY
@@ -591,6 +611,14 @@ config LOONGSON_PCH_MSI
        help
          Support for the Loongson PCH MSI Controller.
 
+config LOONGSON_PCH_LPC
+       bool "Loongson PCH LPC Controller"
+       depends on MACH_LOONGSON64
+       default (MACH_LOONGSON64 && LOONGARCH)
+       select IRQ_DOMAIN_HIERARCHY
+       help
+         Support for the Loongson PCH LPC Controller.
+
 config MST_IRQ
        bool "MStar Interrupt Controller"
        depends on ARCH_MEDIATEK || ARCH_MSTARV7 || COMPILE_TEST
index 5b67450..e559007 100644 (file)
@@ -103,11 +103,14 @@ obj-$(CONFIG_LS1X_IRQ)                    += irq-ls1x.o
 obj-$(CONFIG_TI_SCI_INTR_IRQCHIP)      += irq-ti-sci-intr.o
 obj-$(CONFIG_TI_SCI_INTA_IRQCHIP)      += irq-ti-sci-inta.o
 obj-$(CONFIG_TI_PRUSS_INTC)            += irq-pruss-intc.o
+obj-$(CONFIG_IRQ_LOONGARCH_CPU)                += irq-loongarch-cpu.o
 obj-$(CONFIG_LOONGSON_LIOINTC)         += irq-loongson-liointc.o
+obj-$(CONFIG_LOONGSON_EIOINTC)         += irq-loongson-eiointc.o
 obj-$(CONFIG_LOONGSON_HTPIC)           += irq-loongson-htpic.o
 obj-$(CONFIG_LOONGSON_HTVEC)           += irq-loongson-htvec.o
 obj-$(CONFIG_LOONGSON_PCH_PIC)         += irq-loongson-pch-pic.o
 obj-$(CONFIG_LOONGSON_PCH_MSI)         += irq-loongson-pch-msi.o
+obj-$(CONFIG_LOONGSON_PCH_LPC)         += irq-loongson-pch-lpc.o
 obj-$(CONFIG_MST_IRQ)                  += irq-mst-intc.o
 obj-$(CONFIG_SL28CPLD_INTC)            += irq-sl28cpld.o
 obj-$(CONFIG_MACH_REALTEK_RTL)         += irq-realtek-rtl.o
index 2d25bca..736c282 100644 (file)
@@ -2381,11 +2381,17 @@ static void __init gic_acpi_setup_kvm_info(void)
        vgic_set_kvm_info(&gic_v3_kvm_info);
 }
 
+static struct fwnode_handle *gsi_domain_handle;
+
+static struct fwnode_handle *gic_v3_get_gsi_domain_id(u32 gsi)
+{
+       return gsi_domain_handle;
+}
+
 static int __init
 gic_acpi_init(union acpi_subtable_headers *header, const unsigned long end)
 {
        struct acpi_madt_generic_distributor *dist;
-       struct fwnode_handle *domain_handle;
        size_t size;
        int i, err;
 
@@ -2417,18 +2423,18 @@ gic_acpi_init(union acpi_subtable_headers *header, const unsigned long end)
        if (err)
                goto out_redist_unmap;
 
-       domain_handle = irq_domain_alloc_fwnode(&dist->base_address);
-       if (!domain_handle) {
+       gsi_domain_handle = irq_domain_alloc_fwnode(&dist->base_address);
+       if (!gsi_domain_handle) {
                err = -ENOMEM;
                goto out_redist_unmap;
        }
 
        err = gic_init_bases(acpi_data.dist_base, acpi_data.redist_regs,
-                            acpi_data.nr_redist_regions, 0, domain_handle);
+                            acpi_data.nr_redist_regions, 0, gsi_domain_handle);
        if (err)
                goto out_fwhandle_free;
 
-       acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, domain_handle);
+       acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, gic_v3_get_gsi_domain_id);
 
        if (static_branch_likely(&supports_deactivate_key))
                gic_acpi_setup_kvm_info();
@@ -2436,7 +2442,7 @@ gic_acpi_init(union acpi_subtable_headers *header, const unsigned long end)
        return 0;
 
 out_fwhandle_free:
-       irq_domain_free_fwnode(domain_handle);
+       irq_domain_free_fwnode(gsi_domain_handle);
 out_redist_unmap:
        for (i = 0; i < acpi_data.nr_redist_regions; i++)
                if (acpi_data.redist_regs[i].redist_base)
index 820404c..4c7bae0 100644 (file)
@@ -1682,11 +1682,17 @@ static void __init gic_acpi_setup_kvm_info(void)
        vgic_set_kvm_info(&gic_v2_kvm_info);
 }
 
+static struct fwnode_handle *gsi_domain_handle;
+
+static struct fwnode_handle *gic_v2_get_gsi_domain_id(u32 gsi)
+{
+       return gsi_domain_handle;
+}
+
 static int __init gic_v2_acpi_init(union acpi_subtable_headers *header,
                                   const unsigned long end)
 {
        struct acpi_madt_generic_distributor *dist;
-       struct fwnode_handle *domain_handle;
        struct gic_chip_data *gic = &gic_data[0];
        int count, ret;
 
@@ -1724,22 +1730,22 @@ static int __init gic_v2_acpi_init(union acpi_subtable_headers *header,
        /*
         * Initialize GIC instance zero (no multi-GIC support).
         */
-       domain_handle = irq_domain_alloc_fwnode(&dist->base_address);
-       if (!domain_handle) {
+       gsi_domain_handle = irq_domain_alloc_fwnode(&dist->base_address);
+       if (!gsi_domain_handle) {
                pr_err("Unable to allocate domain handle\n");
                gic_teardown(gic);
                return -ENOMEM;
        }
 
-       ret = __gic_init_bases(gic, domain_handle);
+       ret = __gic_init_bases(gic, gsi_domain_handle);
        if (ret) {
                pr_err("Failed to initialise GIC\n");
-               irq_domain_free_fwnode(domain_handle);
+               irq_domain_free_fwnode(gsi_domain_handle);
                gic_teardown(gic);
                return ret;
        }
 
-       acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, domain_handle);
+       acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, gic_v2_get_gsi_domain_id);
 
        if (IS_ENABLED(CONFIG_ARM_GIC_V2M))
                gicv2m_init(NULL, gic_data[0].domain);
diff --git a/drivers/irqchip/irq-loongarch-cpu.c b/drivers/irqchip/irq-loongarch-cpu.c
new file mode 100644 (file)
index 0000000..327f3ab
--- /dev/null
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+
+#include <asm/loongarch.h>
+#include <asm/setup.h>
+
+static struct irq_domain *irq_domain;
+struct fwnode_handle *cpuintc_handle;
+
+static u32 lpic_gsi_to_irq(u32 gsi)
+{
+       /* Only pch irqdomain transferring is required for LoongArch. */
+       if (gsi >= GSI_MIN_PCH_IRQ && gsi <= GSI_MAX_PCH_IRQ)
+               return acpi_register_gsi(NULL, gsi, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_HIGH);
+
+       return 0;
+}
+
+static struct fwnode_handle *lpic_get_gsi_domain_id(u32 gsi)
+{
+       int id;
+       struct fwnode_handle *domain_handle = NULL;
+
+       switch (gsi) {
+       case GSI_MIN_CPU_IRQ ... GSI_MAX_CPU_IRQ:
+               if (liointc_handle)
+                       domain_handle = liointc_handle;
+               break;
+
+       case GSI_MIN_LPC_IRQ ... GSI_MAX_LPC_IRQ:
+               if (pch_lpc_handle)
+                       domain_handle = pch_lpc_handle;
+               break;
+
+       case GSI_MIN_PCH_IRQ ... GSI_MAX_PCH_IRQ:
+               id = find_pch_pic(gsi);
+               if (id >= 0 && pch_pic_handle[id])
+                       domain_handle = pch_pic_handle[id];
+               break;
+       }
+
+       return domain_handle;
+}
+
+static void mask_loongarch_irq(struct irq_data *d)
+{
+       clear_csr_ecfg(ECFGF(d->hwirq));
+}
+
+static void unmask_loongarch_irq(struct irq_data *d)
+{
+       set_csr_ecfg(ECFGF(d->hwirq));
+}
+
+static struct irq_chip cpu_irq_controller = {
+       .name           = "CPUINTC",
+       .irq_mask       = mask_loongarch_irq,
+       .irq_unmask     = unmask_loongarch_irq,
+};
+
+static void handle_cpu_irq(struct pt_regs *regs)
+{
+       int hwirq;
+       unsigned int estat = read_csr_estat() & CSR_ESTAT_IS;
+
+       while ((hwirq = ffs(estat))) {
+               estat &= ~BIT(hwirq - 1);
+               generic_handle_domain_irq(irq_domain, hwirq - 1);
+       }
+}
+
+static int loongarch_cpu_intc_map(struct irq_domain *d, unsigned int irq,
+                            irq_hw_number_t hwirq)
+{
+       irq_set_noprobe(irq);
+       irq_set_chip_and_handler(irq, &cpu_irq_controller, handle_percpu_irq);
+
+       return 0;
+}
+
+static const struct irq_domain_ops loongarch_cpu_intc_irq_domain_ops = {
+       .map = loongarch_cpu_intc_map,
+       .xlate = irq_domain_xlate_onecell,
+};
+
+static int __init
+liointc_parse_madt(union acpi_subtable_headers *header,
+                      const unsigned long end)
+{
+       struct acpi_madt_lio_pic *liointc_entry = (struct acpi_madt_lio_pic *)header;
+
+       return liointc_acpi_init(irq_domain, liointc_entry);
+}
+
+static int __init
+eiointc_parse_madt(union acpi_subtable_headers *header,
+                      const unsigned long end)
+{
+       struct acpi_madt_eio_pic *eiointc_entry = (struct acpi_madt_eio_pic *)header;
+
+       return eiointc_acpi_init(irq_domain, eiointc_entry);
+}
+
+static int __init acpi_cascade_irqdomain_init(void)
+{
+       acpi_table_parse_madt(ACPI_MADT_TYPE_LIO_PIC,
+                             liointc_parse_madt, 0);
+       acpi_table_parse_madt(ACPI_MADT_TYPE_EIO_PIC,
+                             eiointc_parse_madt, 0);
+       return 0;
+}
+
+static int __init cpuintc_acpi_init(union acpi_subtable_headers *header,
+                                  const unsigned long end)
+{
+       if (irq_domain)
+               return 0;
+
+       /* Mask interrupts. */
+       clear_csr_ecfg(ECFG0_IM);
+       clear_csr_estat(ESTATF_IP);
+
+       cpuintc_handle = irq_domain_alloc_fwnode(NULL);
+       irq_domain = irq_domain_create_linear(cpuintc_handle, EXCCODE_INT_NUM,
+                                       &loongarch_cpu_intc_irq_domain_ops, NULL);
+
+       if (!irq_domain)
+               panic("Failed to add irqdomain for LoongArch CPU");
+
+       set_handle_irq(&handle_cpu_irq);
+       acpi_set_irq_model(ACPI_IRQ_MODEL_LPIC, lpic_get_gsi_domain_id);
+       acpi_set_gsi_to_irq_fallback(lpic_gsi_to_irq);
+       acpi_cascade_irqdomain_init();
+
+       return 0;
+}
+
+IRQCHIP_ACPI_DECLARE(cpuintc_v1, ACPI_MADT_TYPE_CORE_PIC,
+               NULL, ACPI_MADT_CORE_PIC_VERSION_V1, cpuintc_acpi_init);
diff --git a/drivers/irqchip/irq-loongson-eiointc.c b/drivers/irqchip/irq-loongson-eiointc.c
new file mode 100644 (file)
index 0000000..80d8ca6
--- /dev/null
@@ -0,0 +1,395 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Loongson Extend I/O Interrupt Controller support
+ *
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+
+#define pr_fmt(fmt) "eiointc: " fmt
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+
+#define EIOINTC_REG_NODEMAP    0x14a0
+#define EIOINTC_REG_IPMAP      0x14c0
+#define EIOINTC_REG_ENABLE     0x1600
+#define EIOINTC_REG_BOUNCE     0x1680
+#define EIOINTC_REG_ISR                0x1800
+#define EIOINTC_REG_ROUTE      0x1c00
+
+#define VEC_REG_COUNT          4
+#define VEC_COUNT_PER_REG      64
+#define VEC_COUNT              (VEC_REG_COUNT * VEC_COUNT_PER_REG)
+#define VEC_REG_IDX(irq_id)    ((irq_id) / VEC_COUNT_PER_REG)
+#define VEC_REG_BIT(irq_id)     ((irq_id) % VEC_COUNT_PER_REG)
+#define EIOINTC_ALL_ENABLE     0xffffffff
+
+#define MAX_EIO_NODES          (NR_CPUS / CORES_PER_EIO_NODE)
+
+static int nr_pics;
+
+struct eiointc_priv {
+       u32                     node;
+       nodemask_t              node_map;
+       cpumask_t               cpuspan_map;
+       struct fwnode_handle    *domain_handle;
+       struct irq_domain       *eiointc_domain;
+};
+
+static struct eiointc_priv *eiointc_priv[MAX_IO_PICS];
+
+static void eiointc_enable(void)
+{
+       uint64_t misc;
+
+       misc = iocsr_read64(LOONGARCH_IOCSR_MISC_FUNC);
+       misc |= IOCSR_MISC_FUNC_EXT_IOI_EN;
+       iocsr_write64(misc, LOONGARCH_IOCSR_MISC_FUNC);
+}
+
+static int cpu_to_eio_node(int cpu)
+{
+       return cpu_logical_map(cpu) / CORES_PER_EIO_NODE;
+}
+
+static void eiointc_set_irq_route(int pos, unsigned int cpu, unsigned int mnode, nodemask_t *node_map)
+{
+       int i, node, cpu_node, route_node;
+       unsigned char coremap;
+       uint32_t pos_off, data, data_byte, data_mask;
+
+       pos_off = pos & ~3;
+       data_byte = pos & 3;
+       data_mask = ~BIT_MASK(data_byte) & 0xf;
+
+       /* Calculate node and coremap of target irq */
+       cpu_node = cpu_logical_map(cpu) / CORES_PER_EIO_NODE;
+       coremap = BIT(cpu_logical_map(cpu) % CORES_PER_EIO_NODE);
+
+       for_each_online_cpu(i) {
+               node = cpu_to_eio_node(i);
+               if (!node_isset(node, *node_map))
+                       continue;
+
+               /* EIO node 0 is in charge of inter-node interrupt dispatch */
+               route_node = (node == mnode) ? cpu_node : node;
+               data = ((coremap | (route_node << 4)) << (data_byte * 8));
+               csr_any_send(EIOINTC_REG_ROUTE + pos_off, data, data_mask, node * CORES_PER_EIO_NODE);
+       }
+}
+
+static DEFINE_RAW_SPINLOCK(affinity_lock);
+
+static int eiointc_set_irq_affinity(struct irq_data *d, const struct cpumask *affinity, bool force)
+{
+       unsigned int cpu;
+       unsigned long flags;
+       uint32_t vector, regaddr;
+       struct cpumask intersect_affinity;
+       struct eiointc_priv *priv = d->domain->host_data;
+
+       raw_spin_lock_irqsave(&affinity_lock, flags);
+
+       cpumask_and(&intersect_affinity, affinity, cpu_online_mask);
+       cpumask_and(&intersect_affinity, &intersect_affinity, &priv->cpuspan_map);
+
+       if (cpumask_empty(&intersect_affinity)) {
+               raw_spin_unlock_irqrestore(&affinity_lock, flags);
+               return -EINVAL;
+       }
+       cpu = cpumask_first(&intersect_affinity);
+
+       vector = d->hwirq;
+       regaddr = EIOINTC_REG_ENABLE + ((vector >> 5) << 2);
+
+       /* Mask target vector */
+       csr_any_send(regaddr, EIOINTC_ALL_ENABLE & (~BIT(vector & 0x1F)), 0x0, 0);
+       /* Set route for target vector */
+       eiointc_set_irq_route(vector, cpu, priv->node, &priv->node_map);
+       /* Unmask target vector */
+       csr_any_send(regaddr, EIOINTC_ALL_ENABLE, 0x0, 0);
+
+       irq_data_update_effective_affinity(d, cpumask_of(cpu));
+
+       raw_spin_unlock_irqrestore(&affinity_lock, flags);
+
+       return IRQ_SET_MASK_OK;
+}
+
+static int eiointc_index(int node)
+{
+       int i;
+
+       for (i = 0; i < nr_pics; i++) {
+               if (node_isset(node, eiointc_priv[i]->node_map))
+                       return i;
+       }
+
+       return -1;
+}
+
+static int eiointc_router_init(unsigned int cpu)
+{
+       int i, bit;
+       uint32_t data;
+       uint32_t node = cpu_to_eio_node(cpu);
+       uint32_t index = eiointc_index(node);
+
+       if (index < 0) {
+               pr_err("Error: invalid nodemap!\n");
+               return -1;
+       }
+
+       if ((cpu_logical_map(cpu) % CORES_PER_EIO_NODE) == 0) {
+               eiointc_enable();
+
+               for (i = 0; i < VEC_COUNT / 32; i++) {
+                       data = (((1 << (i * 2 + 1)) << 16) | (1 << (i * 2)));
+                       iocsr_write32(data, EIOINTC_REG_NODEMAP + i * 4);
+               }
+
+               for (i = 0; i < VEC_COUNT / 32 / 4; i++) {
+                       bit = BIT(1 + index); /* Route to IP[1 + index] */
+                       data = bit | (bit << 8) | (bit << 16) | (bit << 24);
+                       iocsr_write32(data, EIOINTC_REG_IPMAP + i * 4);
+               }
+
+               for (i = 0; i < VEC_COUNT / 4; i++) {
+                       /* Route to Node-0 Core-0 */
+                       if (index == 0)
+                               bit = BIT(cpu_logical_map(0));
+                       else
+                               bit = (eiointc_priv[index]->node << 4) | 1;
+
+                       data = bit | (bit << 8) | (bit << 16) | (bit << 24);
+                       iocsr_write32(data, EIOINTC_REG_ROUTE + i * 4);
+               }
+
+               for (i = 0; i < VEC_COUNT / 32; i++) {
+                       data = 0xffffffff;
+                       iocsr_write32(data, EIOINTC_REG_ENABLE + i * 4);
+                       iocsr_write32(data, EIOINTC_REG_BOUNCE + i * 4);
+               }
+       }
+
+       return 0;
+}
+
+static void eiointc_irq_dispatch(struct irq_desc *desc)
+{
+       int i;
+       u64 pending;
+       bool handled = false;
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+       struct eiointc_priv *priv = irq_desc_get_handler_data(desc);
+
+       chained_irq_enter(chip, desc);
+
+       for (i = 0; i < VEC_REG_COUNT; i++) {
+               pending = iocsr_read64(EIOINTC_REG_ISR + (i << 3));
+               iocsr_write64(pending, EIOINTC_REG_ISR + (i << 3));
+               while (pending) {
+                       int bit = __ffs(pending);
+                       int irq = bit + VEC_COUNT_PER_REG * i;
+
+                       generic_handle_domain_irq(priv->eiointc_domain, irq);
+                       pending &= ~BIT(bit);
+                       handled = true;
+               }
+       }
+
+       if (!handled)
+               spurious_interrupt();
+
+       chained_irq_exit(chip, desc);
+}
+
+static void eiointc_ack_irq(struct irq_data *d)
+{
+}
+
+static void eiointc_mask_irq(struct irq_data *d)
+{
+}
+
+static void eiointc_unmask_irq(struct irq_data *d)
+{
+}
+
+static struct irq_chip eiointc_irq_chip = {
+       .name                   = "EIOINTC",
+       .irq_ack                = eiointc_ack_irq,
+       .irq_mask               = eiointc_mask_irq,
+       .irq_unmask             = eiointc_unmask_irq,
+       .irq_set_affinity       = eiointc_set_irq_affinity,
+};
+
+static int eiointc_domain_alloc(struct irq_domain *domain, unsigned int virq,
+                               unsigned int nr_irqs, void *arg)
+{
+       int ret;
+       unsigned int i, type;
+       unsigned long hwirq = 0;
+       struct eiointc *priv = domain->host_data;
+
+       ret = irq_domain_translate_onecell(domain, arg, &hwirq, &type);
+       if (ret)
+               return ret;
+
+       for (i = 0; i < nr_irqs; i++) {
+               irq_domain_set_info(domain, virq + i, hwirq + i, &eiointc_irq_chip,
+                                       priv, handle_edge_irq, NULL, NULL);
+       }
+
+       return 0;
+}
+
+static void eiointc_domain_free(struct irq_domain *domain, unsigned int virq,
+                               unsigned int nr_irqs)
+{
+       int i;
+
+       for (i = 0; i < nr_irqs; i++) {
+               struct irq_data *d = irq_domain_get_irq_data(domain, virq + i);
+
+               irq_set_handler(virq + i, NULL);
+               irq_domain_reset_irq_data(d);
+       }
+}
+
+static const struct irq_domain_ops eiointc_domain_ops = {
+       .translate      = irq_domain_translate_onecell,
+       .alloc          = eiointc_domain_alloc,
+       .free           = eiointc_domain_free,
+};
+
+static void acpi_set_vec_parent(int node, struct irq_domain *parent, struct acpi_vector_group *vec_group)
+{
+       int i;
+
+       if (cpu_has_flatmode)
+               node = cpu_to_node(node * CORES_PER_EIO_NODE);
+
+       for (i = 0; i < MAX_IO_PICS; i++) {
+               if (node == vec_group[i].node) {
+                       vec_group[i].parent = parent;
+                       return;
+               }
+       }
+}
+
+struct irq_domain *acpi_get_vec_parent(int node, struct acpi_vector_group *vec_group)
+{
+       int i;
+
+       for (i = 0; i < MAX_IO_PICS; i++) {
+               if (node == vec_group[i].node)
+                       return vec_group[i].parent;
+       }
+       return NULL;
+}
+
+static int __init
+pch_pic_parse_madt(union acpi_subtable_headers *header,
+                      const unsigned long end)
+{
+       struct acpi_madt_bio_pic *pchpic_entry = (struct acpi_madt_bio_pic *)header;
+       unsigned int node = (pchpic_entry->address >> 44) & 0xf;
+       struct irq_domain *parent = acpi_get_vec_parent(node, pch_group);
+
+       if (parent)
+               return pch_pic_acpi_init(parent, pchpic_entry);
+
+       return -EINVAL;
+}
+
+static int __init
+pch_msi_parse_madt(union acpi_subtable_headers *header,
+                      const unsigned long end)
+{
+       struct acpi_madt_msi_pic *pchmsi_entry = (struct acpi_madt_msi_pic *)header;
+       struct irq_domain *parent = acpi_get_vec_parent(eiointc_priv[nr_pics - 1]->node, msi_group);
+
+       if (parent)
+               return pch_msi_acpi_init(parent, pchmsi_entry);
+
+       return -EINVAL;
+}
+
+static int __init acpi_cascade_irqdomain_init(void)
+{
+       acpi_table_parse_madt(ACPI_MADT_TYPE_BIO_PIC,
+                             pch_pic_parse_madt, 0);
+       acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC,
+                             pch_msi_parse_madt, 1);
+       return 0;
+}
+
+int __init eiointc_acpi_init(struct irq_domain *parent,
+                                    struct acpi_madt_eio_pic *acpi_eiointc)
+{
+       int i, parent_irq;
+       unsigned long node_map;
+       struct eiointc_priv *priv;
+
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->domain_handle = irq_domain_alloc_fwnode((phys_addr_t *)acpi_eiointc);
+       if (!priv->domain_handle) {
+               pr_err("Unable to allocate domain handle\n");
+               goto out_free_priv;
+       }
+
+       priv->node = acpi_eiointc->node;
+       node_map = acpi_eiointc->node_map ? : -1ULL;
+
+       for_each_possible_cpu(i) {
+               if (node_map & (1ULL << cpu_to_eio_node(i))) {
+                       node_set(cpu_to_eio_node(i), priv->node_map);
+                       cpumask_or(&priv->cpuspan_map, &priv->cpuspan_map, cpumask_of(i));
+               }
+       }
+
+       /* Setup IRQ domain */
+       priv->eiointc_domain = irq_domain_create_linear(priv->domain_handle, VEC_COUNT,
+                                       &eiointc_domain_ops, priv);
+       if (!priv->eiointc_domain) {
+               pr_err("loongson-eiointc: cannot add IRQ domain\n");
+               goto out_free_handle;
+       }
+
+       eiointc_priv[nr_pics++] = priv;
+
+       eiointc_router_init(0);
+
+       parent_irq = irq_create_mapping(parent, acpi_eiointc->cascade);
+       irq_set_chained_handler_and_data(parent_irq, eiointc_irq_dispatch, priv);
+
+       cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_LOONGARCH_STARTING,
+                                 "irqchip/loongarch/intc:starting",
+                                 eiointc_router_init, NULL);
+
+       acpi_set_vec_parent(acpi_eiointc->node, priv->eiointc_domain, pch_group);
+       acpi_set_vec_parent(acpi_eiointc->node, priv->eiointc_domain, msi_group);
+       acpi_cascade_irqdomain_init();
+
+       return 0;
+
+out_free_handle:
+       irq_domain_free_fwnode(priv->domain_handle);
+       priv->domain_handle = NULL;
+out_free_priv:
+       kfree(priv);
+
+       return -ENOMEM;
+}
index 8d05d8b..c4f3c88 100644 (file)
@@ -23,7 +23,7 @@
 #endif
 
 #define LIOINTC_CHIP_IRQ       32
-#define LIOINTC_NUM_PARENT 4
+#define LIOINTC_NUM_PARENT     4
 #define LIOINTC_NUM_CORES      4
 
 #define LIOINTC_INTC_CHIP_START        0x20
@@ -58,6 +58,8 @@ struct liointc_priv {
        bool                            has_lpc_irq_errata;
 };
 
+struct fwnode_handle *liointc_handle;
+
 static void liointc_chained_handle_irq(struct irq_desc *desc)
 {
        struct liointc_handler_data *handler = irq_desc_get_handler_data(desc);
@@ -153,97 +155,79 @@ static void liointc_resume(struct irq_chip_generic *gc)
        irq_gc_unlock_irqrestore(gc, flags);
 }
 
-static const char * const parent_names[] = {"int0", "int1", "int2", "int3"};
-static const char * const core_reg_names[] = {"isr0", "isr1", "isr2", "isr3"};
+static int parent_irq[LIOINTC_NUM_PARENT];
+static u32 parent_int_map[LIOINTC_NUM_PARENT];
+static const char *const parent_names[] = {"int0", "int1", "int2", "int3"};
+static const char *const core_reg_names[] = {"isr0", "isr1", "isr2", "isr3"};
 
-static void __iomem *liointc_get_reg_byname(struct device_node *node,
-                                               const char *name)
+static int liointc_domain_xlate(struct irq_domain *d, struct device_node *ctrlr,
+                            const u32 *intspec, unsigned int intsize,
+                            unsigned long *out_hwirq, unsigned int *out_type)
 {
-       int index = of_property_match_string(node, "reg-names", name);
-
-       if (index < 0)
-               return NULL;
-
-       return of_iomap(node, index);
+       if (WARN_ON(intsize < 1))
+               return -EINVAL;
+       *out_hwirq = intspec[0] - GSI_MIN_CPU_IRQ;
+       *out_type = IRQ_TYPE_NONE;
+       return 0;
 }
 
-static int __init liointc_of_init(struct device_node *node,
-                                 struct device_node *parent)
+static const struct irq_domain_ops acpi_irq_gc_ops = {
+       .map    = irq_map_generic_chip,
+       .unmap  = irq_unmap_generic_chip,
+       .xlate  = liointc_domain_xlate,
+};
+
+static int liointc_init(phys_addr_t addr, unsigned long size, int revision,
+               struct fwnode_handle *domain_handle, struct device_node *node)
 {
+       int i, err;
+       void __iomem *base;
+       struct irq_chip_type *ct;
        struct irq_chip_generic *gc;
        struct irq_domain *domain;
-       struct irq_chip_type *ct;
        struct liointc_priv *priv;
-       void __iomem *base;
-       u32 of_parent_int_map[LIOINTC_NUM_PARENT];
-       int parent_irq[LIOINTC_NUM_PARENT];
-       bool have_parent = FALSE;
-       int sz, i, err = 0;
 
        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
        if (!priv)
                return -ENOMEM;
 
-       if (of_device_is_compatible(node, "loongson,liointc-2.0")) {
-               base = liointc_get_reg_byname(node, "main");
-               if (!base) {
-                       err = -ENODEV;
-                       goto out_free_priv;
-               }
+       base = ioremap(addr, size);
+       if (!base)
+               goto out_free_priv;
 
-               for (i = 0; i < LIOINTC_NUM_CORES; i++)
-                       priv->core_isr[i] = liointc_get_reg_byname(node, core_reg_names[i]);
-               if (!priv->core_isr[0]) {
-                       err = -ENODEV;
-                       goto out_iounmap_base;
-               }
-       } else {
-               base = of_iomap(node, 0);
-               if (!base) {
-                       err = -ENODEV;
-                       goto out_free_priv;
-               }
+       for (i = 0; i < LIOINTC_NUM_CORES; i++)
+               priv->core_isr[i] = base + LIOINTC_REG_INTC_STATUS;
 
-               for (i = 0; i < LIOINTC_NUM_CORES; i++)
-                       priv->core_isr[i] = base + LIOINTC_REG_INTC_STATUS;
-       }
+       for (i = 0; i < LIOINTC_NUM_PARENT; i++)
+               priv->handler[i].parent_int_map = parent_int_map[i];
 
-       for (i = 0; i < LIOINTC_NUM_PARENT; i++) {
-               parent_irq[i] = of_irq_get_byname(node, parent_names[i]);
-               if (parent_irq[i] > 0)
-                       have_parent = TRUE;
-       }
-       if (!have_parent) {
-               err = -ENODEV;
-               goto out_iounmap_isr;
-       }
+       if (revision > 1) {
+               for (i = 0; i < LIOINTC_NUM_CORES; i++) {
+                       int index = of_property_match_string(node,
+                                       "reg-names", core_reg_names[i]);
 
-       sz = of_property_read_variable_u32_array(node,
-                                               "loongson,parent_int_map",
-                                               &of_parent_int_map[0],
-                                               LIOINTC_NUM_PARENT,
-                                               LIOINTC_NUM_PARENT);
-       if (sz < 4) {
-               pr_err("loongson-liointc: No parent_int_map\n");
-               err = -ENODEV;
-               goto out_iounmap_isr;
-       }
+                       if (index < 0)
+                               return -EINVAL;
 
-       for (i = 0; i < LIOINTC_NUM_PARENT; i++)
-               priv->handler[i].parent_int_map = of_parent_int_map[i];
+                       priv->core_isr[i] = of_iomap(node, index);
+               }
+       }
 
        /* Setup IRQ domain */
-       domain = irq_domain_add_linear(node, 32,
+       if (!acpi_disabled)
+               domain = irq_domain_create_linear(domain_handle, LIOINTC_CHIP_IRQ,
+                                       &acpi_irq_gc_ops, priv);
+       else
+               domain = irq_domain_create_linear(domain_handle, LIOINTC_CHIP_IRQ,
                                        &irq_generic_chip_ops, priv);
        if (!domain) {
                pr_err("loongson-liointc: cannot add IRQ domain\n");
-               err = -EINVAL;
-               goto out_iounmap_isr;
+               goto out_iounmap;
        }
 
-       err = irq_alloc_domain_generic_chips(domain, 32, 1,
-                                       node->full_name, handle_level_irq,
-                                       IRQ_NOPROBE, 0, 0);
+       err = irq_alloc_domain_generic_chips(domain, LIOINTC_CHIP_IRQ, 1,
+                                       (node ? node->full_name : "LIOINTC"),
+                                       handle_level_irq, 0, IRQ_NOPROBE, 0);
        if (err) {
                pr_err("loongson-liointc: unable to register IRQ domain\n");
                goto out_free_domain;
@@ -299,24 +283,93 @@ static int __init liointc_of_init(struct device_node *node,
                                liointc_chained_handle_irq, &priv->handler[i]);
        }
 
+       liointc_handle = domain_handle;
        return 0;
 
 out_free_domain:
        irq_domain_remove(domain);
-out_iounmap_isr:
-       for (i = 0; i < LIOINTC_NUM_CORES; i++) {
-               if (!priv->core_isr[i])
-                       continue;
-               iounmap(priv->core_isr[i]);
-       }
-out_iounmap_base:
+out_iounmap:
        iounmap(base);
 out_free_priv:
        kfree(priv);
 
-       return err;
+       return -EINVAL;
+}
+
+#ifdef CONFIG_OF
+
+static int __init liointc_of_init(struct device_node *node,
+                                 struct device_node *parent)
+{
+       bool have_parent = FALSE;
+       int sz, i, index, revision, err = 0;
+       struct resource res;
+
+       if (!of_device_is_compatible(node, "loongson,liointc-2.0")) {
+               index = 0;
+               revision = 1;
+       } else {
+               index = of_property_match_string(node, "reg-names", "main");
+               revision = 2;
+       }
+
+       if (of_address_to_resource(node, index, &res))
+               return -EINVAL;
+
+       for (i = 0; i < LIOINTC_NUM_PARENT; i++) {
+               parent_irq[i] = of_irq_get_byname(node, parent_names[i]);
+               if (parent_irq[i] > 0)
+                       have_parent = TRUE;
+       }
+       if (!have_parent)
+               return -ENODEV;
+
+       sz = of_property_read_variable_u32_array(node,
+                                               "loongson,parent_int_map",
+                                               &parent_int_map[0],
+                                               LIOINTC_NUM_PARENT,
+                                               LIOINTC_NUM_PARENT);
+       if (sz < 4) {
+               pr_err("loongson-liointc: No parent_int_map\n");
+               return -ENODEV;
+       }
+
+       err = liointc_init(res.start, resource_size(&res),
+                       revision, of_node_to_fwnode(node), node);
+       if (err < 0)
+               return err;
+
+       return 0;
 }
 
 IRQCHIP_DECLARE(loongson_liointc_1_0, "loongson,liointc-1.0", liointc_of_init);
 IRQCHIP_DECLARE(loongson_liointc_1_0a, "loongson,liointc-1.0a", liointc_of_init);
 IRQCHIP_DECLARE(loongson_liointc_2_0, "loongson,liointc-2.0", liointc_of_init);
+
+#endif
+
+#ifdef CONFIG_ACPI
+int __init liointc_acpi_init(struct irq_domain *parent, struct acpi_madt_lio_pic *acpi_liointc)
+{
+       int ret;
+       struct fwnode_handle *domain_handle;
+
+       parent_int_map[0] = acpi_liointc->cascade_map[0];
+       parent_int_map[1] = acpi_liointc->cascade_map[1];
+
+       parent_irq[0] = irq_create_mapping(parent, acpi_liointc->cascade[0]);
+       parent_irq[1] = irq_create_mapping(parent, acpi_liointc->cascade[1]);
+
+       domain_handle = irq_domain_alloc_fwnode((phys_addr_t *)acpi_liointc);
+       if (!domain_handle) {
+               pr_err("Unable to allocate domain handle\n");
+               return -ENOMEM;
+       }
+       ret = liointc_init(acpi_liointc->address, acpi_liointc->size,
+                          1, domain_handle, NULL);
+       if (ret)
+               irq_domain_free_fwnode(domain_handle);
+
+       return ret;
+}
+#endif
diff --git a/drivers/irqchip/irq-loongson-pch-lpc.c b/drivers/irqchip/irq-loongson-pch-lpc.c
new file mode 100644 (file)
index 0000000..bf23249
--- /dev/null
@@ -0,0 +1,205 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Loongson LPC Interrupt Controller support
+ *
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+
+#define pr_fmt(fmt) "lpc: " fmt
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+
+/* Registers */
+#define LPC_INT_CTL            0x00
+#define LPC_INT_ENA            0x04
+#define LPC_INT_STS            0x08
+#define LPC_INT_CLR            0x0c
+#define LPC_INT_POL            0x10
+#define LPC_COUNT              16
+
+/* LPC_INT_CTL */
+#define LPC_INT_CTL_EN         BIT(31)
+
+struct pch_lpc {
+       void __iomem            *base;
+       struct irq_domain       *lpc_domain;
+       raw_spinlock_t          lpc_lock;
+       u32                     saved_reg_ctl;
+       u32                     saved_reg_ena;
+       u32                     saved_reg_pol;
+};
+
+struct fwnode_handle *pch_lpc_handle;
+
+static void lpc_irq_ack(struct irq_data *d)
+{
+       unsigned long flags;
+       struct pch_lpc *priv = d->domain->host_data;
+
+       raw_spin_lock_irqsave(&priv->lpc_lock, flags);
+       writel(0x1 << d->hwirq, priv->base + LPC_INT_CLR);
+       raw_spin_unlock_irqrestore(&priv->lpc_lock, flags);
+}
+
+static void lpc_irq_mask(struct irq_data *d)
+{
+       unsigned long flags;
+       struct pch_lpc *priv = d->domain->host_data;
+
+       raw_spin_lock_irqsave(&priv->lpc_lock, flags);
+       writel(readl(priv->base + LPC_INT_ENA) & (~(0x1 << (d->hwirq))),
+                       priv->base + LPC_INT_ENA);
+       raw_spin_unlock_irqrestore(&priv->lpc_lock, flags);
+}
+
+static void lpc_irq_unmask(struct irq_data *d)
+{
+       unsigned long flags;
+       struct pch_lpc *priv = d->domain->host_data;
+
+       raw_spin_lock_irqsave(&priv->lpc_lock, flags);
+       writel(readl(priv->base + LPC_INT_ENA) | (0x1 << (d->hwirq)),
+                       priv->base + LPC_INT_ENA);
+       raw_spin_unlock_irqrestore(&priv->lpc_lock, flags);
+}
+
+static int lpc_irq_set_type(struct irq_data *d, unsigned int type)
+{
+       u32 val;
+       u32 mask = 0x1 << (d->hwirq);
+       struct pch_lpc *priv = d->domain->host_data;
+
+       if (!(type & IRQ_TYPE_LEVEL_MASK))
+               return 0;
+
+       val = readl(priv->base + LPC_INT_POL);
+
+       if (type == IRQ_TYPE_LEVEL_HIGH)
+               val |= mask;
+       else
+               val &= ~mask;
+
+       writel(val, priv->base + LPC_INT_POL);
+
+       return 0;
+}
+
+static const struct irq_chip pch_lpc_irq_chip = {
+       .name                   = "PCH LPC",
+       .irq_mask               = lpc_irq_mask,
+       .irq_unmask             = lpc_irq_unmask,
+       .irq_ack                = lpc_irq_ack,
+       .irq_set_type           = lpc_irq_set_type,
+       .flags                  = IRQCHIP_SKIP_SET_WAKE,
+};
+
+static void lpc_irq_dispatch(struct irq_desc *desc)
+{
+       u32 pending, bit;
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+       struct pch_lpc *priv = irq_desc_get_handler_data(desc);
+
+       chained_irq_enter(chip, desc);
+
+       pending = readl(priv->base + LPC_INT_ENA);
+       pending &= readl(priv->base + LPC_INT_STS);
+       if (!pending)
+               spurious_interrupt();
+
+       while (pending) {
+               bit = __ffs(pending);
+
+               generic_handle_domain_irq(priv->lpc_domain, bit);
+               pending &= ~BIT(bit);
+       }
+       chained_irq_exit(chip, desc);
+}
+
+static int pch_lpc_map(struct irq_domain *d, unsigned int irq,
+                       irq_hw_number_t hw)
+{
+       irq_set_chip_and_handler(irq, &pch_lpc_irq_chip, handle_level_irq);
+       return 0;
+}
+
+static const struct irq_domain_ops pch_lpc_domain_ops = {
+       .map            = pch_lpc_map,
+       .translate      = irq_domain_translate_twocell,
+};
+
+static void pch_lpc_reset(struct pch_lpc *priv)
+{
+       /* Enable the LPC interrupt, bit31: en  bit30: edge */
+       writel(LPC_INT_CTL_EN, priv->base + LPC_INT_CTL);
+       writel(0, priv->base + LPC_INT_ENA);
+       /* Clear all 18-bit interrpt bit */
+       writel(GENMASK(17, 0), priv->base + LPC_INT_CLR);
+}
+
+static int pch_lpc_disabled(struct pch_lpc *priv)
+{
+       return (readl(priv->base + LPC_INT_ENA) == 0xffffffff) &&
+                       (readl(priv->base + LPC_INT_STS) == 0xffffffff);
+}
+
+int __init pch_lpc_acpi_init(struct irq_domain *parent,
+                                       struct acpi_madt_lpc_pic *acpi_pchlpc)
+{
+       int parent_irq;
+       struct pch_lpc *priv;
+       struct irq_fwspec fwspec;
+       struct fwnode_handle *irq_handle;
+
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       raw_spin_lock_init(&priv->lpc_lock);
+
+       priv->base = ioremap(acpi_pchlpc->address, acpi_pchlpc->size);
+       if (!priv->base)
+               goto free_priv;
+
+       if (pch_lpc_disabled(priv)) {
+               pr_err("Failed to get LPC status\n");
+               goto iounmap_base;
+       }
+
+       irq_handle = irq_domain_alloc_named_fwnode("lpcintc");
+       if (!irq_handle) {
+               pr_err("Unable to allocate domain handle\n");
+               goto iounmap_base;
+       }
+
+       priv->lpc_domain = irq_domain_create_linear(irq_handle, LPC_COUNT,
+                                       &pch_lpc_domain_ops, priv);
+       if (!priv->lpc_domain) {
+               pr_err("Failed to create IRQ domain\n");
+               goto free_irq_handle;
+       }
+       pch_lpc_reset(priv);
+
+       fwspec.fwnode = parent->fwnode;
+       fwspec.param[0] = acpi_pchlpc->cascade + GSI_MIN_PCH_IRQ;
+       fwspec.param[1] = IRQ_TYPE_LEVEL_HIGH;
+       fwspec.param_count = 2;
+       parent_irq = irq_create_fwspec_mapping(&fwspec);
+       irq_set_chained_handler_and_data(parent_irq, lpc_irq_dispatch, priv);
+
+       pch_lpc_handle = irq_handle;
+       return 0;
+
+free_irq_handle:
+       irq_domain_free_fwnode(irq_handle);
+iounmap_base:
+       iounmap(priv->base);
+free_priv:
+       kfree(priv);
+
+       return -ENOMEM;
+}
index e3801c4..d0e8551 100644 (file)
@@ -15,6 +15,8 @@
 #include <linux/pci.h>
 #include <linux/slab.h>
 
+static int nr_pics;
+
 struct pch_msi_data {
        struct mutex    msi_map_lock;
        phys_addr_t     doorbell;
@@ -23,6 +25,8 @@ struct pch_msi_data {
        unsigned long   *msi_map;
 };
 
+static struct fwnode_handle *pch_msi_handle[MAX_IO_PICS];
+
 static void pch_msi_mask_msi_irq(struct irq_data *d)
 {
        pci_msi_mask_irq(d);
@@ -154,12 +158,12 @@ static const struct irq_domain_ops pch_msi_middle_domain_ops = {
 };
 
 static int pch_msi_init_domains(struct pch_msi_data *priv,
-                               struct device_node *node,
-                               struct irq_domain *parent)
+                               struct irq_domain *parent,
+                               struct fwnode_handle *domain_handle)
 {
        struct irq_domain *middle_domain, *msi_domain;
 
-       middle_domain = irq_domain_create_linear(of_node_to_fwnode(node),
+       middle_domain = irq_domain_create_linear(domain_handle,
                                                priv->num_irqs,
                                                &pch_msi_middle_domain_ops,
                                                priv);
@@ -171,7 +175,7 @@ static int pch_msi_init_domains(struct pch_msi_data *priv,
        middle_domain->parent = parent;
        irq_domain_update_bus_token(middle_domain, DOMAIN_BUS_NEXUS);
 
-       msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(node),
+       msi_domain = pci_msi_create_irq_domain(domain_handle,
                                               &pch_msi_domain_info,
                                               middle_domain);
        if (!msi_domain) {
@@ -183,19 +187,11 @@ static int pch_msi_init_domains(struct pch_msi_data *priv,
        return 0;
 }
 
-static int pch_msi_init(struct device_node *node,
-                           struct device_node *parent)
+static int pch_msi_init(phys_addr_t msg_address, int irq_base, int irq_count,
+                       struct irq_domain *parent_domain, struct fwnode_handle *domain_handle)
 {
-       struct pch_msi_data *priv;
-       struct irq_domain *parent_domain;
-       struct resource res;
        int ret;
-
-       parent_domain = irq_find_host(parent);
-       if (!parent_domain) {
-               pr_err("Failed to find the parent domain\n");
-               return -ENXIO;
-       }
+       struct pch_msi_data *priv;
 
        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
        if (!priv)
@@ -203,48 +199,95 @@ static int pch_msi_init(struct device_node *node,
 
        mutex_init(&priv->msi_map_lock);
 
-       ret = of_address_to_resource(node, 0, &res);
-       if (ret) {
-               pr_err("Failed to allocate resource\n");
-               goto err_priv;
-       }
-
-       priv->doorbell = res.start;
-
-       if (of_property_read_u32(node, "loongson,msi-base-vec",
-                               &priv->irq_first)) {
-               pr_err("Unable to parse MSI vec base\n");
-               ret = -EINVAL;
-               goto err_priv;
-       }
-
-       if (of_property_read_u32(node, "loongson,msi-num-vecs",
-                               &priv->num_irqs)) {
-               pr_err("Unable to parse MSI vec number\n");
-               ret = -EINVAL;
-               goto err_priv;
-       }
+       priv->doorbell = msg_address;
+       priv->irq_first = irq_base;
+       priv->num_irqs = irq_count;
 
        priv->msi_map = bitmap_zalloc(priv->num_irqs, GFP_KERNEL);
-       if (!priv->msi_map) {
-               ret = -ENOMEM;
+       if (!priv->msi_map)
                goto err_priv;
-       }
 
        pr_debug("Registering %d MSIs, starting at %d\n",
                 priv->num_irqs, priv->irq_first);
 
-       ret = pch_msi_init_domains(priv, node, parent_domain);
+       ret = pch_msi_init_domains(priv, parent_domain, domain_handle);
        if (ret)
                goto err_map;
 
+       pch_msi_handle[nr_pics++] = domain_handle;
        return 0;
 
 err_map:
        bitmap_free(priv->msi_map);
 err_priv:
        kfree(priv);
-       return ret;
+
+       return -EINVAL;
+}
+
+#ifdef CONFIG_OF
+static int pch_msi_of_init(struct device_node *node, struct device_node *parent)
+{
+       int err;
+       int irq_base, irq_count;
+       struct resource res;
+       struct irq_domain *parent_domain;
+
+       parent_domain = irq_find_host(parent);
+       if (!parent_domain) {
+               pr_err("Failed to find the parent domain\n");
+               return -ENXIO;
+       }
+
+       if (of_address_to_resource(node, 0, &res)) {
+               pr_err("Failed to allocate resource\n");
+               return -EINVAL;
+       }
+
+       if (of_property_read_u32(node, "loongson,msi-base-vec", &irq_base)) {
+               pr_err("Unable to parse MSI vec base\n");
+               return -EINVAL;
+       }
+
+       if (of_property_read_u32(node, "loongson,msi-num-vecs", &irq_count)) {
+               pr_err("Unable to parse MSI vec number\n");
+               return -EINVAL;
+       }
+
+       err = pch_msi_init(res.start, irq_base, irq_count, parent_domain, of_node_to_fwnode(node));
+       if (err < 0)
+               return err;
+
+       return 0;
 }
 
-IRQCHIP_DECLARE(pch_msi, "loongson,pch-msi-1.0", pch_msi_init);
+IRQCHIP_DECLARE(pch_msi, "loongson,pch-msi-1.0", pch_msi_of_init);
+#endif
+
+#ifdef CONFIG_ACPI
+struct fwnode_handle *get_pch_msi_handle(int pci_segment)
+{
+       int i;
+
+       for (i = 0; i < MAX_IO_PICS; i++) {
+               if (msi_group[i].pci_segment == pci_segment)
+                       return pch_msi_handle[i];
+       }
+       return NULL;
+}
+
+int __init pch_msi_acpi_init(struct irq_domain *parent,
+                                       struct acpi_madt_msi_pic *acpi_pchmsi)
+{
+       int ret;
+       struct fwnode_handle *domain_handle;
+
+       domain_handle = irq_domain_alloc_fwnode((phys_addr_t *)acpi_pchmsi);
+       ret = pch_msi_init(acpi_pchmsi->msg_address, acpi_pchmsi->start,
+                               acpi_pchmsi->count, parent, domain_handle);
+       if (ret < 0)
+               irq_domain_free_fwnode(domain_handle);
+
+       return ret;
+}
+#endif
index a4eb8a2..b6f1392 100644 (file)
 #define PIC_REG_IDX(irq_id)    ((irq_id) / PIC_COUNT_PER_REG)
 #define PIC_REG_BIT(irq_id)    ((irq_id) % PIC_COUNT_PER_REG)
 
+static int nr_pics;
+
 struct pch_pic {
        void __iomem            *base;
        struct irq_domain       *pic_domain;
        u32                     ht_vec_base;
        raw_spinlock_t          pic_lock;
+       u32                     vec_count;
+       u32                     gsi_base;
 };
 
+static struct pch_pic *pch_pic_priv[MAX_IO_PICS];
+
+struct fwnode_handle *pch_pic_handle[MAX_IO_PICS];
+
+int find_pch_pic(u32 gsi)
+{
+       int i;
+
+       /* Find the PCH_PIC that manages this GSI. */
+       for (i = 0; i < MAX_IO_PICS; i++) {
+               struct pch_pic *priv = pch_pic_priv[i];
+
+               if (!priv)
+                       return -1;
+
+               if (gsi >= priv->gsi_base && gsi < (priv->gsi_base + priv->vec_count))
+                       return i;
+       }
+
+       pr_err("ERROR: Unable to locate PCH_PIC for GSI %d\n", gsi);
+       return -1;
+}
+
 static void pch_pic_bitset(struct pch_pic *priv, int offset, int bit)
 {
        u32 reg;
@@ -139,6 +166,28 @@ static struct irq_chip pch_pic_irq_chip = {
        .irq_set_type           = pch_pic_set_type,
 };
 
+static int pch_pic_domain_translate(struct irq_domain *d,
+                                       struct irq_fwspec *fwspec,
+                                       unsigned long *hwirq,
+                                       unsigned int *type)
+{
+       struct pch_pic *priv = d->host_data;
+       struct device_node *of_node = to_of_node(fwspec->fwnode);
+
+       if (fwspec->param_count < 1)
+               return -EINVAL;
+
+       if (of_node) {
+               *hwirq = fwspec->param[0] + priv->ht_vec_base;
+               *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
+       } else {
+               *hwirq = fwspec->param[0] - priv->gsi_base;
+               *type = IRQ_TYPE_NONE;
+       }
+
+       return 0;
+}
+
 static int pch_pic_alloc(struct irq_domain *domain, unsigned int virq,
                              unsigned int nr_irqs, void *arg)
 {
@@ -149,13 +198,13 @@ static int pch_pic_alloc(struct irq_domain *domain, unsigned int virq,
        struct irq_fwspec parent_fwspec;
        struct pch_pic *priv = domain->host_data;
 
-       err = irq_domain_translate_twocell(domain, fwspec, &hwirq, &type);
+       err = pch_pic_domain_translate(domain, fwspec, &hwirq, &type);
        if (err)
                return err;
 
        parent_fwspec.fwnode = domain->parent->fwnode;
        parent_fwspec.param_count = 1;
-       parent_fwspec.param[0] = hwirq + priv->ht_vec_base;
+       parent_fwspec.param[0] = hwirq;
 
        err = irq_domain_alloc_irqs_parent(domain, virq, 1, &parent_fwspec);
        if (err)
@@ -170,7 +219,7 @@ static int pch_pic_alloc(struct irq_domain *domain, unsigned int virq,
 }
 
 static const struct irq_domain_ops pch_pic_domain_ops = {
-       .translate      = irq_domain_translate_twocell,
+       .translate      = pch_pic_domain_translate,
        .alloc          = pch_pic_alloc,
        .free           = irq_domain_free_irqs_parent,
 };
@@ -180,7 +229,7 @@ static void pch_pic_reset(struct pch_pic *priv)
        int i;
 
        for (i = 0; i < PIC_COUNT; i++) {
-               /* Write vectored ID */
+               /* Write vector ID */
                writeb(priv->ht_vec_base + i, priv->base + PCH_INT_HTVEC(i));
                /* Hardcode route to HT0 Lo */
                writeb(1, priv->base + PCH_INT_ROUTE(i));
@@ -198,50 +247,37 @@ static void pch_pic_reset(struct pch_pic *priv)
        }
 }
 
-static int pch_pic_of_init(struct device_node *node,
-                               struct device_node *parent)
+static int pch_pic_init(phys_addr_t addr, unsigned long size, int vec_base,
+                       struct irq_domain *parent_domain, struct fwnode_handle *domain_handle,
+                       u32 gsi_base)
 {
        struct pch_pic *priv;
-       struct irq_domain *parent_domain;
-       int err;
 
        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
        if (!priv)
                return -ENOMEM;
 
        raw_spin_lock_init(&priv->pic_lock);
-       priv->base = of_iomap(node, 0);
-       if (!priv->base) {
-               err = -ENOMEM;
+       priv->base = ioremap(addr, size);
+       if (!priv->base)
                goto free_priv;
-       }
 
-       parent_domain = irq_find_host(parent);
-       if (!parent_domain) {
-               pr_err("Failed to find the parent domain\n");
-               err = -ENXIO;
-               goto iounmap_base;
-       }
-
-       if (of_property_read_u32(node, "loongson,pic-base-vec",
-                               &priv->ht_vec_base)) {
-               pr_err("Failed to determine pic-base-vec\n");
-               err = -EINVAL;
-               goto iounmap_base;
-       }
+       priv->ht_vec_base = vec_base;
+       priv->vec_count = ((readq(priv->base) >> 48) & 0xff) + 1;
+       priv->gsi_base = gsi_base;
 
        priv->pic_domain = irq_domain_create_hierarchy(parent_domain, 0,
-                                                      PIC_COUNT,
-                                                      of_node_to_fwnode(node),
-                                                      &pch_pic_domain_ops,
-                                                      priv);
+                                               priv->vec_count, domain_handle,
+                                               &pch_pic_domain_ops, priv);
+
        if (!priv->pic_domain) {
                pr_err("Failed to create IRQ domain\n");
-               err = -ENOMEM;
                goto iounmap_base;
        }
 
        pch_pic_reset(priv);
+       pch_pic_handle[nr_pics] = domain_handle;
+       pch_pic_priv[nr_pics++] = priv;
 
        return 0;
 
@@ -250,7 +286,86 @@ iounmap_base:
 free_priv:
        kfree(priv);
 
-       return err;
+       return -EINVAL;
+}
+
+#ifdef CONFIG_OF
+
+static int pch_pic_of_init(struct device_node *node,
+                               struct device_node *parent)
+{
+       int err, vec_base;
+       struct resource res;
+       struct irq_domain *parent_domain;
+
+       if (of_address_to_resource(node, 0, &res))
+               return -EINVAL;
+
+       parent_domain = irq_find_host(parent);
+       if (!parent_domain) {
+               pr_err("Failed to find the parent domain\n");
+               return -ENXIO;
+       }
+
+       if (of_property_read_u32(node, "loongson,pic-base-vec", &vec_base)) {
+               pr_err("Failed to determine pic-base-vec\n");
+               return -EINVAL;
+       }
+
+       err = pch_pic_init(res.start, resource_size(&res), vec_base,
+                               parent_domain, of_node_to_fwnode(node), 0);
+       if (err < 0)
+               return err;
+
+       return 0;
 }
 
 IRQCHIP_DECLARE(pch_pic, "loongson,pch-pic-1.0", pch_pic_of_init);
+
+#endif
+
+#ifdef CONFIG_ACPI
+static int __init
+pch_lpc_parse_madt(union acpi_subtable_headers *header,
+                      const unsigned long end)
+{
+       struct acpi_madt_lpc_pic *pchlpc_entry = (struct acpi_madt_lpc_pic *)header;
+
+       return pch_lpc_acpi_init(pch_pic_priv[0]->pic_domain, pchlpc_entry);
+}
+
+static int __init acpi_cascade_irqdomain_init(void)
+{
+       acpi_table_parse_madt(ACPI_MADT_TYPE_LPC_PIC,
+                             pch_lpc_parse_madt, 0);
+       return 0;
+}
+
+int __init pch_pic_acpi_init(struct irq_domain *parent,
+                                       struct acpi_madt_bio_pic *acpi_pchpic)
+{
+       int ret, vec_base;
+       struct fwnode_handle *domain_handle;
+
+       vec_base = acpi_pchpic->gsi_base - GSI_MIN_PCH_IRQ;
+
+       domain_handle = irq_domain_alloc_fwnode((phys_addr_t *)acpi_pchpic);
+       if (!domain_handle) {
+               pr_err("Unable to allocate domain handle\n");
+               return -ENOMEM;
+       }
+
+       ret = pch_pic_init(acpi_pchpic->address, acpi_pchpic->size,
+                               vec_base, parent, domain_handle, acpi_pchpic->gsi_base);
+
+       if (ret < 0) {
+               irq_domain_free_fwnode(domain_handle);
+               return ret;
+       }
+
+       if (acpi_pchpic->id == 0)
+               acpi_cascade_irqdomain_init();
+
+       return ret;
+}
+#endif
index b8d96d3..9dbd738 100644 (file)
@@ -293,7 +293,7 @@ config PCI_HYPERV_INTERFACE
 config PCI_LOONGSON
        bool "LOONGSON PCI Controller"
        depends on MACH_LOONGSON64 || COMPILE_TEST
-       depends on OF
+       depends on OF || ACPI
        depends on PCI_QUIRKS
        default MACH_LOONGSON64
        help
index 50a8e1d..05c5040 100644 (file)
@@ -9,6 +9,8 @@
 #include <linux/of_pci.h>
 #include <linux/pci.h>
 #include <linux/pci_ids.h>
+#include <linux/pci-acpi.h>
+#include <linux/pci-ecam.h>
 
 #include "../pci.h"
 
 #define DEV_PCIE_PORT_2        0x7a29
 
 #define DEV_LS2K_APB   0x7a02
-#define DEV_LS7A_CONF  0x7a10
+#define DEV_LS7A_GMAC  0x7a03
+#define DEV_LS7A_DC1   0x7a06
 #define DEV_LS7A_LPC   0x7a0c
+#define DEV_LS7A_AHCI  0x7a08
+#define DEV_LS7A_CONF  0x7a10
+#define DEV_LS7A_GNET  0x7a13
+#define DEV_LS7A_EHCI  0x7a14
+#define DEV_LS7A_DC2   0x7a36
+#define DEV_LS7A_HDMI  0x7a37
 
 #define FLAG_CFG0      BIT(0)
 #define FLAG_CFG1      BIT(1)
 #define FLAG_DEV_FIX   BIT(2)
+#define FLAG_DEV_HIDDEN        BIT(3)
+
+struct loongson_pci_data {
+       u32 flags;
+       struct pci_ops *ops;
+};
 
 struct loongson_pci {
        void __iomem *cfg0_base;
        void __iomem *cfg1_base;
        struct platform_device *pdev;
-       u32 flags;
+       const struct loongson_pci_data *data;
 };
 
 /* Fixup wrong class code in PCIe bridges */
@@ -92,55 +107,106 @@ static void loongson_mrrs_quirk(struct pci_dev *dev)
 }
 DECLARE_PCI_FIXUP_ENABLE(PCI_ANY_ID, PCI_ANY_ID, loongson_mrrs_quirk);
 
-static void __iomem *cfg1_map(struct loongson_pci *priv, int bus,
-                               unsigned int devfn, int where)
+static void loongson_pci_pin_quirk(struct pci_dev *pdev)
 {
-       unsigned long addroff = 0x0;
+       pdev->pin = 1 + (PCI_FUNC(pdev->devfn) & 3);
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON,
+                       DEV_LS7A_DC1, loongson_pci_pin_quirk);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON,
+                       DEV_LS7A_DC2, loongson_pci_pin_quirk);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON,
+                       DEV_LS7A_GMAC, loongson_pci_pin_quirk);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON,
+                       DEV_LS7A_AHCI, loongson_pci_pin_quirk);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON,
+                       DEV_LS7A_EHCI, loongson_pci_pin_quirk);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON,
+                       DEV_LS7A_GNET, loongson_pci_pin_quirk);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON,
+                       DEV_LS7A_HDMI, loongson_pci_pin_quirk);
+
+static struct loongson_pci *pci_bus_to_loongson_pci(struct pci_bus *bus)
+{
+       struct pci_config_window *cfg;
 
-       if (bus != 0)
-               addroff |= BIT(28); /* Type 1 Access */
-       addroff |= (where & 0xff) | ((where & 0xf00) << 16);
-       addroff |= (bus << 16) | (devfn << 8);
-       return priv->cfg1_base + addroff;
+       if (acpi_disabled)
+               return (struct loongson_pci *)(bus->sysdata);
+
+       cfg = bus->sysdata;
+       return (struct loongson_pci *)(cfg->priv);
 }
 
-static void __iomem *cfg0_map(struct loongson_pci *priv, int bus,
-                               unsigned int devfn, int where)
+static void __iomem *cfg0_map(struct loongson_pci *priv, struct pci_bus *bus,
+                             unsigned int devfn, int where)
 {
        unsigned long addroff = 0x0;
+       unsigned char busnum = bus->number;
 
-       if (bus != 0)
+       if (!pci_is_root_bus(bus)) {
                addroff |= BIT(24); /* Type 1 Access */
-       addroff |= (bus << 16) | (devfn << 8) | where;
+               addroff |= (busnum << 16);
+       }
+       addroff |= (devfn << 8) | where;
        return priv->cfg0_base + addroff;
 }
 
-static void __iomem *pci_loongson_map_bus(struct pci_bus *bus, unsigned int devfn,
-                              int where)
+static void __iomem *cfg1_map(struct loongson_pci *priv, struct pci_bus *bus,
+                             unsigned int devfn, int where)
 {
+       unsigned long addroff = 0x0;
        unsigned char busnum = bus->number;
-       struct pci_host_bridge *bridge = pci_find_host_bridge(bus);
-       struct loongson_pci *priv =  pci_host_bridge_priv(bridge);
+
+       if (!pci_is_root_bus(bus)) {
+               addroff |= BIT(28); /* Type 1 Access */
+               addroff |= (busnum << 16);
+       }
+       addroff |= (devfn << 8) | (where & 0xff) | ((where & 0xf00) << 16);
+       return priv->cfg1_base + addroff;
+}
+
+static bool pdev_may_exist(struct pci_bus *bus, unsigned int device,
+                          unsigned int function)
+{
+       return !(pci_is_root_bus(bus) &&
+               (device >= 9 && device <= 20) && (function > 0));
+}
+
+static void __iomem *pci_loongson_map_bus(struct pci_bus *bus,
+                                         unsigned int devfn, int where)
+{
+       unsigned int device = PCI_SLOT(devfn);
+       unsigned int function = PCI_FUNC(devfn);
+       struct loongson_pci *priv = pci_bus_to_loongson_pci(bus);
 
        /*
         * Do not read more than one device on the bus other than
-        * the host bus. For our hardware the root bus is always bus 0.
+        * the host bus.
         */
-       if (priv->flags & FLAG_DEV_FIX && busnum != 0 &&
-               PCI_SLOT(devfn) > 0)
-               return NULL;
+       if ((priv->data->flags & FLAG_DEV_FIX) && bus->self) {
+               if (!pci_is_root_bus(bus) && (device > 0))
+                       return NULL;
+       }
+
+       /* Don't access non-existent devices */
+       if (priv->data->flags & FLAG_DEV_HIDDEN) {
+               if (!pdev_may_exist(bus, device, function))
+                       return NULL;
+       }
 
        /* CFG0 can only access standard space */
        if (where < PCI_CFG_SPACE_SIZE && priv->cfg0_base)
-               return cfg0_map(priv, busnum, devfn, where);
+               return cfg0_map(priv, bus, devfn, where);
 
        /* CFG1 can access extended space */
        if (where < PCI_CFG_SPACE_EXP_SIZE && priv->cfg1_base)
-               return cfg1_map(priv, busnum, devfn, where);
+               return cfg1_map(priv, bus, devfn, where);
 
        return NULL;
 }
 
+#ifdef CONFIG_OF
+
 static int loongson_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
        int irq;
@@ -159,20 +225,42 @@ static int loongson_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
        return val;
 }
 
-/* H/w only accept 32-bit PCI operations */
+/* LS2K/LS7A accept 8/16/32-bit PCI config operations */
 static struct pci_ops loongson_pci_ops = {
+       .map_bus = pci_loongson_map_bus,
+       .read   = pci_generic_config_read,
+       .write  = pci_generic_config_write,
+};
+
+/* RS780/SR5690 only accept 32-bit PCI config operations */
+static struct pci_ops loongson_pci_ops32 = {
        .map_bus = pci_loongson_map_bus,
        .read   = pci_generic_config_read32,
        .write  = pci_generic_config_write32,
 };
 
+static const struct loongson_pci_data ls2k_pci_data = {
+       .flags = FLAG_CFG1 | FLAG_DEV_FIX | FLAG_DEV_HIDDEN,
+       .ops = &loongson_pci_ops,
+};
+
+static const struct loongson_pci_data ls7a_pci_data = {
+       .flags = FLAG_CFG1 | FLAG_DEV_FIX | FLAG_DEV_HIDDEN,
+       .ops = &loongson_pci_ops,
+};
+
+static const struct loongson_pci_data rs780e_pci_data = {
+       .flags = FLAG_CFG0,
+       .ops = &loongson_pci_ops32,
+};
+
 static const struct of_device_id loongson_pci_of_match[] = {
        { .compatible = "loongson,ls2k-pci",
-               .data = (void *)(FLAG_CFG0 | FLAG_CFG1 | FLAG_DEV_FIX), },
+               .data = &ls2k_pci_data, },
        { .compatible = "loongson,ls7a-pci",
-               .data = (void *)(FLAG_CFG0 | FLAG_CFG1 | FLAG_DEV_FIX), },
+               .data = &ls7a_pci_data, },
        { .compatible = "loongson,rs780e-pci",
-               .data = (void *)(FLAG_CFG0), },
+               .data = &rs780e_pci_data, },
        {}
 };
 
@@ -193,20 +281,20 @@ static int loongson_pci_probe(struct platform_device *pdev)
 
        priv = pci_host_bridge_priv(bridge);
        priv->pdev = pdev;
-       priv->flags = (unsigned long)of_device_get_match_data(dev);
+       priv->data = of_device_get_match_data(dev);
 
-       regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!regs) {
-               dev_err(dev, "missing mem resources for cfg0\n");
-               return -EINVAL;
+       if (priv->data->flags & FLAG_CFG0) {
+               regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+               if (!regs)
+                       dev_err(dev, "missing mem resources for cfg0\n");
+               else {
+                       priv->cfg0_base = devm_pci_remap_cfg_resource(dev, regs);
+                       if (IS_ERR(priv->cfg0_base))
+                               return PTR_ERR(priv->cfg0_base);
+               }
        }
 
-       priv->cfg0_base = devm_pci_remap_cfg_resource(dev, regs);
-       if (IS_ERR(priv->cfg0_base))
-               return PTR_ERR(priv->cfg0_base);
-
-       /* CFG1 is optional */
-       if (priv->flags & FLAG_CFG1) {
+       if (priv->data->flags & FLAG_CFG1) {
                regs = platform_get_resource(pdev, IORESOURCE_MEM, 1);
                if (!regs)
                        dev_info(dev, "missing mem resource for cfg1\n");
@@ -218,7 +306,7 @@ static int loongson_pci_probe(struct platform_device *pdev)
        }
 
        bridge->sysdata = priv;
-       bridge->ops = &loongson_pci_ops;
+       bridge->ops = priv->data->ops;
        bridge->map_irq = loongson_map_irq;
 
        return pci_host_probe(bridge);
@@ -232,3 +320,41 @@ static struct platform_driver loongson_pci_driver = {
        .probe = loongson_pci_probe,
 };
 builtin_platform_driver(loongson_pci_driver);
+
+#endif
+
+#ifdef CONFIG_ACPI
+
+static int loongson_pci_ecam_init(struct pci_config_window *cfg)
+{
+       struct device *dev = cfg->parent;
+       struct loongson_pci *priv;
+       struct loongson_pci_data *data;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       cfg->priv = priv;
+       data->flags = FLAG_CFG1 | FLAG_DEV_HIDDEN;
+       priv->data = data;
+       priv->cfg1_base = cfg->win - (cfg->busr.start << 16);
+
+       return 0;
+}
+
+const struct pci_ecam_ops loongson_pci_ecam_ops = {
+       .bus_shift = 16,
+       .init      = loongson_pci_ecam_init,
+       .pci_ops   = {
+               .map_bus = pci_loongson_map_bus,
+               .read    = pci_generic_config_read,
+               .write   = pci_generic_config_write,
+       }
+};
+
+#endif
index cfaf40a..60c55d2 100644 (file)
@@ -41,8 +41,10 @@ const char *pci_power_names[] = {
 };
 EXPORT_SYMBOL_GPL(pci_power_names);
 
+#ifdef CONFIG_X86_32
 int isa_dma_bridge_buggy;
 EXPORT_SYMBOL(isa_dma_bridge_buggy);
+#endif
 
 int pci_pci_problems;
 EXPORT_SYMBOL(pci_pci_problems);
index 41aeaa2..6fc6450 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/kernel.h>
 #include <linux/export.h>
 #include <linux/pci.h>
+#include <linux/isa-dma.h> /* isa_dma_bridge_buggy */
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/acpi.h>
@@ -30,7 +31,6 @@
 #include <linux/pm_runtime.h>
 #include <linux/suspend.h>
 #include <linux/switchtec.h>
-#include <asm/dma.h>   /* isa_dma_bridge_buggy */
 #include "pci.h"
 
 static ktime_t fixup_debug_start(struct pci_dev *dev,
@@ -239,6 +239,7 @@ static void quirk_passive_release(struct pci_dev *dev)
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82441,      quirk_passive_release);
 DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL,  PCI_DEVICE_ID_INTEL_82441,      quirk_passive_release);
 
+#ifdef CONFIG_X86_32
 /*
  * The VIA VP2/VP3/MVP3 seem to have some 'features'. There may be a
  * workaround but VIA don't answer queries. If you happen to have good
@@ -265,6 +266,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL,   PCI_DEVICE_ID_AL_M1533,         quirk_isa_dma
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NEC,     PCI_DEVICE_ID_NEC_CBUS_1,       quirk_isa_dma_hangs);
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NEC,     PCI_DEVICE_ID_NEC_CBUS_2,       quirk_isa_dma_hangs);
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NEC,     PCI_DEVICE_ID_NEC_CBUS_3,       quirk_isa_dma_hangs);
+#endif
 
 /*
  * Intel NM10 "TigerPoint" LPC PM1a_STS.BM_STS must be clear
index 2fa0f7d..8f76956 100644 (file)
@@ -17,6 +17,7 @@
 #include <asm/dma.h>
 #include <asm/irq.h>
 #include <linux/pci.h>
+#include <linux/libata.h>
 #include <linux/ioport.h>
 #include <linux/init.h>
 
@@ -322,8 +323,8 @@ static int pci_dev_uses_irq(struct pnp_dev *pnp, struct pci_dev *pci,
                 * treat the compatibility IRQs as busy.
                 */
                if ((progif & 0x5) != 0x5)
-                       if (pci_get_legacy_ide_irq(pci, 0) == irq ||
-                           pci_get_legacy_ide_irq(pci, 1) == irq) {
+                       if (ATA_PRIMARY_IRQ(pci) == irq ||
+                           ATA_SECONDARY_IRQ(pci) == irq) {
                                pnp_dbg(&pnp->dev, "  legacy IDE device %s "
                                        "using irq %d\n", pci_name(pci), irq);
                                return 1;
index 6bb3cd3..6869f10 100644 (file)
@@ -1,17 +1,30 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * linux/include/asm-generic/pci.h
- *
- *  Copyright (C) 2003 Russell King
- */
-#ifndef _ASM_GENERIC_PCI_H
-#define _ASM_GENERIC_PCI_H
+/* SPDX-License-Identifier: GPL-2.0-only */
 
-#ifndef HAVE_ARCH_PCI_GET_LEGACY_IDE_IRQ
-static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel)
+#ifndef __ASM_GENERIC_PCI_H
+#define __ASM_GENERIC_PCI_H
+
+#ifndef PCIBIOS_MIN_IO
+#define PCIBIOS_MIN_IO         0
+#endif
+
+#ifndef PCIBIOS_MIN_MEM
+#define PCIBIOS_MIN_MEM                0
+#endif
+
+#ifndef pcibios_assign_all_busses
+/* For bootloaders that do not initialize the PCI bus */
+#define pcibios_assign_all_busses() 1
+#endif
+
+/* Enable generic resource mapping code in drivers/pci/ */
+#define ARCH_GENERIC_PCI_MMAP_RESOURCE
+
+#ifdef CONFIG_PCI_DOMAINS
+static inline int pci_proc_domain(struct pci_bus *bus)
 {
-       return channel ? 15 : 14;
+       /* always show the domain in /proc */
+       return 1;
 }
-#endif /* HAVE_ARCH_PCI_GET_LEGACY_IDE_IRQ */
+#endif /* CONFIG_PCI_DOMAINS */
 
-#endif /* _ASM_GENERIC_PCI_H */
+#endif /* __ASM_GENERIC_PCI_H */
index 5a2f9bf..8fbb0a5 100644 (file)
@@ -25,6 +25,8 @@ extern void pci_iounmap(struct pci_dev *dev, void __iomem *);
 #ifdef CONFIG_NO_GENERIC_PCI_IOPORT_MAP
 extern void __iomem *__pci_ioport_map(struct pci_dev *dev, unsigned long port,
                                      unsigned int nr);
+#elif !defined(CONFIG_HAS_IOPORT_MAP)
+#define __pci_ioport_map(dev, port, nr) NULL
 #else
 #define __pci_ioport_map(dev, port, nr) ioport_map((port), (nr))
 #endif
index 44975c1..7e7a33b 100644 (file)
@@ -105,6 +105,7 @@ enum acpi_irq_model_id {
        ACPI_IRQ_MODEL_IOSAPIC,
        ACPI_IRQ_MODEL_PLATFORM,
        ACPI_IRQ_MODEL_GIC,
+       ACPI_IRQ_MODEL_LPIC,
        ACPI_IRQ_MODEL_COUNT
 };
 
@@ -356,7 +357,8 @@ int acpi_gsi_to_irq (u32 gsi, unsigned int *irq);
 int acpi_isa_irq_to_gsi (unsigned isa_irq, u32 *gsi);
 
 void acpi_set_irq_model(enum acpi_irq_model_id model,
-                       struct fwnode_handle *fwnode);
+                       struct fwnode_handle *(*)(u32));
+void acpi_set_gsi_to_irq_fallback(u32 (*)(u32));
 
 struct irq_domain *acpi_irq_create_hierarchy(unsigned int flags,
                                             unsigned int size,
index b66c5f3..43360b6 100644 (file)
@@ -150,6 +150,7 @@ enum cpuhp_state {
        CPUHP_AP_IRQ_BCM2836_STARTING,
        CPUHP_AP_IRQ_MIPS_GIC_STARTING,
        CPUHP_AP_IRQ_RISCV_STARTING,
+       CPUHP_AP_IRQ_LOONGARCH_STARTING,
        CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING,
        CPUHP_AP_ARM_MVEBU_COHERENCY,
        CPUHP_AP_MICROCODE_LOADER,
index 5053082..83a4574 100644 (file)
@@ -1121,6 +1121,7 @@ int irq_gc_set_wake(struct irq_data *d, unsigned int on);
 /* Setup functions for irq_chip_generic */
 int irq_map_generic_chip(struct irq_domain *d, unsigned int virq,
                         irq_hw_number_t hw_irq);
+void irq_unmap_generic_chip(struct irq_domain *d, unsigned int virq);
 struct irq_chip_generic *
 irq_alloc_generic_chip(const char *name, int nr_ct, unsigned int irq_base,
                       void __iomem *reg_base, irq_flow_handler_t handler);
diff --git a/include/linux/isa-dma.h b/include/linux/isa-dma.h
new file mode 100644 (file)
index 0000000..61504a8
--- /dev/null
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __LINUX_ISA_DMA_H
+#define __LINUX_ISA_DMA_H
+
+#include <asm/dma.h>
+
+#if defined(CONFIG_PCI) && defined(CONFIG_X86_32)
+extern int isa_dma_bridge_buggy;
+#else
+#define isa_dma_bridge_buggy   (0)
+#endif
+
+#endif /* __LINUX_ISA_DMA_H */
index adea5a4..6b1301e 100644 (file)
@@ -87,6 +87,7 @@ extern const struct pci_ecam_ops xgene_v1_pcie_ecam_ops; /* APM X-Gene PCIe v1 *
 extern const struct pci_ecam_ops xgene_v2_pcie_ecam_ops; /* APM X-Gene PCIe v2.x */
 extern const struct pci_ecam_ops al_pcie_ops;  /* Amazon Annapurna Labs PCIe */
 extern const struct pci_ecam_ops tegra194_pcie_ops; /* Tegra194 PCIe */
+extern const struct pci_ecam_ops loongson_pci_ecam_ops; /* Loongson PCIe */
 #endif
 
 #if IS_ENABLED(CONFIG_PCI_HOST_COMMON)
index f0862eb..c653cd3 100644 (file)
@@ -431,7 +431,7 @@ int irq_map_generic_chip(struct irq_domain *d, unsigned int virq,
        return 0;
 }
 
-static void irq_unmap_generic_chip(struct irq_domain *d, unsigned int virq)
+void irq_unmap_generic_chip(struct irq_domain *d, unsigned int virq)
 {
        struct irq_data *data = irq_domain_get_irq_data(d, virq);
        struct irq_domain_chip_generic *dgc = d->gc;
index 1f45ede..18a8621 100644 (file)
@@ -12,8 +12,8 @@
 #undef HAVE_REALLY_SLOW_DMA_CONTROLLER
 
 #include <linux/export.h>
+#include <linux/isa-dma.h>
 #include <sound/core.h>
-#include <asm/dma.h>
 
 /**
  * snd_dma_program - program an ISA DMA transfer