From d60fc3892c4de4a25658786f941690462c5a5bab Mon Sep 17 00:00:00 2001 From: Tomasz Nowicki Date: Tue, 24 Mar 2015 14:02:49 +0000 Subject: irqchip: Add GICv2 specific ACPI boot support ACPI kernel uses MADT table for proper GIC initialization. It needs to parse GIC related subtables, collect CPU interface and distributor addresses and call driver initialization function (which is hardware abstraction agnostic). In a similar way, FDT initialize GICv1/2. NOTE: This commit allow to initialize GICv1/2 basic functionality. While now simple GICv2 init call is used, any further GIC features require generic infrastructure for proper ACPI irqchip initialization. That mechanism and stacked irqdomains to support GICv2 MSI/virtualization extension, GICv3/4 and its ITS are considered as next steps. CC: Jason Cooper CC: Marc Zyngier CC: Thomas Gleixner Tested-by: Suravee Suthikulpanit Tested-by: Yijing Wang Tested-by: Mark Langsdorf Tested-by: Jon Masters Tested-by: Timur Tabi Tested-by: Robert Richter Acked-by: Robert Richter Acked-by: Marc Zyngier Acked-by: Jason Cooper Reviewed-by: Grant Likely Signed-off-by: Tomasz Nowicki Signed-off-by: Hanjun Guo Signed-off-by: Will Deacon --- drivers/irqchip/irq-gic.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) (limited to 'drivers/irqchip/irq-gic.c') diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 471e1cdc1933..d15a36a93c52 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -33,12 +33,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include @@ -1090,3 +1092,103 @@ IRQCHIP_DECLARE(msm_8660_qgic, "qcom,msm-8660-qgic", gic_of_init); IRQCHIP_DECLARE(msm_qgic2, "qcom,msm-qgic2", gic_of_init); #endif + +#ifdef CONFIG_ACPI +static phys_addr_t dist_phy_base, cpu_phy_base __initdata; + +static int __init +gic_acpi_parse_madt_cpu(struct acpi_subtable_header *header, + const unsigned long end) +{ + struct acpi_madt_generic_interrupt *processor; + phys_addr_t gic_cpu_base; + static int cpu_base_assigned; + + processor = (struct acpi_madt_generic_interrupt *)header; + + if (BAD_MADT_ENTRY(processor, end)) + return -EINVAL; + + /* + * There is no support for non-banked GICv1/2 register in ACPI spec. + * All CPU interface addresses have to be the same. + */ + gic_cpu_base = processor->base_address; + if (cpu_base_assigned && gic_cpu_base != cpu_phy_base) + return -EINVAL; + + cpu_phy_base = gic_cpu_base; + cpu_base_assigned = 1; + return 0; +} + +static int __init +gic_acpi_parse_madt_distributor(struct acpi_subtable_header *header, + const unsigned long end) +{ + struct acpi_madt_generic_distributor *dist; + + dist = (struct acpi_madt_generic_distributor *)header; + + if (BAD_MADT_ENTRY(dist, end)) + return -EINVAL; + + dist_phy_base = dist->base_address; + return 0; +} + +int __init +gic_v2_acpi_init(struct acpi_table_header *table) +{ + void __iomem *cpu_base, *dist_base; + int count; + + /* Collect CPU base addresses */ + count = acpi_parse_entries(ACPI_SIG_MADT, + sizeof(struct acpi_table_madt), + gic_acpi_parse_madt_cpu, table, + ACPI_MADT_TYPE_GENERIC_INTERRUPT, 0); + if (count <= 0) { + pr_err("No valid GICC entries exist\n"); + return -EINVAL; + } + + /* + * Find distributor base address. We expect one distributor entry since + * ACPI 5.1 spec neither support multi-GIC instances nor GIC cascade. + */ + count = acpi_parse_entries(ACPI_SIG_MADT, + sizeof(struct acpi_table_madt), + gic_acpi_parse_madt_distributor, table, + ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR, 0); + if (count <= 0) { + pr_err("No valid GICD entries exist\n"); + return -EINVAL; + } else if (count > 1) { + pr_err("More than one GICD entry detected\n"); + return -EINVAL; + } + + cpu_base = ioremap(cpu_phy_base, ACPI_GIC_CPU_IF_MEM_SIZE); + if (!cpu_base) { + pr_err("Unable to map GICC registers\n"); + return -ENOMEM; + } + + dist_base = ioremap(dist_phy_base, ACPI_GICV2_DIST_MEM_SIZE); + if (!dist_base) { + pr_err("Unable to map GICD registers\n"); + iounmap(cpu_base); + return -ENOMEM; + } + + /* + * Initialize zero GIC instance (no multi-GIC support). Also, set GIC + * as default IRQ domain to allow for GSI registration and GSI to IRQ + * number translation (see acpi_register_gsi() and acpi_gsi_to_irq()). + */ + gic_init_bases(0, -1, dist_base, cpu_base, 0, NULL); + irq_set_default_host(gic_data[0].domain); + return 0; +} +#endif -- cgit v1.2.3 From d8f4f161e31f3ee9768467344e6cc31a0b9d9249 Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Tue, 24 Mar 2015 17:58:51 +0000 Subject: ACPI: move arm64 GSI IRQ model to generic GSI IRQ layer The code deployed to implement GSI linux IRQ numbers mapping on arm64 turns out to be generic enough so that it can be moved to ACPI core code along with its respective config option ACPI_GENERIC_GSI selectable on architectures that can reuse the same code. Current ACPI IRQ mapping code is not integrated in the kernel IRQ domain infrastructure, in particular there is no way to look-up the IRQ domain associated with a particular interrupt controller, so this first version of GSI generic code carries out the GSI<->IRQ mapping relying on the IRQ default domain which is supposed to be always set on a specific architecture in case the domain structure passed to irq_create/find_mapping() functions is missing. This patch moves the arm64 acpi functions that implement the gsi mappings: acpi_gsi_to_irq() acpi_register_gsi() acpi_unregister_gsi() to ACPI core code. Since the generic GSI<->domain mapping is based on IRQ domains, it can be extended as soon as a way to map an interrupt controller to an IRQ domain is implemented for ACPI in the IRQ domain layer. x86 and ia64 code for GSI mappings cannot rely on the generic GSI layer at present for legacy reasons, so they do not select the ACPI_GENERIC_GSI config options and keep relying on their arch specific GSI mapping layer. Cc: Jiang Liu Cc: Catalin Marinas Cc: Rafael J. Wysocki Acked-by: Hanjun Guo Acked-by: Will Deacon Acked-by: Marc Zyngier Signed-off-by: Lorenzo Pieralisi Signed-off-by: Will Deacon --- arch/arm64/Kconfig | 1 + arch/arm64/kernel/acpi.c | 73 -------------------------------- drivers/acpi/Kconfig | 3 ++ drivers/acpi/Makefile | 1 + drivers/acpi/gsi.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/irqchip/irq-gic.c | 2 + 6 files changed, 112 insertions(+), 73 deletions(-) create mode 100644 drivers/acpi/gsi.c (limited to 'drivers/irqchip/irq-gic.c') diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index e5aa081b4845..0659db374731 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1,5 +1,6 @@ config ARM64 def_bool y + select ACPI_GENERIC_GSI if ACPI select ACPI_REDUCED_HARDWARE_ONLY if ACPI select ARCH_BINFMT_ELF_RANDOMIZE_PIE select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c index fe9d8f0df4a3..a70f7141c0f6 100644 --- a/arch/arm64/kernel/acpi.c +++ b/arch/arm64/kernel/acpi.c @@ -75,12 +75,6 @@ static int __init dt_scan_depth1_nodes(unsigned long node, return 0; } -/* - * Since we're on ARM, the default interrupt routing model - * clearly has to be GIC. - */ -enum acpi_irq_model_id acpi_irq_model = ACPI_IRQ_MODEL_GIC; - /* * __acpi_map_table() will be called before page_init(), so early_ioremap() * or early_memremap() should be called here to for ACPI table mapping. @@ -224,73 +218,6 @@ void __init acpi_init_cpus(void) pr_info("%d CPUs enabled, %d CPUs total\n", enabled_cpus, total_cpus); } -int acpi_gsi_to_irq(u32 gsi, unsigned int *irq) -{ - *irq = irq_find_mapping(NULL, gsi); - - return 0; -} -EXPORT_SYMBOL_GPL(acpi_gsi_to_irq); - -/* - * success: return IRQ number (>0) - * failure: return =< 0 - */ -int acpi_register_gsi(struct device *dev, u32 gsi, int trigger, int polarity) -{ - unsigned int irq; - unsigned int irq_type; - - /* - * ACPI have no bindings to indicate SPI or PPI, so we - * use different mappings from DT in ACPI. - * - * For FDT - * PPI interrupt: in the range [0, 15]; - * SPI interrupt: in the range [0, 987]; - * - * For ACPI, GSI should be unique so using - * the hwirq directly for the mapping: - * PPI interrupt: in the range [16, 31]; - * 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; - - /* - * Since only one GIC is supported in ACPI 5.0, we can - * create mapping refer to the default domain - */ - irq = irq_create_mapping(NULL, gsi); - if (!irq) - return irq; - - /* Set irq type if specified and different than the current one */ - if (irq_type != IRQ_TYPE_NONE && - irq_type != irq_get_trigger_type(irq)) - irq_set_irq_type(irq, irq_type); - return irq; -} -EXPORT_SYMBOL_GPL(acpi_register_gsi); - -void acpi_unregister_gsi(u32 gsi) -{ -} -EXPORT_SYMBOL_GPL(acpi_unregister_gsi); - static int __init acpi_parse_fadt(struct acpi_table_header *table) { struct acpi_table_fadt *fadt = (struct acpi_table_fadt *)table; diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index a8f531e2b97c..ab2cbb51c6aa 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -48,6 +48,9 @@ config ACPI_LEGACY_TABLES_LOOKUP config ARCH_MIGHT_HAVE_ACPI_PDC bool +config ACPI_GENERIC_GSI + bool + config ACPI_SYSTEM_POWER_STATES_SUPPORT bool diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index db153c6a75d7..8a063e276530 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -56,6 +56,7 @@ ifdef CONFIG_ACPI_VIDEO acpi-y += video_detect.o endif acpi-y += acpi_lpat.o +acpi-$(CONFIG_ACPI_GENERIC_GSI) += gsi.o # These are (potentially) separate modules diff --git a/drivers/acpi/gsi.c b/drivers/acpi/gsi.c new file mode 100644 index 000000000000..38208f2d0e69 --- /dev/null +++ b/drivers/acpi/gsi.c @@ -0,0 +1,105 @@ +/* + * ACPI GSI IRQ layer + * + * Copyright (C) 2015 ARM Ltd. + * Author: Lorenzo Pieralisi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include + +enum acpi_irq_model_id acpi_irq_model; + +static unsigned int acpi_gsi_get_irq_type(int trigger, int polarity) +{ + switch (polarity) { + case ACPI_ACTIVE_LOW: + return trigger == ACPI_EDGE_SENSITIVE ? + IRQ_TYPE_EDGE_FALLING : + IRQ_TYPE_LEVEL_LOW; + case ACPI_ACTIVE_HIGH: + return trigger == ACPI_EDGE_SENSITIVE ? + IRQ_TYPE_EDGE_RISING : + IRQ_TYPE_LEVEL_HIGH; + case ACPI_ACTIVE_BOTH: + if (trigger == ACPI_EDGE_SENSITIVE) + return IRQ_TYPE_EDGE_BOTH; + default: + return IRQ_TYPE_NONE; + } +} + +/** + * acpi_gsi_to_irq() - Retrieve the linux irq number for a given GSI + * @gsi: GSI IRQ number to map + * @irq: pointer where linux IRQ number is stored + * + * irq location updated with irq value [>0 on success, 0 on failure] + * + * Returns: linux IRQ number on success (>0) + * -EINVAL on failure + */ +int acpi_gsi_to_irq(u32 gsi, unsigned int *irq) +{ + /* + * Only default domain is supported at present, always find + * the mapping corresponding to default domain by passing NULL + * as irq_domain parameter + */ + *irq = irq_find_mapping(NULL, gsi); + /* + * *irq == 0 means no mapping, that should + * be reported as a failure + */ + return (*irq > 0) ? *irq : -EINVAL; +} +EXPORT_SYMBOL_GPL(acpi_gsi_to_irq); + +/** + * acpi_register_gsi() - Map a GSI to a linux IRQ number + * @dev: device for which IRQ has to be mapped + * @gsi: GSI IRQ number + * @trigger: trigger type of the GSI number to be mapped + * @polarity: polarity of the GSI to be mapped + * + * Returns: a valid linux IRQ number on success + * -EINVAL on failure + */ +int acpi_register_gsi(struct device *dev, u32 gsi, int trigger, + int polarity) +{ + unsigned int irq; + unsigned int irq_type = acpi_gsi_get_irq_type(trigger, polarity); + + /* + * There is no way at present to look-up the IRQ domain on ACPI, + * hence always create mapping referring to the default domain + * by passing NULL as irq_domain parameter + */ + irq = irq_create_mapping(NULL, gsi); + if (!irq) + return -EINVAL; + + /* Set irq type if specified and different than the current one */ + if (irq_type != IRQ_TYPE_NONE && + irq_type != irq_get_trigger_type(irq)) + irq_set_irq_type(irq, irq_type); + return irq; +} +EXPORT_SYMBOL_GPL(acpi_register_gsi); + +/** + * acpi_unregister_gsi() - Free a GSI<->linux IRQ number mapping + * @gsi: GSI IRQ number + */ +void acpi_unregister_gsi(u32 gsi) +{ + int irq = irq_find_mapping(NULL, gsi); + + irq_dispose_mapping(irq); +} +EXPORT_SYMBOL_GPL(acpi_unregister_gsi); diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index d15a36a93c52..f1efb53faebc 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -1189,6 +1189,8 @@ gic_v2_acpi_init(struct acpi_table_header *table) */ gic_init_bases(0, -1, dist_base, cpu_base, 0, NULL); irq_set_default_host(gic_data[0].domain); + + acpi_irq_model = ACPI_IRQ_MODEL_GIC; return 0; } #endif -- cgit v1.2.3 From 1dcc73d7bb0429994c54d33b40c5fb82b741a791 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 22 Apr 2015 18:20:04 +0100 Subject: irqchip: gic: Drop support for gic_arch_extn Now that the users of gic_arch_extn have been fixed, drop the "feature" for good. This leads to the removal of some now useless locking. Signed-off-by: Marc Zyngier Cc: linux-arm-kernel@lists.infradead.org Cc: Jason Cooper Signed-off-by: Thomas Gleixner --- drivers/irqchip/irq-gic.c | 71 +---------------------------------------- include/linux/irqchip/arm-gic.h | 2 -- 2 files changed, 1 insertion(+), 72 deletions(-) (limited to 'drivers/irqchip/irq-gic.c') diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index a6ce3476834e..dd989148e689 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -80,19 +80,6 @@ static DEFINE_RAW_SPINLOCK(irq_controller_lock); #define NR_GIC_CPU_IF 8 static u8 gic_cpu_map[NR_GIC_CPU_IF] __read_mostly; -/* - * Supported arch specific GIC irq extension. - * Default make them NULL. - */ -struct irq_chip gic_arch_extn = { - .irq_eoi = NULL, - .irq_mask = NULL, - .irq_unmask = NULL, - .irq_retrigger = NULL, - .irq_set_type = NULL, - .irq_set_wake = NULL, -}; - #ifndef MAX_GIC_NR #define MAX_GIC_NR 1 #endif @@ -165,34 +152,16 @@ static int gic_peek_irq(struct irq_data *d, u32 offset) static void gic_mask_irq(struct irq_data *d) { - unsigned long flags; - - raw_spin_lock_irqsave(&irq_controller_lock, flags); gic_poke_irq(d, GIC_DIST_ENABLE_CLEAR); - if (gic_arch_extn.irq_mask) - gic_arch_extn.irq_mask(d); - raw_spin_unlock_irqrestore(&irq_controller_lock, flags); } static void gic_unmask_irq(struct irq_data *d) { - unsigned long flags; - - raw_spin_lock_irqsave(&irq_controller_lock, flags); - if (gic_arch_extn.irq_unmask) - gic_arch_extn.irq_unmask(d); gic_poke_irq(d, GIC_DIST_ENABLE_SET); - raw_spin_unlock_irqrestore(&irq_controller_lock, flags); } static void gic_eoi_irq(struct irq_data *d) { - if (gic_arch_extn.irq_eoi) { - raw_spin_lock(&irq_controller_lock); - gic_arch_extn.irq_eoi(d); - raw_spin_unlock(&irq_controller_lock); - } - writel_relaxed(gic_irq(d), gic_cpu_base(d) + GIC_CPU_EOI); } @@ -249,8 +218,6 @@ static int gic_set_type(struct irq_data *d, unsigned int type) { void __iomem *base = gic_dist_base(d); unsigned int gicirq = gic_irq(d); - unsigned long flags; - int ret; /* Interrupt configuration for SGIs can't be changed */ if (gicirq < 16) @@ -261,25 +228,7 @@ static int gic_set_type(struct irq_data *d, unsigned int type) type != IRQ_TYPE_EDGE_RISING) return -EINVAL; - raw_spin_lock_irqsave(&irq_controller_lock, flags); - - if (gic_arch_extn.irq_set_type) - gic_arch_extn.irq_set_type(d, type); - - ret = gic_configure_irq(gicirq, type, base, NULL); - - raw_spin_unlock_irqrestore(&irq_controller_lock, flags); - - return ret; -} - -static int gic_retrigger(struct irq_data *d) -{ - if (gic_arch_extn.irq_retrigger) - return gic_arch_extn.irq_retrigger(d); - - /* the genirq layer expects 0 if we can't retrigger in hardware */ - return 0; + return gic_configure_irq(gicirq, type, base, NULL); } #ifdef CONFIG_SMP @@ -310,21 +259,6 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, } #endif -#ifdef CONFIG_PM -static int gic_set_wake(struct irq_data *d, unsigned int on) -{ - int ret = -ENXIO; - - if (gic_arch_extn.irq_set_wake) - ret = gic_arch_extn.irq_set_wake(d, on); - - return ret; -} - -#else -#define gic_set_wake NULL -#endif - static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs) { u32 irqstat, irqnr; @@ -383,11 +317,9 @@ static struct irq_chip gic_chip = { .irq_unmask = gic_unmask_irq, .irq_eoi = gic_eoi_irq, .irq_set_type = gic_set_type, - .irq_retrigger = gic_retrigger, #ifdef CONFIG_SMP .irq_set_affinity = gic_set_affinity, #endif - .irq_set_wake = gic_set_wake, .irq_get_irqchip_state = gic_irq_get_irqchip_state, .irq_set_irqchip_state = gic_irq_set_irqchip_state, }; @@ -1053,7 +985,6 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, set_handle_irq(gic_handle_irq); } - gic_chip.flags |= gic_arch_extn.flags; gic_dist_init(gic); gic_cpu_init(gic); gic_pm_init(gic); diff --git a/include/linux/irqchip/arm-gic.h b/include/linux/irqchip/arm-gic.h index 36ec4ae74634..9de976b4f9a7 100644 --- a/include/linux/irqchip/arm-gic.h +++ b/include/linux/irqchip/arm-gic.h @@ -95,8 +95,6 @@ struct device_node; -extern struct irq_chip gic_arch_extn; - void gic_set_irqchip_flags(unsigned long flags); void gic_init_bases(unsigned int, int, void __iomem *, void __iomem *, u32 offset, struct device_node *); -- cgit v1.2.3