From 788d23930a9724fc85cdf9999521a867d8748019 Mon Sep 17 00:00:00 2001 From: Matthias Brugger Date: Mon, 16 Jan 2017 19:48:20 +0100 Subject: irq/platform-msi: Fix comment about maximal MSIs Commit aff5e06b0dda ("irq/platform-MSI: Increase the maximum MSIs the MSI framework can support") increased the maximum MSIs to 2048. Fix the comment to reflect that. Signed-off-by: Matthias Brugger Cc: gregkh@linuxfoundation.org Cc: majun258@huawei.com Link: http://lkml.kernel.org/r/1484592500-15400-1-git-send-email-mbrugger@suse.com Signed-off-by: Thomas Gleixner --- drivers/base/platform-msi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/platform-msi.c b/drivers/base/platform-msi.c index be6a599bc0c1..0fc7c4da7756 100644 --- a/drivers/base/platform-msi.c +++ b/drivers/base/platform-msi.c @@ -206,7 +206,7 @@ platform_msi_alloc_priv_data(struct device *dev, unsigned int nvec, { struct platform_msi_priv_data *datap; /* - * Limit the number of interrupts to 256 per device. Should we + * Limit the number of interrupts to 2048 per device. Should we * need to bump this up, DEV_ID_SHIFT should be adjusted * accordingly (which would impact the max number of MSI * capable devices). -- cgit v1.2.3 From fa20b176f609c813d2c677f54c814cbb7ea5f1d1 Mon Sep 17 00:00:00 2001 From: Agustin Vega-Frias Date: Thu, 2 Feb 2017 18:23:57 -0500 Subject: ACPI: Generic GSI: Do not attempt to map non-GSI IRQs during bus scan ACPI extended IRQ resources may contain a Resource Source field to specify an alternate interrupt controller, attempting to map them as GSIs is incorrect, so just disable the platform resource. Since this field is currently ignored, we make this change conditional on CONFIG_ACPI_GENERIC_GSI to keep the current behavior on x86 platforms, in case some existing ACPI tables are using this incorrectly. Acked-by: Rafael J. Wysocki Acked-by: Lorenzo Pieralisi Reviewed-by: Hanjun Guo Tested-by: Hanjun Guo Signed-off-by: Agustin Vega-Frias Signed-off-by: Marc Zyngier --- drivers/acpi/resource.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index cb57962ef7c4..8b11d6d385dc 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -43,6 +43,19 @@ static inline bool acpi_iospace_resource_valid(struct resource *res) { return true; } #endif +#if IS_ENABLED(CONFIG_ACPI_GENERIC_GSI) +static inline bool is_gsi(struct acpi_resource_extended_irq *ext_irq) +{ + return ext_irq->resource_source.string_length == 0 && + ext_irq->producer_consumer == ACPI_CONSUMER; +} +#else +static inline bool is_gsi(struct acpi_resource_extended_irq *ext_irq) +{ + return true; +} +#endif + static bool acpi_dev_resource_len_valid(u64 start, u64 end, u64 len, bool io) { u64 reslen = end - start + 1; @@ -470,9 +483,12 @@ bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index, acpi_dev_irqresource_disabled(res, 0); return false; } - acpi_dev_get_irqresource(res, ext_irq->interrupts[index], + if (is_gsi(ext_irq)) + acpi_dev_get_irqresource(res, ext_irq->interrupts[index], ext_irq->triggering, ext_irq->polarity, ext_irq->sharable, false); + else + acpi_dev_irqresource_disabled(res, 0); break; default: res->flags = 0; -- cgit v1.2.3 From d44fa3d46079dc095c1346fa6e5bc96dca1ead41 Mon Sep 17 00:00:00 2001 From: Agustin Vega-Frias Date: Thu, 2 Feb 2017 18:23:58 -0500 Subject: ACPI: Add support for ResourceSource/IRQ domain mapping ACPI extended IRQ resources may contain a ResourceSource to specify an alternate interrupt controller. Introduce acpi_irq_get and use it to implement ResourceSource/IRQ domain mapping. The new API is similar to of_irq_get and allows re-initialization of a platform resource from the ACPI extended IRQ resource, and provides proper behavior for probe deferral when the domain is not yet present when called. Acked-by: Rafael J. Wysocki Acked-by: Lorenzo Pieralisi Reviewed-by: Hanjun Guo Tested-by: Hanjun Guo Signed-off-by: Agustin Vega-Frias Signed-off-by: Marc Zyngier --- drivers/acpi/Makefile | 2 +- drivers/acpi/gsi.c | 98 ---------------- drivers/acpi/irq.c | 297 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/base/platform.c | 10 ++ include/linux/acpi.h | 10 ++ 5 files changed, 318 insertions(+), 99 deletions(-) delete mode 100644 drivers/acpi/gsi.c create mode 100644 drivers/acpi/irq.c diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 9ed087853dee..a391bbc48105 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -55,7 +55,7 @@ acpi-$(CONFIG_DEBUG_FS) += debugfs.o acpi-$(CONFIG_ACPI_NUMA) += numa.o acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o acpi-y += acpi_lpat.o -acpi-$(CONFIG_ACPI_GENERIC_GSI) += gsi.o +acpi-$(CONFIG_ACPI_GENERIC_GSI) += irq.o acpi-$(CONFIG_ACPI_WATCHDOG) += acpi_watchdog.o # These are (potentially) separate modules diff --git a/drivers/acpi/gsi.c b/drivers/acpi/gsi.c deleted file mode 100644 index ee9e0f27b2bf..000000000000 --- a/drivers/acpi/gsi.c +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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 -#include - -enum acpi_irq_model_id acpi_irq_model; - -static struct fwnode_handle *acpi_gsi_domain_id; - -/** - * 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) -{ - struct irq_domain *d = irq_find_matching_fwnode(acpi_gsi_domain_id, - DOMAIN_BUS_ANY); - - *irq = irq_find_mapping(d, 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) -{ - struct irq_fwspec fwspec; - - if (WARN_ON(!acpi_gsi_domain_id)) { - pr_warn("GSI: No registered irqchip, giving up\n"); - return -EINVAL; - } - - fwspec.fwnode = acpi_gsi_domain_id; - fwspec.param[0] = gsi; - fwspec.param[1] = acpi_dev_get_irq_type(trigger, polarity); - fwspec.param_count = 2; - - return irq_create_fwspec_mapping(&fwspec); -} -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) -{ - struct irq_domain *d = irq_find_matching_fwnode(acpi_gsi_domain_id, - DOMAIN_BUS_ANY); - int irq = irq_find_mapping(d, gsi); - - irq_dispose_mapping(irq); -} -EXPORT_SYMBOL_GPL(acpi_unregister_gsi); - -/** - * acpi_set_irq_model - Setup the GSI irqdomain information - * @model: the value assigned to acpi_irq_model - * @fwnode: the irq_domain identifier for mapping and looking up - * GSI interrupts - */ -void __init acpi_set_irq_model(enum acpi_irq_model_id model, - struct fwnode_handle *fwnode) -{ - acpi_irq_model = model; - acpi_gsi_domain_id = fwnode; -} diff --git a/drivers/acpi/irq.c b/drivers/acpi/irq.c new file mode 100644 index 000000000000..830299a74b84 --- /dev/null +++ b/drivers/acpi/irq.c @@ -0,0 +1,297 @@ +/* + * 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 +#include + +enum acpi_irq_model_id acpi_irq_model; + +static struct fwnode_handle *acpi_gsi_domain_id; + +/** + * 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) +{ + struct irq_domain *d = irq_find_matching_fwnode(acpi_gsi_domain_id, + DOMAIN_BUS_ANY); + + *irq = irq_find_mapping(d, 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) +{ + struct irq_fwspec fwspec; + + if (WARN_ON(!acpi_gsi_domain_id)) { + pr_warn("GSI: No registered irqchip, giving up\n"); + return -EINVAL; + } + + fwspec.fwnode = acpi_gsi_domain_id; + fwspec.param[0] = gsi; + fwspec.param[1] = acpi_dev_get_irq_type(trigger, polarity); + fwspec.param_count = 2; + + return irq_create_fwspec_mapping(&fwspec); +} +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) +{ + struct irq_domain *d = irq_find_matching_fwnode(acpi_gsi_domain_id, + DOMAIN_BUS_ANY); + int irq = irq_find_mapping(d, gsi); + + irq_dispose_mapping(irq); +} +EXPORT_SYMBOL_GPL(acpi_unregister_gsi); + +/** + * acpi_get_irq_source_fwhandle() - Retrieve fwhandle from IRQ resource source. + * @source: acpi_resource_source to use for the lookup. + * + * Description: + * Retrieve the fwhandle of the device referenced by the given IRQ resource + * source. + * + * Return: + * The referenced device fwhandle or NULL on failure + */ +static struct fwnode_handle * +acpi_get_irq_source_fwhandle(const struct acpi_resource_source *source) +{ + struct fwnode_handle *result; + struct acpi_device *device; + acpi_handle handle; + acpi_status status; + + if (!source->string_length) + return acpi_gsi_domain_id; + + status = acpi_get_handle(NULL, source->string_ptr, &handle); + if (WARN_ON(ACPI_FAILURE(status))) + return NULL; + + device = acpi_bus_get_acpi_device(handle); + if (WARN_ON(!device)) + return NULL; + + result = &device->fwnode; + acpi_bus_put_acpi_device(device); + return result; +} + +/* + * Context for the resource walk used to lookup IRQ resources. + * Contains a return code, the lookup index, and references to the flags + * and fwspec where the result is returned. + */ +struct acpi_irq_parse_one_ctx { + int rc; + unsigned int index; + unsigned long *res_flags; + struct irq_fwspec *fwspec; +}; + +/** + * acpi_irq_parse_one_match - Handle a matching IRQ resource. + * @fwnode: matching fwnode + * @hwirq: hardware IRQ number + * @triggering: triggering attributes of hwirq + * @polarity: polarity attributes of hwirq + * @polarity: polarity attributes of hwirq + * @shareable: shareable attributes of hwirq + * @ctx: acpi_irq_parse_one_ctx updated by this function + * + * Description: + * Handle a matching IRQ resource by populating the given ctx with + * the information passed. + */ +static inline void acpi_irq_parse_one_match(struct fwnode_handle *fwnode, + u32 hwirq, u8 triggering, + u8 polarity, u8 shareable, + struct acpi_irq_parse_one_ctx *ctx) +{ + if (!fwnode) + return; + ctx->rc = 0; + *ctx->res_flags = acpi_dev_irq_flags(triggering, polarity, shareable); + ctx->fwspec->fwnode = fwnode; + ctx->fwspec->param[0] = hwirq; + ctx->fwspec->param[1] = acpi_dev_get_irq_type(triggering, polarity); + ctx->fwspec->param_count = 2; +} + +/** + * acpi_irq_parse_one_cb - Handle the given resource. + * @ares: resource to handle + * @context: context for the walk + * + * Description: + * This is called by acpi_walk_resources passing each resource returned by + * the _CRS method. We only inspect IRQ resources. Since IRQ resources + * might contain multiple interrupts we check if the index is within this + * one's interrupt array, otherwise we subtract the current resource IRQ + * count from the lookup index to prepare for the next resource. + * Once a match is found we call acpi_irq_parse_one_match to populate + * the result and end the walk by returning AE_CTRL_TERMINATE. + * + * Return: + * AE_OK if the walk should continue, AE_CTRL_TERMINATE if a matching + * IRQ resource was found. + */ +static acpi_status acpi_irq_parse_one_cb(struct acpi_resource *ares, + void *context) +{ + struct acpi_irq_parse_one_ctx *ctx = context; + struct acpi_resource_irq *irq; + struct acpi_resource_extended_irq *eirq; + struct fwnode_handle *fwnode; + + switch (ares->type) { + case ACPI_RESOURCE_TYPE_IRQ: + irq = &ares->data.irq; + if (ctx->index >= irq->interrupt_count) { + ctx->index -= irq->interrupt_count; + return AE_OK; + } + fwnode = acpi_gsi_domain_id; + acpi_irq_parse_one_match(fwnode, irq->interrupts[ctx->index], + irq->triggering, irq->polarity, + irq->sharable, ctx); + return AE_CTRL_TERMINATE; + case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: + eirq = &ares->data.extended_irq; + if (eirq->producer_consumer == ACPI_PRODUCER) + return AE_OK; + if (ctx->index >= eirq->interrupt_count) { + ctx->index -= eirq->interrupt_count; + return AE_OK; + } + fwnode = acpi_get_irq_source_fwhandle(&eirq->resource_source); + acpi_irq_parse_one_match(fwnode, eirq->interrupts[ctx->index], + eirq->triggering, eirq->polarity, + eirq->sharable, ctx); + return AE_CTRL_TERMINATE; + } + + return AE_OK; +} + +/** + * acpi_irq_parse_one - Resolve an interrupt for a device + * @handle: the device whose interrupt is to be resolved + * @index: index of the interrupt to resolve + * @fwspec: structure irq_fwspec filled by this function + * @flags: resource flags filled by this function + * + * Description: + * Resolves an interrupt for a device by walking its CRS resources to find + * the appropriate ACPI IRQ resource and populating the given struct irq_fwspec + * and flags. + * + * Return: + * The result stored in ctx.rc by the callback, or the default -EINVAL value + * if an error occurs. + */ +static int acpi_irq_parse_one(acpi_handle handle, unsigned int index, + struct irq_fwspec *fwspec, unsigned long *flags) +{ + struct acpi_irq_parse_one_ctx ctx = { -EINVAL, index, flags, fwspec }; + + acpi_walk_resources(handle, METHOD_NAME__CRS, acpi_irq_parse_one_cb, &ctx); + return ctx.rc; +} + +/** + * acpi_irq_get - Lookup an ACPI IRQ resource and use it to initialize resource. + * @handle: ACPI device handle + * @index: ACPI IRQ resource index to lookup + * @res: Linux IRQ resource to initialize + * + * Description: + * Look for the ACPI IRQ resource with the given index and use it to initialize + * the given Linux IRQ resource. + * + * Return: + * 0 on success + * -EINVAL if an error occurs + * -EPROBE_DEFER if the IRQ lookup/conversion failed + */ +int acpi_irq_get(acpi_handle handle, unsigned int index, struct resource *res) +{ + struct irq_fwspec fwspec; + struct irq_domain *domain; + unsigned long flags; + int rc; + + rc = acpi_irq_parse_one(handle, index, &fwspec, &flags); + if (rc) + return rc; + + domain = irq_find_matching_fwnode(fwspec.fwnode, DOMAIN_BUS_ANY); + if (!domain) + return -EPROBE_DEFER; + + rc = irq_create_fwspec_mapping(&fwspec); + if (rc <= 0) + return -EINVAL; + + res->start = rc; + res->end = rc; + res->flags = flags; + + return 0; +} +EXPORT_SYMBOL_GPL(acpi_irq_get); + +/** + * acpi_set_irq_model - Setup the GSI irqdomain information + * @model: the value assigned to acpi_irq_model + * @fwnode: the irq_domain identifier for mapping and looking up + * GSI interrupts + */ +void __init acpi_set_irq_model(enum acpi_irq_model_id model, + struct fwnode_handle *fwnode) +{ + acpi_irq_model = model; + acpi_gsi_domain_id = fwnode; +} diff --git a/drivers/base/platform.c b/drivers/base/platform.c index c4af00385502..647e4761dbf3 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -102,6 +102,16 @@ int platform_get_irq(struct platform_device *dev, unsigned int num) } r = platform_get_resource(dev, IORESOURCE_IRQ, num); + if (has_acpi_companion(&dev->dev)) { + if (r && r->flags & IORESOURCE_DISABLED) { + int ret; + + ret = acpi_irq_get(ACPI_HANDLE(&dev->dev), num, r); + if (ret) + return ret; + } + } + /* * The resources may pass trigger flags to the irqs that need * to be set up. It so happens that the trigger flags for diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 5b36974ed60a..8e577c2cb0ce 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -1153,4 +1153,14 @@ int parse_spcr(bool earlycon); static inline int parse_spcr(bool earlycon) { return 0; } #endif +#if IS_ENABLED(CONFIG_ACPI_GENERIC_GSI) +int acpi_irq_get(acpi_handle handle, unsigned int index, struct resource *res); +#else +static inline +int acpi_irq_get(acpi_handle handle, unsigned int index, struct resource *res) +{ + return -EINVAL; +} +#endif + #endif /*_LINUX_ACPI_H*/ -- cgit v1.2.3 From f20cc9b00c7b71f9b5e970b6bd4ac93b0d9cfd5b Mon Sep 17 00:00:00 2001 From: Agustin Vega-Frias Date: Thu, 2 Feb 2017 18:23:59 -0500 Subject: irqchip/qcom: Add IRQ combiner driver Driver for interrupt combiners in the Top-level Control and Status Registers (TCSR) hardware block in Qualcomm Technologies chips. An interrupt combiner in this block combines a set of interrupts by OR'ing the individual interrupt signals into a summary interrupt signal routed to a parent interrupt controller, and provides read- only, 32-bit registers to query the status of individual interrupts. The status bit for IRQ n is bit (n % 32) within register (n / 32) of the given combiner. Thus, each combiner can be described as a set of register offsets and the number of IRQs managed. Signed-off-by: Agustin Vega-Frias Signed-off-by: Marc Zyngier --- drivers/irqchip/Kconfig | 9 ++ drivers/irqchip/Makefile | 1 + drivers/irqchip/qcom-irq-combiner.c | 296 ++++++++++++++++++++++++++++++++++++ 3 files changed, 306 insertions(+) create mode 100644 drivers/irqchip/qcom-irq-combiner.c diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index ae96731cd2fb..125528f39e92 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -283,3 +283,12 @@ config EZNPS_GIC config STM32_EXTI bool select IRQ_DOMAIN + +config QCOM_IRQ_COMBINER + bool "QCOM IRQ combiner support" + depends on ARCH_QCOM && ACPI + select IRQ_DOMAIN + select IRQ_DOMAIN_HIERARCHY + help + Say yes here to add support for the IRQ combiner devices embedded + in Qualcomm Technologies chips. diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 0e55d94065bf..5a53186351ef 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -75,3 +75,4 @@ obj-$(CONFIG_LS_SCFG_MSI) += irq-ls-scfg-msi.o obj-$(CONFIG_EZNPS_GIC) += irq-eznps.o obj-$(CONFIG_ARCH_ASPEED) += irq-aspeed-vic.o obj-$(CONFIG_STM32_EXTI) += irq-stm32-exti.o +obj-$(CONFIG_QCOM_IRQ_COMBINER) += qcom-irq-combiner.o diff --git a/drivers/irqchip/qcom-irq-combiner.c b/drivers/irqchip/qcom-irq-combiner.c new file mode 100644 index 000000000000..03251da95397 --- /dev/null +++ b/drivers/irqchip/qcom-irq-combiner.c @@ -0,0 +1,296 @@ +/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only 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. + */ + +/* + * Driver for interrupt combiners in the Top-level Control and Status + * Registers (TCSR) hardware block in Qualcomm Technologies chips. + * An interrupt combiner in this block combines a set of interrupts by + * OR'ing the individual interrupt signals into a summary interrupt + * signal routed to a parent interrupt controller, and provides read- + * only, 32-bit registers to query the status of individual interrupts. + * The status bit for IRQ n is bit (n % 32) within register (n / 32) + * of the given combiner. Thus, each combiner can be described as a set + * of register offsets and the number of IRQs managed. + */ + +#define pr_fmt(fmt) "QCOM80B1:" fmt + +#include +#include +#include +#include + +#define REG_SIZE 32 + +struct combiner_reg { + void __iomem *addr; + unsigned long enabled; +}; + +struct combiner { + struct irq_domain *domain; + int parent_irq; + u32 nirqs; + u32 nregs; + struct combiner_reg regs[0]; +}; + +static inline int irq_nr(u32 reg, u32 bit) +{ + return reg * REG_SIZE + bit; +} + +/* + * Handler for the cascaded IRQ. + */ +static void combiner_handle_irq(struct irq_desc *desc) +{ + struct combiner *combiner = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + u32 reg; + + chained_irq_enter(chip, desc); + + for (reg = 0; reg < combiner->nregs; reg++) { + int virq; + int hwirq; + u32 bit; + u32 status; + + bit = readl_relaxed(combiner->regs[reg].addr); + status = bit & combiner->regs[reg].enabled; + if (!status) + pr_warn_ratelimited("Unexpected IRQ on CPU%d: (%08x %08lx %p)\n", + smp_processor_id(), bit, + combiner->regs[reg].enabled, + combiner->regs[reg].addr); + + while (status) { + bit = __ffs(status); + status &= ~(1 << bit); + hwirq = irq_nr(reg, bit); + virq = irq_find_mapping(combiner->domain, hwirq); + if (virq > 0) + generic_handle_irq(virq); + + } + } + + chained_irq_exit(chip, desc); +} + +static void combiner_irq_chip_mask_irq(struct irq_data *data) +{ + struct combiner *combiner = irq_data_get_irq_chip_data(data); + struct combiner_reg *reg = combiner->regs + data->hwirq / REG_SIZE; + + clear_bit(data->hwirq % REG_SIZE, ®->enabled); +} + +static void combiner_irq_chip_unmask_irq(struct irq_data *data) +{ + struct combiner *combiner = irq_data_get_irq_chip_data(data); + struct combiner_reg *reg = combiner->regs + data->hwirq / REG_SIZE; + + set_bit(data->hwirq % REG_SIZE, ®->enabled); +} + +static struct irq_chip irq_chip = { + .irq_mask = combiner_irq_chip_mask_irq, + .irq_unmask = combiner_irq_chip_unmask_irq, + .name = "qcom-irq-combiner" +}; + +static int combiner_irq_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_chip_and_handler(irq, &irq_chip, handle_level_irq); + irq_set_chip_data(irq, domain->host_data); + irq_set_noprobe(irq); + return 0; +} + +static void combiner_irq_unmap(struct irq_domain *domain, unsigned int irq) +{ + irq_domain_reset_irq_data(irq_get_irq_data(irq)); +} + +static int combiner_irq_translate(struct irq_domain *d, struct irq_fwspec *fws, + unsigned long *hwirq, unsigned int *type) +{ + struct combiner *combiner = d->host_data; + + if (is_acpi_node(fws->fwnode)) { + if (WARN_ON((fws->param_count != 2) || + (fws->param[0] >= combiner->nirqs) || + (fws->param[1] & IORESOURCE_IRQ_LOWEDGE) || + (fws->param[1] & IORESOURCE_IRQ_HIGHEDGE))) + return -EINVAL; + + *hwirq = fws->param[0]; + *type = fws->param[1]; + return 0; + } + + return -EINVAL; +} + +static const struct irq_domain_ops domain_ops = { + .map = combiner_irq_map, + .unmap = combiner_irq_unmap, + .translate = combiner_irq_translate +}; + +static acpi_status count_registers_cb(struct acpi_resource *ares, void *context) +{ + int *count = context; + + if (ares->type == ACPI_RESOURCE_TYPE_GENERIC_REGISTER) + ++(*count); + return AE_OK; +} + +static int count_registers(struct platform_device *pdev) +{ + acpi_handle ahandle = ACPI_HANDLE(&pdev->dev); + acpi_status status; + int count = 0; + + if (!acpi_has_method(ahandle, METHOD_NAME__CRS)) + return -EINVAL; + + status = acpi_walk_resources(ahandle, METHOD_NAME__CRS, + count_registers_cb, &count); + if (ACPI_FAILURE(status)) + return -EINVAL; + return count; +} + +struct get_registers_context { + struct device *dev; + struct combiner *combiner; + int err; +}; + +static acpi_status get_registers_cb(struct acpi_resource *ares, void *context) +{ + struct get_registers_context *ctx = context; + struct acpi_resource_generic_register *reg; + phys_addr_t paddr; + void __iomem *vaddr; + + if (ares->type != ACPI_RESOURCE_TYPE_GENERIC_REGISTER) + return AE_OK; + + reg = &ares->data.generic_reg; + paddr = reg->address; + if ((reg->space_id != ACPI_SPACE_MEM) || + (reg->bit_offset != 0) || + (reg->bit_width > REG_SIZE)) { + dev_err(ctx->dev, "Bad register resource @%pa\n", &paddr); + ctx->err = -EINVAL; + return AE_ERROR; + } + + vaddr = devm_ioremap(ctx->dev, reg->address, REG_SIZE); + if (IS_ERR(vaddr)) { + dev_err(ctx->dev, "Can't map register @%pa\n", &paddr); + ctx->err = PTR_ERR(vaddr); + return AE_ERROR; + } + + ctx->combiner->regs[ctx->combiner->nregs].addr = vaddr; + ctx->combiner->nirqs += reg->bit_width; + ctx->combiner->nregs++; + return AE_OK; +} + +static int get_registers(struct platform_device *pdev, struct combiner *comb) +{ + acpi_handle ahandle = ACPI_HANDLE(&pdev->dev); + acpi_status status; + struct get_registers_context ctx; + + if (!acpi_has_method(ahandle, METHOD_NAME__CRS)) + return -EINVAL; + + ctx.dev = &pdev->dev; + ctx.combiner = comb; + ctx.err = 0; + + status = acpi_walk_resources(ahandle, METHOD_NAME__CRS, + get_registers_cb, &ctx); + if (ACPI_FAILURE(status)) + return ctx.err; + return 0; +} + +static int __init combiner_probe(struct platform_device *pdev) +{ + struct combiner *combiner; + size_t alloc_sz; + u32 nregs; + int err; + + nregs = count_registers(pdev); + if (nregs <= 0) { + dev_err(&pdev->dev, "Error reading register resources\n"); + return -EINVAL; + } + + alloc_sz = sizeof(*combiner) + sizeof(struct combiner_reg) * nregs; + combiner = devm_kzalloc(&pdev->dev, alloc_sz, GFP_KERNEL); + if (!combiner) + return -ENOMEM; + + err = get_registers(pdev, combiner); + if (err < 0) + return err; + + combiner->parent_irq = platform_get_irq(pdev, 0); + if (combiner->parent_irq <= 0) { + dev_err(&pdev->dev, "Error getting IRQ resource\n"); + return -EPROBE_DEFER; + } + + combiner->domain = irq_domain_create_linear(pdev->dev.fwnode, combiner->nirqs, + &domain_ops, combiner); + if (!combiner->domain) + /* Errors printed by irq_domain_create_linear */ + return -ENODEV; + + irq_set_chained_handler_and_data(combiner->parent_irq, + combiner_handle_irq, combiner); + + dev_info(&pdev->dev, "Initialized with [p=%d,n=%d,r=%p]\n", + combiner->parent_irq, combiner->nirqs, combiner->regs[0].addr); + return 0; +} + +static const struct acpi_device_id qcom_irq_combiner_ids[] = { + { "QCOM80B1", }, + { } +}; + +static struct platform_driver qcom_irq_combiner_probe = { + .driver = { + .name = "qcom-irq-combiner", + .acpi_match_table = ACPI_PTR(qcom_irq_combiner_ids), + }, + .probe = combiner_probe, +}; + +static int __init register_qcom_irq_combiner(void) +{ + return platform_driver_register(&qcom_irq_combiner_probe); +} +device_initcall(register_qcom_irq_combiner); -- cgit v1.2.3 From 2fd632a00383e812db7186f903ebed808d9951bd Mon Sep 17 00:00:00 2001 From: Shanker Donthineni Date: Wed, 25 Jan 2017 21:51:41 -0600 Subject: irqchip/gic-v3-its: Enable cacheable attribute Read-allocate hints Read-allocation hints are not enabled for both the GIC-ITS and GICR tables. This forces the hardware to always read the table contents from an external memory (DDR) which is slow compared to cache memory. Most of the tables are often read by hardware. So, it's better to enable Read-allocate hints in addition to Write-allocate hints in order to improve the GICR_PEND, GICR_PROP, Collection, Device, and vCPU tables lookup time. Signed-off-by: Shanker Donthineni Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v3-its.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 69b040f47d56..3cffccad62e9 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -960,7 +960,7 @@ static bool its_parse_baser_device(struct its_node *its, struct its_baser *baser u32 psz, u32 *order) { u64 esz = GITS_BASER_ENTRY_SIZE(its_read_baser(its, baser)); - u64 val = GITS_BASER_InnerShareable | GITS_BASER_WaWb; + u64 val = GITS_BASER_InnerShareable | GITS_BASER_RaWaWb; u32 ids = its->device_ids; u32 new_order = *order; bool indirect = false; @@ -1025,7 +1025,7 @@ static int its_alloc_tables(struct its_node *its) u64 typer = gic_read_typer(its->base + GITS_TYPER); u32 ids = GITS_TYPER_DEVBITS(typer); u64 shr = GITS_BASER_InnerShareable; - u64 cache = GITS_BASER_WaWb; + u64 cache = GITS_BASER_RaWaWb; u32 psz = SZ_64K; int err, i; @@ -1122,7 +1122,7 @@ static void its_cpu_init_lpis(void) /* set PROPBASE */ val = (page_to_phys(gic_rdists->prop_page) | GICR_PROPBASER_InnerShareable | - GICR_PROPBASER_WaWb | + GICR_PROPBASER_RaWaWb | ((LPI_NRBITS - 1) & GICR_PROPBASER_IDBITS_MASK)); gicr_write_propbaser(val, rbase + GICR_PROPBASER); @@ -1147,7 +1147,7 @@ static void its_cpu_init_lpis(void) /* set PENDBASE */ val = (page_to_phys(pend_page) | GICR_PENDBASER_InnerShareable | - GICR_PENDBASER_WaWb); + GICR_PENDBASER_RaWaWb); gicr_write_pendbaser(val, rbase + GICR_PENDBASER); tmp = gicr_read_pendbaser(rbase + GICR_PENDBASER); @@ -1711,7 +1711,7 @@ static int __init its_probe_one(struct resource *res, goto out_free_tables; baser = (virt_to_phys(its->cmd_base) | - GITS_CBASER_WaWb | + GITS_CBASER_RaWaWb | GITS_CBASER_InnerShareable | (ITS_CMD_QUEUE_SZ / SZ_4K - 1) | GITS_CBASER_VALID); -- cgit v1.2.3 From 4d36f136d57aea6f6440886106e246bb7e5918d8 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 19 Dec 2016 17:11:52 +0000 Subject: irqchip/gic-v3-its: Refactor command encoding The way we encode the various ITS command fields is both tedious and error prone. Let's introduce a helper function that performs the encoding, and convert the existing encoders to use that helper. It also has the advantage of expressing the encoding in a way that matches the architecture specification. Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v3-its.c | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 3cffccad62e9..4e01103c6a06 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -193,58 +193,56 @@ struct its_cmd_block { typedef struct its_collection *(*its_cmd_builder_t)(struct its_cmd_block *, struct its_cmd_desc *); +static void its_mask_encode(u64 *raw_cmd, u64 val, int h, int l) +{ + u64 mask = GENMASK_ULL(h, l); + *raw_cmd &= ~mask; + *raw_cmd |= (val << l) & mask; +} + static void its_encode_cmd(struct its_cmd_block *cmd, u8 cmd_nr) { - cmd->raw_cmd[0] &= ~0xffULL; - cmd->raw_cmd[0] |= cmd_nr; + its_mask_encode(&cmd->raw_cmd[0], cmd_nr, 7, 0); } static void its_encode_devid(struct its_cmd_block *cmd, u32 devid) { - cmd->raw_cmd[0] &= BIT_ULL(32) - 1; - cmd->raw_cmd[0] |= ((u64)devid) << 32; + its_mask_encode(&cmd->raw_cmd[0], devid, 63, 32); } static void its_encode_event_id(struct its_cmd_block *cmd, u32 id) { - cmd->raw_cmd[1] &= ~0xffffffffULL; - cmd->raw_cmd[1] |= id; + its_mask_encode(&cmd->raw_cmd[1], id, 31, 0); } static void its_encode_phys_id(struct its_cmd_block *cmd, u32 phys_id) { - cmd->raw_cmd[1] &= 0xffffffffULL; - cmd->raw_cmd[1] |= ((u64)phys_id) << 32; + its_mask_encode(&cmd->raw_cmd[1], phys_id, 63, 32); } static void its_encode_size(struct its_cmd_block *cmd, u8 size) { - cmd->raw_cmd[1] &= ~0x1fULL; - cmd->raw_cmd[1] |= size & 0x1f; + its_mask_encode(&cmd->raw_cmd[1], size, 4, 0); } static void its_encode_itt(struct its_cmd_block *cmd, u64 itt_addr) { - cmd->raw_cmd[2] &= ~0xffffffffffffULL; - cmd->raw_cmd[2] |= itt_addr & 0xffffffffff00ULL; + its_mask_encode(&cmd->raw_cmd[2], itt_addr >> 8, 50, 8); } static void its_encode_valid(struct its_cmd_block *cmd, int valid) { - cmd->raw_cmd[2] &= ~(1ULL << 63); - cmd->raw_cmd[2] |= ((u64)!!valid) << 63; + its_mask_encode(&cmd->raw_cmd[2], !!valid, 63, 63); } static void its_encode_target(struct its_cmd_block *cmd, u64 target_addr) { - cmd->raw_cmd[2] &= ~(0xffffffffULL << 16); - cmd->raw_cmd[2] |= (target_addr & (0xffffffffULL << 16)); + its_mask_encode(&cmd->raw_cmd[2], target_addr >> 16, 50, 16); } static void its_encode_collection(struct its_cmd_block *cmd, u16 col) { - cmd->raw_cmd[2] &= ~0xffffULL; - cmd->raw_cmd[2] |= col; + its_mask_encode(&cmd->raw_cmd[2], col, 15, 0); } static inline void its_fixup_cmd(struct its_cmd_block *cmd) -- cgit v1.2.3 From 4f46de9d2eda184b0e49e32bb2a92c6a06b336f5 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 20 Dec 2016 15:50:14 +0000 Subject: irqchip/gic-v3-its: Drop deprecated GITS_BASER_TYPE_CPU During the development of the GICv3/v4 architecture, it was envisaged to have a CPU table, though the use for it was never completely clear (the collection table serves that role pretty well). It ended being dropped before the specification was published, though it lived on in the driver. In order to avoid people scratching their head too much, let's do the same in the kernel. Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v3-its.c | 2 +- include/linux/irqchip/arm-gic-v3.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 4e01103c6a06..fff024b89a2a 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -822,7 +822,7 @@ static int __init its_alloc_lpi_tables(void) static const char *its_base_type_string[] = { [GITS_BASER_TYPE_DEVICE] = "Devices", [GITS_BASER_TYPE_VCPU] = "Virtual CPUs", - [GITS_BASER_TYPE_CPU] = "Physical CPUs", + [GITS_BASER_TYPE_RESERVED3] = "Reserved (3)", [GITS_BASER_TYPE_COLLECTION] = "Interrupt Collections", [GITS_BASER_TYPE_RESERVED5] = "Reserved (5)", [GITS_BASER_TYPE_RESERVED6] = "Reserved (6)", diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h index e808f8ae6f14..b6e7629f8bb0 100644 --- a/include/linux/irqchip/arm-gic-v3.h +++ b/include/linux/irqchip/arm-gic-v3.h @@ -306,7 +306,7 @@ #define GITS_BASER_TYPE_NONE 0 #define GITS_BASER_TYPE_DEVICE 1 #define GITS_BASER_TYPE_VCPU 2 -#define GITS_BASER_TYPE_CPU 3 +#define GITS_BASER_TYPE_RESERVED3 3 #define GITS_BASER_TYPE_COLLECTION 4 #define GITS_BASER_TYPE_RESERVED5 5 #define GITS_BASER_TYPE_RESERVED6 6 -- cgit v1.2.3 From 6a25ad3a9f9832f24df7987227122b8359f05c8e Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 20 Dec 2016 15:52:26 +0000 Subject: irqchip/gic-v3-its: Rename MAPVI to MAPTI Back in the days when the GICv3/v4 architecture was drafted, the command to an event to an LPI number was called MAPVI. Later on, and to avoid confusion with the GICv4 command VMAPI, it was renamed MAPTI. We've carried the old name for a long time, but it gets in the way of people reading the code in the light of the public architecture specification. Just repaint all the references and kill the old definition. Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v3-its.c | 28 ++++++++++++++-------------- include/linux/irqchip/arm-gic-v3.h | 2 -- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index fff024b89a2a..fcbba10c0ed9 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -161,7 +161,7 @@ struct its_cmd_desc { struct its_device *dev; u32 phys_id; u32 event_id; - } its_mapvi_cmd; + } its_mapti_cmd; struct { struct its_device *dev; @@ -287,18 +287,18 @@ static struct its_collection *its_build_mapc_cmd(struct its_cmd_block *cmd, return desc->its_mapc_cmd.col; } -static struct its_collection *its_build_mapvi_cmd(struct its_cmd_block *cmd, +static struct its_collection *its_build_mapti_cmd(struct its_cmd_block *cmd, struct its_cmd_desc *desc) { struct its_collection *col; - col = dev_event_to_col(desc->its_mapvi_cmd.dev, - desc->its_mapvi_cmd.event_id); + col = dev_event_to_col(desc->its_mapti_cmd.dev, + desc->its_mapti_cmd.event_id); - its_encode_cmd(cmd, GITS_CMD_MAPVI); - its_encode_devid(cmd, desc->its_mapvi_cmd.dev->device_id); - its_encode_event_id(cmd, desc->its_mapvi_cmd.event_id); - its_encode_phys_id(cmd, desc->its_mapvi_cmd.phys_id); + its_encode_cmd(cmd, GITS_CMD_MAPTI); + its_encode_devid(cmd, desc->its_mapti_cmd.dev->device_id); + its_encode_event_id(cmd, desc->its_mapti_cmd.event_id); + its_encode_phys_id(cmd, desc->its_mapti_cmd.phys_id); its_encode_collection(cmd, col->col_id); its_fixup_cmd(cmd); @@ -529,15 +529,15 @@ static void its_send_mapc(struct its_node *its, struct its_collection *col, its_send_single_command(its, its_build_mapc_cmd, &desc); } -static void its_send_mapvi(struct its_device *dev, u32 irq_id, u32 id) +static void its_send_mapti(struct its_device *dev, u32 irq_id, u32 id) { struct its_cmd_desc desc; - desc.its_mapvi_cmd.dev = dev; - desc.its_mapvi_cmd.phys_id = irq_id; - desc.its_mapvi_cmd.event_id = id; + desc.its_mapti_cmd.dev = dev; + desc.its_mapti_cmd.phys_id = irq_id; + desc.its_mapti_cmd.event_id = id; - its_send_single_command(dev->its, its_build_mapvi_cmd, &desc); + its_send_single_command(dev->its, its_build_mapti_cmd, &desc); } static void its_send_movi(struct its_device *dev, @@ -1496,7 +1496,7 @@ static void its_irq_domain_activate(struct irq_domain *domain, its_dev->event_map.col_map[event] = cpumask_first(cpu_mask); /* Map the GIC IRQ and event to the device */ - its_send_mapvi(its_dev, d->hwirq, event); + its_send_mapti(its_dev, d->hwirq, event); } static void its_irq_domain_deactivate(struct irq_domain *domain, diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h index b6e7629f8bb0..400ed7ad2bff 100644 --- a/include/linux/irqchip/arm-gic-v3.h +++ b/include/linux/irqchip/arm-gic-v3.h @@ -320,8 +320,6 @@ #define GITS_CMD_MAPD 0x08 #define GITS_CMD_MAPC 0x09 #define GITS_CMD_MAPTI 0x0a -/* older GIC documentation used MAPVI for this command */ -#define GITS_CMD_MAPVI GITS_CMD_MAPTI #define GITS_CMD_MAPI 0x0b #define GITS_CMD_MOVI 0x01 #define GITS_CMD_DISCARD 0x0f -- cgit v1.2.3 From e3c484b183d3bbef0f0df527e10359163f0bb1db Mon Sep 17 00:00:00 2001 From: Alim Akhtar Date: Wed, 4 Jan 2017 13:54:20 +0530 Subject: irqchip/gic-v3: Remove duplicate definition of GICD_TYPER_LPIS GICD_TYPER_LPIS macro is defined twice in this file. This patch removes the duplicate entry. Fixes: f5c1434c217f ("irqchip: GICv3: rework redistributor structure") Signed-off-by: Alim Akhtar Signed-off-by: Marc Zyngier --- include/linux/irqchip/arm-gic-v3.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h index 400ed7ad2bff..725e86b506f3 100644 --- a/include/linux/irqchip/arm-gic-v3.h +++ b/include/linux/irqchip/arm-gic-v3.h @@ -73,7 +73,6 @@ #define GICD_TYPER_ID_BITS(typer) ((((typer) >> 19) & 0x1f) + 1) #define GICD_TYPER_IRQS(typer) ((((typer) & 0x1f) + 1) * 32) -#define GICD_TYPER_LPIS (1U << 17) #define GICD_IROUTER_SPI_MODE_ONE (0U << 31) #define GICD_IROUTER_SPI_MODE_ANY (1U << 31) -- cgit v1.2.3 From f59098c3ee775c874028f0ade4360cdf3e3b6810 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 30 Jan 2017 20:28:38 +0100 Subject: irqchip: DT bindings for Cortina Gemini irqchip This adds device tree bindings for the Cortina Gemini interrupt controller. They are pretty standard. Cc: Janos Laube Cc: Paulius Zaleckas Cc: Hans Ulli Kroll Cc: Florian Fainelli Cc: devicetree@vger.kernel.org Acked-by: Rob Herring Signed-off-by: Linus Walleij Signed-off-by: Marc Zyngier --- .../cortina,gemini-interrupt-controller.txt | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 Documentation/devicetree/bindings/interrupt-controller/cortina,gemini-interrupt-controller.txt diff --git a/Documentation/devicetree/bindings/interrupt-controller/cortina,gemini-interrupt-controller.txt b/Documentation/devicetree/bindings/interrupt-controller/cortina,gemini-interrupt-controller.txt new file mode 100644 index 000000000000..97c1167fa533 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/cortina,gemini-interrupt-controller.txt @@ -0,0 +1,22 @@ +* Cortina Systems Gemini interrupt controller + +This interrupt controller is found on the Gemini SoCs. + +Required properties: +- compatible: must be "cortina,gemini-interrupt-controller" +- reg: The register bank for the interrupt controller. +- interrupt-controller: Identifies the node as an interrupt controller +- #interrupt-cells: The number of cells to define the interrupts. + Must be 2 as the controller can specify level or rising edge + IRQs. The bindings follows the standard binding for controllers + with two cells specified in + interrupt-controller/interrupts.txt + +Example: + +interrupt-controller@48000000 { + compatible = "cortina,gemini-interrupt-controller"; + reg = <0x48000000 0x1000>; + interrupt-controller; + #interrupt-cells = <2>; +}; -- cgit v1.2.3 From b4d3053c8ce9c71266f9da71c8ece79c0f8ebc23 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 30 Jan 2017 20:28:49 +0100 Subject: irqchip: Add a driver for Cortina Gemini As a part of transitioning the Gemini platform to device tree we create this clean, device-tree-only irqchip driver. Cc: Janos Laube Cc: Paulius Zaleckas Cc: Hans Ulli Kroll Cc: Florian Fainelli Signed-off-by: Linus Walleij Signed-off-by: Marc Zyngier --- drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-gemini.c | 185 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 186 insertions(+) create mode 100644 drivers/irqchip/irq-gemini.c diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 5a53186351ef..152bc40b6762 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_ATH79) += irq-ath79-misc.o obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2836.o obj-$(CONFIG_ARCH_EXYNOS) += exynos-combiner.o +obj-$(CONFIG_ARCH_GEMINI) += irq-gemini.o obj-$(CONFIG_ARCH_HIP04) += irq-hip04.o obj-$(CONFIG_ARCH_LPC32XX) += irq-lpc32xx.o obj-$(CONFIG_ARCH_MMP) += irq-mmp.o diff --git a/drivers/irqchip/irq-gemini.c b/drivers/irqchip/irq-gemini.c new file mode 100644 index 000000000000..495224c743ee --- /dev/null +++ b/drivers/irqchip/irq-gemini.c @@ -0,0 +1,185 @@ +/* + * irqchip for the Cortina Systems Gemini Copyright (C) 2017 Linus + * Walleij + * + * Based on arch/arm/mach-gemini/irq.c + * Copyright (C) 2001-2006 Storlink, Corp. + * Copyright (C) 2008-2009 Paulius Zaleckas + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define GEMINI_NUM_IRQS 32 + +#define GEMINI_IRQ_SOURCE(base_addr) (base_addr + 0x00) +#define GEMINI_IRQ_MASK(base_addr) (base_addr + 0x04) +#define GEMINI_IRQ_CLEAR(base_addr) (base_addr + 0x08) +#define GEMINI_IRQ_MODE(base_addr) (base_addr + 0x0C) +#define GEMINI_IRQ_POLARITY(base_addr) (base_addr + 0x10) +#define GEMINI_IRQ_STATUS(base_addr) (base_addr + 0x14) +#define GEMINI_FIQ_SOURCE(base_addr) (base_addr + 0x20) +#define GEMINI_FIQ_MASK(base_addr) (base_addr + 0x24) +#define GEMINI_FIQ_CLEAR(base_addr) (base_addr + 0x28) +#define GEMINI_FIQ_MODE(base_addr) (base_addr + 0x2C) +#define GEMINI_FIQ_POLARITY(base_addr) (base_addr + 0x30) +#define GEMINI_FIQ_STATUS(base_addr) (base_addr + 0x34) + +/** + * struct gemini_irq_data - irq data container for the Gemini IRQ controller + * @base: memory offset in virtual memory + * @chip: chip container for this instance + * @domain: IRQ domain for this instance + */ +struct gemini_irq_data { + void __iomem *base; + struct irq_chip chip; + struct irq_domain *domain; +}; + +static void gemini_irq_mask(struct irq_data *d) +{ + struct gemini_irq_data *g = irq_data_get_irq_chip_data(d); + unsigned int mask; + + mask = readl(GEMINI_IRQ_MASK(g->base)); + mask &= ~BIT(irqd_to_hwirq(d)); + writel(mask, GEMINI_IRQ_MASK(g->base)); +} + +static void gemini_irq_unmask(struct irq_data *d) +{ + struct gemini_irq_data *g = irq_data_get_irq_chip_data(d); + unsigned int mask; + + mask = readl(GEMINI_IRQ_MASK(g->base)); + mask |= BIT(irqd_to_hwirq(d)); + writel(mask, GEMINI_IRQ_MASK(g->base)); +} + +static void gemini_irq_ack(struct irq_data *d) +{ + struct gemini_irq_data *g = irq_data_get_irq_chip_data(d); + + writel(BIT(irqd_to_hwirq(d)), GEMINI_IRQ_CLEAR(g->base)); +} + +static int gemini_irq_set_type(struct irq_data *d, unsigned int trigger) +{ + struct gemini_irq_data *g = irq_data_get_irq_chip_data(d); + int offset = irqd_to_hwirq(d); + u32 mode, polarity; + + mode = readl(GEMINI_IRQ_MODE(g->base)); + polarity = readl(GEMINI_IRQ_POLARITY(g->base)); + + if (trigger & (IRQ_TYPE_LEVEL_HIGH)) { + irq_set_handler_locked(d, handle_level_irq); + /* Disable edge detection */ + mode &= ~BIT(offset); + polarity &= ~BIT(offset); + } else if (trigger & IRQ_TYPE_EDGE_RISING) { + irq_set_handler_locked(d, handle_edge_irq); + mode |= BIT(offset); + polarity |= BIT(offset); + } else if (trigger & IRQ_TYPE_EDGE_FALLING) { + irq_set_handler_locked(d, handle_edge_irq); + mode |= BIT(offset); + polarity &= ~BIT(offset); + } else { + irq_set_handler_locked(d, handle_bad_irq); + pr_warn("GEMINI IRQ: no supported trigger selected for line %d\n", + offset); + } + + writel(mode, GEMINI_IRQ_MODE(g->base)); + writel(polarity, GEMINI_IRQ_POLARITY(g->base)); + + return 0; +} + +static struct irq_chip gemini_irq_chip = { + .name = "GEMINI", + .irq_ack = gemini_irq_ack, + .irq_mask = gemini_irq_mask, + .irq_unmask = gemini_irq_unmask, + .irq_set_type = gemini_irq_set_type, +}; + +/* Local static for the IRQ entry call */ +static struct gemini_irq_data girq; + +asmlinkage void __exception_irq_entry gemini_irqchip_handle_irq(struct pt_regs *regs) +{ + struct gemini_irq_data *g = &girq; + int irq; + u32 status; + + while ((status = readl(GEMINI_IRQ_STATUS(g->base)))) { + irq = ffs(status) - 1; + handle_domain_irq(g->domain, irq, regs); + } +} + +static int gemini_irqdomain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) +{ + struct gemini_irq_data *g = d->host_data; + + irq_set_chip_data(irq, g); + /* All IRQs should set up their type, flags as bad by default */ + irq_set_chip_and_handler(irq, &gemini_irq_chip, handle_bad_irq); + irq_set_probe(irq); + + return 0; +} + +static void gemini_irqdomain_unmap(struct irq_domain *d, unsigned int irq) +{ + irq_set_chip_and_handler(irq, NULL, NULL); + irq_set_chip_data(irq, NULL); +} + +static const struct irq_domain_ops gemini_irqdomain_ops = { + .map = gemini_irqdomain_map, + .unmap = gemini_irqdomain_unmap, + .xlate = irq_domain_xlate_onetwocell, +}; + +int __init gemini_of_init_irq(struct device_node *node, + struct device_node *parent) +{ + struct gemini_irq_data *g = &girq; + + /* + * Disable the idle handler by default since it is buggy + * For more info see arch/arm/mach-gemini/idle.c + */ + cpu_idle_poll_ctrl(true); + + g->base = of_iomap(node, 0); + WARN(!g->base, "unable to map gemini irq registers\n"); + + /* Disable all interrupts */ + writel(0, GEMINI_IRQ_MASK(g->base)); + writel(0, GEMINI_FIQ_MASK(g->base)); + + g->domain = irq_domain_add_simple(node, GEMINI_NUM_IRQS, 0, + &gemini_irqdomain_ops, g); + set_handle_irq(gemini_irqchip_handle_irq); + + return 0; +} +IRQCHIP_DECLARE(gemini, "cortina,gemini-interrupt-controller", + gemini_of_init_irq); -- cgit v1.2.3 From 4cfffcfa5106492f5785924ce2e9af49f075999b Mon Sep 17 00:00:00 2001 From: Marcin Nowakowski Date: Wed, 25 Jan 2017 15:08:25 +0100 Subject: irqchip/mips-gic: Fix local interrupts Some local interrupts are not initialised properly at the moment and cannot be used since the domain's alloc method is never called for them. This has been observed earlier and partially fixed in commit e875bd66dfb ("irqchip/mips-gic: Fix local interrupts"), but that change still relied on the interrupt to be requested by an external driver (eg. drivers/clocksource/mips-gic-timer.c). This does however not solve the issue for interrupts that are not referenced by any driver through the device tree and results in request_irq() calls returning -ENOSYS. It can be observed when attempting to use perf tool to access hardware performance counters. Fix this by explicitly calling irq_create_fwspec_mapping() for local interrupts. Fixes: e875bd66dfb ("irqchip/mips-gic: Fix local interrupts") Signed-off-by: Marcin Nowakowski Cc: Paul Burton Cc: Thomas Gleixner Cc: Jason Cooper Cc: Marc Zyngier Cc: linux-mips@linux-mips.org Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-mips-gic.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c index c01c09e9916d..11d12bccc4e7 100644 --- a/drivers/irqchip/irq-mips-gic.c +++ b/drivers/irqchip/irq-mips-gic.c @@ -968,6 +968,34 @@ static struct irq_domain_ops gic_ipi_domain_ops = { .match = gic_ipi_domain_match, }; +static void __init gic_map_single_int(struct device_node *node, + unsigned int irq) +{ + unsigned int linux_irq; + struct irq_fwspec local_int_fwspec = { + .fwnode = &node->fwnode, + .param_count = 3, + .param = { + [0] = GIC_LOCAL, + [1] = irq, + [2] = IRQ_TYPE_NONE, + }, + }; + + if (!gic_local_irq_is_routable(irq)) + return; + + linux_irq = irq_create_fwspec_mapping(&local_int_fwspec); + WARN_ON(!linux_irq); +} + +static void __init gic_map_interrupts(struct device_node *node) +{ + gic_map_single_int(node, GIC_LOCAL_INT_TIMER); + gic_map_single_int(node, GIC_LOCAL_INT_PERFCTR); + gic_map_single_int(node, GIC_LOCAL_INT_FDC); +} + static void __init __gic_init(unsigned long gic_base_addr, unsigned long gic_addrspace_size, unsigned int cpu_vec, unsigned int irqbase, @@ -1067,6 +1095,7 @@ static void __init __gic_init(unsigned long gic_base_addr, } gic_basic_init(); + gic_map_interrupts(node); } void __init gic_init(unsigned long gic_base_addr, -- cgit v1.2.3 From 5bc13c2cbeec776a2b724cb51bf11fbc30c23318 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Wed, 1 Feb 2017 18:38:25 +0100 Subject: irqchip/gic-v3-its: Fix command buffer allocation The its command buffer must be page aligned, but kzalloc() is not guaranteed to be (though it is mostly when allocating 64k). Use __get_free_pages() as this is used for other buffers as well. Signed-off-by: Robert Richter [Marc: fixed the error path] Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v3-its.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index fcbba10c0ed9..ee928de49737 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -1691,7 +1691,8 @@ static int __init its_probe_one(struct resource *res, its->ite_size = ((gic_read_typer(its_base + GITS_TYPER) >> 4) & 0xf) + 1; its->numa_node = numa_node; - its->cmd_base = kzalloc(ITS_CMD_QUEUE_SZ, GFP_KERNEL); + its->cmd_base = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, + get_order(ITS_CMD_QUEUE_SZ)); if (!its->cmd_base) { err = -ENOMEM; goto out_free_its; @@ -1749,7 +1750,7 @@ static int __init its_probe_one(struct resource *res, out_free_tables: its_free_tables(its); out_free_cmd: - kfree(its->cmd_base); + free_pages((unsigned long)its->cmd_base, get_order(ITS_CMD_QUEUE_SZ)); out_free_its: kfree(its); out_unmap: -- cgit v1.2.3 From 34d677a90f0682a26327dbefc0db1cf81647e7bc Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 19 Dec 2016 17:16:45 +0000 Subject: irqchip/gic-v3-its: Zero command on allocation When reusing commands from the ring buffer, it would be better to zero them out, even if the ITS should ignore the unused fields. Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v3-its.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index ee928de49737..4a895c6d6805 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -411,6 +411,12 @@ static struct its_cmd_block *its_allocate_entry(struct its_node *its) if (its->cmd_write == (its->cmd_base + ITS_CMD_QUEUE_NR_ENTRIES)) its->cmd_write = its->cmd_base; + /* Clear command */ + cmd->raw_cmd[0] = 0; + cmd->raw_cmd[1] = 0; + cmd->raw_cmd[2] = 0; + cmd->raw_cmd[3] = 0; + return cmd; } -- cgit v1.2.3 From 2b5e77308f3356faad640b24af6dc5aa7233eb2d Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 10 Feb 2017 13:23:23 +0100 Subject: irqdesc: Add a resource managed version of irq_alloc_descs() Add a devres flavor of __devm_irq_alloc_descs() and corresponding helper macros. Signed-off-by: Bartosz Golaszewski Cc: Marc Zyngier Cc: linux-doc@vger.kernel.org Cc: Jonathan Corbet Link: http://lkml.kernel.org/r/1486729403-21132-1-git-send-email-bgolaszewski@baylibre.com Signed-off-by: Thomas Gleixner --- Documentation/driver-model/devres.txt | 5 ++++ include/linux/irq.h | 19 ++++++++++++ kernel/irq/devres.c | 55 +++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+) diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index ca9d1eb46bc0..bf34d5b3a733 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -306,6 +306,11 @@ IRQ devm_request_any_context_irq() devm_request_irq() devm_request_threaded_irq() + devm_irq_alloc_descs() + devm_irq_alloc_desc() + devm_irq_alloc_desc_at() + devm_irq_alloc_desc_from() + devm_irq_alloc_descs_from() LED devm_led_classdev_register() diff --git a/include/linux/irq.h b/include/linux/irq.h index e79875574b39..d915caecafa1 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -715,6 +715,10 @@ unsigned int arch_dynirq_lower_bound(unsigned int from); int __irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node, struct module *owner, const struct cpumask *affinity); +int __devm_irq_alloc_descs(struct device *dev, int irq, unsigned int from, + unsigned int cnt, int node, struct module *owner, + const struct cpumask *affinity); + /* use macros to avoid needing export.h for THIS_MODULE */ #define irq_alloc_descs(irq, from, cnt, node) \ __irq_alloc_descs(irq, from, cnt, node, THIS_MODULE, NULL) @@ -731,6 +735,21 @@ int __irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node, #define irq_alloc_descs_from(from, cnt, node) \ irq_alloc_descs(-1, from, cnt, node) +#define devm_irq_alloc_descs(dev, irq, from, cnt, node) \ + __devm_irq_alloc_descs(dev, irq, from, cnt, node, THIS_MODULE, NULL) + +#define devm_irq_alloc_desc(dev, node) \ + devm_irq_alloc_descs(dev, -1, 0, 1, node) + +#define devm_irq_alloc_desc_at(dev, at, node) \ + devm_irq_alloc_descs(dev, at, at, 1, node) + +#define devm_irq_alloc_desc_from(dev, from, node) \ + devm_irq_alloc_descs(dev, -1, from, 1, node) + +#define devm_irq_alloc_descs_from(dev, from, cnt, node) \ + devm_irq_alloc_descs(dev, -1, from, cnt, node) + void irq_free_descs(unsigned int irq, unsigned int cnt); static inline void irq_free_desc(unsigned int irq) { diff --git a/kernel/irq/devres.c b/kernel/irq/devres.c index 74d90a754268..18babef39361 100644 --- a/kernel/irq/devres.c +++ b/kernel/irq/devres.c @@ -2,6 +2,7 @@ #include #include #include +#include /* * Device resource management aware IRQ request/free implementation. @@ -137,3 +138,57 @@ void devm_free_irq(struct device *dev, unsigned int irq, void *dev_id) free_irq(irq, dev_id); } EXPORT_SYMBOL(devm_free_irq); + +struct irq_desc_devres { + unsigned int from; + unsigned int cnt; +}; + +static void devm_irq_desc_release(struct device *dev, void *res) +{ + struct irq_desc_devres *this = res; + + irq_free_descs(this->from, this->cnt); +} + +/** + * __devm_irq_alloc_descs - Allocate and initialize a range of irq descriptors + * for a managed device + * @dev: Device to allocate the descriptors for + * @irq: Allocate for specific irq number if irq >= 0 + * @from: Start the search from this irq number + * @cnt: Number of consecutive irqs to allocate + * @node: Preferred node on which the irq descriptor should be allocated + * @owner: Owning module (can be NULL) + * @affinity: Optional pointer to an affinity mask array of size @cnt + * which hints where the irq descriptors should be allocated + * and which default affinities to use + * + * Returns the first irq number or error code. + * + * Note: Use the provided wrappers (devm_irq_alloc_desc*) for simplicity. + */ +int __devm_irq_alloc_descs(struct device *dev, int irq, unsigned int from, + unsigned int cnt, int node, struct module *owner, + const struct cpumask *affinity) +{ + struct irq_desc_devres *dr; + int base; + + dr = devres_alloc(devm_irq_desc_release, sizeof(*dr), GFP_KERNEL); + if (!dr) + return -ENOMEM; + + base = __irq_alloc_descs(irq, from, cnt, node, owner, affinity); + if (base < 0) { + devres_free(dr); + return base; + } + + dr->from = base; + dr->cnt = cnt; + devres_add(dev, dr); + + return base; +} +EXPORT_SYMBOL_GPL(__devm_irq_alloc_descs); -- cgit v1.2.3 From f435da416beaacc8934fc21820d9488269b39c98 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 10 Feb 2017 09:54:16 -0700 Subject: genirq: Fix /proc/interrupts output alignment If the irq_desc being output does not have a domain associated the information following the 'name' is not aligned correctly. Signed-off-by: H Hartley Sweeten Link: http://lkml.kernel.org/r/20170210165416.5629-1-hsweeten@visionengravers.com Signed-off-by: Thomas Gleixner --- kernel/irq/proc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/irq/proc.c b/kernel/irq/proc.c index feaa813b84a9..c53edad7b459 100644 --- a/kernel/irq/proc.c +++ b/kernel/irq/proc.c @@ -487,6 +487,8 @@ int show_interrupts(struct seq_file *p, void *v) } if (desc->irq_data.domain) seq_printf(p, " %*d", prec, (int) desc->irq_data.hwirq); + else + seq_printf(p, " %*s", prec, ""); #ifdef CONFIG_GENERIC_IRQ_SHOW_LEVEL seq_printf(p, " %-8s", irqd_is_level_type(&desc->irq_data) ? "Level" : "Edge"); #endif -- cgit v1.2.3 From 899b5fbf9d3fcb721690b4d58cf58cc018517003 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 12 Feb 2017 16:31:44 +0100 Subject: genirq/devres: Use dev_name(dev) as default for devname Allow the devname parameter to be NULL and use dev_name(dev) in this case. This should be an appropriate default for most use cases. Signed-off-by: Heiner Kallweit Link: http://lkml.kernel.org/r/05c63d67-30b4-7026-02d5-ce7fb7bc185f@gmail.com Signed-off-by: Thomas Gleixner --- kernel/irq/devres.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/kernel/irq/devres.c b/kernel/irq/devres.c index 18babef39361..1613bfd48365 100644 --- a/kernel/irq/devres.c +++ b/kernel/irq/devres.c @@ -34,7 +34,7 @@ static int devm_irq_match(struct device *dev, void *res, void *data) * @thread_fn: function to be called in a threaded interrupt context. NULL * for devices which handle everything in @handler * @irqflags: Interrupt type flags - * @devname: An ascii name for the claiming device + * @devname: An ascii name for the claiming device, dev_name(dev) if NULL * @dev_id: A cookie passed back to the handler function * * Except for the extra @dev argument, this function takes the @@ -58,6 +58,9 @@ int devm_request_threaded_irq(struct device *dev, unsigned int irq, if (!dr) return -ENOMEM; + if (!devname) + devname = dev_name(dev); + rc = request_threaded_irq(irq, handler, thread_fn, irqflags, devname, dev_id); if (rc) { @@ -81,7 +84,7 @@ EXPORT_SYMBOL(devm_request_threaded_irq); * @thread_fn: function to be called in a threaded interrupt context. NULL * for devices which handle everything in @handler * @irqflags: Interrupt type flags - * @devname: An ascii name for the claiming device + * @devname: An ascii name for the claiming device, dev_name(dev) if NULL * @dev_id: A cookie passed back to the handler function * * Except for the extra @dev argument, this function takes the @@ -104,6 +107,9 @@ int devm_request_any_context_irq(struct device *dev, unsigned int irq, if (!dr) return -ENOMEM; + if (!devname) + devname = dev_name(dev); + rc = request_any_context_irq(irq, handler, irqflags, devname, dev_id); if (rc < 0) { devres_free(dr); -- cgit v1.2.3 From 2f44e29cef006a4b0a4ecf7d4c5aac7d0fbb505c Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 14 Feb 2017 22:53:12 +0100 Subject: genirq/msi: Add stubs for get_cached_msi_msg/pci_write_msi_msg A bug fix to the MSIx handling in vfio added references to functions that may not be defined if MSI is disabled in the kernel, resulting in this link error: drivers/built-in.o: In function `vfio_msi_set_vector_signal': :(.text+0x450808): undefined reference to `get_cached_msi_msg' :(.text+0x45080c): undefined reference to `write_msi_msg' As suggested by Alex Williamson, add stub implementations for get_cached_msi_msg() and pci_write_msi_msg(). In case this bugfix gets backported, please note that the #ifdef has changed over time, originally both functions were implemented in drivers/pci/msi.c and controlled by CONFIG_PCI_MSI, while nowadays get_cached_msi_msg() is part of the generic MSI support and can be used without PCI. Fixes: b8f02af096b1 ("vfio/pci: Restore MSIx message prior to enabling") Signed-off-by: Arnd Bergmann Cc: Marc Zyngier Cc: Alex Williamson Cc: Bjorn Helgaas Cc: Bart Van Assche Link: http://lkml.kernel.org/r/1413190208.4202.34.camel@ul30vt.home Link: http://lkml.kernel.org/r/20170214215343.3307861-1-arnd@arndb.de Signed-off-by: Thomas Gleixner --- include/linux/msi.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/include/linux/msi.h b/include/linux/msi.h index 0db320b7bb15..a83b84ff70e5 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -17,7 +17,13 @@ struct msi_desc; struct pci_dev; struct platform_msi_priv_data; void __get_cached_msi_msg(struct msi_desc *entry, struct msi_msg *msg); +#ifdef CONFIG_GENERIC_MSI_IRQ void get_cached_msi_msg(unsigned int irq, struct msi_msg *msg); +#else +static inline void get_cached_msi_msg(unsigned int irq, struct msi_msg *msg) +{ +} +#endif typedef void (*irq_write_msi_msg_t)(struct msi_desc *desc, struct msi_msg *msg); @@ -116,11 +122,15 @@ struct msi_desc { struct pci_dev *msi_desc_to_pci_dev(struct msi_desc *desc); void *msi_desc_to_pci_sysdata(struct msi_desc *desc); +void pci_write_msi_msg(unsigned int irq, struct msi_msg *msg); #else /* CONFIG_PCI_MSI */ static inline void *msi_desc_to_pci_sysdata(struct msi_desc *desc) { return NULL; } +static inline void pci_write_msi_msg(unsigned int irq, struct msi_msg *msg) +{ +} #endif /* CONFIG_PCI_MSI */ struct msi_desc *alloc_msi_entry(struct device *dev, int nvec, @@ -128,7 +138,6 @@ struct msi_desc *alloc_msi_entry(struct device *dev, int nvec, void free_msi_entry(struct msi_desc *entry); void __pci_read_msi_msg(struct msi_desc *entry, struct msi_msg *msg); void __pci_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg); -void pci_write_msi_msg(unsigned int irq, struct msi_msg *msg); u32 __pci_msix_desc_mask_irq(struct msi_desc *desc, u32 flag); u32 __pci_msi_desc_mask_irq(struct msi_desc *desc, u32 mask, u32 flag); -- cgit v1.2.3 From 5d4bac9a5f4ef24b2482529bda6661a58e5b5b65 Mon Sep 17 00:00:00 2001 From: Jeremy Kerr Date: Thu, 16 Feb 2017 12:24:09 +0800 Subject: genirq: Clarify logic calculating bogus irqreturn_t values Although irqreturn_t is an enum, we treat it (and its enumeration constants) as a bitmask. However, bad_action_ret() uses a less-than operator to determine whether an irqreturn_t falls within allowable bit values, which means we need to know the signededness of an enum type to read the logic, which is implementation-dependent. This change explicitly uses an unsigned type for the comparison. We do this instead of changing to a bitwise test, as the latter compiles to increased instructions in this hot path. It looks like we get the correct behaviour currently (bad_action_ret(-1) returns 1), so this is purely a readability fix. Signed-off-by: Jeremy Kerr Link: http://lkml.kernel.org/r/1487219049-4061-1-git-send-email-jk@ozlabs.org Signed-off-by: Thomas Gleixner --- kernel/irq/spurious.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kernel/irq/spurious.c b/kernel/irq/spurious.c index 5707f97a3e6a..061ba7eed4ed 100644 --- a/kernel/irq/spurious.c +++ b/kernel/irq/spurious.c @@ -175,7 +175,9 @@ out: static inline int bad_action_ret(irqreturn_t action_ret) { - if (likely(action_ret <= (IRQ_HANDLED | IRQ_WAKE_THREAD))) + unsigned int r = action_ret; + + if (likely(r <= (IRQ_HANDLED | IRQ_WAKE_THREAD))) return 0; return 1; } -- cgit v1.2.3 From 3900dea4cda7c28d7921370bc4d22b08463ed94c Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 18 Feb 2017 09:34:34 +0100 Subject: irqchip/qcom: Fix error handling 'devm_ioremap()' returns NULL on error, not an error pointer. Fixes: f20cc9b00c7b ("irqchip/qcom: Add IRQ combiner driver") Signed-off-by: Christophe JAILLET Cc: marc.zyngier@arm.com Cc: kernel-janitors@vger.kernel.org Cc: jason@lakedaemon.net Link: http://lkml.kernel.org/r/20170218083434.2289-1-christophe.jaillet@wanadoo.fr Signed-off-by: Thomas Gleixner --- drivers/irqchip/qcom-irq-combiner.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/irqchip/qcom-irq-combiner.c b/drivers/irqchip/qcom-irq-combiner.c index 03251da95397..226558698344 100644 --- a/drivers/irqchip/qcom-irq-combiner.c +++ b/drivers/irqchip/qcom-irq-combiner.c @@ -202,9 +202,9 @@ static acpi_status get_registers_cb(struct acpi_resource *ares, void *context) } vaddr = devm_ioremap(ctx->dev, reg->address, REG_SIZE); - if (IS_ERR(vaddr)) { + if (!vaddr) { dev_err(ctx->dev, "Can't map register @%pa\n", &paddr); - ctx->err = PTR_ERR(vaddr); + ctx->err = -ENOMEM; return AE_ERROR; } -- cgit v1.2.3