From 9e2c986cb460bf97154f18e85aa833739a1e8dc7 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 11 Apr 2016 09:57:53 +0100 Subject: irqchip: Add per-cpu interrupt partitioning library We've unfortunately started seeing a situation where percpu interrupts are partitioned in the system: one arbitrary set of CPUs has an interrupt connected to a type of device, while another disjoint set of CPUs has the same interrupt connected to another type of device. This makes it impossible to have a device driver requesting this interrupt using the current percpu-interrupt abstraction, as the same interrupt number is now potentially claimed by at least two drivers, and we forbid interrupt sharing on per-cpu interrupt. A solution to this is to turn things upside down. Let's assume that our system describes all the possible partitions for a given interrupt, and give each of them a unique identifier. It is then possible to create a namespace where the affinity identifier itself is a form of interrupt number. At this point, it becomes easy to implement a set of partitions as a cascaded irqchip, each affinity identifier being the HW irq. This allows us to keep a number of nice properties: - Each partition results in a separate percpu-interrupt (with a restrictied affinity), which keeps drivers happy. - Because the underlying interrupt is still per-cpu, the overhead of the indirection can be kept pretty minimal. - The core code can ignore most of that crap. For that purpose, we implement a small library that deals with some of the boilerplate code, relying on platform-specific drivers to provide a description of the affinity sets and a set of callbacks. Signed-off-by: Marc Zyngier Cc: Mark Rutland Cc: devicetree@vger.kernel.org Cc: Jason Cooper Cc: Will Deacon Cc: Rob Herring Link: http://lkml.kernel.org/r/1460365075-7316-4-git-send-email-marc.zyngier@arm.com Signed-off-by: Thomas Gleixner --- drivers/irqchip/Kconfig | 3 + drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-partition-percpu.c | 256 +++++++++++++++++++++++++++++++++ 3 files changed, 260 insertions(+) create mode 100644 drivers/irqchip/irq-partition-percpu.c (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 3e124793e224..ea1836b25156 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -244,3 +244,6 @@ config IRQ_MXS config MVEBU_ODMI bool select GENERIC_MSI_IRQ_DOMAIN + +config PARTITION_PERCPU + bool diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index b03cfcbbac6b..e354b00c173d 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_REALVIEW_DT) += irq-gic-realview.o obj-$(CONFIG_ARM_GIC_V2M) += irq-gic-v2m.o obj-$(CONFIG_ARM_GIC_V3) += irq-gic-v3.o irq-gic-common.o obj-$(CONFIG_ARM_GIC_V3_ITS) += irq-gic-v3-its.o irq-gic-v3-its-pci-msi.o irq-gic-v3-its-platform-msi.o +obj-$(CONFIG_PARTITION_PERCPU) += irq-partition-percpu.o obj-$(CONFIG_HISILICON_IRQ_MBIGEN) += irq-mbigen.o obj-$(CONFIG_ARM_NVIC) += irq-nvic.o obj-$(CONFIG_ARM_VIC) += irq-vic.o diff --git a/drivers/irqchip/irq-partition-percpu.c b/drivers/irqchip/irq-partition-percpu.c new file mode 100644 index 000000000000..ccd72c2cbc23 --- /dev/null +++ b/drivers/irqchip/irq-partition-percpu.c @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2016 ARM Limited, All Rights Reserved. + * Author: Marc Zyngier + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct partition_desc { + int nr_parts; + struct partition_affinity *parts; + struct irq_domain *domain; + struct irq_desc *chained_desc; + unsigned long *bitmap; + struct irq_domain_ops ops; +}; + +static bool partition_check_cpu(struct partition_desc *part, + unsigned int cpu, unsigned int hwirq) +{ + return cpumask_test_cpu(cpu, &part->parts[hwirq].mask); +} + +static void partition_irq_mask(struct irq_data *d) +{ + struct partition_desc *part = irq_data_get_irq_chip_data(d); + struct irq_chip *chip = irq_desc_get_chip(part->chained_desc); + struct irq_data *data = irq_desc_get_irq_data(part->chained_desc); + + if (partition_check_cpu(part, smp_processor_id(), d->hwirq) && + chip->irq_mask) + chip->irq_mask(data); +} + +static void partition_irq_unmask(struct irq_data *d) +{ + struct partition_desc *part = irq_data_get_irq_chip_data(d); + struct irq_chip *chip = irq_desc_get_chip(part->chained_desc); + struct irq_data *data = irq_desc_get_irq_data(part->chained_desc); + + if (partition_check_cpu(part, smp_processor_id(), d->hwirq) && + chip->irq_unmask) + chip->irq_unmask(data); +} + +static int partition_irq_set_irqchip_state(struct irq_data *d, + enum irqchip_irq_state which, + bool val) +{ + struct partition_desc *part = irq_data_get_irq_chip_data(d); + struct irq_chip *chip = irq_desc_get_chip(part->chained_desc); + struct irq_data *data = irq_desc_get_irq_data(part->chained_desc); + + if (partition_check_cpu(part, smp_processor_id(), d->hwirq) && + chip->irq_set_irqchip_state) + return chip->irq_set_irqchip_state(data, which, val); + + return -EINVAL; +} + +static int partition_irq_get_irqchip_state(struct irq_data *d, + enum irqchip_irq_state which, + bool *val) +{ + struct partition_desc *part = irq_data_get_irq_chip_data(d); + struct irq_chip *chip = irq_desc_get_chip(part->chained_desc); + struct irq_data *data = irq_desc_get_irq_data(part->chained_desc); + + if (partition_check_cpu(part, smp_processor_id(), d->hwirq) && + chip->irq_get_irqchip_state) + return chip->irq_get_irqchip_state(data, which, val); + + return -EINVAL; +} + +static int partition_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct partition_desc *part = irq_data_get_irq_chip_data(d); + struct irq_chip *chip = irq_desc_get_chip(part->chained_desc); + struct irq_data *data = irq_desc_get_irq_data(part->chained_desc); + + if (chip->irq_set_type) + return chip->irq_set_type(data, type); + + return -EINVAL; +} + +static void partition_irq_print_chip(struct irq_data *d, struct seq_file *p) +{ + struct partition_desc *part = irq_data_get_irq_chip_data(d); + struct irq_chip *chip = irq_desc_get_chip(part->chained_desc); + struct irq_data *data = irq_desc_get_irq_data(part->chained_desc); + + seq_printf(p, " %5s-%lu", chip->name, data->hwirq); +} + +static struct irq_chip partition_irq_chip = { + .irq_mask = partition_irq_mask, + .irq_unmask = partition_irq_unmask, + .irq_set_type = partition_irq_set_type, + .irq_get_irqchip_state = partition_irq_get_irqchip_state, + .irq_set_irqchip_state = partition_irq_set_irqchip_state, + .irq_print_chip = partition_irq_print_chip, +}; + +static void partition_handle_irq(struct irq_desc *desc) +{ + struct partition_desc *part = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + int cpu = smp_processor_id(); + int hwirq; + + chained_irq_enter(chip, desc); + + for_each_set_bit(hwirq, part->bitmap, part->nr_parts) { + if (partition_check_cpu(part, cpu, hwirq)) + break; + } + + if (unlikely(hwirq == part->nr_parts)) { + handle_bad_irq(desc); + } else { + unsigned int irq; + irq = irq_find_mapping(part->domain, hwirq); + generic_handle_irq(irq); + } + + chained_irq_exit(chip, desc); +} + +static int partition_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *arg) +{ + int ret; + irq_hw_number_t hwirq; + unsigned int type; + struct irq_fwspec *fwspec = arg; + struct partition_desc *part; + + BUG_ON(nr_irqs != 1); + ret = domain->ops->translate(domain, fwspec, &hwirq, &type); + if (ret) + return ret; + + part = domain->host_data; + + set_bit(hwirq, part->bitmap); + irq_set_chained_handler_and_data(irq_desc_get_irq(part->chained_desc), + partition_handle_irq, part); + irq_set_percpu_devid_partition(virq, &part->parts[hwirq].mask); + irq_domain_set_info(domain, virq, hwirq, &partition_irq_chip, part, + handle_percpu_devid_irq, NULL, NULL); + irq_set_status_flags(virq, IRQ_NOAUTOEN); + + return 0; +} + +static void partition_domain_free(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs) +{ + struct irq_data *d; + + BUG_ON(nr_irqs != 1); + + d = irq_domain_get_irq_data(domain, virq); + irq_set_handler(virq, NULL); + irq_domain_reset_irq_data(d); +} + +int partition_translate_id(struct partition_desc *desc, void *partition_id) +{ + struct partition_affinity *part = NULL; + int i; + + for (i = 0; i < desc->nr_parts; i++) { + if (desc->parts[i].partition_id == partition_id) { + part = &desc->parts[i]; + break; + } + } + + if (WARN_ON(!part)) { + pr_err("Failed to find partition\n"); + return -EINVAL; + } + + return i; +} + +struct partition_desc *partition_create_desc(struct fwnode_handle *fwnode, + struct partition_affinity *parts, + int nr_parts, + int chained_irq, + const struct irq_domain_ops *ops) +{ + struct partition_desc *desc; + struct irq_domain *d; + + BUG_ON(!ops->select || !ops->translate); + + desc = kzalloc(sizeof(*desc), GFP_KERNEL); + if (!desc) + return NULL; + + desc->ops = *ops; + desc->ops.free = partition_domain_free; + desc->ops.alloc = partition_domain_alloc; + + d = irq_domain_create_linear(fwnode, nr_parts, &desc->ops, desc); + if (!d) + goto out; + desc->domain = d; + + desc->bitmap = kzalloc(sizeof(long) * BITS_TO_LONGS(nr_parts), + GFP_KERNEL); + if (WARN_ON(!desc->bitmap)) + goto out; + + desc->chained_desc = irq_to_desc(chained_irq); + desc->nr_parts = nr_parts; + desc->parts = parts; + + return desc; +out: + if (d) + irq_domain_remove(d); + kfree(desc); + + return NULL; +} + +struct irq_domain *partition_get_domain(struct partition_desc *dsc) +{ + if (dsc) + return dsc->domain; + + return NULL; +} -- cgit v1.2.3 From e3825ba1af3a27d7522c9f5f929f5a13b8b138ae Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 11 Apr 2016 09:57:54 +0100 Subject: irqchip/gic-v3: Add support for partitioned PPIs Plug the partitioning layer into the GICv3 PPI code, parsing the DT and building the partition affinities and providing the generic code with partition data and callbacks. Signed-off-by: Marc Zyngier Cc: Mark Rutland Cc: devicetree@vger.kernel.org Cc: Jason Cooper Cc: Will Deacon Cc: Rob Herring Link: http://lkml.kernel.org/r/1460365075-7316-5-git-send-email-marc.zyngier@arm.com Signed-off-by: Thomas Gleixner --- drivers/irqchip/Kconfig | 1 + drivers/irqchip/irq-gic-v3.c | 176 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 175 insertions(+), 2 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index ea1836b25156..6c17de7997b9 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -27,6 +27,7 @@ config ARM_GIC_V3 select IRQ_DOMAIN select MULTI_IRQ_HANDLER select IRQ_DOMAIN_HIERARCHY + select PARTITION_PERCPU config ARM_GIC_V3_ITS bool diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 5b7d3c2129d8..f83e5f4ec701 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -29,6 +29,7 @@ #include #include +#include #include #include @@ -44,6 +45,7 @@ struct redist_region { }; struct gic_chip_data { + struct fwnode_handle *fwnode; void __iomem *dist_base; struct redist_region *redist_regions; struct rdists rdists; @@ -51,6 +53,7 @@ struct gic_chip_data { u64 redist_stride; u32 nr_redist_regions; unsigned int irq_nr; + struct partition_desc *ppi_descs[16]; }; static struct gic_chip_data gic_data __read_mostly; @@ -812,10 +815,62 @@ static void gic_irq_domain_free(struct irq_domain *domain, unsigned int virq, } } +static int gic_irq_domain_select(struct irq_domain *d, + struct irq_fwspec *fwspec, + enum irq_domain_bus_token bus_token) +{ + /* Not for us */ + if (fwspec->fwnode != d->fwnode) + return 0; + + /* If this is not DT, then we have a single domain */ + if (!is_of_node(fwspec->fwnode)) + return 1; + + /* + * If this is a PPI and we have a 4th (non-null) parameter, + * then we need to match the partition domain. + */ + if (fwspec->param_count >= 4 && + fwspec->param[0] == 1 && fwspec->param[3] != 0) + return d == partition_get_domain(gic_data.ppi_descs[fwspec->param[1]]); + + return d == gic_data.domain; +} + static const struct irq_domain_ops gic_irq_domain_ops = { .translate = gic_irq_domain_translate, .alloc = gic_irq_domain_alloc, .free = gic_irq_domain_free, + .select = gic_irq_domain_select, +}; + +static int partition_domain_translate(struct irq_domain *d, + struct irq_fwspec *fwspec, + unsigned long *hwirq, + unsigned int *type) +{ + struct device_node *np; + int ret; + + np = of_find_node_by_phandle(fwspec->param[3]); + if (WARN_ON(!np)) + return -EINVAL; + + ret = partition_translate_id(gic_data.ppi_descs[fwspec->param[1]], + of_node_to_fwnode(np)); + if (ret < 0) + return ret; + + *hwirq = ret; + *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK; + + return 0; +} + +static const struct irq_domain_ops partition_domain_ops = { + .translate = partition_domain_translate, + .select = gic_irq_domain_select, }; static void gicv3_enable_quirks(void) @@ -843,6 +898,7 @@ static int __init gic_init_bases(void __iomem *dist_base, if (static_key_true(&supports_deactivate)) pr_info("GIC: Using split EOI/Deactivate mode\n"); + gic_data.fwnode = handle; gic_data.dist_base = dist_base; gic_data.redist_regions = rdist_regs; gic_data.nr_redist_regions = nr_redist_regions; @@ -901,6 +957,119 @@ static int __init gic_validate_dist_version(void __iomem *dist_base) return 0; } +static int get_cpu_number(struct device_node *dn) +{ + const __be32 *cell; + u64 hwid; + int i; + + cell = of_get_property(dn, "reg", NULL); + if (!cell) + return -1; + + hwid = of_read_number(cell, of_n_addr_cells(dn)); + + /* + * Non affinity bits must be set to 0 in the DT + */ + if (hwid & ~MPIDR_HWID_BITMASK) + return -1; + + for (i = 0; i < num_possible_cpus(); i++) + if (cpu_logical_map(i) == hwid) + return i; + + return -1; +} + +/* Create all possible partitions at boot time */ +static void gic_populate_ppi_partitions(struct device_node *gic_node) +{ + struct device_node *parts_node, *child_part; + int part_idx = 0, i; + int nr_parts; + struct partition_affinity *parts; + + parts_node = of_find_node_by_name(gic_node, "ppi-partitions"); + if (!parts_node) + return; + + nr_parts = of_get_child_count(parts_node); + + if (!nr_parts) + return; + + parts = kzalloc(sizeof(*parts) * nr_parts, GFP_KERNEL); + if (WARN_ON(!parts)) + return; + + for_each_child_of_node(parts_node, child_part) { + struct partition_affinity *part; + int n; + + part = &parts[part_idx]; + + part->partition_id = of_node_to_fwnode(child_part); + + pr_info("GIC: PPI partition %s[%d] { ", + child_part->name, part_idx); + + n = of_property_count_elems_of_size(child_part, "affinity", + sizeof(u32)); + WARN_ON(n <= 0); + + for (i = 0; i < n; i++) { + int err, cpu; + u32 cpu_phandle; + struct device_node *cpu_node; + + err = of_property_read_u32_index(child_part, "affinity", + i, &cpu_phandle); + if (WARN_ON(err)) + continue; + + cpu_node = of_find_node_by_phandle(cpu_phandle); + if (WARN_ON(!cpu_node)) + continue; + + cpu = get_cpu_number(cpu_node); + if (WARN_ON(cpu == -1)) + continue; + + pr_cont("%s[%d] ", cpu_node->full_name, cpu); + + cpumask_set_cpu(cpu, &part->mask); + } + + pr_cont("}\n"); + part_idx++; + } + + for (i = 0; i < 16; i++) { + unsigned int irq; + struct partition_desc *desc; + struct irq_fwspec ppi_fwspec = { + .fwnode = gic_data.fwnode, + .param_count = 3, + .param = { + [0] = 1, + [1] = i, + [2] = IRQ_TYPE_NONE, + }, + }; + + irq = irq_create_fwspec_mapping(&ppi_fwspec); + if (WARN_ON(!irq)) + continue; + desc = partition_create_desc(gic_data.fwnode, parts, nr_parts, + irq, &partition_domain_ops); + if (WARN_ON(!desc)) + continue; + + gic_data.ppi_descs[i] = desc; + } +} + static int __init gic_of_init(struct device_node *node, struct device_node *parent) { void __iomem *dist_base; @@ -952,8 +1121,11 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare err = gic_init_bases(dist_base, rdist_regs, nr_redist_regions, redist_stride, &node->fwnode); - if (!err) - return 0; + if (err) + goto out_unmap_rdist; + + gic_populate_ppi_partitions(node); + return 0; out_unmap_rdist: for (i = 0; i < nr_redist_regions; i++) -- cgit v1.2.3 From b8f3ebe630a4f1b4ff9340103d3b565ad5d78d43 Mon Sep 17 00:00:00 2001 From: Minghuan Lian Date: Wed, 23 Mar 2016 19:08:20 +0800 Subject: irqchip: Add Layerscape SCFG MSI controller support Some kind of Freescale Layerscape SoC provides a MSI implementation which uses two SCFG registers MSIIR and MSIR to support 32 MSI interrupts for each PCIe controller. The patch is to support it. Signed-off-by: Minghuan Lian Tested-by: Alexander Stein Acked-by: Marc Zyngier Signed-off-by: Marc Zyngier --- drivers/irqchip/Kconfig | 5 + drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-ls-scfg-msi.c | 240 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 246 insertions(+) create mode 100644 drivers/irqchip/irq-ls-scfg-msi.c (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 6c17de7997b9..81f88ada3a61 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -246,5 +246,10 @@ config MVEBU_ODMI bool select GENERIC_MSI_IRQ_DOMAIN +config LS_SCFG_MSI + def_bool y if SOC_LS1021A || ARCH_LAYERSCAPE + depends on PCI && PCI_MSI + select PCI_MSI_IRQ_DOMAIN + config PARTITION_PERCPU bool diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index e354b00c173d..8cedf9060565 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -66,3 +66,4 @@ obj-$(CONFIG_INGENIC_IRQ) += irq-ingenic.o obj-$(CONFIG_IMX_GPCV2) += irq-imx-gpcv2.o obj-$(CONFIG_PIC32_EVIC) += irq-pic32-evic.o obj-$(CONFIG_MVEBU_ODMI) += irq-mvebu-odmi.o +obj-$(CONFIG_LS_SCFG_MSI) += irq-ls-scfg-msi.o diff --git a/drivers/irqchip/irq-ls-scfg-msi.c b/drivers/irqchip/irq-ls-scfg-msi.c new file mode 100644 index 000000000000..02cca74cab94 --- /dev/null +++ b/drivers/irqchip/irq-ls-scfg-msi.c @@ -0,0 +1,240 @@ +/* + * Freescale SCFG MSI(-X) support + * + * Copyright (C) 2016 Freescale Semiconductor. + * + * Author: Minghuan Lian + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#define MSI_MAX_IRQS 32 +#define MSI_IBS_SHIFT 3 +#define MSIR 4 + +struct ls_scfg_msi { + spinlock_t lock; + struct platform_device *pdev; + struct irq_domain *parent; + struct irq_domain *msi_domain; + void __iomem *regs; + phys_addr_t msiir_addr; + int irq; + DECLARE_BITMAP(used, MSI_MAX_IRQS); +}; + +static struct irq_chip ls_scfg_msi_irq_chip = { + .name = "MSI", + .irq_mask = pci_msi_mask_irq, + .irq_unmask = pci_msi_unmask_irq, +}; + +static struct msi_domain_info ls_scfg_msi_domain_info = { + .flags = (MSI_FLAG_USE_DEF_DOM_OPS | + MSI_FLAG_USE_DEF_CHIP_OPS | + MSI_FLAG_PCI_MSIX), + .chip = &ls_scfg_msi_irq_chip, +}; + +static void ls_scfg_msi_compose_msg(struct irq_data *data, struct msi_msg *msg) +{ + struct ls_scfg_msi *msi_data = irq_data_get_irq_chip_data(data); + + msg->address_hi = upper_32_bits(msi_data->msiir_addr); + msg->address_lo = lower_32_bits(msi_data->msiir_addr); + msg->data = data->hwirq << MSI_IBS_SHIFT; +} + +static int ls_scfg_msi_set_affinity(struct irq_data *irq_data, + const struct cpumask *mask, bool force) +{ + return -EINVAL; +} + +static struct irq_chip ls_scfg_msi_parent_chip = { + .name = "SCFG", + .irq_compose_msi_msg = ls_scfg_msi_compose_msg, + .irq_set_affinity = ls_scfg_msi_set_affinity, +}; + +static int ls_scfg_msi_domain_irq_alloc(struct irq_domain *domain, + unsigned int virq, + unsigned int nr_irqs, + void *args) +{ + struct ls_scfg_msi *msi_data = domain->host_data; + int pos, err = 0; + + WARN_ON(nr_irqs != 1); + + spin_lock(&msi_data->lock); + pos = find_first_zero_bit(msi_data->used, MSI_MAX_IRQS); + if (pos < MSI_MAX_IRQS) + __set_bit(pos, msi_data->used); + else + err = -ENOSPC; + spin_unlock(&msi_data->lock); + + if (err) + return err; + + irq_domain_set_info(domain, virq, pos, + &ls_scfg_msi_parent_chip, msi_data, + handle_simple_irq, NULL, NULL); + + return 0; +} + +static void ls_scfg_msi_domain_irq_free(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs) +{ + struct irq_data *d = irq_domain_get_irq_data(domain, virq); + struct ls_scfg_msi *msi_data = irq_data_get_irq_chip_data(d); + int pos; + + pos = d->hwirq; + if (pos < 0 || pos >= MSI_MAX_IRQS) { + pr_err("failed to teardown msi. Invalid hwirq %d\n", pos); + return; + } + + spin_lock(&msi_data->lock); + __clear_bit(pos, msi_data->used); + spin_unlock(&msi_data->lock); +} + +static const struct irq_domain_ops ls_scfg_msi_domain_ops = { + .alloc = ls_scfg_msi_domain_irq_alloc, + .free = ls_scfg_msi_domain_irq_free, +}; + +static void ls_scfg_msi_irq_handler(struct irq_desc *desc) +{ + struct ls_scfg_msi *msi_data = irq_desc_get_handler_data(desc); + unsigned long val; + int pos, virq; + + chained_irq_enter(irq_desc_get_chip(desc), desc); + + val = ioread32be(msi_data->regs + MSIR); + for_each_set_bit(pos, &val, MSI_MAX_IRQS) { + virq = irq_find_mapping(msi_data->parent, (31 - pos)); + if (virq) + generic_handle_irq(virq); + } + + chained_irq_exit(irq_desc_get_chip(desc), desc); +} + +static int ls_scfg_msi_domains_init(struct ls_scfg_msi *msi_data) +{ + /* Initialize MSI domain parent */ + msi_data->parent = irq_domain_add_linear(NULL, + MSI_MAX_IRQS, + &ls_scfg_msi_domain_ops, + msi_data); + if (!msi_data->parent) { + dev_err(&msi_data->pdev->dev, "failed to create IRQ domain\n"); + return -ENOMEM; + } + + msi_data->msi_domain = pci_msi_create_irq_domain( + of_node_to_fwnode(msi_data->pdev->dev.of_node), + &ls_scfg_msi_domain_info, + msi_data->parent); + if (!msi_data->msi_domain) { + dev_err(&msi_data->pdev->dev, "failed to create MSI domain\n"); + irq_domain_remove(msi_data->parent); + return -ENOMEM; + } + + return 0; +} + +static int ls_scfg_msi_probe(struct platform_device *pdev) +{ + struct ls_scfg_msi *msi_data; + struct resource *res; + int ret; + + msi_data = devm_kzalloc(&pdev->dev, sizeof(*msi_data), GFP_KERNEL); + if (!msi_data) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + msi_data->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(msi_data->regs)) { + dev_err(&pdev->dev, "failed to initialize 'regs'\n"); + return PTR_ERR(msi_data->regs); + } + msi_data->msiir_addr = res->start; + + msi_data->irq = platform_get_irq(pdev, 0); + if (msi_data->irq <= 0) { + dev_err(&pdev->dev, "failed to get MSI irq\n"); + return -ENODEV; + } + + msi_data->pdev = pdev; + spin_lock_init(&msi_data->lock); + + ret = ls_scfg_msi_domains_init(msi_data); + if (ret) + return ret; + + irq_set_chained_handler_and_data(msi_data->irq, + ls_scfg_msi_irq_handler, + msi_data); + + platform_set_drvdata(pdev, msi_data); + + return 0; +} + +static int ls_scfg_msi_remove(struct platform_device *pdev) +{ + struct ls_scfg_msi *msi_data = platform_get_drvdata(pdev); + + irq_set_chained_handler_and_data(msi_data->irq, NULL, NULL); + + irq_domain_remove(msi_data->msi_domain); + irq_domain_remove(msi_data->parent); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static const struct of_device_id ls_scfg_msi_id[] = { + { .compatible = "fsl,1s1021a-msi", }, + { .compatible = "fsl,1s1043a-msi", }, + {}, +}; + +static struct platform_driver ls_scfg_msi_driver = { + .driver = { + .name = "ls-scfg-msi", + .of_match_table = ls_scfg_msi_id, + }, + .probe = ls_scfg_msi_probe, + .remove = ls_scfg_msi_remove, +}; + +module_platform_driver(ls_scfg_msi_driver); + +MODULE_AUTHOR("Minghuan Lian "); +MODULE_DESCRIPTION("Freescale Layerscape SCFG MSI controller driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From f86c4fbd930ff6fecf3d8a1c313182bd0f49f496 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 26 Apr 2016 12:00:00 +0100 Subject: irqchip/gic: Ensure ordering between read of INTACK and shared data When an IPI is generated by a CPU, the pattern looks roughly like: smp_wmb(); On the receiving CPU we rely on the fact that, once we've taken the interrupt, then the freshly written shared data must be visible to us. Put another way, the CPU isn't going to speculate taking an interrupt. Unfortunately, this assumption turns out to be broken. Consider that CPUx wants to send an IPI to CPUy, which will cause CPUy to read some shared_data. Before CPUx has done anything, a random peripheral raises an IRQ to the GIC and the IRQ line on CPUy is raised. CPUy then takes the IRQ and starts executing the entry code, heading towards gic_handle_irq. Furthermore, let's assume that a bunch of the previous interrupts handled by CPUy were SGIs, so the branch predictor kicks in and speculates that irqnr will be <16 and we're likely to head into handle_IPI. The prefetcher then grabs a speculative copy of shared_data which contains a stale value. Meanwhile, CPUx gets round to updating shared_data and asking the GIC to send an SGI to CPUy. Internally, the GIC decides that the SGI is more important than the peripheral interrupt (which hasn't yet been ACKed) but doesn't need to do anything to CPUy, because the IRQ line is already raised. CPUy then reads the ACK register on the GIC, sees the SGI value which confirms the branch prediction and we end up with a stale shared_data value. This patch fixes the problem by adding an smp_rmb() to the IPI entry code in gic_handle_irq. As it turns out, the combination of a control dependency and an ISB instruction from the EOI in the GICv3 driver is enough to provide the ordering we need, so we add a comment there justifying the absence of an explicit smp_rmb(). Cc: stable@vger.kernel.org Signed-off-by: Will Deacon Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v3.c | 7 +++++++ drivers/irqchip/irq-gic.c | 8 ++++++++ 2 files changed, 15 insertions(+) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index f83e5f4ec701..c3870a8f1be0 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -367,6 +367,13 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs if (static_key_true(&supports_deactivate)) gic_write_dir(irqnr); #ifdef CONFIG_SMP + /* + * Unlike GICv2, we don't need an smp_rmb() here. + * The control dependency from gic_read_iar to + * the ISB in gic_write_eoir is enough to ensure + * that any shared data read by handle_IPI will + * be read after the ACK. + */ handle_IPI(irqnr, regs); #else WARN_ONCE(true, "Unexpected SGI received!\n"); diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 282344b95ec2..5c4da5808b15 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -344,6 +344,14 @@ static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs) if (static_key_true(&supports_deactivate)) writel_relaxed(irqstat, cpu_base + GIC_CPU_DEACTIVATE); #ifdef CONFIG_SMP + /* + * Ensure any shared data written by the CPU sending + * the IPI is read after we've read the ACK register + * on the GIC. + * + * Pairs with the write barrier in gic_raise_softirq + */ + smp_rmb(); handle_IPI(irqnr, regs); #endif continue; -- cgit v1.2.3 From 8cb17b5ed0177d1b613aadd53c7e570b81625728 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Mon, 25 Apr 2016 04:00:38 +0300 Subject: irqchip: Add LPC32xx interrupt controller driver The change adds improved support of NXP LPC32xx MIC, SIC1 and SIC2 interrupt controllers. This is a list of new features in comparison to the legacy driver: * irq types are taken from device tree settings, no more need to hardcode them, * old driver is based on irq_domain_add_legacy, which causes problems with handling MIC hardware interrupt 0 produced by SIC1, * there is one driver for MIC, SIC1 and SIC2, no more need to handle them separately, e.g. have two separate handlers for SIC1 and SIC2, * the driver does not have any dependencies on hardcoded register offsets, * the driver is much simpler for maintenance, * SPARSE_IRQS option is supported. Legacy LPC32xx interrupt controller driver was broken since commit 76ba59f8366f ("genirq: Add irq_domain-aware core IRQ handler"), which requires a private interrupt handler, otherwise any SIC1 generated interrupt (mapped to MIC hwirq 0) breaks the kernel with the message "unexpected IRQ trap at vector 00". The change disables compilation of a legacy driver found at arch/arm/mach-lpc32xx/irq.c, the file will be removed in a separate commit. Fixes: 76ba59f8366f ("genirq: Add irq_domain-aware core IRQ handler") Tested-by: Sylvain Lemieux Signed-off-by: Vladimir Zapolskiy Signed-off-by: Marc Zyngier --- drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-lpc32xx.c | 238 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 239 insertions(+) create mode 100644 drivers/irqchip/irq-lpc32xx.c (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 8cedf9060565..f828244b44c2 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2836.o obj-$(CONFIG_ARCH_EXYNOS) += exynos-combiner.o obj-$(CONFIG_ARCH_HIP04) += irq-hip04.o +obj-$(CONFIG_ARCH_LPC32XX) += irq-lpc32xx.o obj-$(CONFIG_ARCH_MMP) += irq-mmp.o obj-$(CONFIG_IRQ_MXS) += irq-mxs.o obj-$(CONFIG_ARCH_TEGRA) += irq-tegra.o diff --git a/drivers/irqchip/irq-lpc32xx.c b/drivers/irqchip/irq-lpc32xx.c new file mode 100644 index 000000000000..1034aeb2e98a --- /dev/null +++ b/drivers/irqchip/irq-lpc32xx.c @@ -0,0 +1,238 @@ +/* + * Copyright 2015-2016 Vladimir Zapolskiy + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define LPC32XX_INTC_MASK 0x00 +#define LPC32XX_INTC_RAW 0x04 +#define LPC32XX_INTC_STAT 0x08 +#define LPC32XX_INTC_POL 0x0C +#define LPC32XX_INTC_TYPE 0x10 +#define LPC32XX_INTC_FIQ 0x14 + +#define NR_LPC32XX_IC_IRQS 32 + +struct lpc32xx_irq_chip { + void __iomem *base; + struct irq_domain *domain; + struct irq_chip chip; +}; + +static struct lpc32xx_irq_chip *lpc32xx_mic_irqc; + +static inline u32 lpc32xx_ic_read(struct lpc32xx_irq_chip *ic, u32 reg) +{ + return readl_relaxed(ic->base + reg); +} + +static inline void lpc32xx_ic_write(struct lpc32xx_irq_chip *ic, + u32 reg, u32 val) +{ + writel_relaxed(val, ic->base + reg); +} + +static void lpc32xx_irq_mask(struct irq_data *d) +{ + struct lpc32xx_irq_chip *ic = irq_data_get_irq_chip_data(d); + u32 val, mask = BIT(d->hwirq); + + val = lpc32xx_ic_read(ic, LPC32XX_INTC_MASK) & ~mask; + lpc32xx_ic_write(ic, LPC32XX_INTC_MASK, val); +} + +static void lpc32xx_irq_unmask(struct irq_data *d) +{ + struct lpc32xx_irq_chip *ic = irq_data_get_irq_chip_data(d); + u32 val, mask = BIT(d->hwirq); + + val = lpc32xx_ic_read(ic, LPC32XX_INTC_MASK) | mask; + lpc32xx_ic_write(ic, LPC32XX_INTC_MASK, val); +} + +static void lpc32xx_irq_ack(struct irq_data *d) +{ + struct lpc32xx_irq_chip *ic = irq_data_get_irq_chip_data(d); + u32 mask = BIT(d->hwirq); + + lpc32xx_ic_write(ic, LPC32XX_INTC_RAW, mask); +} + +static int lpc32xx_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct lpc32xx_irq_chip *ic = irq_data_get_irq_chip_data(d); + u32 val, mask = BIT(d->hwirq); + bool high, edge; + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + edge = true; + high = true; + break; + case IRQ_TYPE_EDGE_FALLING: + edge = true; + high = false; + break; + case IRQ_TYPE_LEVEL_HIGH: + edge = false; + high = true; + break; + case IRQ_TYPE_LEVEL_LOW: + edge = false; + high = false; + break; + default: + pr_info("unsupported irq type %d\n", type); + return -EINVAL; + } + + irqd_set_trigger_type(d, type); + + val = lpc32xx_ic_read(ic, LPC32XX_INTC_POL); + if (high) + val |= mask; + else + val &= ~mask; + lpc32xx_ic_write(ic, LPC32XX_INTC_POL, val); + + val = lpc32xx_ic_read(ic, LPC32XX_INTC_TYPE); + if (edge) { + val |= mask; + irq_set_handler_locked(d, handle_edge_irq); + } else { + val &= ~mask; + irq_set_handler_locked(d, handle_level_irq); + } + lpc32xx_ic_write(ic, LPC32XX_INTC_TYPE, val); + + return 0; +} + +static void __exception_irq_entry lpc32xx_handle_irq(struct pt_regs *regs) +{ + struct lpc32xx_irq_chip *ic = lpc32xx_mic_irqc; + u32 hwirq = lpc32xx_ic_read(ic, LPC32XX_INTC_STAT), irq; + + while (hwirq) { + irq = __ffs(hwirq); + hwirq &= ~BIT(irq); + handle_domain_irq(lpc32xx_mic_irqc->domain, irq, regs); + } +} + +static void lpc32xx_sic_handler(struct irq_desc *desc) +{ + struct lpc32xx_irq_chip *ic = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + u32 hwirq = lpc32xx_ic_read(ic, LPC32XX_INTC_STAT), irq; + + chained_irq_enter(chip, desc); + + while (hwirq) { + irq = __ffs(hwirq); + hwirq &= ~BIT(irq); + generic_handle_irq(irq_find_mapping(ic->domain, irq)); + } + + chained_irq_exit(chip, desc); +} + +static int lpc32xx_irq_domain_map(struct irq_domain *id, unsigned int virq, + irq_hw_number_t hw) +{ + struct lpc32xx_irq_chip *ic = id->host_data; + + irq_set_chip_data(virq, ic); + irq_set_chip_and_handler(virq, &ic->chip, handle_level_irq); + irq_set_status_flags(virq, IRQ_LEVEL); + irq_set_noprobe(virq); + + return 0; +} + +static void lpc32xx_irq_domain_unmap(struct irq_domain *id, unsigned int virq) +{ + irq_set_chip_and_handler(virq, NULL, NULL); +} + +static const struct irq_domain_ops lpc32xx_irq_domain_ops = { + .map = lpc32xx_irq_domain_map, + .unmap = lpc32xx_irq_domain_unmap, + .xlate = irq_domain_xlate_twocell, +}; + +static int __init lpc32xx_of_ic_init(struct device_node *node, + struct device_node *parent) +{ + struct lpc32xx_irq_chip *irqc; + bool is_mic = of_device_is_compatible(node, "nxp,lpc3220-mic"); + const __be32 *reg = of_get_property(node, "reg", NULL); + u32 parent_irq, i, addr = reg ? be32_to_cpu(*reg) : 0; + + irqc = kzalloc(sizeof(*irqc), GFP_KERNEL); + if (!irqc) + return -ENOMEM; + + irqc->base = of_iomap(node, 0); + if (!irqc->base) { + pr_err("%s: unable to map registers\n", node->full_name); + kfree(irqc); + return -EINVAL; + } + + irqc->chip.irq_ack = lpc32xx_irq_ack; + irqc->chip.irq_mask = lpc32xx_irq_mask; + irqc->chip.irq_unmask = lpc32xx_irq_unmask; + irqc->chip.irq_set_type = lpc32xx_irq_set_type; + if (is_mic) + irqc->chip.name = kasprintf(GFP_KERNEL, "%08x.mic", addr); + else + irqc->chip.name = kasprintf(GFP_KERNEL, "%08x.sic", addr); + + irqc->domain = irq_domain_add_linear(node, NR_LPC32XX_IC_IRQS, + &lpc32xx_irq_domain_ops, irqc); + if (!irqc->domain) { + pr_err("unable to add irq domain\n"); + iounmap(irqc->base); + kfree(irqc->chip.name); + kfree(irqc); + return -ENODEV; + } + + if (is_mic) { + lpc32xx_mic_irqc = irqc; + set_handle_irq(lpc32xx_handle_irq); + } else { + for (i = 0; i < of_irq_count(node); i++) { + parent_irq = irq_of_parse_and_map(node, i); + if (parent_irq) + irq_set_chained_handler_and_data(parent_irq, + lpc32xx_sic_handler, irqc); + } + } + + lpc32xx_ic_write(irqc, LPC32XX_INTC_MASK, 0x00); + lpc32xx_ic_write(irqc, LPC32XX_INTC_POL, 0x00); + lpc32xx_ic_write(irqc, LPC32XX_INTC_TYPE, 0x00); + + return 0; +} + +IRQCHIP_DECLARE(nxp_lpc32xx_mic, "nxp,lpc3220-mic", lpc32xx_of_ic_init); +IRQCHIP_DECLARE(nxp_lpc32xx_sic, "nxp,lpc3220-sic", lpc32xx_of_ic_init); -- cgit v1.2.3 From 466b7d168881f477ec8ddcc42d4206618acccf18 Mon Sep 17 00:00:00 2001 From: Shanker Donthineni Date: Wed, 9 Mar 2016 22:10:49 -0600 Subject: irqchip/gicv3-its: Don't allow devices whose ID is outside range We are not checking whether the requested device identifier fits into the device table memory or not. The function its_create_device() assumes that enough memory has been allocated for whole DevID space (reported by ITS_TYPER.Devbits) during the ITS probe() and continues to initialize ITS hardware. This assumption is not perfect, sometimes we reduce memory size either because of its size crossing MAX_ORDER-1 or BASERn max size limit. The MAPD command fails if 'Device ID' is outside of device table range. Add a simple validation check to avoid MAPD failures since we are not handling ITS command errors. This change also helps to return an error -ENOMEM instead of success to caller. Signed-off-by: Shanker Donthineni Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v3-its.c | 42 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 39261798c59f..6bd881be24ea 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -54,6 +54,16 @@ struct its_collection { u16 col_id; }; +/* + * The ITS_BASER structure - contains memory information and cached + * value of BASER register configuration. + */ +struct its_baser { + void *base; + u64 val; + u32 order; +}; + /* * The ITS structure - contains most of the infrastructure, with the * top-level MSI domain, the command queue, the collections, and the @@ -66,14 +76,12 @@ struct its_node { unsigned long phys_base; struct its_cmd_block *cmd_base; struct its_cmd_block *cmd_write; - struct { - void *base; - u32 order; - } tables[GITS_BASER_NR_REGS]; + struct its_baser tables[GITS_BASER_NR_REGS]; struct its_collection *collections; struct list_head its_device_list; u64 flags; u32 ite_size; + u32 device_ids; }; #define ITS_ITT_ALIGN SZ_256 @@ -838,6 +846,8 @@ static int its_alloc_tables(const char *node_name, struct its_node *its) ids = GITS_TYPER_DEVBITS(typer); } + its->device_ids = ids; + for (i = 0; i < GITS_BASER_NR_REGS; i++) { u64 val = readq_relaxed(its->base + GITS_BASER + i * 8); u64 type = GITS_BASER_TYPE(val); @@ -913,6 +923,7 @@ retry_baser: } val |= alloc_pages - 1; + its->tables[i].val = val; writeq_relaxed(val, its->base + GITS_BASER + i * 8); tmp = readq_relaxed(its->base + GITS_BASER + i * 8); @@ -1138,9 +1149,22 @@ static struct its_device *its_find_device(struct its_node *its, u32 dev_id) return its_dev; } +static struct its_baser *its_get_baser(struct its_node *its, u32 type) +{ + int i; + + for (i = 0; i < GITS_BASER_NR_REGS; i++) { + if (GITS_BASER_TYPE(its->tables[i].val) == type) + return &its->tables[i]; + } + + return NULL; +} + static struct its_device *its_create_device(struct its_node *its, u32 dev_id, int nvecs) { + struct its_baser *baser; struct its_device *dev; unsigned long *lpi_map; unsigned long flags; @@ -1151,6 +1175,16 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id, int nr_ites; int sz; + baser = its_get_baser(its, GITS_BASER_TYPE_DEVICE); + + /* Don't allow 'dev_id' that exceeds single, flat table limit */ + if (baser) { + if (dev_id >= (PAGE_ORDER_TO_SIZE(baser->order) / + GITS_BASER_ENTRY_SIZE(baser->val))) + return NULL; + } else if (ilog2(dev_id) >= its->device_ids) + return NULL; + dev = kzalloc(sizeof(*dev), GFP_KERNEL); /* * At least one bit of EventID is being used, hence a minimum -- cgit v1.2.3 From 086eec2de00ef5c1ac12ddb5a537289fbdc22689 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 4 Apr 2016 14:17:36 +0300 Subject: irqchip/mbigen: Checking for IS_ERR() instead of NULL of_platform_device_create() returns NULL on error, it never returns error pointers. Fixes: ed2a1002d25c ('irqchip/mbigen: Handle multiple device nodes in a mbigen module') Acked-by: Marc Zyngier Signed-off-by: Dan Carpenter Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-mbigen.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-mbigen.c b/drivers/irqchip/irq-mbigen.c index d67baa231c13..03b79b061d24 100644 --- a/drivers/irqchip/irq-mbigen.c +++ b/drivers/irqchip/irq-mbigen.c @@ -263,8 +263,8 @@ static int mbigen_device_probe(struct platform_device *pdev) parent = platform_bus_type.dev_root; child = of_platform_device_create(np, NULL, parent); - if (IS_ERR(child)) - return PTR_ERR(child); + if (!child) + return -ENOMEM; if (of_property_read_u32(child->dev.of_node, "num-pins", &num_pins) < 0) { -- cgit v1.2.3 From 1228d53d3df7826eaa6fe2fa267ee41131f9825d Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 5 May 2016 16:41:05 +0200 Subject: irqchip/irq-alpine-msi: Don't use Acked-by: Marc Zyngier Signed-off-by: Christoph Hellwig Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-alpine-msi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-alpine-msi.c b/drivers/irqchip/irq-alpine-msi.c index 25384255b30f..63d980995d17 100644 --- a/drivers/irqchip/irq-alpine-msi.c +++ b/drivers/irqchip/irq-alpine-msi.c @@ -23,7 +23,7 @@ #include #include -#include +#include /* MSIX message address format: local GIC target */ #define ALPINE_MSIX_SPI_TARGET_CLUSTER0 BIT(16) -- cgit v1.2.3 From 74c967aaffeace8a85fc3d7be773fd165ebde3da Mon Sep 17 00:00:00 2001 From: Ray Jui Date: Thu, 5 May 2016 09:32:01 -0700 Subject: irqchip/gic-v2m: Add workaround for Broadcom NS2 GICv2m erratum Alex Barba discovered Broadcom NS2 GICv2m implementation has an erratum where the MSI data needs to be the SPI number subtracted by an offset of 32, for the correct MSI interrupt to be triggered. Here we are adding the workaround based on readings from the MSI_IIDR register, which contains a value unique to Broadcom NS2 GICv2m Reported-by: Alex Barba Acked-by: Marc Zyngier Signed-off-by: Ray Jui Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v2m.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c index 28f047c61baa..ad0d2960b664 100644 --- a/drivers/irqchip/irq-gic-v2m.c +++ b/drivers/irqchip/irq-gic-v2m.c @@ -49,6 +49,9 @@ /* APM X-Gene with GICv2m MSI_IIDR register value */ #define XGENE_GICV2M_MSI_IIDR 0x06000170 +/* Broadcom NS2 GICv2m MSI_IIDR register value */ +#define BCM_NS2_GICV2M_MSI_IIDR 0x0000013f + /* List of flags for specific v2m implementation */ #define GICV2M_NEEDS_SPI_OFFSET 0x00000001 @@ -62,6 +65,7 @@ struct v2m_data { void __iomem *base; /* GICv2m virt address */ u32 spi_start; /* The SPI number that MSIs start */ u32 nr_spis; /* The number of SPIs for MSIs */ + u32 spi_offset; /* offset to be subtracted from SPI number */ unsigned long *bm; /* MSI vector bitmap */ u32 flags; /* v2m flags for specific implementation */ }; @@ -102,7 +106,7 @@ static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) msg->data = data->hwirq; if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET) - msg->data -= v2m->spi_start; + msg->data -= v2m->spi_offset; } static struct irq_chip gicv2m_irq_chip = { @@ -340,9 +344,20 @@ static int __init gicv2m_init_one(struct fwnode_handle *fwnode, * different from the standard GICv2m implementation where * the MSI data is the absolute value within the range from * spi_start to (spi_start + num_spis). + * + * Broadom NS2 GICv2m implementation has an erratum where the MSI data + * is 'spi_number - 32' */ - if (readl_relaxed(v2m->base + V2M_MSI_IIDR) == XGENE_GICV2M_MSI_IIDR) + switch (readl_relaxed(v2m->base + V2M_MSI_IIDR)) { + case XGENE_GICV2M_MSI_IIDR: + v2m->flags |= GICV2M_NEEDS_SPI_OFFSET; + v2m->spi_offset = v2m->spi_start; + break; + case BCM_NS2_GICV2M_MSI_IIDR: v2m->flags |= GICV2M_NEEDS_SPI_OFFSET; + v2m->spi_offset = 32; + break; + } v2m->bm = kzalloc(sizeof(long) * BITS_TO_LONGS(v2m->nr_spis), GFP_KERNEL); -- cgit v1.2.3 From 7c9b973061b03af62734f613f6abec46c0dd4a88 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 6 May 2016 19:41:56 +0100 Subject: irqchip/gic-v3: Configure all interrupts as non-secure Group-1 The GICv3 driver wrongly assumes that it runs on the non-secure side of a secure-enabled system, while it could be on a system with a single security state, or a GICv3 with GICD_CTLR.DS set. Either way, it is important to configure this properly, or interrupts will simply not be delivered on this HW. Cc: stable@vger.kernel.org Reported-by: Peter Maydell Tested-by: Peter Maydell Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v3.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index c3870a8f1be0..1a1ea4f733c1 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -393,6 +393,15 @@ static void __init gic_dist_init(void) writel_relaxed(0, base + GICD_CTLR); gic_dist_wait_for_rwp(); + /* + * Configure SPIs as non-secure Group-1. This will only matter + * if the GIC only has a single security state. This will not + * do the right thing if the kernel is running in secure mode, + * but that's not the intended use case anyway. + */ + for (i = 32; i < gic_data.irq_nr; i += 32) + writel_relaxed(~0, base + GICD_IGROUPR + i / 8); + gic_dist_config(base, gic_data.irq_nr, gic_dist_wait_for_rwp); /* Enable distributor with ARE, Group1 */ @@ -510,6 +519,9 @@ static void gic_cpu_init(void) rbase = gic_data_rdist_sgi_base(); + /* Configure SGIs/PPIs as non-secure Group-1 */ + writel_relaxed(~0, rbase + GICR_IGROUPR0); + gic_cpu_config(rbase, gic_redist_wait_for_rwp); /* Give LPIs a spin */ -- cgit v1.2.3 From a2a8fa5563d8740efaa8e62f653a6a85569c858b Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 10 May 2016 16:14:37 +0100 Subject: irqchip: Mask the non-type/sense bits when translating an IRQ The firmware parameter that contains the IRQ sense bits may also contain other data. When return the IRQ type, bits outside of these sense bits should be masked. If these bits are not masked and irq_create_fwspec_mapping() is called to map an IRQ, then the comparison of the type returned from irq_domain_translate() will never match that returned by irq_get_trigger_type() (because this function masks the none sense bits) and so we will always call irq_set_irq_type() to program the type even if it was not really necessary. Currently, the downside to this is unnecessarily re-programmming the type but nevertheless this should be avoided. The Tegra LIC and TI Crossbar irqchips all have client instances (from reviewing the device-tree sources) where bits outside the IRQ sense bits are set, but do not mask these bits. Therefore, ensure these bits are masked for these irqchips. Signed-off-by: Jon Hunter Acked-by: Marc Zyngier Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-crossbar.c | 2 +- drivers/irqchip/irq-tegra.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index 75573fa431ba..1eef56a89b1f 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -183,7 +183,7 @@ static int crossbar_domain_translate(struct irq_domain *d, return -EINVAL; *hwirq = fwspec->param[1]; - *type = fwspec->param[2]; + *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK; return 0; } diff --git a/drivers/irqchip/irq-tegra.c b/drivers/irqchip/irq-tegra.c index 50be9639e27e..e902f081e16c 100644 --- a/drivers/irqchip/irq-tegra.c +++ b/drivers/irqchip/irq-tegra.c @@ -235,7 +235,7 @@ static int tegra_ictlr_domain_translate(struct irq_domain *d, return -EINVAL; *hwirq = fwspec->param[1]; - *type = fwspec->param[2]; + *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK; return 0; } -- cgit v1.2.3 From ec1a454d6136b93e26902ead21025efc08047d05 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 10 May 2016 16:14:38 +0100 Subject: irqchip/gic: Don't unnecessarily write the IRQ configuration If the interrupt configuration matches the current configuration, then don't bother writing the configuration again. Signed-off-by: Jon Hunter Acked-by: Marc Zyngier Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-common.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic-common.c b/drivers/irqchip/irq-gic-common.c index f174ce0ca361..ffff5a45f1e3 100644 --- a/drivers/irqchip/irq-gic-common.c +++ b/drivers/irqchip/irq-gic-common.c @@ -50,13 +50,17 @@ int gic_configure_irq(unsigned int irq, unsigned int type, else if (type & IRQ_TYPE_EDGE_BOTH) val |= confmask; + /* If the current configuration is the same, then we are done */ + if (val == oldval) + return 0; + /* * Write back the new configuration, and possibly re-enable - * the interrupt. If we tried to write a new configuration and failed, + * the interrupt. If we fail to write a new configuration, * return an error. */ writel_relaxed(val, base + GIC_DIST_CONFIG + confoff); - if (readl_relaxed(base + GIC_DIST_CONFIG + confoff) != val && val != oldval) + if (readl_relaxed(base + GIC_DIST_CONFIG + confoff) != val) ret = -EINVAL; if (sync_access) -- cgit v1.2.3 From 992345a58e0cbcd63c26226e3e818b4583c4a34d Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 10 May 2016 16:14:39 +0100 Subject: irqchip/gic: WARN if setting the interrupt type for a PPI fails Setting the interrupt type for private peripheral interrupts (PPIs) may not be supported by a given GIC because it is IMPLEMENTATION DEFINED whether this is allowed. There is no way to know if setting the type is supported for a given GIC and so the value written is read back to verify it matches the desired configuration. If it does not match then an error is return. There are cases where the interrupt configuration read from firmware (such as a device-tree blob), has been incorrect and hence gic_configure_irq() has returned an error. This error has gone undetected because the error code returned was ignored but the interrupt still worked fine because the configuration for the interrupt could not be overwritten. Given that this has done undetected and that failing to set the configuration for a PPI may not be a catastrophic, don't return an error but WARN if we fail to configure a PPI. This will allows us to fix up any places in the kernel where we should be checking the return status and maintain backward compatibility with firmware images that may have incorrect PPI configurations. Signed-off-by: Jon Hunter Acked-by: Marc Zyngier Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-common.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic-common.c b/drivers/irqchip/irq-gic-common.c index ffff5a45f1e3..97c0028e8388 100644 --- a/drivers/irqchip/irq-gic-common.c +++ b/drivers/irqchip/irq-gic-common.c @@ -56,12 +56,20 @@ int gic_configure_irq(unsigned int irq, unsigned int type, /* * Write back the new configuration, and possibly re-enable - * the interrupt. If we fail to write a new configuration, - * return an error. + * the interrupt. If we fail to write a new configuration for + * an SPI then WARN and return an error. If we fail to write the + * configuration for a PPI this is most likely because the GIC + * does not allow us to set the configuration or we are in a + * non-secure mode, and hence it may not be catastrophic. */ writel_relaxed(val, base + GIC_DIST_CONFIG + confoff); - if (readl_relaxed(base + GIC_DIST_CONFIG + confoff) != val) - ret = -EINVAL; + if (readl_relaxed(base + GIC_DIST_CONFIG + confoff) != val) { + if (WARN_ON(irq >= 32)) + ret = -EINVAL; + else + pr_warn("GIC: PPI%d is secure or misconfigured\n", + irq - 16); + } if (sync_access) sync_access(); -- cgit v1.2.3 From 26acfe7463dc7162e595a8f7bd7ef3e06111e25d Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 10 May 2016 16:14:40 +0100 Subject: irqchip/gic: Don't initialise chip if mapping IO space fails If we fail to map the address space for the GIC distributor or CPU interface, then don't attempt to initialise the chip, just WARN and return. Signed-off-by: Jon Hunter Acked-by: Marc Zyngier Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 5c4da5808b15..612a3584a282 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -1209,10 +1209,14 @@ gic_of_init(struct device_node *node, struct device_node *parent) return -ENODEV; dist_base = of_iomap(node, 0); - WARN(!dist_base, "unable to map gic dist registers\n"); + if (WARN(!dist_base, "unable to map gic dist registers\n")) + return -ENOMEM; cpu_base = of_iomap(node, 1); - WARN(!cpu_base, "unable to map gic cpu registers\n"); + if (WARN(!cpu_base, "unable to map gic cpu registers\n")) { + iounmap(dist_base); + return -ENOMEM; + } /* * Disable split EOI/Deactivate if either HYP is not available -- cgit v1.2.3 From c2baa2f3f42fc6ea302bb666889c5843986a19a3 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 10 May 2016 16:14:41 +0100 Subject: irqchip/gic: Remove static irq_chip definition for eoimode1 There are only 3 differences (not including the name) in the definitions of the gic_chip and gic_eoimode1_chip structures. Instead of statically defining the gic_eoimode1_chip structure, remove it and populate the eoimode1 functions dynamically for the appropriate GIC irqchips. Signed-off-by: Jon Hunter Acked-by: Marc Zyngier Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 612a3584a282..a8cb86bea544 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -399,20 +399,6 @@ static struct irq_chip gic_chip = { IRQCHIP_MASK_ON_SUSPEND, }; -static struct irq_chip gic_eoimode1_chip = { - .name = "GICv2", - .irq_mask = gic_eoimode1_mask_irq, - .irq_unmask = gic_unmask_irq, - .irq_eoi = gic_eoimode1_eoi_irq, - .irq_set_type = gic_set_type, - .irq_get_irqchip_state = gic_irq_get_irqchip_state, - .irq_set_irqchip_state = gic_irq_set_irqchip_state, - .irq_set_vcpu_affinity = gic_irq_set_vcpu_affinity, - .flags = IRQCHIP_SET_TYPE_MASKED | - IRQCHIP_SKIP_SET_WAKE | - IRQCHIP_MASK_ON_SUSPEND, -}; - void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq) { BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR); @@ -1034,10 +1020,14 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start, gic = &gic_data[gic_nr]; /* Initialize irq_chip */ + gic->chip = gic_chip; + if (static_key_true(&supports_deactivate) && gic_nr == 0) { - gic->chip = gic_eoimode1_chip; + gic->chip.irq_mask = gic_eoimode1_mask_irq; + gic->chip.irq_eoi = gic_eoimode1_eoi_irq; + gic->chip.irq_set_vcpu_affinity = gic_irq_set_vcpu_affinity; + gic->chip.name = "GICv2"; } else { - gic->chip = gic_chip; gic->chip.name = kasprintf(GFP_KERNEL, "GIC-%d", gic_nr); } -- cgit v1.2.3 From dc9722cc57eba336038b2ade111436656c5a87d0 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 10 May 2016 16:14:42 +0100 Subject: irqchip/gic: Return an error if GIC initialisation fails If the GIC initialisation fails, then currently we do not return an error or clean-up afterwards. Although for root controllers, this failure may be fatal anyway, for secondary controllers, it may not be fatal and so return an error on failure and clean-up. Update the functions gic_cpu_init() and gic_pm_init() to return an error instead of calling BUG() and perform any necessary clean-up. For non-banked GIC controllers, make sure that we free any memory allocated if we fail to initialise the IRQ domain. Please note that free_percpu() only frees memory if the pointer passed to it is not NULL and so it is unnecessary to check if both pointers are valid or not. Signed-off-by: Jon Hunter Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic.c | 99 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 73 insertions(+), 26 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index a8cb86bea544..7cf138286442 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -467,7 +467,7 @@ static void __init gic_dist_init(struct gic_chip_data *gic) writel_relaxed(GICD_ENABLE, base + GIC_DIST_CTRL); } -static void gic_cpu_init(struct gic_chip_data *gic) +static int gic_cpu_init(struct gic_chip_data *gic) { void __iomem *dist_base = gic_data_dist_base(gic); void __iomem *base = gic_data_cpu_base(gic); @@ -483,7 +483,9 @@ static void gic_cpu_init(struct gic_chip_data *gic) /* * Get what the GIC says our CPU mask is. */ - BUG_ON(cpu >= NR_GIC_CPU_IF); + if (WARN_ON(cpu >= NR_GIC_CPU_IF)) + return -EINVAL; + cpu_mask = gic_get_cpumask(gic); gic_cpu_map[cpu] = cpu_mask; @@ -500,6 +502,8 @@ static void gic_cpu_init(struct gic_chip_data *gic) writel_relaxed(GICC_INT_PRI_THRESHOLD, base + GIC_CPU_PRIMASK); gic_cpu_if_up(gic); + + return 0; } int gic_cpu_if_down(unsigned int gic_nr) @@ -713,26 +717,39 @@ static struct notifier_block gic_notifier_block = { .notifier_call = gic_notifier, }; -static void __init gic_pm_init(struct gic_chip_data *gic) +static int __init gic_pm_init(struct gic_chip_data *gic) { gic->saved_ppi_enable = __alloc_percpu(DIV_ROUND_UP(32, 32) * 4, sizeof(u32)); - BUG_ON(!gic->saved_ppi_enable); + if (WARN_ON(!gic->saved_ppi_enable)) + return -ENOMEM; gic->saved_ppi_active = __alloc_percpu(DIV_ROUND_UP(32, 32) * 4, sizeof(u32)); - BUG_ON(!gic->saved_ppi_active); + if (WARN_ON(!gic->saved_ppi_active)) + goto free_ppi_enable; gic->saved_ppi_conf = __alloc_percpu(DIV_ROUND_UP(32, 16) * 4, sizeof(u32)); - BUG_ON(!gic->saved_ppi_conf); + if (WARN_ON(!gic->saved_ppi_conf)) + goto free_ppi_active; if (gic == &gic_data[0]) cpu_pm_register_notifier(&gic_notifier_block); + + return 0; + +free_ppi_active: + free_percpu(gic->saved_ppi_active); +free_ppi_enable: + free_percpu(gic->saved_ppi_enable); + + return -ENOMEM; } #else -static void __init gic_pm_init(struct gic_chip_data *gic) +static int __init gic_pm_init(struct gic_chip_data *gic) { + return 0; } #endif @@ -1005,13 +1022,13 @@ static const struct irq_domain_ops gic_irq_domain_ops = { .unmap = gic_irq_domain_unmap, }; -static void __init __gic_init_bases(unsigned int gic_nr, int irq_start, +static int __init __gic_init_bases(unsigned int gic_nr, int irq_start, void __iomem *dist_base, void __iomem *cpu_base, u32 percpu_offset, struct fwnode_handle *handle) { irq_hw_number_t hwirq_base; struct gic_chip_data *gic; - int gic_irqs, irq_base, i; + int gic_irqs, irq_base, i, ret; BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR); @@ -1026,7 +1043,7 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start, gic->chip.irq_mask = gic_eoimode1_mask_irq; gic->chip.irq_eoi = gic_eoimode1_eoi_irq; gic->chip.irq_set_vcpu_affinity = gic_irq_set_vcpu_affinity; - gic->chip.name = "GICv2"; + gic->chip.name = kasprintf(GFP_KERNEL, "GICv2"); } else { gic->chip.name = kasprintf(GFP_KERNEL, "GIC-%d", gic_nr); } @@ -1036,17 +1053,16 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start, gic->chip.irq_set_affinity = gic_set_affinity; #endif -#ifdef CONFIG_GIC_NON_BANKED - if (percpu_offset) { /* Frankein-GIC without banked registers... */ + if (IS_ENABLED(CONFIG_GIC_NON_BANKED) && percpu_offset) { + /* Frankein-GIC without banked registers... */ unsigned int cpu; gic->dist_base.percpu_base = alloc_percpu(void __iomem *); gic->cpu_base.percpu_base = alloc_percpu(void __iomem *); if (WARN_ON(!gic->dist_base.percpu_base || !gic->cpu_base.percpu_base)) { - free_percpu(gic->dist_base.percpu_base); - free_percpu(gic->cpu_base.percpu_base); - return; + ret = -ENOMEM; + goto error; } for_each_possible_cpu(cpu) { @@ -1058,9 +1074,8 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start, } gic_set_base_accessor(gic, gic_get_percpu_base); - } else -#endif - { /* Normal, sane GIC... */ + } else { + /* Normal, sane GIC... */ WARN(percpu_offset, "GIC_NON_BANKED not enabled, ignoring %08x offset!", percpu_offset); @@ -1110,8 +1125,10 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start, hwirq_base, &gic_irq_domain_ops, gic); } - if (WARN_ON(!gic->domain)) - return; + if (WARN_ON(!gic->domain)) { + ret = -ENODEV; + goto error; + } if (gic_nr == 0) { /* @@ -1131,8 +1148,25 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start, } gic_dist_init(gic); - gic_cpu_init(gic); - gic_pm_init(gic); + ret = gic_cpu_init(gic); + if (ret) + goto error; + + ret = gic_pm_init(gic); + if (ret) + goto error; + + return 0; + +error: + if (IS_ENABLED(CONFIG_GIC_NON_BANKED) && percpu_offset) { + free_percpu(gic->dist_base.percpu_base); + free_percpu(gic->cpu_base.percpu_base); + } + + kfree(gic->chip.name); + + return ret; } void __init gic_init(unsigned int gic_nr, int irq_start, @@ -1193,7 +1227,7 @@ gic_of_init(struct device_node *node, struct device_node *parent) void __iomem *cpu_base; void __iomem *dist_base; u32 percpu_offset; - int irq; + int irq, ret; if (WARN_ON(!node)) return -ENODEV; @@ -1218,8 +1252,14 @@ gic_of_init(struct device_node *node, struct device_node *parent) if (of_property_read_u32(node, "cpu-offset", &percpu_offset)) percpu_offset = 0; - __gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset, + ret = __gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset, &node->fwnode); + if (ret) { + iounmap(dist_base); + iounmap(cpu_base); + return ret; + } + if (!gic_cnt) gic_init_physaddr(node); @@ -1308,7 +1348,7 @@ static int __init gic_v2_acpi_init(struct acpi_subtable_header *header, struct acpi_madt_generic_distributor *dist; void __iomem *cpu_base, *dist_base; struct fwnode_handle *domain_handle; - int count; + int count, ret; /* Collect CPU base addresses */ count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT, @@ -1351,7 +1391,14 @@ static int __init gic_v2_acpi_init(struct acpi_subtable_header *header, return -ENOMEM; } - __gic_init_bases(0, -1, dist_base, cpu_base, 0, domain_handle); + ret = __gic_init_bases(0, -1, dist_base, cpu_base, 0, domain_handle); + if (ret) { + pr_err("Failed to initialise GIC\n"); + irq_domain_free_fwnode(domain_handle); + iounmap(cpu_base); + iounmap(dist_base); + return ret; + } acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, domain_handle); -- cgit v1.2.3 From 6e5b5924d9a897062527b6fc9b06f31f7bfd5744 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 10 May 2016 16:14:43 +0100 Subject: irqchip/gic: Pass GIC pointer to save/restore functions Instead of passing the GIC index to the save/restore functions pass a pointer to the GIC chip data. This will allow these save/restore functions to be re-used by a platform driver where the GIC chip data structure is allocated dynamically and so there is no applicable index for identifying the GIC. Signed-off-by: Jon Hunter Acked-by: Marc Zyngier Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic.c | 74 +++++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 35 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 7cf138286442..d75aa1a8d6da 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -529,34 +529,35 @@ int gic_cpu_if_down(unsigned int gic_nr) * this function, no interrupts will be delivered by the GIC, and another * platform-specific wakeup source must be enabled. */ -static void gic_dist_save(unsigned int gic_nr) +static void gic_dist_save(struct gic_chip_data *gic) { unsigned int gic_irqs; void __iomem *dist_base; int i; - BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR); + if (WARN_ON(!gic)) + return; - gic_irqs = gic_data[gic_nr].gic_irqs; - dist_base = gic_data_dist_base(&gic_data[gic_nr]); + gic_irqs = gic->gic_irqs; + dist_base = gic_data_dist_base(gic); if (!dist_base) return; for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++) - gic_data[gic_nr].saved_spi_conf[i] = + gic->saved_spi_conf[i] = readl_relaxed(dist_base + GIC_DIST_CONFIG + i * 4); for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++) - gic_data[gic_nr].saved_spi_target[i] = + gic->saved_spi_target[i] = readl_relaxed(dist_base + GIC_DIST_TARGET + i * 4); for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++) - gic_data[gic_nr].saved_spi_enable[i] = + gic->saved_spi_enable[i] = readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4); for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++) - gic_data[gic_nr].saved_spi_active[i] = + gic->saved_spi_active[i] = readl_relaxed(dist_base + GIC_DIST_ACTIVE_SET + i * 4); } @@ -567,16 +568,17 @@ static void gic_dist_save(unsigned int gic_nr) * handled normally, but any edge interrupts that occured will not be seen by * the GIC and need to be handled by the platform-specific wakeup source. */ -static void gic_dist_restore(unsigned int gic_nr) +static void gic_dist_restore(struct gic_chip_data *gic) { unsigned int gic_irqs; unsigned int i; void __iomem *dist_base; - BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR); + if (WARN_ON(!gic)) + return; - gic_irqs = gic_data[gic_nr].gic_irqs; - dist_base = gic_data_dist_base(&gic_data[gic_nr]); + gic_irqs = gic->gic_irqs; + dist_base = gic_data_dist_base(gic); if (!dist_base) return; @@ -584,7 +586,7 @@ static void gic_dist_restore(unsigned int gic_nr) writel_relaxed(GICD_DISABLE, dist_base + GIC_DIST_CTRL); for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++) - writel_relaxed(gic_data[gic_nr].saved_spi_conf[i], + writel_relaxed(gic->saved_spi_conf[i], dist_base + GIC_DIST_CONFIG + i * 4); for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++) @@ -592,85 +594,87 @@ static void gic_dist_restore(unsigned int gic_nr) dist_base + GIC_DIST_PRI + i * 4); for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++) - writel_relaxed(gic_data[gic_nr].saved_spi_target[i], + writel_relaxed(gic->saved_spi_target[i], dist_base + GIC_DIST_TARGET + i * 4); for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++) { writel_relaxed(GICD_INT_EN_CLR_X32, dist_base + GIC_DIST_ENABLE_CLEAR + i * 4); - writel_relaxed(gic_data[gic_nr].saved_spi_enable[i], + writel_relaxed(gic->saved_spi_enable[i], dist_base + GIC_DIST_ENABLE_SET + i * 4); } for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++) { writel_relaxed(GICD_INT_EN_CLR_X32, dist_base + GIC_DIST_ACTIVE_CLEAR + i * 4); - writel_relaxed(gic_data[gic_nr].saved_spi_active[i], + writel_relaxed(gic->saved_spi_active[i], dist_base + GIC_DIST_ACTIVE_SET + i * 4); } writel_relaxed(GICD_ENABLE, dist_base + GIC_DIST_CTRL); } -static void gic_cpu_save(unsigned int gic_nr) +static void gic_cpu_save(struct gic_chip_data *gic) { int i; u32 *ptr; void __iomem *dist_base; void __iomem *cpu_base; - BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR); + if (WARN_ON(!gic)) + return; - dist_base = gic_data_dist_base(&gic_data[gic_nr]); - cpu_base = gic_data_cpu_base(&gic_data[gic_nr]); + dist_base = gic_data_dist_base(gic); + cpu_base = gic_data_cpu_base(gic); if (!dist_base || !cpu_base) return; - ptr = raw_cpu_ptr(gic_data[gic_nr].saved_ppi_enable); + ptr = raw_cpu_ptr(gic->saved_ppi_enable); for (i = 0; i < DIV_ROUND_UP(32, 32); i++) ptr[i] = readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4); - ptr = raw_cpu_ptr(gic_data[gic_nr].saved_ppi_active); + ptr = raw_cpu_ptr(gic->saved_ppi_active); for (i = 0; i < DIV_ROUND_UP(32, 32); i++) ptr[i] = readl_relaxed(dist_base + GIC_DIST_ACTIVE_SET + i * 4); - ptr = raw_cpu_ptr(gic_data[gic_nr].saved_ppi_conf); + ptr = raw_cpu_ptr(gic->saved_ppi_conf); for (i = 0; i < DIV_ROUND_UP(32, 16); i++) ptr[i] = readl_relaxed(dist_base + GIC_DIST_CONFIG + i * 4); } -static void gic_cpu_restore(unsigned int gic_nr) +static void gic_cpu_restore(struct gic_chip_data *gic) { int i; u32 *ptr; void __iomem *dist_base; void __iomem *cpu_base; - BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR); + if (WARN_ON(!gic)) + return; - dist_base = gic_data_dist_base(&gic_data[gic_nr]); - cpu_base = gic_data_cpu_base(&gic_data[gic_nr]); + dist_base = gic_data_dist_base(gic); + cpu_base = gic_data_cpu_base(gic); if (!dist_base || !cpu_base) return; - ptr = raw_cpu_ptr(gic_data[gic_nr].saved_ppi_enable); + ptr = raw_cpu_ptr(gic->saved_ppi_enable); for (i = 0; i < DIV_ROUND_UP(32, 32); i++) { writel_relaxed(GICD_INT_EN_CLR_X32, dist_base + GIC_DIST_ENABLE_CLEAR + i * 4); writel_relaxed(ptr[i], dist_base + GIC_DIST_ENABLE_SET + i * 4); } - ptr = raw_cpu_ptr(gic_data[gic_nr].saved_ppi_active); + ptr = raw_cpu_ptr(gic->saved_ppi_active); for (i = 0; i < DIV_ROUND_UP(32, 32); i++) { writel_relaxed(GICD_INT_EN_CLR_X32, dist_base + GIC_DIST_ACTIVE_CLEAR + i * 4); writel_relaxed(ptr[i], dist_base + GIC_DIST_ACTIVE_SET + i * 4); } - ptr = raw_cpu_ptr(gic_data[gic_nr].saved_ppi_conf); + ptr = raw_cpu_ptr(gic->saved_ppi_conf); for (i = 0; i < DIV_ROUND_UP(32, 16); i++) writel_relaxed(ptr[i], dist_base + GIC_DIST_CONFIG + i * 4); @@ -679,7 +683,7 @@ static void gic_cpu_restore(unsigned int gic_nr) dist_base + GIC_DIST_PRI + i * 4); writel_relaxed(GICC_INT_PRI_THRESHOLD, cpu_base + GIC_CPU_PRIMASK); - gic_cpu_if_up(&gic_data[gic_nr]); + gic_cpu_if_up(gic); } static int gic_notifier(struct notifier_block *self, unsigned long cmd, void *v) @@ -694,18 +698,18 @@ static int gic_notifier(struct notifier_block *self, unsigned long cmd, void *v) #endif switch (cmd) { case CPU_PM_ENTER: - gic_cpu_save(i); + gic_cpu_save(&gic_data[i]); break; case CPU_PM_ENTER_FAILED: case CPU_PM_EXIT: - gic_cpu_restore(i); + gic_cpu_restore(&gic_data[i]); break; case CPU_CLUSTER_PM_ENTER: - gic_dist_save(i); + gic_dist_save(&gic_data[i]); break; case CPU_CLUSTER_PM_ENTER_FAILED: case CPU_CLUSTER_PM_EXIT: - gic_dist_restore(i); + gic_dist_restore(&gic_data[i]); break; } } -- cgit v1.2.3 From f673b9b5cb5453fa14032d99edd55f49ac3980cc Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 10 May 2016 16:14:44 +0100 Subject: irqchip/gic: Store GIC configuration parameters Store the GIC configuration parameters in the GIC chip data structure. This will allow us to simplify the code by reducing the number of parameters passed between functions. Update the __gic_init_bases() function so that we only need to pass a pointer to the GIC chip data structure and no longer need to pass the GIC index in order to look-up the chip data. Signed-off-by: Jon Hunter Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic.c | 114 ++++++++++++++++++++++++++-------------------- 1 file changed, 65 insertions(+), 49 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index d75aa1a8d6da..f4c14f9ca15b 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -72,6 +72,9 @@ struct gic_chip_data { struct irq_chip chip; union gic_base dist_base; union gic_base cpu_base; + void __iomem *raw_dist_base; + void __iomem *raw_cpu_base; + u32 percpu_offset; #ifdef CONFIG_CPU_PM u32 saved_spi_enable[DIV_ROUND_UP(1020, 32)]; u32 saved_spi_active[DIV_ROUND_UP(1020, 32)]; @@ -1026,38 +1029,36 @@ static const struct irq_domain_ops gic_irq_domain_ops = { .unmap = gic_irq_domain_unmap, }; -static int __init __gic_init_bases(unsigned int gic_nr, int irq_start, - void __iomem *dist_base, void __iomem *cpu_base, - u32 percpu_offset, struct fwnode_handle *handle) +static int __init __gic_init_bases(struct gic_chip_data *gic, int irq_start, + struct fwnode_handle *handle) { irq_hw_number_t hwirq_base; - struct gic_chip_data *gic; int gic_irqs, irq_base, i, ret; - BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR); + if (WARN_ON(!gic || gic->domain)) + return -EINVAL; gic_check_cpu_features(); - gic = &gic_data[gic_nr]; - /* Initialize irq_chip */ gic->chip = gic_chip; - if (static_key_true(&supports_deactivate) && gic_nr == 0) { + if (static_key_true(&supports_deactivate) && gic == &gic_data[0]) { gic->chip.irq_mask = gic_eoimode1_mask_irq; gic->chip.irq_eoi = gic_eoimode1_eoi_irq; gic->chip.irq_set_vcpu_affinity = gic_irq_set_vcpu_affinity; gic->chip.name = kasprintf(GFP_KERNEL, "GICv2"); } else { - gic->chip.name = kasprintf(GFP_KERNEL, "GIC-%d", gic_nr); + gic->chip.name = kasprintf(GFP_KERNEL, "GIC-%d", + (int)(gic - &gic_data[0])); } #ifdef CONFIG_SMP - if (gic_nr == 0) + if (gic == &gic_data[0]) gic->chip.irq_set_affinity = gic_set_affinity; #endif - if (IS_ENABLED(CONFIG_GIC_NON_BANKED) && percpu_offset) { + if (IS_ENABLED(CONFIG_GIC_NON_BANKED) && gic->percpu_offset) { /* Frankein-GIC without banked registers... */ unsigned int cpu; @@ -1072,19 +1073,21 @@ static int __init __gic_init_bases(unsigned int gic_nr, int irq_start, for_each_possible_cpu(cpu) { u32 mpidr = cpu_logical_map(cpu); u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0); - unsigned long offset = percpu_offset * core_id; - *per_cpu_ptr(gic->dist_base.percpu_base, cpu) = dist_base + offset; - *per_cpu_ptr(gic->cpu_base.percpu_base, cpu) = cpu_base + offset; + unsigned long offset = gic->percpu_offset * core_id; + *per_cpu_ptr(gic->dist_base.percpu_base, cpu) = + gic->raw_dist_base + offset; + *per_cpu_ptr(gic->cpu_base.percpu_base, cpu) = + gic->raw_cpu_base + offset; } gic_set_base_accessor(gic, gic_get_percpu_base); } else { /* Normal, sane GIC... */ - WARN(percpu_offset, + WARN(gic->percpu_offset, "GIC_NON_BANKED not enabled, ignoring %08x offset!", - percpu_offset); - gic->dist_base.common_base = dist_base; - gic->cpu_base.common_base = cpu_base; + gic->percpu_offset); + gic->dist_base.common_base = gic->raw_dist_base; + gic->cpu_base.common_base = gic->raw_cpu_base; gic_set_base_accessor(gic, gic_get_common_base); } @@ -1107,7 +1110,7 @@ static int __init __gic_init_bases(unsigned int gic_nr, int irq_start, * For primary GICs, skip over SGIs. * For secondary GICs, skip over PPIs, too. */ - if (gic_nr == 0 && (irq_start & 31) > 0) { + if (gic == &gic_data[0] && (irq_start & 31) > 0) { hwirq_base = 16; if (irq_start != -1) irq_start = (irq_start & ~31) + 16; @@ -1134,7 +1137,7 @@ static int __init __gic_init_bases(unsigned int gic_nr, int irq_start, goto error; } - if (gic_nr == 0) { + if (gic == &gic_data[0]) { /* * Initialize the CPU interface map to all CPUs. * It will be refined as each CPU probes its ID. @@ -1163,7 +1166,7 @@ static int __init __gic_init_bases(unsigned int gic_nr, int irq_start, return 0; error: - if (IS_ENABLED(CONFIG_GIC_NON_BANKED) && percpu_offset) { + if (IS_ENABLED(CONFIG_GIC_NON_BANKED) && gic->percpu_offset) { free_percpu(gic->dist_base.percpu_base); free_percpu(gic->cpu_base.percpu_base); } @@ -1176,12 +1179,22 @@ error: void __init gic_init(unsigned int gic_nr, int irq_start, void __iomem *dist_base, void __iomem *cpu_base) { + struct gic_chip_data *gic; + + if (WARN_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR)) + return; + /* * Non-DT/ACPI systems won't run a hypervisor, so let's not * bother with these... */ static_key_slow_dec(&supports_deactivate); - __gic_init_bases(gic_nr, irq_start, dist_base, cpu_base, 0, NULL); + + gic = &gic_data[gic_nr]; + gic->raw_dist_base = dist_base; + gic->raw_cpu_base = cpu_base; + + __gic_init_bases(gic, irq_start, NULL); } #ifdef CONFIG_OF @@ -1228,21 +1241,24 @@ static bool gic_check_eoimode(struct device_node *node, void __iomem **base) int __init gic_of_init(struct device_node *node, struct device_node *parent) { - void __iomem *cpu_base; - void __iomem *dist_base; - u32 percpu_offset; + struct gic_chip_data *gic; int irq, ret; if (WARN_ON(!node)) return -ENODEV; - dist_base = of_iomap(node, 0); - if (WARN(!dist_base, "unable to map gic dist registers\n")) + if (WARN_ON(gic_cnt >= CONFIG_ARM_GIC_MAX_NR)) + return -EINVAL; + + gic = &gic_data[gic_cnt]; + + gic->raw_dist_base = of_iomap(node, 0); + if (WARN(!gic->raw_dist_base, "unable to map gic dist registers\n")) return -ENOMEM; - cpu_base = of_iomap(node, 1); - if (WARN(!cpu_base, "unable to map gic cpu registers\n")) { - iounmap(dist_base); + gic->raw_cpu_base = of_iomap(node, 1); + if (WARN(!gic->raw_cpu_base, "unable to map gic cpu registers\n")) { + iounmap(gic->raw_dist_base); return -ENOMEM; } @@ -1250,17 +1266,16 @@ gic_of_init(struct device_node *node, struct device_node *parent) * Disable split EOI/Deactivate if either HYP is not available * or the CPU interface is too small. */ - if (gic_cnt == 0 && !gic_check_eoimode(node, &cpu_base)) + if (gic_cnt == 0 && !gic_check_eoimode(node, &gic->raw_cpu_base)) static_key_slow_dec(&supports_deactivate); - if (of_property_read_u32(node, "cpu-offset", &percpu_offset)) - percpu_offset = 0; + if (of_property_read_u32(node, "cpu-offset", &gic->percpu_offset)) + gic->percpu_offset = 0; - ret = __gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset, - &node->fwnode); + ret = __gic_init_bases(gic, -1, &node->fwnode); if (ret) { - iounmap(dist_base); - iounmap(cpu_base); + iounmap(gic->raw_dist_base); + iounmap(gic->raw_cpu_base); return ret; } @@ -1350,8 +1365,8 @@ static int __init gic_v2_acpi_init(struct acpi_subtable_header *header, const unsigned long end) { struct acpi_madt_generic_distributor *dist; - void __iomem *cpu_base, *dist_base; struct fwnode_handle *domain_handle; + struct gic_chip_data *gic = &gic_data[0]; int count, ret; /* Collect CPU base addresses */ @@ -1362,17 +1377,18 @@ static int __init gic_v2_acpi_init(struct acpi_subtable_header *header, return -EINVAL; } - cpu_base = ioremap(cpu_phy_base, ACPI_GIC_CPU_IF_MEM_SIZE); - if (!cpu_base) { + gic->raw_cpu_base = ioremap(cpu_phy_base, ACPI_GIC_CPU_IF_MEM_SIZE); + if (!gic->raw_cpu_base) { pr_err("Unable to map GICC registers\n"); return -ENOMEM; } dist = (struct acpi_madt_generic_distributor *)header; - dist_base = ioremap(dist->base_address, ACPI_GICV2_DIST_MEM_SIZE); - if (!dist_base) { + gic->raw_dist_base = ioremap(dist->base_address, + ACPI_GICV2_DIST_MEM_SIZE); + if (!gic->raw_dist_base) { pr_err("Unable to map GICD registers\n"); - iounmap(cpu_base); + iounmap(gic->raw_cpu_base); return -ENOMEM; } @@ -1387,20 +1403,20 @@ static int __init gic_v2_acpi_init(struct acpi_subtable_header *header, /* * Initialize GIC instance zero (no multi-GIC support). */ - domain_handle = irq_domain_alloc_fwnode(dist_base); + domain_handle = irq_domain_alloc_fwnode(gic->raw_dist_base); if (!domain_handle) { pr_err("Unable to allocate domain handle\n"); - iounmap(cpu_base); - iounmap(dist_base); + iounmap(gic->raw_cpu_base); + iounmap(gic->raw_dist_base); return -ENOMEM; } - ret = __gic_init_bases(0, -1, dist_base, cpu_base, 0, domain_handle); + ret = __gic_init_bases(gic, -1, domain_handle); if (ret) { pr_err("Failed to initialise GIC\n"); irq_domain_free_fwnode(domain_handle); - iounmap(cpu_base); - iounmap(dist_base); + iounmap(gic->raw_cpu_base); + iounmap(gic->raw_dist_base); return ret; } -- cgit v1.2.3 From d6490461a102094891d8a2712c51365f86ac1a40 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 10 May 2016 16:14:45 +0100 Subject: irqchip/gic: Add helper functions for GIC setup and teardown Move the code that sets-up a GIC via device-tree into it's own function and add a generic function for GIC teardown that can be used for both device-tree and ACPI to unmap the GIC memory. Signed-off-by: Jon Hunter Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic.c | 61 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 19 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index f4c14f9ca15b..113e2d02c812 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -1197,6 +1197,17 @@ void __init gic_init(unsigned int gic_nr, int irq_start, __gic_init_bases(gic, irq_start, NULL); } +static void gic_teardown(struct gic_chip_data *gic) +{ + if (WARN_ON(!gic)) + return; + + if (gic->raw_dist_base) + iounmap(gic->raw_dist_base); + if (gic->raw_cpu_base) + iounmap(gic->raw_cpu_base); +} + #ifdef CONFIG_OF static int gic_cnt __initdata; @@ -1238,6 +1249,30 @@ static bool gic_check_eoimode(struct device_node *node, void __iomem **base) return true; } +static int gic_of_setup(struct gic_chip_data *gic, struct device_node *node) +{ + if (!gic || !node) + return -EINVAL; + + gic->raw_dist_base = of_iomap(node, 0); + if (WARN(!gic->raw_dist_base, "unable to map gic dist registers\n")) + goto error; + + gic->raw_cpu_base = of_iomap(node, 1); + if (WARN(!gic->raw_cpu_base, "unable to map gic cpu registers\n")) + goto error; + + if (of_property_read_u32(node, "cpu-offset", &gic->percpu_offset)) + gic->percpu_offset = 0; + + return 0; + +error: + gic_teardown(gic); + + return -ENOMEM; +} + int __init gic_of_init(struct device_node *node, struct device_node *parent) { @@ -1252,15 +1287,9 @@ gic_of_init(struct device_node *node, struct device_node *parent) gic = &gic_data[gic_cnt]; - gic->raw_dist_base = of_iomap(node, 0); - if (WARN(!gic->raw_dist_base, "unable to map gic dist registers\n")) - return -ENOMEM; - - gic->raw_cpu_base = of_iomap(node, 1); - if (WARN(!gic->raw_cpu_base, "unable to map gic cpu registers\n")) { - iounmap(gic->raw_dist_base); - return -ENOMEM; - } + ret = gic_of_setup(gic, node); + if (ret) + return ret; /* * Disable split EOI/Deactivate if either HYP is not available @@ -1269,13 +1298,9 @@ gic_of_init(struct device_node *node, struct device_node *parent) if (gic_cnt == 0 && !gic_check_eoimode(node, &gic->raw_cpu_base)) static_key_slow_dec(&supports_deactivate); - if (of_property_read_u32(node, "cpu-offset", &gic->percpu_offset)) - gic->percpu_offset = 0; - ret = __gic_init_bases(gic, -1, &node->fwnode); if (ret) { - iounmap(gic->raw_dist_base); - iounmap(gic->raw_cpu_base); + gic_teardown(gic); return ret; } @@ -1388,7 +1413,7 @@ static int __init gic_v2_acpi_init(struct acpi_subtable_header *header, ACPI_GICV2_DIST_MEM_SIZE); if (!gic->raw_dist_base) { pr_err("Unable to map GICD registers\n"); - iounmap(gic->raw_cpu_base); + gic_teardown(gic); return -ENOMEM; } @@ -1406,8 +1431,7 @@ static int __init gic_v2_acpi_init(struct acpi_subtable_header *header, domain_handle = irq_domain_alloc_fwnode(gic->raw_dist_base); if (!domain_handle) { pr_err("Unable to allocate domain handle\n"); - iounmap(gic->raw_cpu_base); - iounmap(gic->raw_dist_base); + gic_teardown(gic); return -ENOMEM; } @@ -1415,8 +1439,7 @@ static int __init gic_v2_acpi_init(struct acpi_subtable_header *header, if (ret) { pr_err("Failed to initialise GIC\n"); irq_domain_free_fwnode(domain_handle); - iounmap(gic->raw_cpu_base); - iounmap(gic->raw_dist_base); + gic_teardown(gic); return ret; } -- cgit v1.2.3 From 0dc17be87647cd4c891e748a0753ad0606dc5775 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 13 Apr 2016 13:28:41 -0700 Subject: irqchip/bcm2836: Drop smp_set_ops on arm64 builds For arm64, the bootloader will instead be implementing the spin-table enable method. Signed-off-by: Eric Anholt Acked-by: Stephen Warren Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-bcm2836.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-bcm2836.c b/drivers/irqchip/irq-bcm2836.c index b6e950d4782a..43aefb25bb5f 100644 --- a/drivers/irqchip/irq-bcm2836.c +++ b/drivers/irqchip/irq-bcm2836.c @@ -223,6 +223,7 @@ static struct notifier_block bcm2836_arm_irqchip_cpu_notifier = { .priority = 100, }; +#ifdef CONFIG_ARM int __init bcm2836_smp_boot_secondary(unsigned int cpu, struct task_struct *idle) { @@ -238,7 +239,7 @@ int __init bcm2836_smp_boot_secondary(unsigned int cpu, static const struct smp_operations bcm2836_smp_ops __initconst = { .smp_boot_secondary = bcm2836_smp_boot_secondary, }; - +#endif #endif static const struct irq_domain_ops bcm2836_arm_irqchip_intc_ops = { @@ -256,8 +257,11 @@ bcm2836_arm_irqchip_smp_init(void) register_cpu_notifier(&bcm2836_arm_irqchip_cpu_notifier); set_smp_cross_call(bcm2836_arm_irqchip_send_ipi); + +#ifdef CONFIG_ARM smp_set_ops(&bcm2836_smp_ops); #endif +#endif } /* -- cgit v1.2.3 From cb290d827eb0378447095093b4ea712482d31ae8 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 13 Apr 2016 13:28:42 -0700 Subject: irqchip/bcm2836: Fix compiler warning on 64-bit build Signed-off-by: Eric Anholt Acked-by: Stephen Warren Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-bcm2836.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-bcm2836.c b/drivers/irqchip/irq-bcm2836.c index 43aefb25bb5f..f8141bd392c1 100644 --- a/drivers/irqchip/irq-bcm2836.c +++ b/drivers/irqchip/irq-bcm2836.c @@ -253,7 +253,7 @@ bcm2836_arm_irqchip_smp_init(void) /* Unmask IPIs to the boot CPU. */ bcm2836_arm_irqchip_cpu_notify(&bcm2836_arm_irqchip_cpu_notifier, CPU_STARTING, - (void *)smp_processor_id()); + (void *)(uintptr_t)smp_processor_id()); register_cpu_notifier(&bcm2836_arm_irqchip_cpu_notifier); set_smp_cross_call(bcm2836_arm_irqchip_send_ipi); -- cgit v1.2.3 From a1dcbd11d09be1cb357b2f217c0eaa1461128da0 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 13 Apr 2016 13:28:43 -0700 Subject: irqchip/bcm2836: Use a more generic memory barrier call dsb() requires an argument on arm64, so we needed to add "sy". Instead, take this opportunity to switch to the same smp_wmb() call that gic uses for its IPIs. This is a less strong barrier than we were doing before (dmb(ishst) compared to dsb(sy)), but it seems to be the correct one. Signed-off-by: Eric Anholt Acked-by: Stephen Warren Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-bcm2836.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-bcm2836.c b/drivers/irqchip/irq-bcm2836.c index f8141bd392c1..72ff1d5c5de6 100644 --- a/drivers/irqchip/irq-bcm2836.c +++ b/drivers/irqchip/irq-bcm2836.c @@ -195,7 +195,7 @@ static void bcm2836_arm_irqchip_send_ipi(const struct cpumask *mask, * Ensure that stores to normal memory are visible to the * other CPUs before issuing the IPI. */ - dsb(); + smp_wmb(); for_each_cpu(cpu, mask) { writel(1 << ipi, mailbox0_base + 16 * cpu); -- cgit v1.2.3