diff options
21 files changed, 997 insertions, 615 deletions
diff --git a/Documentation/devicetree/bindings/interrupt-controller/cortina,gemini-interrupt-controller.txt b/Documentation/devicetree/bindings/interrupt-controller/faraday,ftintc010.txt index 97c1167fa533..24428d47f487 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/cortina,gemini-interrupt-controller.txt +++ b/Documentation/devicetree/bindings/interrupt-controller/faraday,ftintc010.txt @@ -1,9 +1,12 @@ -* Cortina Systems Gemini interrupt controller +* Faraday Technologt FTINTC010 interrupt controller -This interrupt controller is found on the Gemini SoCs. +This interrupt controller is a stock IP block from Faraday Technology found +in the Gemini SoCs and other designs. Required properties: -- compatible: must be "cortina,gemini-interrupt-controller" +- compatible: must be one of + "faraday,ftintc010" + "cortina,gemini-interrupt-controller" (deprecated) - 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. @@ -15,7 +18,7 @@ Required properties: Example: interrupt-controller@48000000 { - compatible = "cortina,gemini-interrupt-controller"; + compatible = "faraday,ftintc010" reg = <0x48000000 0x1000>; interrupt-controller; #interrupt-cells = <2>; diff --git a/Documentation/devicetree/bindings/interrupt-controller/mediatek,cirq.txt b/Documentation/devicetree/bindings/interrupt-controller/mediatek,cirq.txt new file mode 100644 index 000000000000..a7efdbc3de5b --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/mediatek,cirq.txt @@ -0,0 +1,35 @@ +* Mediatek 27xx cirq + +In Mediatek SOCs, the CIRQ is a low power interrupt controller designed to +work outside MCUSYS which comprises with Cortex-Ax cores,CCI and GIC. +The external interrupts (outside MCUSYS) will feed through CIRQ and connect +to GIC in MCUSYS. When CIRQ is enabled, it will record the edge-sensitive +interrupts and generate a pulse signal to parent interrupt controller when +flush command is executed. With CIRQ, MCUSYS can be completely turned off +to improve the system power consumption without losing interrupts. + +Required properties: +- compatible: should be one of + - "mediatek,mt2701-cirq" for mt2701 CIRQ + - "mediatek,mt8135-cirq" for mt8135 CIRQ + - "mediatek,mt8173-cirq" for mt8173 CIRQ + and "mediatek,cirq" as a fallback. +- interrupt-controller : Identifies the node as an interrupt controller. +- #interrupt-cells : Use the same format as specified by GIC in arm,gic.txt. +- interrupt-parent: phandle of irq parent for cirq. The parent must + use the same interrupt-cells format as GIC. +- reg: Physical base address of the cirq registers and length of memory + mapped region. +- mediatek,ext-irq-range: Identifies external irq number range in different + SOCs. + +Example: + cirq: interrupt-controller@10204000 { + compatible = "mediatek,mt2701-cirq", + "mediatek,mtk-cirq"; + interrupt-controller; + #interrupt-cells = <3>; + interrupt-parent = <&sysirq>; + reg = <0 0x10204000 0 0x400>; + mediatek,ext-irq-start = <32 200>; + }; diff --git a/Documentation/devicetree/bindings/interrupt-controller/mediatek,sysirq.txt b/Documentation/devicetree/bindings/interrupt-controller/mediatek,sysirq.txt index 9d1d72c65489..a89c03bb1a81 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/mediatek,sysirq.txt +++ b/Documentation/devicetree/bindings/interrupt-controller/mediatek,sysirq.txt @@ -21,13 +21,16 @@ Required properties: - interrupt-parent: phandle of irq parent for sysirq. The parent must use the same interrupt-cells format as GIC. - reg: Physical base address of the intpol registers and length of memory - mapped region. + mapped region. Could be multiple bases here. Ex: mt6797 needs 2 reg, others + need 1. Example: - sysirq: interrupt-controller@10200100 { - compatible = "mediatek,mt6589-sysirq", "mediatek,mt6577-sysirq"; + sysirq: intpol-controller@10200620 { + compatible = "mediatek,mt6797-sysirq", + "mediatek,mt6577-sysirq"; interrupt-controller; #interrupt-cells = <3>; interrupt-parent = <&gic>; - reg = <0 0x10200100 0 0x1c>; + reg = <0 0x10220620 0 0x20>, + <0 0x10220690 0 0x10>; }; diff --git a/arch/arm/mach-moxart/Kconfig b/arch/arm/mach-moxart/Kconfig index f69e28b85e88..70db2abf6163 100644 --- a/arch/arm/mach-moxart/Kconfig +++ b/arch/arm/mach-moxart/Kconfig @@ -3,8 +3,8 @@ menuconfig ARCH_MOXART depends on ARCH_MULTI_V4 select CPU_FA526 select ARM_DMA_MEM_BUFFERABLE + select FARADAY_FTINTC010 select MOXART_TIMER - select GENERIC_IRQ_CHIP select GPIOLIB select PHYLIB if NETDEVICES help diff --git a/drivers/base/platform-msi.c b/drivers/base/platform-msi.c index 0fc7c4da7756..d35e9a20caf7 100644 --- a/drivers/base/platform-msi.c +++ b/drivers/base/platform-msi.c @@ -345,8 +345,7 @@ platform_msi_create_device_domain(struct device *dev, data->host_data = host_data; domain = irq_domain_create_hierarchy(dev->msi_domain, 0, nvec, - of_node_to_fwnode(dev->of_node), - ops, data); + dev->fwnode, ops, data); if (!domain) goto free_priv; diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 8162121bb1bc..595d0c95563b 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -115,6 +115,12 @@ config DW_APB_ICTL select GENERIC_IRQ_CHIP select IRQ_DOMAIN +config FARADAY_FTINTC010 + bool + select IRQ_DOMAIN + select MULTI_IRQ_HANDLER + select SPARSE_IRQ + config HISILICON_IRQ_MBIGEN bool select ARM_GIC_V3 diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 152bc40b6762..b64c59b838a0 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -6,7 +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_FARADAY_FTINTC010) += irq-ftintc010.o obj-$(CONFIG_ARCH_HIP04) += irq-hip04.o obj-$(CONFIG_ARCH_LPC32XX) += irq-lpc32xx.o obj-$(CONFIG_ARCH_MMP) += irq-mmp.o @@ -16,7 +16,6 @@ obj-$(CONFIG_ARCH_S3C24XX) += irq-s3c24xx.o obj-$(CONFIG_DW_APB_ICTL) += irq-dw-apb-ictl.o obj-$(CONFIG_METAG) += irq-metag-ext.o obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o -obj-$(CONFIG_ARCH_MOXART) += irq-moxart.o obj-$(CONFIG_CLPS711X_IRQCHIP) += irq-clps711x.o obj-$(CONFIG_OR1K_PIC) += irq-or1k-pic.o obj-$(CONFIG_ORION_IRQCHIP) += irq-orion.o @@ -62,7 +61,7 @@ obj-$(CONFIG_BCM7120_L2_IRQ) += irq-bcm7120-l2.o obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o obj-$(CONFIG_MIPS_GIC) += irq-mips-gic.o -obj-$(CONFIG_ARCH_MEDIATEK) += irq-mtk-sysirq.o +obj-$(CONFIG_ARCH_MEDIATEK) += irq-mtk-sysirq.o irq-mtk-cirq.o obj-$(CONFIG_ARCH_DIGICOLOR) += irq-digicolor.o obj-$(CONFIG_RENESAS_H8300H_INTC) += irq-renesas-h8300h.o obj-$(CONFIG_RENESAS_H8S_INTC) += irq-renesas-h8s.o diff --git a/drivers/irqchip/irq-atmel-aic5.c b/drivers/irqchip/irq-atmel-aic5.c index 2a624d87a035..c04ee9a23d09 100644 --- a/drivers/irqchip/irq-atmel-aic5.c +++ b/drivers/irqchip/irq-atmel-aic5.c @@ -150,6 +150,8 @@ static int aic5_set_type(struct irq_data *d, unsigned type) } #ifdef CONFIG_PM +static u32 *smr_cache; + static void aic5_suspend(struct irq_data *d) { struct irq_domain *domain = d->domain; @@ -159,6 +161,12 @@ static void aic5_suspend(struct irq_data *d) int i; u32 mask; + if (smr_cache) + for (i = 0; i < domain->revmap_size; i++) { + irq_reg_writel(bgc, i, AT91_AIC5_SSR); + smr_cache[i] = irq_reg_readl(bgc, AT91_AIC5_SMR); + } + irq_gc_lock(bgc); for (i = 0; i < dgc->irqs_per_chip; i++) { mask = 1 << i; @@ -184,9 +192,21 @@ static void aic5_resume(struct irq_data *d) u32 mask; irq_gc_lock(bgc); + + if (smr_cache) { + irq_reg_writel(bgc, 0xffffffff, AT91_AIC5_SPU); + for (i = 0; i < domain->revmap_size; i++) { + irq_reg_writel(bgc, i, AT91_AIC5_SSR); + irq_reg_writel(bgc, i, AT91_AIC5_SVR); + irq_reg_writel(bgc, smr_cache[i], AT91_AIC5_SMR); + } + } + for (i = 0; i < dgc->irqs_per_chip; i++) { mask = 1 << i; - if ((mask & gc->mask_cache) == (mask & gc->wake_active)) + + if (!smr_cache && + ((mask & gc->mask_cache) == (mask & gc->wake_active))) continue; irq_reg_writel(bgc, i + gc->irq_base, AT91_AIC5_SSR); @@ -342,6 +362,13 @@ static int __init aic5_of_init(struct device_node *node, static int __init sama5d2_aic5_of_init(struct device_node *node, struct device_node *parent) { +#ifdef CONFIG_PM + smr_cache = kcalloc(DIV_ROUND_UP(NR_SAMA5D2_IRQS, 32) * 32, + sizeof(*smr_cache), GFP_KERNEL); + if (!smr_cache) + return -ENOMEM; +#endif + return aic5_of_init(node, parent, NR_SAMA5D2_IRQS); } IRQCHIP_DECLARE(sama5d2_aic5, "atmel,sama5d2-aic", sama5d2_aic5_of_init); diff --git a/drivers/irqchip/irq-ftintc010.c b/drivers/irqchip/irq-ftintc010.c new file mode 100644 index 000000000000..cd2dc8bbbe9c --- /dev/null +++ b/drivers/irqchip/irq-ftintc010.c @@ -0,0 +1,194 @@ +/* + * irqchip for the Faraday Technology FTINTC010 Copyright (C) 2017 Linus + * Walleij <linus.walleij@linaro.org> + * + * Based on arch/arm/mach-gemini/irq.c + * Copyright (C) 2001-2006 Storlink, Corp. + * Copyright (C) 2008-2009 Paulius Zaleckas <paulius.zaleckas@gmail.com> + */ +#include <linux/bitops.h> +#include <linux/irq.h> +#include <linux/io.h> +#include <linux/irqchip.h> +#include <linux/irqchip/versatile-fpga.h> +#include <linux/irqdomain.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/cpu.h> + +#include <asm/exception.h> +#include <asm/mach/irq.h> + +#define FT010_NUM_IRQS 32 + +#define FT010_IRQ_SOURCE(base_addr) (base_addr + 0x00) +#define FT010_IRQ_MASK(base_addr) (base_addr + 0x04) +#define FT010_IRQ_CLEAR(base_addr) (base_addr + 0x08) +/* Selects level- or edge-triggered */ +#define FT010_IRQ_MODE(base_addr) (base_addr + 0x0C) +/* Selects active low/high or falling/rising edge */ +#define FT010_IRQ_POLARITY(base_addr) (base_addr + 0x10) +#define FT010_IRQ_STATUS(base_addr) (base_addr + 0x14) +#define FT010_FIQ_SOURCE(base_addr) (base_addr + 0x20) +#define FT010_FIQ_MASK(base_addr) (base_addr + 0x24) +#define FT010_FIQ_CLEAR(base_addr) (base_addr + 0x28) +#define FT010_FIQ_MODE(base_addr) (base_addr + 0x2C) +#define FT010_FIQ_POLARITY(base_addr) (base_addr + 0x30) +#define FT010_FIQ_STATUS(base_addr) (base_addr + 0x34) + +/** + * struct ft010_irq_data - irq data container for the Faraday IRQ controller + * @base: memory offset in virtual memory + * @chip: chip container for this instance + * @domain: IRQ domain for this instance + */ +struct ft010_irq_data { + void __iomem *base; + struct irq_chip chip; + struct irq_domain *domain; +}; + +static void ft010_irq_mask(struct irq_data *d) +{ + struct ft010_irq_data *f = irq_data_get_irq_chip_data(d); + unsigned int mask; + + mask = readl(FT010_IRQ_MASK(f->base)); + mask &= ~BIT(irqd_to_hwirq(d)); + writel(mask, FT010_IRQ_MASK(f->base)); +} + +static void ft010_irq_unmask(struct irq_data *d) +{ + struct ft010_irq_data *f = irq_data_get_irq_chip_data(d); + unsigned int mask; + + mask = readl(FT010_IRQ_MASK(f->base)); + mask |= BIT(irqd_to_hwirq(d)); + writel(mask, FT010_IRQ_MASK(f->base)); +} + +static void ft010_irq_ack(struct irq_data *d) +{ + struct ft010_irq_data *f = irq_data_get_irq_chip_data(d); + + writel(BIT(irqd_to_hwirq(d)), FT010_IRQ_CLEAR(f->base)); +} + +static int ft010_irq_set_type(struct irq_data *d, unsigned int trigger) +{ + struct ft010_irq_data *f = irq_data_get_irq_chip_data(d); + int offset = irqd_to_hwirq(d); + u32 mode, polarity; + + mode = readl(FT010_IRQ_MODE(f->base)); + polarity = readl(FT010_IRQ_POLARITY(f->base)); + + if (trigger & (IRQ_TYPE_LEVEL_LOW)) { + irq_set_handler_locked(d, handle_level_irq); + mode &= ~BIT(offset); + polarity |= BIT(offset); + } else if (trigger & (IRQ_TYPE_LEVEL_HIGH)) { + irq_set_handler_locked(d, handle_level_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 if (trigger & IRQ_TYPE_EDGE_RISING) { + 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("Faraday IRQ: no supported trigger selected for line %d\n", + offset); + } + + writel(mode, FT010_IRQ_MODE(f->base)); + writel(polarity, FT010_IRQ_POLARITY(f->base)); + + return 0; +} + +static struct irq_chip ft010_irq_chip = { + .name = "FTINTC010", + .irq_ack = ft010_irq_ack, + .irq_mask = ft010_irq_mask, + .irq_unmask = ft010_irq_unmask, + .irq_set_type = ft010_irq_set_type, +}; + +/* Local static for the IRQ entry call */ +static struct ft010_irq_data firq; + +asmlinkage void __exception_irq_entry ft010_irqchip_handle_irq(struct pt_regs *regs) +{ + struct ft010_irq_data *f = &firq; + int irq; + u32 status; + + while ((status = readl(FT010_IRQ_STATUS(f->base)))) { + irq = ffs(status) - 1; + handle_domain_irq(f->domain, irq, regs); + } +} + +static int ft010_irqdomain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) +{ + struct ft010_irq_data *f = d->host_data; + + irq_set_chip_data(irq, f); + /* All IRQs should set up their type, flags as bad by default */ + irq_set_chip_and_handler(irq, &ft010_irq_chip, handle_bad_irq); + irq_set_probe(irq); + + return 0; +} + +static void ft010_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 ft010_irqdomain_ops = { + .map = ft010_irqdomain_map, + .unmap = ft010_irqdomain_unmap, + .xlate = irq_domain_xlate_onetwocell, +}; + +int __init ft010_of_init_irq(struct device_node *node, + struct device_node *parent) +{ + struct ft010_irq_data *f = &firq; + + /* + * 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); + + f->base = of_iomap(node, 0); + WARN(!f->base, "unable to map gemini irq registers\n"); + + /* Disable all interrupts */ + writel(0, FT010_IRQ_MASK(f->base)); + writel(0, FT010_FIQ_MASK(f->base)); + + f->domain = irq_domain_add_simple(node, FT010_NUM_IRQS, 0, + &ft010_irqdomain_ops, f); + set_handle_irq(ft010_irqchip_handle_irq); + + return 0; +} +IRQCHIP_DECLARE(faraday, "faraday,ftintc010", + ft010_of_init_irq); +IRQCHIP_DECLARE(gemini, "cortina,gemini-interrupt-controller", + ft010_of_init_irq); +IRQCHIP_DECLARE(moxa, "moxa,moxart-ic", + ft010_of_init_irq); diff --git a/drivers/irqchip/irq-gemini.c b/drivers/irqchip/irq-gemini.c deleted file mode 100644 index 495224c743ee..000000000000 --- a/drivers/irqchip/irq-gemini.c +++ /dev/null @@ -1,185 +0,0 @@ -/* - * irqchip for the Cortina Systems Gemini Copyright (C) 2017 Linus - * Walleij <linus.walleij@linaro.org> - * - * Based on arch/arm/mach-gemini/irq.c - * Copyright (C) 2001-2006 Storlink, Corp. - * Copyright (C) 2008-2009 Paulius Zaleckas <paulius.zaleckas@teltonika.lt> - */ -#include <linux/bitops.h> -#include <linux/irq.h> -#include <linux/io.h> -#include <linux/irqchip.h> -#include <linux/irqchip/versatile-fpga.h> -#include <linux/irqdomain.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/of_address.h> -#include <linux/of_irq.h> -#include <linux/cpu.h> - -#include <asm/exception.h> -#include <asm/mach/irq.h> - -#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); diff --git a/drivers/irqchip/irq-gic-v3-its-platform-msi.c b/drivers/irqchip/irq-gic-v3-its-platform-msi.c index 470b4aa7d62c..9e9dda33eb17 100644 --- a/drivers/irqchip/irq-gic-v3-its-platform-msi.c +++ b/drivers/irqchip/irq-gic-v3-its-platform-msi.c @@ -15,6 +15,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <linux/acpi_iort.h> #include <linux/device.h> #include <linux/msi.h> #include <linux/of.h> @@ -24,15 +25,11 @@ static struct irq_chip its_pmsi_irq_chip = { .name = "ITS-pMSI", }; -static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev, - int nvec, msi_alloc_info_t *info) +static int of_pmsi_get_dev_id(struct irq_domain *domain, struct device *dev, + u32 *dev_id) { - struct msi_domain_info *msi_info; - u32 dev_id; int ret, index = 0; - msi_info = msi_get_domain_info(domain->parent); - /* Suck the DeviceID out of the msi-parent property */ do { struct of_phandle_args args; @@ -43,11 +40,32 @@ static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev, if (args.np == irq_domain_get_of_node(domain)) { if (WARN_ON(args.args_count != 1)) return -EINVAL; - dev_id = args.args[0]; + *dev_id = args.args[0]; break; } } while (!ret); + return ret; +} + +int __weak iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id) +{ + return -1; +} + +static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev, + int nvec, msi_alloc_info_t *info) +{ + struct msi_domain_info *msi_info; + u32 dev_id; + int ret; + + msi_info = msi_get_domain_info(domain->parent); + + if (dev->of_node) + ret = of_pmsi_get_dev_id(domain, dev, &dev_id); + else + ret = iort_pmsi_get_dev_id(dev, &dev_id); if (ret) return ret; @@ -73,34 +91,79 @@ static struct of_device_id its_device_id[] = { {}, }; -static int __init its_pmsi_init(void) +static int __init its_pmsi_init_one(struct fwnode_handle *fwnode, + const char *name) { - struct device_node *np; struct irq_domain *parent; + parent = irq_find_matching_fwnode(fwnode, DOMAIN_BUS_NEXUS); + if (!parent || !msi_get_domain_info(parent)) { + pr_err("%s: unable to locate ITS domain\n", name); + return -ENXIO; + } + + if (!platform_msi_create_irq_domain(fwnode, &its_pmsi_domain_info, + parent)) { + pr_err("%s: unable to create platform domain\n", name); + return -ENXIO; + } + + pr_info("Platform MSI: %s domain created\n", name); + return 0; +} + +#ifdef CONFIG_ACPI +static int __init +its_pmsi_parse_madt(struct acpi_subtable_header *header, + const unsigned long end) +{ + struct acpi_madt_generic_translator *its_entry; + struct fwnode_handle *domain_handle; + const char *node_name; + int err = -ENXIO; + + its_entry = (struct acpi_madt_generic_translator *)header; + node_name = kasprintf(GFP_KERNEL, "ITS@0x%lx", + (long)its_entry->base_address); + domain_handle = iort_find_domain_token(its_entry->translation_id); + if (!domain_handle) { + pr_err("%s: Unable to locate ITS domain handle\n", node_name); + goto out; + } + + err = its_pmsi_init_one(domain_handle, node_name); + +out: + kfree(node_name); + return err; +} + +static void __init its_pmsi_acpi_init(void) +{ + acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_TRANSLATOR, + its_pmsi_parse_madt, 0); +} +#else +static inline void its_pmsi_acpi_init(void) { } +#endif + +static void __init its_pmsi_of_init(void) +{ + struct device_node *np; + for (np = of_find_matching_node(NULL, its_device_id); np; np = of_find_matching_node(np, its_device_id)) { if (!of_property_read_bool(np, "msi-controller")) continue; - parent = irq_find_matching_host(np, DOMAIN_BUS_NEXUS); - if (!parent || !msi_get_domain_info(parent)) { - pr_err("%s: unable to locate ITS domain\n", - np->full_name); - continue; - } - - if (!platform_msi_create_irq_domain(of_node_to_fwnode(np), - &its_pmsi_domain_info, - parent)) { - pr_err("%s: unable to create platform domain\n", - np->full_name); - continue; - } - - pr_info("Platform MSI: %s domain created\n", np->full_name); + its_pmsi_init_one(of_node_to_fwnode(np), np->full_name); } +} +static int __init its_pmsi_init(void) +{ + its_pmsi_of_init(); + its_pmsi_acpi_init(); return 0; } early_initcall(its_pmsi_init); diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index f77f840d2b5f..45ea193325d2 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -16,13 +16,13 @@ */ #include <linux/acpi.h> +#include <linux/acpi_iort.h> #include <linux/bitmap.h> #include <linux/cpu.h> #include <linux/delay.h> #include <linux/dma-iommu.h> #include <linux/interrupt.h> #include <linux/irqdomain.h> -#include <linux/acpi_iort.h> #include <linux/log2.h> #include <linux/mm.h> #include <linux/msi.h> diff --git a/drivers/irqchip/irq-imx-gpcv2.c b/drivers/irqchip/irq-imx-gpcv2.c index 2d203b422129..9463f3557e82 100644 --- a/drivers/irqchip/irq-imx-gpcv2.c +++ b/drivers/irqchip/irq-imx-gpcv2.c @@ -268,6 +268,11 @@ static int __init imx_gpcv2_irqchip_init(struct device_node *node, imx_gpcv2_instance = cd; register_syscore_ops(&imx_gpcv2_syscore_ops); + /* + * Clear the OF_POPULATED flag set in of_irq_init so that + * later the GPC power domain driver will not be skipped. + */ + of_node_clear_flag(node, OF_POPULATED); return 0; } diff --git a/drivers/irqchip/irq-mbigen.c b/drivers/irqchip/irq-mbigen.c index 03b79b061d24..d2306c821ebb 100644 --- a/drivers/irqchip/irq-mbigen.c +++ b/drivers/irqchip/irq-mbigen.c @@ -16,6 +16,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <linux/acpi.h> #include <linux/interrupt.h> #include <linux/irqchip.h> #include <linux/module.h> @@ -180,7 +181,7 @@ static int mbigen_domain_translate(struct irq_domain *d, unsigned long *hwirq, unsigned int *type) { - if (is_of_node(fwspec->fwnode)) { + if (is_of_node(fwspec->fwnode) || is_acpi_device_node(fwspec->fwnode)) { if (fwspec->param_count != 2) return -EINVAL; @@ -236,27 +237,15 @@ static struct irq_domain_ops mbigen_domain_ops = { .free = irq_domain_free_irqs_common, }; -static int mbigen_device_probe(struct platform_device *pdev) +static int mbigen_of_create_domain(struct platform_device *pdev, + struct mbigen_device *mgn_chip) { - struct mbigen_device *mgn_chip; + struct device *parent; struct platform_device *child; struct irq_domain *domain; struct device_node *np; - struct device *parent; - struct resource *res; u32 num_pins; - mgn_chip = devm_kzalloc(&pdev->dev, sizeof(*mgn_chip), GFP_KERNEL); - if (!mgn_chip) - return -ENOMEM; - - mgn_chip->pdev = pdev; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - mgn_chip->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(mgn_chip->base)) - return PTR_ERR(mgn_chip->base); - for_each_child_of_node(pdev->dev.of_node, np) { if (!of_property_read_bool(np, "interrupt-controller")) continue; @@ -280,6 +269,91 @@ static int mbigen_device_probe(struct platform_device *pdev) return -ENOMEM; } + return 0; +} + +#ifdef CONFIG_ACPI +static int mbigen_acpi_create_domain(struct platform_device *pdev, + struct mbigen_device *mgn_chip) +{ + struct irq_domain *domain; + u32 num_pins = 0; + int ret; + + /* + * "num-pins" is the total number of interrupt pins implemented in + * this mbigen instance, and mbigen is an interrupt controller + * connected to ITS converting wired interrupts into MSI, so we + * use "num-pins" to alloc MSI vectors which are needed by client + * devices connected to it. + * + * Here is the DSDT device node used for mbigen in firmware: + * Device(MBI0) { + * Name(_HID, "HISI0152") + * Name(_UID, Zero) + * Name(_CRS, ResourceTemplate() { + * Memory32Fixed(ReadWrite, 0xa0080000, 0x10000) + * }) + * + * Name(_DSD, Package () { + * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), + * Package () { + * Package () {"num-pins", 378} + * } + * }) + * } + */ + ret = device_property_read_u32(&pdev->dev, "num-pins", &num_pins); + if (ret || num_pins == 0) + return -EINVAL; + + domain = platform_msi_create_device_domain(&pdev->dev, num_pins, + mbigen_write_msg, + &mbigen_domain_ops, + mgn_chip); + if (!domain) + return -ENOMEM; + + return 0; +} +#else +static inline int mbigen_acpi_create_domain(struct platform_device *pdev, + struct mbigen_device *mgn_chip) |