This patch set trys to convert GIC to ACPI which based on patches from Amit, I rework them and try to get things simpler.
In MADT table, there are GIC cpu interface base address and GIC distributor base address, use them to convert GIC to ACPI.
GIC should be converted after all the devices refer to GIC are converted. But this patch set can be safely applied to linaro-acpi git tree if we don't remove the DT node of GIC, the node of GIC can be removed after all the devices converted to ACPI.
v2: a) fix missed iounmap() when return in error path; b) add stab for acpi_gic_init(void) when !CONFIG_ACPI C) modify the GSI mapping from DT to ACPI d) remove the hack code and modify the change log a bit
Amit Daniel Kachhap (2): irqdomain: Add a new API irq_create_acpi_mapping() ACPI: ARM: Update acpi_register_gsi to register with the core IRQ subsystem
Hanjun Guo (2): irqchip / GIC: do not set default host for GIC domain multi-times when ACPI enabled ARM / GIC: convert GIC to ACPI using the information in MADT
arch/arm64/kernel/irq.c | 4 ++ drivers/acpi/plat/arm/boot.c | 98 ++++++++++++++++++++++++++++++++++++------ drivers/irqchip/irq-gic.c | 5 ++- include/linux/acpi.h | 8 ++++ kernel/irq/irqdomain.c | 27 ++++++++++++ 5 files changed, 127 insertions(+), 15 deletions(-)
If there are multi-GICs in the system, default host for GIC domain will be set for several times, fix it.
Signed-off-by: Hanjun Guo hanjun.guo@linaro.org --- drivers/irqchip/irq-gic.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 039b4a0..b158c6f 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -958,7 +958,10 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, return;
#ifdef CONFIG_ACPI - irq_set_default_host(gic->domain); + /* do not set default host for GIC domain multi-times. + * TODO: we should consider more when multi GICs supported */ + if (!gic_nr) + irq_set_default_host(gic->domain); #endif
#ifdef CONFIG_SMP
From: Amit Daniel Kachhap amit.daniel@samsung.com
This patch introduces a new API for acpi based irq mapping.
[hanjun: which is based on the patch posted by Amit, but delete the reference to gic_irq_domain_xlate() which can simplify the code a lot.]
Signed-off-by: Amit Daniel Kachhap amit.daniel@samsung.com Signed-off-by: Hanjun Guo hanjun.guo@linaro.org --- include/linux/acpi.h | 2 ++ kernel/irq/irqdomain.c | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+)
diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 6324f8b..1dbfa2c 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -121,6 +121,8 @@ int acpi_map_lsapic(acpi_handle handle, int *pcpu); int acpi_unmap_lsapic(int cpu); #endif /* CONFIG_ACPI_HOTPLUG_CPU */
+unsigned int irq_create_acpi_mapping(irq_hw_number_t hwirq, + unsigned int type); int acpi_register_ioapic(acpi_handle handle, u64 phys_addr, u32 gsi_base); int acpi_unregister_ioapic(acpi_handle handle, u32 gsi_base); void acpi_irq_stats_init(void); diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 706724e..1d791ff 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -502,6 +502,33 @@ unsigned int irq_create_of_mapping(struct device_node *controller, } EXPORT_SYMBOL_GPL(irq_create_of_mapping);
+#ifdef CONFIG_ACPI +unsigned int irq_create_acpi_mapping(irq_hw_number_t hwirq, + unsigned int type) +{ + struct irq_domain *domain; + unsigned int virq; + + domain = irq_default_domain; + if (!domain) { + pr_warn("no irq domain found !\n"); + return 0; + } + + /* Create mapping */ + virq = irq_create_mapping(domain, hwirq); + if (!virq) + return virq; + + /* Set type if specified and different than the current one */ + if (type != IRQ_TYPE_NONE && + type != irq_get_trigger_type(virq)) + irq_set_irq_type(virq, type); + return virq; +} +EXPORT_SYMBOL_GPL(irq_create_acpi_mapping); +#endif + /** * irq_dispose_mapping() - Unmap an interrupt * @virq: linux irq number of the interrupt to unmap
From: Amit Daniel Kachhap amit.daniel@samsung.com
This API is similar to DT based irq_of_parse_and_map but does link parent/child IRQ controllers. This is tested for primary GIC PPI and GIC SPI interrupts and not for secondary child irq controllers.
[Hanjun: Add some comments for interrupt mapping from DT to ACPI, and rework it according to the API of irq_create_acpi_mapping()]
Signed-off-by: Amit Daniel Kachhap amit.daniel@samsung.com Signed-off-by: Hanjun Guo hanjun.guo@linaro.org --- drivers/acpi/plat/arm/boot.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-)
diff --git a/drivers/acpi/plat/arm/boot.c b/drivers/acpi/plat/arm/boot.c index 4fcdd9f..c42d493 100644 --- a/drivers/acpi/plat/arm/boot.c +++ b/drivers/acpi/plat/arm/boot.c @@ -254,12 +254,37 @@ int (*__acpi_register_gsi)(struct device *dev, u32 gsi, int acpi_register_gsi(struct device *dev, u32 gsi, int trigger, int polarity) { unsigned int irq; - unsigned int plat_gsi = gsi; + unsigned int irq_type;
- plat_gsi = (*__acpi_register_gsi)(dev, gsi, trigger, polarity); - - irq = gsi_to_irq(plat_gsi); + /* + * ACPI have no bindings to indicate SPI or PPI, so we + * use following mapping from DT to ACPI. + * + * For FDT + * PPI interrupt: in the range [0, 15]; + * SPI interrupt: in the range [0, 987]; + * + * For ACPI, using identity mapping for hwirq: + * PPI interrupt: in the range [16, 32]; + * SPI interrupt: in the range [32, 1019]; + */
+ if (trigger == ACPI_EDGE_SENSITIVE && + polarity == ACPI_ACTIVE_LOW) + irq_type = IRQ_TYPE_EDGE_FALLING; + else if (trigger == ACPI_EDGE_SENSITIVE && + polarity == ACPI_ACTIVE_HIGH) + irq_type = IRQ_TYPE_EDGE_RISING; + else if (trigger == ACPI_LEVEL_SENSITIVE && + polarity == ACPI_ACTIVE_LOW) + irq_type = IRQ_TYPE_LEVEL_LOW; + else if (trigger == ACPI_LEVEL_SENSITIVE && + polarity == ACPI_ACTIVE_HIGH) + irq_type = IRQ_TYPE_LEVEL_HIGH; + else + irq_type = IRQ_TYPE_NONE; + + irq = irq_create_acpi_mapping(gsi, irq_type); return irq; } EXPORT_SYMBOL_GPL(acpi_register_gsi);
In MADT table, there are GIC cpu interface base address and GIC distributor base address, use them to convert GIC to ACPI.
Signed-off-by: Hanjun Guo hanjun.guo@linaro.org --- arch/arm64/kernel/irq.c | 4 +++ drivers/acpi/plat/arm/boot.c | 65 +++++++++++++++++++++++++++++++++++------- include/linux/acpi.h | 6 ++++ 3 files changed, 65 insertions(+), 10 deletions(-)
diff --git a/arch/arm64/kernel/irq.c b/arch/arm64/kernel/irq.c index 473e5db..f27af5f 100644 --- a/arch/arm64/kernel/irq.c +++ b/arch/arm64/kernel/irq.c @@ -25,6 +25,7 @@ #include <linux/irq.h> #include <linux/smp.h> #include <linux/init.h> +#include <linux/acpi.h> #include <linux/irqchip.h> #include <linux/seq_file.h> #include <linux/ratelimit.h> @@ -79,6 +80,9 @@ void __init init_IRQ(void) { irqchip_init(); if (!handle_arch_irq) + acpi_gic_init(); + + if (!handle_arch_irq) panic("No interrupt controller found."); }
diff --git a/drivers/acpi/plat/arm/boot.c b/drivers/acpi/plat/arm/boot.c index c42d493..83e1af9 100644 --- a/drivers/acpi/plat/arm/boot.c +++ b/drivers/acpi/plat/arm/boot.c @@ -37,6 +37,7 @@ #include <linux/bootmem.h> #include <linux/ioport.h> #include <linux/pci.h> +#include <linux/irqchip/arm-gic.h>
#include <asm/pgtable.h> #include <asm/io.h> @@ -80,6 +81,8 @@ int acpi_sci_override_gsi __initdata; int acpi_skip_timer_override __initdata; int acpi_use_timer_override __initdata; int acpi_fix_pin2_polarity __initdata; + +/* GIC cpu interface base address on ARM */ static u64 acpi_lapic_addr __initdata;
struct acpi_arm_root acpi_arm_rsdp_info; /* info about RSDP from FDT */ @@ -198,11 +201,22 @@ acpi_parse_gic(struct acpi_subtable_header *header, const unsigned long end) return 0; }
+#ifdef CONFIG_ARM_GIC +/* + * Hard code here, we can not get memory size from MADT (but FDT does), + * this size is described in ARMv8 foudation model's User Guide + */ +#define GIC_DISTRIBUTOR_MEMORY_SIZE (SZ_8K) +#define GIC_CPU_INTERFACE_MEMORY_SIZE (SZ_4K) + +/* GIC distributor is something like IOAPIC on x86 */ static int __init acpi_parse_gic_distributor(struct acpi_subtable_header *header, const unsigned long end) { struct acpi_madt_generic_distributor *distributor = NULL; + void __iomem *dist_base = NULL; + void __iomem *cpu_base = NULL;
distributor = (struct acpi_madt_generic_distributor *)header;
@@ -211,10 +225,35 @@ acpi_parse_gic_distributor(struct acpi_subtable_header *header,
acpi_table_print_madt_entry(header);
- /* TODO: handle with the base_address and irq_base for irq system */ + dist_base = ioremap(distributor->base_address, + GIC_CPU_INTERFACE_MEMORY_SIZE); + if (!dist_base) { + pr_warn("unable to map gic dist registers\n"); + return -ENOMEM; + } + + /* acpi_lapic_addr is stored in early_acpi_boot_init(), + * we can use it here with no worries + */ + cpu_base = ioremap(acpi_lapic_addr, GIC_CPU_INTERFACE_MEMORY_SIZE); + if (!cpu_base) { + iounmap(dist_base); + pr_warn("unable to map gic cpu registers\n"); + return -ENOMEM; + } + + gic_init(distributor->gic_id, -1, dist_base, cpu_base);
return 0; } +#else +static int __init +acpi_parse_gic_distributor(struct acpi_subtable_header *header, + const unsigned long end) +{ + return -ENODEV; +} +#endif /* CONFIG_ARM_GIC */
int acpi_gsi_to_irq(u32 gsi, unsigned int *irq) { @@ -655,16 +694,8 @@ static void __init acpi_process_madt(void) * Parse MADT LAPIC entries */ error = acpi_parse_madt_lapic_entries(); - if (!error) { + if (!error) acpi_lapic = 1; - - /* - * Parse MADT IO-APIC entries - */ - error = acpi_parse_madt_ioapic_entries(); - if (!error) - acpi_set_irq_model_gic(); - } }
/* @@ -794,6 +825,20 @@ void __init arm_acpi_reserve_memory() memblock_remove(addr - section_offset, num_sections * SECTION_SIZE); }
+int __init acpi_gic_init(void) +{ + int error; + + /* + * Parse MADT GIC distributor entries + */ + error = acpi_parse_madt_ioapic_entries(); + if (!error) + acpi_set_irq_model_gic(); + + return error; +} + static int __init parse_acpi(char *arg) { if (!arg) diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 1dbfa2c..7545c44 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -92,6 +92,7 @@ char *__acpi_map_table(phys_addr_t phys_addr, unsigned long size); void __acpi_unmap_table(char *map, unsigned long size); int early_acpi_boot_init(void); int acpi_boot_init (void); +int acpi_gic_init(void); void acpi_boot_table_init (void); int acpi_mps_check (void); int acpi_numa_init (void); @@ -421,6 +422,11 @@ static inline int acpi_boot_init(void) return 0; }
+static inline int acpi_gic_init(void) +{ + return 0; +} + static inline void acpi_boot_table_init(void) { return;