diff options
28 files changed, 1190 insertions, 127 deletions
diff --git a/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun4i-ic.txt b/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun4i-ic.txt index b290ca150d30..404352524c3a 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun4i-ic.txt +++ b/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun4i-ic.txt @@ -2,7 +2,9 @@ Allwinner Sunxi Interrupt Controller Required properties: -- compatible : should be "allwinner,sun4i-a10-ic" +- compatible : should be one of the following: + "allwinner,sun4i-a10-ic" + "allwinner,suniv-f1c100s-ic" - reg : Specifies base physical address and size of the registers. - interrupt-controller : Identifies the node as an interrupt controller - #interrupt-cells : Specifies the number of cells needed to encode an diff --git a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt index 3ea78c4ef887..b83bb8249074 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt +++ b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt @@ -7,7 +7,9 @@ Interrupts (LPI). Main node required properties: -- compatible : should at least contain "arm,gic-v3". +- compatible : should at least contain "arm,gic-v3" or either + "qcom,msm8996-gic-v3", "arm,gic-v3" for msm8996 SoCs + to address SoC specific bugs/quirks - interrupt-controller : Identifies the node as an interrupt controller - #interrupt-cells : Specifies the number of cells needed to encode an interrupt source. Must be a single cell with a value of at least 3. diff --git a/Documentation/devicetree/bindings/interrupt-controller/fsl,irqsteer.txt b/Documentation/devicetree/bindings/interrupt-controller/fsl,irqsteer.txt new file mode 100644 index 000000000000..45790ce6f5b9 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/fsl,irqsteer.txt @@ -0,0 +1,34 @@ +Freescale IRQSTEER Interrupt multiplexer + +Required properties: + +- compatible: should be: + - "fsl,imx8m-irqsteer" + - "fsl,imx-irqsteer" +- reg: Physical base address and size of registers. +- interrupts: Should contain the parent interrupt line used to multiplex the + input interrupts. +- clocks: Should contain one clock for entry in clock-names + see Documentation/devicetree/bindings/clock/clock-bindings.txt +- clock-names: + - "ipg": main logic clock +- interrupt-controller: Identifies the node as an interrupt controller. +- #interrupt-cells: Specifies the number of cells needed to encode an + interrupt source. The value must be 1. +- fsl,channel: The output channel that all input IRQs should be steered into. +- fsl,irq-groups: Number of IRQ groups managed by this controller instance. + Each group manages 64 input interrupts. + +Example: + + interrupt-controller@32e2d000 { + compatible = "fsl,imx8m-irqsteer", "fsl,imx-irqsteer"; + reg = <0x32e2d000 0x1000>; + interrupts = <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk IMX8MQ_CLK_DISP_APB_ROOT>; + clock-names = "ipg"; + fsl,channel = <0>; + fsl,irq-groups = <1>; + interrupt-controller; + #interrupt-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/interrupt-controller/rda,8810pl-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/rda,8810pl-intc.txt new file mode 100644 index 000000000000..e0062aebf025 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/rda,8810pl-intc.txt @@ -0,0 +1,61 @@ +RDA Micro RDA8810PL Interrupt Controller + +The interrupt controller in RDA8810PL SoC is a custom interrupt controller +which supports up to 32 interrupts. + +Required properties: + +- compatible: Should be "rda,8810pl-intc". +- reg: Specifies base physical address of the registers set. +- interrupt-controller: Identifies the node as an interrupt controller. +- #interrupt-cells: Specifies the number of cells needed to encode an + interrupt source. The value shall be 2. + +The interrupt sources are as follows: + +ID Name +------------ +0: PULSE_DUMMY +1: I2C +2: NAND_NFSC +3: SDMMC1 +4: SDMMC2 +5: SDMMC3 +6: SPI1 +7: SPI2 +8: SPI3 +9: UART1 +10: UART2 +11: UART3 +12: GPIO1 +13: GPIO2 +14: GPIO3 +15: KEYPAD +16: TIMER +17: TIMEROS +18: COMREG0 +19: COMREG1 +20: USB +21: DMC +22: DMA +23: CAMERA +24: GOUDA +25: GPU +26: VPU_JPG +27: VPU_HOST +28: VOC +29: AUIFC0 +30: AUIFC1 +31: L2CC + +Example: + apb@20800000 { + compatible = "simple-bus"; + ... + intc: interrupt-controller@0 { + compatible = "rda,8810pl-intc"; + reg = <0x0 0x1000>; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; diff --git a/Documentation/devicetree/bindings/interrupt-controller/st,stm32-exti.txt b/Documentation/devicetree/bindings/interrupt-controller/st,stm32-exti.txt index 6a36bf66d932..cd01b2292ec6 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/st,stm32-exti.txt +++ b/Documentation/devicetree/bindings/interrupt-controller/st,stm32-exti.txt @@ -14,6 +14,10 @@ Required properties: (only needed for exti controller with multiple exti under same parent interrupt: st,stm32-exti and st,stm32h7-exti) +Optional properties: + +- hwlocks: reference to a phandle of a hardware spinlock provider node. + Example: exti: interrupt-controller@40013c00 { diff --git a/MAINTAINERS b/MAINTAINERS index 6682420421c1..ca8940e640b9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3669,8 +3669,10 @@ W: https://github.com/CirrusLogic/linux-drivers/wiki S: Supported F: Documentation/devicetree/bindings/mfd/madera.txt F: Documentation/devicetree/bindings/pinctrl/cirrus,madera-pinctrl.txt +F: include/linux/irqchip/irq-madera* F: include/linux/mfd/madera/* F: drivers/gpio/gpio-madera* +F: drivers/irqchip/irq-madera* F: drivers/mfd/madera* F: drivers/mfd/cs47l* F: drivers/pinctrl/cirrus/* diff --git a/drivers/base/platform-msi.c b/drivers/base/platform-msi.c index f39a920496fb..8da314b81eab 100644 --- a/drivers/base/platform-msi.c +++ b/drivers/base/platform-msi.c @@ -368,14 +368,16 @@ void platform_msi_domain_free(struct irq_domain *domain, unsigned int virq, unsigned int nvec) { struct platform_msi_priv_data *data = domain->host_data; - struct msi_desc *desc; - for_each_msi_entry(desc, data->dev) { + struct msi_desc *desc, *tmp; + for_each_msi_entry_safe(desc, tmp, data->dev) { if (WARN_ON(!desc->irq || desc->nvec_used != 1)) return; if (!(desc->irq >= virq && desc->irq < (virq + nvec))) continue; irq_domain_free_irqs_common(domain, desc->irq, 1); + list_del(&desc->list); + free_msi_entry(desc); } } diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 51a5ef0e96ed..3d1e60779078 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -150,6 +150,9 @@ config IMGPDC_IRQ select GENERIC_IRQ_CHIP select IRQ_DOMAIN +config MADERA_IRQ + tristate + config IRQ_MIPS_CPU bool select GENERIC_IRQ_CHIP @@ -195,6 +198,10 @@ config JCORE_AIC help Support for the J-Core integrated AIC. +config RDA_INTC + bool + select IRQ_DOMAIN + config RENESAS_INTC_IRQPIN bool select IRQ_DOMAIN @@ -391,6 +398,14 @@ config CSKY_APB_INTC by C-SKY single core SOC system. It use mmio map apb-bus to visit the controller's register. +config IMX_IRQSTEER + bool "i.MX IRQSTEER support" + depends on ARCH_MXC || COMPILE_TEST + default ARCH_MXC + select IRQ_DOMAIN + help + Support for the i.MX IRQSTEER interrupt multiplexer/remapper. + endmenu config SIFIVE_PLIC diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 794c13d3ac3d..c93713d24b86 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_IMGPDC_IRQ) += irq-imgpdc.o obj-$(CONFIG_IRQ_MIPS_CPU) += irq-mips-cpu.o obj-$(CONFIG_SIRF_IRQ) += irq-sirfsoc.o obj-$(CONFIG_JCORE_AIC) += irq-jcore-aic.o +obj-$(CONFIG_RDA_INTC) += irq-rda-intc.o obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o @@ -91,3 +92,5 @@ obj-$(CONFIG_QCOM_PDC) += qcom-pdc.o obj-$(CONFIG_CSKY_MPINTC) += irq-csky-mpintc.o obj-$(CONFIG_CSKY_APB_INTC) += irq-csky-apb-intc.o obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o +obj-$(CONFIG_IMX_IRQSTEER) += irq-imx-irqsteer.o +obj-$(CONFIG_MADERA_IRQ) += irq-madera.o diff --git a/drivers/irqchip/irq-bcm2835.c b/drivers/irqchip/irq-bcm2835.c index d2da8a1e6b1b..418245d31921 100644 --- a/drivers/irqchip/irq-bcm2835.c +++ b/drivers/irqchip/irq-bcm2835.c @@ -1,17 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2010 Broadcom * Copyright 2012 Simon Arlott, Chris Boot, Stephen Warren * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - * * Quirk 1: Shortcut interrupts don't set the bank 1/2 register pending bits * * If an interrupt fires on bank 1 that isn't in the shortcuts list, bit 8 diff --git a/drivers/irqchip/irq-bcm2836.c b/drivers/irqchip/irq-bcm2836.c index dfe4a460340b..2038693f074c 100644 --- a/drivers/irqchip/irq-bcm2836.c +++ b/drivers/irqchip/irq-bcm2836.c @@ -1,17 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Root interrupt controller for the BCM2836 (Raspberry Pi 2). * * Copyright 2015 Broadcom - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. */ #include <linux/cpu.h> diff --git a/drivers/irqchip/irq-gic-common.c b/drivers/irqchip/irq-gic-common.c index 01e673c680cd..3c93c6f4d1f1 100644 --- a/drivers/irqchip/irq-gic-common.c +++ b/drivers/irqchip/irq-gic-common.c @@ -36,6 +36,18 @@ void gic_set_kvm_info(const struct gic_kvm_info *info) gic_kvm_info = info; } +void gic_enable_of_quirks(const struct device_node *np, + const struct gic_quirk *quirks, void *data) +{ + for (; quirks->desc; quirks++) { + if (!of_device_is_compatible(np, quirks->compatible)) + continue; + if (quirks->init(data)) + pr_info("GIC: enabling workaround for %s\n", + quirks->desc); + } +} + void gic_enable_quirks(u32 iidr, const struct gic_quirk *quirks, void *data) { diff --git a/drivers/irqchip/irq-gic-common.h b/drivers/irqchip/irq-gic-common.h index 3919cd7c5285..97e58fb6c232 100644 --- a/drivers/irqchip/irq-gic-common.h +++ b/drivers/irqchip/irq-gic-common.h @@ -23,6 +23,7 @@ struct gic_quirk { const char *desc; + const char *compatible; bool (*init)(void *data); u32 iidr; u32 mask; @@ -35,6 +36,8 @@ void gic_dist_config(void __iomem *base, int gic_irqs, void gic_cpu_config(void __iomem *base, void (*sync_access)(void)); void gic_enable_quirks(u32 iidr, const struct gic_quirk *quirks, void *data); +void gic_enable_of_quirks(const struct device_node *np, + const struct gic_quirk *quirks, void *data); void gic_set_kvm_info(const struct gic_kvm_info *info); diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 8f87f40c9460..d3d4f65b377b 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -41,6 +41,8 @@ #include "irq-gic-common.h" +#define FLAGS_WORKAROUND_GICR_WAKER_MSM8996 (1ULL << 0) + struct redist_region { void __iomem *redist_base; phys_addr_t phys_base; @@ -55,6 +57,7 @@ struct gic_chip_data { struct irq_domain *domain; u64 redist_stride; u32 nr_redist_regions; + u64 flags; bool has_rss; unsigned int irq_nr; struct partition_desc *ppi_descs[16]; @@ -139,6 +142,9 @@ static void gic_enable_redist(bool enable) u32 count = 1000000; /* 1s! */ u32 val; + if (gic_data.flags & FLAGS_WORKAROUND_GICR_WAKER_MSM8996) + return; + rbase = gic_data_rdist_rd_base(); val = readl_relaxed(rbase + GICR_WAKER); @@ -1067,6 +1073,15 @@ static const struct irq_domain_ops partition_domain_ops = { .select = gic_irq_domain_select, }; +static bool gic_enable_quirk_msm8996(void *data) +{ + struct gic_chip_data *d = data; + + d->flags |= FLAGS_WORKAROUND_GICR_WAKER_MSM8996; + + return true; +} + static int __init gic_init_bases(void __iomem *dist_base, struct redist_region *rdist_regs, u32 nr_redist_regions, @@ -1271,6 +1286,16 @@ static void __init gic_of_setup_kvm_info(struct device_node *node) gic_set_kvm_info(&gic_v3_kvm_info); } +static const struct gic_quirk gic_quirks[] = { + { + .desc = "GICv3: Qualcomm MSM8996 broken firmware", + .compatible = "qcom,msm8996-gic-v3", + .init = gic_enable_quirk_msm8996, + }, + { + } +}; + static int __init gic_of_init(struct device_node *node, struct device_node *parent) { void __iomem *dist_base; @@ -1318,6 +1343,8 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare if (of_property_read_u64(node, "redistributor-stride", &redist_stride)) redist_stride = 0; + gic_enable_of_quirks(node, gic_quirks, &gic_data); + err = gic_init_bases(dist_base, rdist_regs, nr_redist_regions, redist_stride, &node->fwnode); if (err) diff --git a/drivers/irqchip/irq-imx-gpcv2.c b/drivers/irqchip/irq-imx-gpcv2.c index 4760307ab43f..66501ea4fd75 100644 --- a/drivers/irqchip/irq-imx-gpcv2.c +++ b/drivers/irqchip/irq-imx-gpcv2.c @@ -17,6 +17,9 @@ #define GPC_IMR1_CORE0 0x30 #define GPC_IMR1_CORE1 0x40 +#define GPC_IMR1_CORE2 0x1c0 +#define GPC_IMR1_CORE3 0x1d0 + struct gpcv2_irqchip_data { struct raw_spinlock rlock; @@ -28,6 +31,11 @@ struct gpcv2_irqchip_data { static struct gpcv2_irqchip_data *imx_gpcv2_instance; +static void __iomem *gpcv2_idx_to_reg(struct gpcv2_irqchip_data *cd, int i) +{ + return cd->gpc_base + cd->cpu2wakeup + i * 4; +} + static int gpcv2_wakeup_source_save(void) { struct gpcv2_irqchip_data *cd; @@ -39,7 +47,7 @@ static int gpcv2_wakeup_source_save(void) return 0; for (i = 0; i < IMR_NUM; i++) { - reg = cd->gpc_base + cd->cpu2wakeup + i * 4; + reg = gpcv2_idx_to_reg(cd, i); cd->saved_irq_mask[i] = readl_relaxed(reg); writel_relaxed(cd->wakeup_sources[i], reg); } @@ -50,17 +58,14 @@ static int gpcv2_wakeup_source_save(void) static void gpcv2_wakeup_source_restore(void) { struct gpcv2_irqchip_data *cd; - void __iomem *reg; int i; cd = imx_gpcv2_instance; if (!cd) return; - for (i = 0; i < IMR_NUM; i++) { - reg = cd->gpc_base + cd->cpu2wakeup + i * 4; - writel_relaxed(cd->saved_irq_mask[i], reg); - } + for (i = 0; i < IMR_NUM; i++) + writel_relaxed(cd->saved_irq_mask[i], gpcv2_idx_to_reg(cd, i)); } static struct syscore_ops imx_gpcv2_syscore_ops = { @@ -73,12 +78,10 @@ static int imx_gpcv2_irq_set_wake(struct irq_data *d, unsigned int on) struct gpcv2_irqchip_data *cd = d->chip_data; unsigned int idx = d->hwirq / 32; unsigned long flags; - void __iomem *reg; u32 mask, val; raw_spin_lock_irqsave(&cd->rlock, flags); - reg = cd->gpc_base + cd->cpu2wakeup + idx * 4; - mask = 1 << d->hwirq % 32; + mask = BIT(d->hwirq % 32); val = cd->wakeup_sources[idx]; cd->wakeup_sources[idx] = on ? (val & ~mask) : (val | mask); @@ -99,9 +102,9 @@ static void imx_gpcv2_irq_unmask(struct irq_data *d) u32 val; raw_spin_lock(&cd->rlock); - reg = cd->gpc_base + cd->cpu2wakeup + d->hwirq / 32 * 4; + reg = gpcv2_idx_to_reg(cd, d->hwirq / 32); val = readl_relaxed(reg); - val &= ~(1 << d->hwirq % 32); + val &= ~BIT(d->hwirq % 32); writel_relaxed(val, reg); raw_spin_unlock(&cd->rlock); @@ -115,9 +118,9 @@ static void imx_gpcv2_irq_mask(struct irq_data *d) u32 val; raw_spin_lock(&cd->rlock); - reg = cd->gpc_base + cd->cpu2wakeup + d->hwirq / 32 * 4; + reg = gpcv2_idx_to_reg(cd, d->hwirq / 32); val = readl_relaxed(reg); - val |= 1 << (d->hwirq % 32); + val |= BIT(d->hwirq % 32); writel_relaxed(val, reg); raw_spin_unlock(&cd->rlock); @@ -192,11 +195,19 @@ static const struct irq_domain_ops gpcv2_irqchip_data_domain_ops = { .free = irq_domain_free_irqs_common, }; +static const struct of_device_id gpcv2_of_match[] = { + { .compatible = "fsl,imx7d-gpc", .data = (const void *) 2 }, + { .compatible = "fsl,imx8mq-gpc", .data = (const void *) 4 }, + { /* END */ } +}; + static int __init imx_gpcv2_irqchip_init(struct device_node *node, struct device_node *parent) { struct irq_domain *parent_domain, *domain; struct gpcv2_irqchip_data *cd; + const struct of_device_id *id; + unsigned long core_num; int i; if (!parent) { @@ -204,6 +215,14 @@ static int __init imx_gpcv2_irqchip_init(struct device_node *node, return -ENODEV; } + id = of_match_node(gpcv2_of_match, node); + if (!id) { + pr_err("%pOF: unknown compatibility string\n", node); + return -ENODEV; + } + + core_num = (unsigned long)id->data; + parent_domain = irq_find_host(parent); if (!parent_domain) { pr_err("%pOF: unable to get parent domain\n", node); @@ -212,7 +231,7 @@ static int __init imx_gpcv2_irqchip_init(struct device_node *node, cd = kzalloc(sizeof(struct gpcv2_irqchip_data), GFP_KERNEL); if (!cd) { - pr_err("kzalloc failed!\n"); + pr_err("%pOF: kzalloc failed!\n", node); return -ENOMEM; } @@ -220,7 +239,7 @@ static int __init imx_gpcv2_irqchip_init(struct device_node *node, cd->gpc_base = of_iomap(node, 0); if (!cd->gpc_base) { - pr_err("fsl-gpcv2: unable to map gpc registers\n"); + pr_err("%pOF: unable to map gpc registers\n", node); kfree(cd); return -ENOMEM; } @@ -236,8 +255,17 @@ static int __init imx_gpcv2_irqchip_init(struct device_node *node, /* Initially mask all interrupts */ for (i = 0; i < IMR_NUM; i++) { - writel_relaxed(~0, cd->gpc_base + GPC_IMR1_CORE0 + i * 4); - writel_relaxed(~0, cd->gpc_base + GPC_IMR1_CORE1 + i * 4); + void __iomem *reg = cd->gpc_base + i * 4; + + switch (core_num) { + case 4: + writel_relaxed(~0, reg + GPC_IMR1_CORE2); + writel_relaxed(~0, reg + GPC_IMR1_CORE3); + /* fall through */ + case 2: + writel_relaxed(~0, reg + GPC_IMR1_CORE0); + writel_relaxed(~0, reg + GPC_IMR1_CORE1); + } cd->wakeup_sources[i] = ~0; } @@ -262,4 +290,5 @@ static int __init imx_gpcv2_irqchip_init(struct device_node *node, return 0; } -IRQCHIP_DECLARE(imx_gpcv2, "fsl,imx7d-gpc", imx_gpcv2_irqchip_init); +IRQCHIP_DECLARE(imx_gpcv2_imx7d, "fsl,imx7d-gpc", imx_gpcv2_irqchip_init); +IRQCHIP_DECLARE(imx_gpcv2_imx8mq, "fsl,imx8mq-gpc", imx_gpcv2_irqchip_init); diff --git a/drivers/irqchip/irq-imx-irqsteer.c b/drivers/irqchip/irq-imx-irqsteer.c new file mode 100644 index 000000000000..5b3f1d735685 --- /dev/null +++ b/drivers/irqchip/irq-imx-irqsteer.c @@ -0,0 +1,261 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2017 NXP + * Copyright (C) 2018 Pengutronix, Lucas Stach <kernel@pengutronix.de> + */ + +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/irqdomain.h> +#include <linux/kernel.h> +#include <linux/of_platform.h> +#include <linux/spinlock.h> + +#define CTRL_STRIDE_OFF(_t, _r) (_t * 8 * _r) +#define CHANCTRL 0x0 +#define CHANMASK(n, t) (CTRL_STRIDE_OFF(t, 0) + 0x4 * (n) + 0x4) +#define CHANSET(n, t) (CTRL_STRIDE_OFF(t, 1) + 0x4 * (n) + 0x4) +#define CHANSTATUS(n, t) (CTRL_STRIDE_OFF(t, 2) + 0x4 * (n) + 0x4) +#define CHAN_MINTDIS(t) (CTRL_STRIDE_OFF(t, 3) + 0x4) +#define CHAN_MASTRSTAT(t) (CTRL_STRIDE_OFF(t, 3) + 0x8) + +struct irqsteer_data { + void __iomem *regs; + struct clk *ipg_clk; + int irq; + raw_spinlock_t lock; + int irq_groups; + int channel; + struct irq_domain *domain; + u32 *saved_reg; +}; + +static int imx_irqsteer_get_reg_index(struct irqsteer_data *data, + unsigned long irqnum) +{ + return (data->irq_groups * 2 - irqnum / 32 - 1); +} + +static void imx_irqsteer_irq_unmask(struct irq_data *d) +{ + struct irqsteer_data *data = d->chip_data; + int idx = imx_irqsteer_get_reg_index(data, d->hwirq); + unsigned long flags; + u32 val; + + raw_spin_lock_irqsave(&data->lock, flags); + val = readl_relaxed(data->regs + CHANMASK(idx, data->irq_groups)); + val |= BIT(d->hwirq % 32); + writel_relaxed(val, data->regs + CHANMASK(idx, data->irq_groups)); + raw_spin_unlock_irqrestore(&data->lock, flags); +} + +static void imx_irqsteer_irq_mask(struct irq_data *d) +{ + struct irqsteer_data *data = d->chip_data; + int idx = imx_irqsteer_get_reg_index(data, d->hwirq); + unsigned long flags; + u32 val; + + raw_spin_lock_irqsave(&data->lock, flags); + val = readl_relaxed(data->regs + CHANMASK(idx, data->irq_groups)); + val &= ~BIT(d->hwirq % 32); + writel_relaxed(val, data->regs + CHANMASK(idx, data->irq_groups)); + raw_spin_unlock_irqrestore(&data->lock, flags); +} + +static struct irq_chip imx_irqsteer_irq_chip = { + .name = "irqsteer", + .irq_mask = imx_irqsteer_irq_mask, + .irq_unmask = imx_irqsteer_irq_unmask, +}; + +static int imx_irqsteer_irq_map(struct irq_domain *h, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_status_flags(irq, IRQ_LEVEL); + irq_set_chip_data(irq, h->host_data); + irq_set_chip_and_handler(irq, &imx_irqsteer_irq_chip, handle_level_irq); + + return 0; +} + +static const struct irq_domain_ops imx_irqsteer_domain_ops = { + .map = imx_irqsteer_irq_map, + .xlate = irq_domain_xlate_onecell, +}; + +static void imx_irqsteer_irq_handler(struct irq_desc *desc) +{ + struct irqsteer_data *data = irq_desc_get_handler_data(desc); + int i; + + chained_irq_enter(irq_desc_get_chip(desc), desc); + + for (i = 0; i < data->irq_groups * 64; i += 32) { + int idx = imx_irqsteer_get_reg_index(data, i); + unsigned long irqmap; + int pos, virq; + + irqmap = readl_relaxed(data->regs + + CHANSTATUS(idx, data->irq_groups)); + + for_each_set_bit(pos, &irqmap, 32) { + virq = irq_find_mapping(data->domain, pos + i); + if (virq) + generic_handle_irq(virq); + } + } + + chained_irq_exit(irq_desc_get_chip(desc), desc); +} + +static int imx_irqsteer_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct irqsteer_data *data; + struct resource *res; + int ret; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + data->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(data->regs)) { + dev_err(&pdev->dev, "failed to initialize reg\n"); + return PTR_ERR(data->regs); + } + + data->irq = platform_get_irq(pdev, 0); + if (data->irq <= 0) { + dev_err(&pdev->dev, "failed to get irq\n"); + return -ENODEV; + } + + data->ipg_clk = devm_clk_get(&pdev->dev, "ipg"); + if (IS_ERR(data->ipg_clk)) { + ret = PTR_ERR(data->ipg_clk); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, "failed to get ipg clk: %d\n", ret); + return ret; + } + + raw_spin_lock_init(&data->lock); + + of_property_read_u32(np, "fsl,irq-groups", &data->irq_groups); + of_property_read_u32(np, "fsl,channel", &data->channel); + + if (IS_ENABLED(CONFIG_PM_SLEEP)) { + data->saved_reg = devm_kzalloc(&pdev->dev, + sizeof(u32) * data->irq_groups * 2, + GFP_KERNEL); + if (!data->saved_reg) + return -ENOMEM; + } + + ret = clk_prepare_enable(data->ipg_clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable ipg clk: %d\n", ret); + return ret; + } + + /* steer all IRQs into configured channel */ + writel_relaxed(BIT(data->channel), data->regs + CHANCTRL); + + data->domain = irq_domain_add_linear(np, data->irq_groups * 64, + &imx_irqsteer_domain_ops, data); + if (!data->domain) { + dev_err(&pdev->dev, "failed to create IRQ domain\n"); + clk_disable_unprepare(data->ipg_clk); + return -ENOMEM; + } + + irq_set_chained_handler_and_data(data->irq, imx_irqsteer_irq_handler, + data); + + platform_set_drvdata(pdev, data); + + return 0; +} + +static int imx_irqsteer_remove(struct platform_device *pdev) +{ + struct irqsteer_data *irqsteer_data = platform_get_drvdata(pdev); + + irq_set_chained_handler_and_data(irqsteer_data->irq, NULL, NULL); + irq_domain_remove(irqsteer_data->domain); + + clk_disable_unprepare(irqsteer_data->ipg_clk); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static void imx_irqsteer_save_regs(struct irqsteer_data *data) +{ + int i; + + for (i = 0; i < data->irq_groups * 2; i++) + data->saved_reg[i] = readl_relaxed(data->regs + + CHANMASK(i, data->irq_groups)); +} + +static void imx_irqsteer_restore_regs(struct irqsteer_data *data) +{ + int i; + + writel_relaxed(BIT(data->channel), data->regs + CHANCTRL); + for (i = 0; i < data->irq_groups * 2; i++) + writel_relaxed(data->saved_reg[i], + data->regs + CHANMASK(i, data->irq_groups)); +} + +static int imx_irqsteer_suspend(struct device *dev) +{ + struct irqsteer_data *irqsteer_data = dev_get_drvdata(dev); + + imx_irqsteer_save_regs(irqsteer_data); + clk_disable_unprepare(irqsteer_data->ipg_clk); + + return 0; +} + +static int imx_irqsteer_resume(struct device *dev) +{ + struct irqsteer_data *irqsteer_data = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(irqsteer_data->ipg_clk); + if (ret) { + dev_err(dev, "failed to enable ipg clk: %d\n", ret); + return ret; + } + imx_irqsteer_restore_regs(irqsteer_data); + + return 0; +} +#endif + +static const struct dev_pm_ops imx_irqsteer_pm_ops = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_irqsteer_suspend, imx_irqsteer_resume) +}; + +static const struct of_device_id imx_irqsteer_dt_ids[] = { + { .compatible = "fsl,imx-irqsteer", }, + {}, +}; + +static struct platform_driver imx_irqsteer_driver = { + .driver = { + .name = "imx-irqsteer", + .of_match_table = imx_irqsteer_dt_ids, + .pm = &imx_irqsteer_pm_ops, + }, + .probe = imx_irqsteer_probe, + .remove = imx_irqsteer_remove, +}; +builtin_platform_driver(imx_irqsteer_driver); diff --git a/drivers/irqchip/irq-madera.c b/drivers/irqchip/irq-madera.c new file mode 100644 index 000000000000..e9256dee1a45 --- /dev/null +++ b/drivers/irqchip/irq-madera.c @@ -0,0 +1,256 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Interrupt support for Cirrus Logic Madera codecs + * + * Copyright (C) 2015-2018 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#include <linux/module.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/of_irq.h> +#include <linux/irqchip/irq-madera.h> +#include <linux/mfd/madera/core.h> +#include <linux/mfd/madera/pdata.h> +#include <linux/mfd/madera/registers.h> + +#define MADERA_IRQ(_irq, _reg) \ + [MADERA_IRQ_ ## _irq] = { \ + .reg_offset = (_reg) - MADERA_IRQ1_STATUS_2, \ + .mask = MADERA_ ## _irq ## _EINT1 \ + } + +/* Mappings are the same for all Madera codecs */ +static const struct regmap_irq madera_irqs[MADERA_NUM_IRQ] = { + MADERA_IRQ(FLL1_LOCK, MADERA_IRQ1_STATUS_2), + MADERA_IRQ(FLL2_LOCK, MADERA_IRQ1_STATUS_2), + MADERA_IRQ(FLL3_LOCK, MADERA_IRQ1_STATUS_2), + MADERA_IRQ(FLLAO_LOCK, MADERA_IRQ1_STATUS_2), + + MADERA_IRQ(MICDET1, MADERA_IRQ1_STATUS_6), + MADERA_IRQ(MICDET2, MADERA_IRQ1_STATUS_6), + MADERA_IRQ(HPDET, MADERA_IRQ1_STATUS_6), + + MADERA_IRQ(MICD_CLAMP_RISE, MADERA_IRQ1_STATUS_7), + MADERA_IRQ(MICD_CLAMP_FALL, MADERA_IRQ1_STATUS_7), + MADERA_IRQ(JD1_RISE, MADERA_IRQ1_STATUS_7), + MADERA_IRQ(JD1_FALL, MADERA_IRQ1_STATUS_7), + + MADERA_IRQ(ASRC2_IN1_LOCK, MADERA_IRQ1_STATUS_9), + MADERA_IRQ(ASRC2_IN2_LOCK, MADERA_IRQ1_STATUS_9), + MADERA_IRQ(ASRC1_IN1_LOCK, MADERA_IRQ1_STATUS_9), + MADERA_IRQ(ASRC1_IN2_LOCK, MADERA_IRQ1_STATUS_9), + MADERA_IRQ(DRC2_SIG_DET, MADERA_IRQ1_STATUS_9), + MADERA_IRQ(DRC1_SIG_DET, MADERA_IRQ1_STATUS_9), + + MADERA_IRQ(DSP_IRQ1, MADERA_IRQ1_STATUS_11), + MADERA_IRQ(DSP_IRQ2, MADERA_IRQ1_STATUS_11), + MADERA_IRQ(DSP_IRQ3, MADERA_IRQ1_STATUS_11), + MADERA_IRQ(DSP_IRQ4, MADERA_IRQ1_STATUS_11), + MADERA_IRQ(DSP_IRQ5, MADERA_IRQ1_STATUS_11), + MADERA_IRQ(DSP_IRQ6, MADERA_IRQ1_STATUS_11), + MADERA_IRQ(DSP_IRQ7, MADERA_IRQ1_STATUS_11), + MADERA_IRQ(DSP_IRQ8, MADERA_IRQ1_STATUS_11), + MADERA_IRQ(DSP_IRQ9, MADERA_IRQ1_STATUS_11), + MADERA_IRQ(DSP_IRQ10, MADERA_IRQ1_STATUS_11), + MADERA_IRQ(DSP_IRQ11, MADERA_IRQ1_STATUS_11), + MADERA_IRQ(DSP_IRQ12, MADERA_IRQ1_STATUS_11), + MADERA_IRQ(DSP_IRQ13, MADERA_IRQ1_STATUS_11), + MADERA_IRQ(DSP_IRQ14, MADERA_IRQ1_STATUS_11), + MADERA_IRQ(DSP_IRQ15, MADERA_IRQ1_STATUS_11), + MADERA_IRQ(DSP_IRQ16, MADERA_IRQ1_STATUS_11), + + MADERA_IRQ(HP3R_SC, MADERA_IRQ1_STATUS_12), + MADERA_IRQ(HP3L_SC, MADERA_IRQ1_STATUS_12), + MADERA_IRQ(HP2R_SC, MADERA_IRQ1_STATUS_12), + MADERA_IRQ(HP2L_SC, MADERA_IRQ1_STATUS_12), + MADERA_IRQ(HP1R_SC, MADERA_IRQ1_STATUS_12), + MADERA_IRQ(HP1L_SC, MADERA_IRQ1_STATUS_12), + + MADERA_IRQ(SPK_OVERHEAT_WARN, MADERA_IRQ1_STATUS_15), + MADERA_IRQ(SPK_OVERHEAT, MADERA_IRQ1_STATUS_15), + + MADERA_IRQ(DSP1_BUS_ERR, MADERA_IRQ1_STATUS_33), + MADERA_IRQ(DSP2_BUS_ERR, MADERA_IRQ1_STATUS_33), + MADERA_IRQ(DSP3_BUS_ERR, MADERA_IRQ1_STATUS_33), + MADERA_IRQ(DSP4_BUS_ERR, MADERA_IRQ1_STATUS_33), + MADERA_IRQ(DSP5_BUS_ERR, MADERA_IRQ1_STATUS_33), + MADERA_IRQ(DSP6_BUS_ERR, MADERA_IRQ1_STATUS_33), + MADERA_IRQ(DSP7_BUS_ERR, MADERA_IRQ1_STATUS_33), +}; + +static const struct regmap_irq_chip madera_irq_chip = { + .name = "madera IRQ", + .status_base = MADERA_IRQ1_STATUS_2, + .mask_base = MADERA_IRQ1_MASK_2, + .ack_base = MADERA_IRQ1_STATUS_2, + .runtime_pm = true, + .num_regs = 32, + .irqs = madera_irqs, + .num_irqs = ARRAY_SIZE(madera_irqs), +}; + +#ifdef CONFIG_PM_SLEEP +static int madera_suspend(struct device *dev) +{ + struct madera *madera = dev_get_drvdata(dev->parent); + + dev_dbg(madera->irq_dev, "Suspend, disabling IRQ\n"); + + /* + * A runtime resume would be needed to access the chip interrupt + * controller but runtime pm doesn't function during suspend. + * Temporarily disable interrupts until we reach suspend_noirq state. + */ + disable_irq(madera->irq); + + return 0; +} + +static int madera_suspend_noirq(struct device *dev) +{ + struct madera *madera = dev_get_drvdata(dev->parent); + + dev_dbg(madera->irq_dev, "No IRQ suspend, reenabling IRQ\n"); + + /* Re-enable interrupts to service wakeup interrupts from the chip */ + enable_irq(madera->irq); + + return 0; +} + +static int madera_resume_noirq(struct device *dev) +{ + struct madera *madera = dev_get_drvdata(dev->parent); + + dev_dbg(madera->irq_dev, "No IRQ resume, disabling IRQ\n"); + + /* + * We can't handle interrupts until runtime pm is available again. + * Disable them temporarily. + */ + disable_irq(madera->irq); + + return 0; +} + +static int madera_resume(struct device *dev) +{ + struct madera *madera = dev_get_drvdata(dev->parent); + + dev_dbg(madera->irq_dev, "Resume, reenabling IRQ\n"); + + /* Interrupts can now be handled */ + enable_irq(madera->irq); + + return 0; +} +#endif + +static const struct dev_pm_ops madera_irq_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(madera_suspend, madera_resume) + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(madera_suspend_noirq, + madera_resume_noirq) +}; + +static int madera_irq_probe(struct platform_device *pdev) +{ + struct madera *madera = dev_get_drvdata(pdev->dev.parent); + struct irq_data *irq_data; + unsigned int irq_flags = 0; + int ret; + + dev_dbg(&pdev->dev, "probe\n"); + + /* + * Read the flags from the interrupt controller if not specified + * by pdata + */ + irq_flags = madera->pdata.irq_flags; + if (!irq_flags) { + irq_data = irq_get_irq_data(madera->irq); + if (!irq_data) { + dev_err(&pdev->dev, "Invalid IRQ: %d\n", madera->irq); + return -EINVAL; + } + + irq_flags = irqd_get_trigger_type(irq_data); + + /* Codec defaults to trigger low, use this if no flags given */ + if (irq_flags == IRQ_TYPE_NONE) + irq_flags = IRQF_TRIGGER_LOW; + } + + if (irq_flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { + dev_err(&pdev->dev, "Host interrupt not level-triggered\n"); + return -EINVAL; + } + + /* + * The silicon always starts at active-low, check if we need to + * switch to active-high. + */ + if (irq_flags & IRQF_TRIGGER_HIGH) { + ret = regmap_update_bits(madera->regmap, MADERA_IRQ1_CTRL, + MADERA_IRQ_POL_MASK, 0); + if (ret) { + dev_err(&pdev->dev, + "Failed to set IRQ polarity: %d\n", ret); + return ret; + } + } + + /* + * NOTE: regmap registers this against the OF node of the parent of + * the regmap - that is, against the mfd driver + */ + ret = regmap_add_irq_chip(madera->regmap, madera->irq, IRQF_ONESHOT, 0, + &madera_irq_chip, &madera->irq_data); + if (ret) { + dev_err(&pdev->dev, "add_irq_chip failed: %d\n", ret); + return ret; + } + + /* Save dev in parent MFD struct so it is accessible to siblings */ + madera->irq_dev = &pdev->dev; + + return 0; +} + +static int madera_irq_remove(struct platform_device *pdev) +{ + struct madera *madera = dev_get_drvdata(pdev->dev.parent); + + /* + * The IRQ is disabled by the parent MFD driver before + * it starts cleaning up all child drivers + */ + madera->irq_dev = NULL; + regmap_del_irq_chip(madera->irq, madera->irq_data); + + return 0; +} + +static struct platform_driver madera_irq_driver = { + .probe = &madera_irq_probe, + .remove = &madera_irq_remove, + .driver = { + .name = "madera-irq", + .pm = &madera_irq_pm_ops, + } +}; +module_platform_driver(madera_irq_driver); + +MODULE_SOFTDEP("pre: madera"); +MODULE_DESCRIPTION("Madera IRQ driver"); +MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/irqchip/irq-mscc-ocelot.c b/drivers/irqchip/irq-mscc-ocelot.c index b63e40c00a02..88143c0b700c 100644 --- a/drivers/irqchip/irq-mscc-ocelot.c +++ b/drivers/irqchip/irq-mscc-ocelot.c @@ -72,7 +72,7 @@ static int __init ocelot_irq_init(struct device_node *node, domain = irq_domain_add_linear(node, OCELOT_NR_IRQ, &irq_generic_chip_ops, NULL); if (!domain) { - pr_err("%s: unable to add irq domain\n", node->name); + pr_err("%pOFn: unable to add irq domain\n", node); return -ENOMEM; } @@ -80,14 +80,14 @@ static int __init ocelot_irq_init(struct device_node *node, "icpu", handle_level_irq, 0, 0, 0); if (ret) { - pr_err("%s: unable to alloc irq domain gc\n", node->name); + pr_err("%pOFn: unable to alloc irq domain gc\n", node); goto err_domain_remove; } gc = irq_get_domain_generic_chip(domain, 0); gc->reg_base = of_iomap(node, 0); if (!gc->reg_base) { - pr_err("%s: unable to map resource\n", node->name); + pr_err("%pOFn: unable to map resource\n", node); ret = -ENOMEM; goto err_gc_free; } diff --git a/drivers/irqchip/irq-rda-intc.c b/drivers/irqchip/irq-rda-intc.c new file mode 100644 index 000000000000..960168303b73 --- /dev/null +++ b/drivers/irqchip/irq-rda-intc.c @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * RDA8810PL SoC irqchip driver + * + * Copyright RDA Microelectronics Company Limited + * Copyright (c) 2017 Andreas Färber + * Copyright (c) 2018 Manivannan Sadhasivam + */ + +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqchip.h> +#include <linux/irqdomain.h> +#include <linux/of_address.h> + +#include <asm/exception.h> + +#define RDA_INTC_FINALSTATUS 0x00 +#define RDA_INTC_MASK_SET 0x08 +#define RDA_INTC_MASK_CLR 0x0c + +#define RDA_IRQ_MASK_ALL 0xFFFFFFFF + +#define RDA_NR_IRQS 32 + +static void __iomem *rda_intc_base; +static struct irq_domain *rda_irq_domain; + +static void rda_intc_mask_irq(struct irq_data *d) +{ + writel_relaxed(BIT(d->hwirq), rda_intc_base + RDA_INTC_MASK_CLR); +} + +static void rda_intc_unmask_irq(struct irq_data *d) +{ + writel_relaxed(BIT(d->hwirq), rda_intc_base + RDA_INTC_MASK_SET); +} + +static int rda_intc_set_type(struct irq_data *data, unsigned int flow_type) +{ + /* Hardware supports only level triggered interrupts */ + if ((flow_type & (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW)) == flow_type) + return 0; + + return -EINVAL; +} + +static void __exception_irq_entry rda_handle_irq(struct pt_regs *regs) +{ + u32 stat = readl_relaxed(rda_intc_base + RDA_INTC_FINALSTATUS); + u32 hwirq; + + while (stat) { + hwirq = __fls(stat); + handle_domain_irq(rda_irq_domain, hwirq, regs); + stat &= ~BIT(hwirq); + } +} + +static struct irq_chip rda_irq_chip = { + .name = "rda-intc", + .irq_mask = rda_intc_mask_irq, + .irq_unmask = rda_intc_unmask_irq, + .irq_set_type = rda_intc_set_type, +}; + +static int rda_irq_map(struct irq_domain *d, + unsigned int virq, irq_hw_number_t hw) +{ + irq_set_status_flags(virq, IRQ_LEVEL); + irq_set_chip_and_handler(virq, &rda_irq_chip, handle_level_irq); + irq_set_chip_data(virq, d->host_data); + irq_set_probe(virq); + + return 0; +} + +static const struct irq_domain_ops rda_irq_domain_ops = { + .map = rda_irq_map, + .xlate = irq_domain_xlate_onecell, +}; + +static int __init rda8810_intc_init(struct device_node *node, + struct device_node *parent) +{ + rda_intc_base = of_io_request_and_map(node, 0, "rda-intc"); + if (IS_ERR(rda_intc_base)) + return PTR_ERR(rda_intc_base); + + /* Mask all interrupt sources */ + writel_relaxed(RDA_IRQ_MASK_ALL, rda_intc_base + RDA_INTC_MASK_CLR); + + rda_irq_domain = irq_domain_create_linear(&node->fwnode, RDA_NR_IRQS, + &rda_irq_domain_ops, + rda_intc_base); + if (!rda_irq_domain) { + iounmap(rda_intc_base); + return -ENOMEM; + } + + set_handle_irq(rda_handle_irq); + + return 0; +} + +IRQCHIP_DECLARE(rda_intc, "rda,8810pl-intc", rda8810_intc_init); diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c index c6e6c9e9137a..8c039525703f 100644 --- a/drivers/irqchip/irq-renesas-intc-irqpin.c +++ b/drivers/irqchip/irq-renesas-intc-irqpin.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Renesas INTC External IRQ Pin Driver * * Copyright (C) 2013 Magnus Damm - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/init.h> diff --git a/drivers/irqchip/irq-renesas-irqc.c b/drivers/irqchip/irq-renesas-irqc.c index a4f11124024d..a449a7c839b3 100644 --- a/drivers/irqchip/irq-renesas-irqc.c +++ b/drivers/irqchip/irq-renesas-irqc.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Renesas IRQC Driver * * Copyright (C) 2013 Magnus Damm - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/init.h> diff --git a/drivers/irqchip/irq-stm32-exti.c b/drivers/irqchip/irq-stm32-exti.c index 0a2088e12d96..6edfd4bfa169 100644 --- a/drivers/irqchip/irq-stm32-exti.c +++ b/drivers/irqchip/irq-stm32-exti.c @@ -6,6 +6,8 @@ */ #include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/hwspinlock.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/irq.h> @@ -20,6 +22,9 @@ #define IRQS_PER_BANK 32 +#define HWSPNLCK_TIMEOUT 1000 /* usec */ +#define HWSPNLCK_RETRY_DELAY 100 /* usec */ + struct stm32_exti_bank { u32 imr_ofst; u32 emr_ofst; @@ -32,6 +37,12 @@ struct stm32_exti_bank { #define UNDEF_REG ~0 +enum stm32_exti_hwspinlock { + HWSPINLOCK_UNKNOWN, + HWSPINLOCK_NONE, + HWSPINLOCK_READY, +}; + struct stm32_desc_irq { u32 exti; u32 irq_parent; @@ -58,6 +69,9 @@ struct stm32_exti_host_data { void __iomem *base; struct stm32_exti_chip_data *chips_data; const struct stm32_exti_drv_data *drv_data; + struct device_node *node; + enum stm32_exti_hwspinlock hwlock_state; + struct hwspinlock *hwlock; }; static struct stm32_exti_host_data *stm32_host_data; @@ -269,6 +283,64 @@ static int stm32_exti_set_type(struct irq_data *d, return 0; } +static int stm32_exti_hwspin_lock(struct stm32_exti_chip_data *chip_data) +{ + struct stm32_exti_host_data *host_data = chip_data->host_data; + struct hwspinlock *hwlock; + int id, ret = 0, timeout = 0; + + /* first time, check for hwspinlock availability */ + if (unlikely(host_data->hwlock_state == HWSPINLOCK_UNKNOWN)) { + id = of_hwspin_lock_get_id(host_data->node, 0); + if (id >= 0) { + hwlock = hwspin_lock_request_specific(id); + if (hwlock) { + /* found valid hwspinlock */ + host_data->hwlock_state = HWSPINLOCK_READY; + host_data->hwlock = hwlock; + pr_debug("%s hwspinlock = %d\n", __func__, id); + } else { + host_data->hwlock_state = HWSPINLOCK_NONE; + } + } else if (id != -EPROBE_DEFER) { + host_data->hwlock_state = HWSPINLOCK_NONE; + } else { + /* hwspinlock driver shall be ready at that stage */ + ret = -EPROBE_DEFER; + } + } + + if (likely(host_data->hwlock_state == HWSPINLOCK_READY)) { + /* + * Use the x_raw API since we are under spin_lock protection. + * Do not use the x_timeout API because we are under irq_disable + * mode (see __setup_irq()) + */ + do { + ret = hwspin_trylock_raw(host_data->hwlock); + if (!ret) + return 0; + + udelay(HWSPNLCK_RETRY_DELAY); + timeout += HWSPNLCK_RETRY_DELAY; + } while (timeout < HWSPNLCK_TIMEOUT); + + if (ret == -EBUSY) + ret = -ETIMEDOUT; + } + + if (ret) + pr_err("%s can't get hwspinlock (%d)\n", __func__, ret); + + return ret; +} + +static void stm32_exti_hwspin_unlock(struct stm32_exti_chip_data *chip_data) +{ + if (likely(chip_data->host_data->hwlock_state == HWSPINLOCK_READY)) + hwspin_unlock_raw(chip_data->host_data->hwlock); +} + static int stm32_irq_set_type(struct irq_data *d, unsigned int type) { struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); @@ -279,21 +351,26 @@ static int stm32_irq_set_type(struct irq_data *d, unsigned int type) irq_gc_lock(gc); + err = stm32_exti_hwspin_lock(chip_data); + if (err) + goto unlock; + rtsr = irq_reg_readl(gc, stm32_bank->rtsr_ofst); ftsr = irq_reg_readl(gc, stm32_bank->ftsr_ofst); err = stm32_exti_set_type(d, type, &rtsr, &ftsr); - if (err) { - irq_gc_unlock(gc); - return err; - } + if (err) + goto unspinlock; irq_reg_writel(gc, rtsr, stm32_bank->rtsr_ofst); irq_reg_writel(gc, ftsr, stm32_bank->ftsr_ofst); +unspinlock: + stm32_exti_hwspin_unlock(chip_data); +unlock: irq_gc_unlock(gc); - return 0; + return err; } static void stm32_chip_suspend(struct stm32_exti_chip_data *chip_data, @@ -460,20 +537,27 @@ static int stm32_exti_h_set_type(struct irq_data *d, unsigned int type) int err; raw_spin_lock(&chip_data->rlock); + + err = stm32_exti_hwspin_lock(chip_data); + if (err) + goto unlock; + rtsr = readl_relaxed(base + stm32_bank->rtsr_ofst); ftsr = readl_relaxed(base + stm32_bank->ftsr_ofst); err = stm32_exti_set_type(d, type, &rtsr, &ftsr); - if (err) { - raw_spin_unlock(&chip_data->rlock); - return err; - } + if (err) + goto unspinlock; writel_relaxed(rtsr, base + stm32_bank->rtsr_ofst); writel_relaxed(ftsr, base + stm32_bank->ftsr_ofst); + +unspinlock: + stm32_exti_hwspin_unlock(chip_data); +unlock: raw_spin_unlock(&chip_data->rlock); - return 0; + return err; } static int stm32_exti_h_set_wake(struct irq_data *d, unsigned int on) @@ -599,6 +683,8 @@ stm32_exti_host_data *stm32_exti_host_init(const struct stm32_exti_drv_data *dd, return NULL; host_data->drv_data = dd; + host_data->node = node; + host_data->hwlock_state = HWSPINLOCK_UNKNOWN; host_data->chips_data = kcalloc(dd->bank_nr, sizeof(struct stm32_exti_chip_data), GFP_KERNEL); @@ -625,8 +711,7 @@ free_host_data: static struct stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data, - u32 bank_idx, - struct device_node *node) + u32 bank_idx) { const struct stm32_exti_bank *stm32_bank; struct stm32_exti_chip_data *chip_data; @@ -656,8 +741,7 @@ stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data, if (stm32_bank->fpr_ofst != UNDEF_REG) writel_relaxed(~0UL, base + stm32_bank->fpr_ofst); - pr_info("%s: bank%d, External IRQs available:%#x\n", - node->full_name, bank_idx, irqs_mask); + pr_info("%pOF: bank%d\n", h_data->node, bank_idx); return chip_data; } @@ -678,8 +762,8 @@ static int __init stm32_exti_init(const struct stm32_exti_drv_data *drv_data, domain = irq_domain_add_linear(node, drv_data->bank_nr * IRQS_PER_BANK, &irq_exti_domain_ops, NULL); if (!domain) { - pr_err("%s: Could not register interrupt domain.\n", - node->name); + pr_err("%pOFn: Could not register interrupt domain.\n", + node); ret = -ENOMEM; goto out_unmap; } @@ -697,7 +781,7 @@ static int __init stm32_exti_init(const struct stm32_exti_drv_data *drv_data, struct stm32_exti_chip_data *chip_data; stm32_bank = drv_data->exti_banks[i]; - chip_data = stm32_exti_chip_init(host_data, i, node); + chip_data = stm32_exti_chip_init(host_data, i); gc = irq_get_domain_generic_chip(domain, i * IRQS_PER_BANK); @@ -760,7 +844,7 @@ __init stm32_exti_hierarchy_init(const struct stm32_exti_drv_data *drv_data, return -ENOMEM; for (i = 0; i < drv_data->bank_nr; i++) - stm32_exti_chip_init(host_data, i, node); + stm32_exti_chip_init(host_data, i); domain = irq_domain_add_hierarchy(parent_domain, 0, drv_data->bank_nr * IRQS_PER_BANK, @@ -768,7 +852,7 @@ __init stm32_exti_hierarchy_init(const struct stm32_exti_drv_data *drv_data, host_data); if (!domain) { - pr_err("%s: Could not register exti domain.\n", node->name); + pr_err("%pOFn: Could not register exti domain.\n", node); ret = -ENOMEM; goto out_unmap; } diff --git a/drivers/irqchip/irq-sun4i.c b/drivers/irqchip/irq-sun4i.c index e3e5b9132b75..fb78d6623556 100644 --- a/drivers/irqchip/irq-sun4i.c +++ b/drivers/irqchip/irq-sun4i.c @@ -28,11 +28,21 @@ #define SUN4I_IRQ_NMI_CTRL_REG 0x0c #define SUN4I_IRQ_PENDING_REG(x) (0x10 + 0x4 * x) #define SUN4I_IRQ_FIQ_PENDING_REG(x) (0x20 + 0x4 * x) -#define SUN4I_IRQ_ENABLE_REG(x) (0x40 + 0x4 * x) -#define SUN4I_IRQ_MASK_REG(x) (0x50 + 0x4 * x) +#define SUN4I_IRQ_ENABLE_REG(data, x) ((data)->enable_reg_offset + 0x4 * x) +#define SUN4I_IRQ_MASK_REG(data, x) ((data)->mask_reg_offset + 0x4 * x) +#define SUN4I_IRQ_ENABLE_REG_OFFSET 0x40 +#define SUN4I_IRQ_MASK_REG_OFFSET 0x50 +#define SUNIV_IRQ_ENABLE_REG_OFFSET 0x20 +#define SUNIV_IRQ_MASK_REG_OFFSET 0x30 + +struct sun4i_irq_chip_data { + void __iomem *irq_base; + struct irq_domain *irq_domain; + u32 enable_reg_offset; + u32 mask_reg_offset; +}; -static void __iomem *sun4i_irq_base; -static struct irq_domain *sun4i_irq_domain; +static struct sun4i_irq_chip_data *irq_ic_data; static void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs); @@ -43,7 +53,7 @@ static void sun4i_irq_ack(struct irq_data *irqd) if (irq != 0) return; /* Only IRQ 0 / the ENMI needs to be acked */ - writel(BIT(0), sun4i_irq_base + SUN4I_IRQ_PENDING_REG(0)); + writel(BIT(0), irq_ic_data->irq_base + SUN4I_IRQ_PENDING_REG(0)); } static void sun4i_irq_mask(struct irq_data *irqd) @@ -53,9 +63,10 @@ static void sun4i_irq_mask(struct irq_data *irqd) int reg = irq / 32; u32 val; - val = readl(sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(reg)); + val = readl(irq_ic_data->irq_base + + SUN4I_IRQ_ENABLE_REG(irq_ic_data, reg)); writel(val & ~(1 << irq_off), - sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(reg)); + irq_ic_data->irq_base + SUN4I_IRQ_ENABLE_REG(irq_ic_data, reg)); } static void sun4i_irq_unmask(struct irq_data *irqd) @@ -65,9 +76,10 @@ static void sun4i_irq_unmask(struct irq_data *irqd) int reg = irq / 32; u32 val; - val = readl(sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(reg)); + val = readl(irq_ic_data->irq_base + + SUN4I_IRQ_ENABLE_REG(irq_ic_data, reg)); writel(val | (1 << irq_off), - sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(reg)); + irq_ic_data->irq_base + SUN4I_IRQ_ENABLE_REG(irq_ic_data, reg)); } static struct irq_chip sun4i_irq_chip = { @@ -95,42 +107,76 @@ static const struct irq_domain_ops sun4i_irq_ops = { static int __init sun4i_of_init(struct device_node *node, struct device_node *parent) { - sun4i_irq_base = of_iomap(node, 0); - if (!sun4i_irq_base) + irq_ic_data->irq_base = of_iomap(node, 0); + if (!irq_ic_data->irq_base) panic("%pOF: unable to map IC registers\n", node); /* Disable all interrupts */ - writel(0, sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(0)); - writel(0, sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(1)); - writel(0, sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(2)); + writel(0, irq_ic_data->irq_base + SUN4I_IRQ_ENABLE_REG(irq_ic_data, 0)); + writel(0, irq_ic_data->irq_base + SUN4I_IRQ_ENABLE_REG(irq_ic_data, 1)); + writel(0, irq_ic_data->irq_base + SUN4I_IRQ_ENABLE_REG(irq_ic_data, 2)); /* Unmask all the interrupts, ENABLE_REG(x) is used for masking */ - writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(0)); - writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(1)); - writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(2)); + writel(0, irq_ic_data->irq_base + SUN4I_IRQ_MASK_REG(irq_ic_data, 0)); + writel(0, irq_ic_data->irq_base + SUN4I_IRQ_MASK_REG(irq_ic_data, 1)); + writel(0, irq_ic_data->irq_base + SUN4I_IRQ_MASK_REG(irq_ic_data, 2)); /* Clear all the pending interrupts */ - writel(0xffffffff, sun4i_irq_base + SUN4I_IRQ_PENDING_REG(0)); - writel(0xffffffff, sun4i_irq_base + SUN4I_IRQ_PENDING_REG(1)); - writel(0xffffffff, sun4i_irq_base + SUN4I_IRQ_PENDING_REG(2)); + writel(0xffffffff, irq_ic_data->irq_base + SUN4I_IRQ_PENDING_REG(0)); + writel(0xffffffff, irq_ic_data->irq_base + SUN4I_IRQ_PENDING_REG(1)); + writel(0xffffffff, irq_ic_data->irq_base + SUN4I_IRQ_PENDING_REG(2)); /* Enable protection mode */ - writel(0x01, sun4i_irq_base + SUN4I_IRQ_PROTECTION_REG); + writel(0x01, irq_ic_data->irq_base + SUN4I_IRQ_PROTECTION_REG); /* Configure the external interrupt source type */ - writel(0x00, sun4i_irq_base + SUN4I_IRQ_NMI_CTRL_REG); + writel(0x00, irq_ic_data->irq_base + SUN4I_IRQ_NMI_CTRL_REG); - sun4i_irq_domain = irq_domain_add_linear(node, 3 * 32, + irq_ic_data->irq_domain = irq_domain_add_linear(node, 3 * 32, &sun4i_irq_ops, NULL); - if (!sun4i_irq_domain) + if (!irq_ic_data->irq_domain) panic("%pOF: unable to create IRQ domain\n", node); set_handle_irq(sun4i_handle_irq); return 0; } -IRQCHIP_DECLARE(allwinner_sun4i_ic, "allwinner,sun4i-a10-ic", sun4i_of_init); + +static int __init sun4i_ic_of_init(struct device_node *node, + struct device_node *parent) +{ + irq_ic_data = kzalloc(sizeof(struct sun4i_irq_chip_data), GFP_KERNEL); + if (!irq_ic_data) { + pr_err("kzalloc failed!\n"); + return -ENOMEM; + } + + irq_ic_data->enable_reg_offset = SUN4I_IRQ_ENABLE_REG_OFFSET; + irq_ic_data->mask_reg_offset = SUN4I_IRQ_MASK_REG_OFFSET; + + return sun4i_of_init(node, parent); +} + +IRQCHIP_DECLARE(allwinner_sun4i_ic, "allwinner,sun4i-a10-ic", sun4i_ic_of_init); + +static int __init suniv_ic_of_init(struct device_node *node, + struct device_node *parent) +{ + irq_ic_data = kzalloc(sizeof(struct sun4i_irq_chip_data), GFP_KERNEL); + if (!irq_ic_data) { + pr_err("kzalloc failed!\n"); + return -ENOMEM; + } + + irq_ic_data->enable_reg_offset = SUNIV_IRQ_ENABLE_REG_OFFSET; + irq_ic_data->mask_reg_offset = SUNIV_IRQ_MASK_REG_OFFSET; + + return sun4i_of_init(node, parent); +} + +IRQCHIP_DECLARE(allwinner_sunvi_ic, "allwinner,suniv-f1c100s-ic", + suniv_ic_of_init); static void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs) { @@ -146,13 +192,15 @@ static void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs) * the extra check in the common case of 1 hapening after having * read the vector-reg once. */ - hwirq = readl(sun4i_irq_base + SUN4I_IRQ_VECTOR_REG) >> 2; + hwirq = readl(irq_ic_data->irq_base + SUN4I_IRQ_VECTOR_REG) >> 2; if (hwirq == 0 && - !(readl(sun4i_irq_base + SUN4I_IRQ_PENDING_REG(0)) & BIT(0))) + !(readl(irq_ic_data->irq_base + SUN4I_IRQ_PENDING_REG(0)) & + BIT(0))) return; do { - handle_domain_irq(sun4i_irq_domain, hwirq, regs); - hwirq = readl(sun4i_irq_base + SUN4I_IRQ_VECTOR_REG) >> 2; + handle_domain_irq(irq_ic_data->irq_domain, hwirq, regs); + hwirq = readl(irq_ic_data->irq_base + + SUN4I_IRQ_VECTOR_REG) >> 2; } while (hwirq != 0); } diff --git a/drivers/irqchip/irq-tango.c b/drivers/irqchip/irq-tango.c index 580e2d72b9ba..ae28d8648679 100644 --- a/drivers/irqchip/irq-tango.c +++ b/drivers/irqchip/irq-tango.c @@ -184,11 +184,11 @@ static int __init tangox_irq_init(void __iomem *base, struct resource *baseres, irq = irq_of_parse_and_map(node, 0); if (!irq) - panic("%s: failed to get IRQ", node->name); + panic("%pOFn: failed to get IRQ", node); err = of_address_to_resource(node, 0, &res); if (err) - panic("%s: failed to get address", node->name); + panic("%pOFn: failed to get address", node); chip = kzalloc(sizeof(*chip), GFP_KERNEL); chip->ctl = res.start - baseres->start; @@ -196,12 +196,12 @@ static int __init tangox_irq_init(void __iomem *base, struct resource *baseres, dom = irq_domain_add_linear(node, 64, &irq_generic_chip_ops, chip); if (!dom) - panic("%s: failed to create irqdomain", node->name); + panic("%pOFn: failed to create irqdomain", node); err = irq_alloc_domain_generic_chips(dom, 32, 2, node->name, handle_level_irq, 0, 0, 0); if (err) - panic("%s: failed to allocate irqchip", node->name); + panic("%pOFn: failed to allocate irqchip", node); tangox_irq_domain_init(dom); @@ -219,7 +219,7 @@ static int __init tangox_of_irq_init(struct device_node *node, base = of_iomap(node, 0); if (!base) - panic("%s: of_iomap failed", node->name); + panic("%pOFn: of_iomap failed", node); of_address_to_resource(node, 0, &res); diff --git a/include/linux/irq_sim.h b/include/linux/irq_sim.h index 630a57e55db6..4500d453a63e 100644 --- a/include/linux/irq_sim.h +++ b/include/linux/irq_sim.h @@ -16,7 +16,7 @@ struct irq_sim_work_ctx { struct irq_work work; - int irq; + unsigned long *pending; }; struct irq_sim_irq_ctx { diff --git a/include/linux/irqchip/irq-madera.h b/include/linux/irqchip/irq-madera.h new file mode 100644 index 000000000000..1160fa3769ae --- /dev/null +++ b/include/linux/irqchip/irq-madera.h @@ -0,0 +1,132 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Interrupt support for Cirrus Logic Madera codecs + * + * Copyright (C) 2016-2018 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#ifndef IRQCHIP_MADERA_H +#define IRQCHIP_MADERA_H + +#include <linux/interrupt.h> +#include <linux/mfd/madera/core.h> + +#define MADERA_IRQ_FLL1_LOCK 0 +#define MADERA_IRQ_FLL2_LOCK 1 +#define MADERA_IRQ_FLL3_LOCK 2 +#define MADERA_IRQ_FLLAO_LOCK 3 +#define MADERA_IRQ_CLK_SYS_ERR 4 +#define MADERA_IRQ_CLK_ASYNC_ERR 5 +#define MADERA_IRQ_CLK_DSP_ERR 6 +#define MADERA_IRQ_HPDET 7 +#define MADERA_IRQ_MICDET1 8 +#define MADERA_IRQ_MICDET2 9 +#define MADERA_IRQ_JD1_RISE 10 +#define MADERA_IRQ_JD1_FALL 11 +#define MADERA_IRQ_JD2_RISE 12 +#define MADERA_IRQ_JD2_FALL 13 +#define MADERA_IRQ_MICD_CLAMP_RISE 14 +#define MADERA_IRQ_MICD_CLAMP_FALL 15 +#define MADERA_IRQ_DRC2_SIG_DET 16 +#define MADERA_IRQ_DRC1_SIG_DET 17 +#define MADERA_IRQ_ASRC1_IN1_LOCK 18 +#define MADERA_IRQ_ASRC1_IN2_LOCK 19 +#define MADERA_IRQ_ASRC2_IN1_LOCK 20 +#define MADERA_IRQ_ASRC2_IN2_LOCK 21 +#define MADERA_IRQ_DSP_IRQ1 22 +#define MADERA_IRQ_DSP_IRQ2 23 +#define MADERA_IRQ_DSP_IRQ3 24 +#define MADERA_IRQ_DSP_IRQ4 25 +#define MADERA_IRQ_DSP_IRQ5 26 +#define MADERA_IRQ_DSP_IRQ6 27 +#define MADERA_IRQ_DSP_IRQ7 28 +#define MADERA_IRQ_DSP_IRQ8 29 +#define MADERA_IRQ_DSP_IRQ9 30 +#define MADERA_IRQ_DSP_IRQ10 31 +#define MADERA_IRQ_DSP_IRQ11 32 +#define MADERA_IRQ_DSP_IRQ12 33 +#define MADERA_IRQ_DSP_IRQ13 34 +#define MADERA_IRQ_DSP_IRQ14 35 +#define MADERA_IRQ_DSP_IRQ15 36 +#define MADERA_IRQ_DSP_IRQ16 37 +#define MADERA_IRQ_HP1L_SC 38 +#define MADERA_IRQ_HP1R_SC 39 +#define MADERA_IRQ_HP2L_SC 40 +#define MADERA_IRQ_HP2R_SC 41 +#define MADERA_IRQ_HP3L_SC 42 +#define MADERA_IRQ_HP3R_SC 43 +#define MADERA_IRQ_SPKOUTL_SC 44 +#define MADERA_IRQ_SPKOUTR_SC 45 +#define MADERA_IRQ_HP1L_ENABLE_DONE 46 +#define MADERA_IRQ_HP1R_ENABLE_DONE 47 +#define MADERA_IRQ_HP2L_ENABLE_DONE 48 +#define MADERA_IRQ_HP2R_ENABLE_DONE 49 +#define MADERA_IRQ_HP3L_ENABLE_DONE 50 +#define MADERA_IRQ_HP3R_ENABLE_DONE 51 +#define MADERA_IRQ_SPKOUTL_ENABLE_DONE 52 +#define MADERA_IRQ_SPKOUTR_ENABLE_DONE 53 +#define MADERA_IRQ_SPK_SHUTDOWN 54 +#define MADERA_IRQ_SPK_OVERHEAT 55 +#define MADERA_IRQ_SPK_OVERHEAT_WARN 56 +#define MADERA_IRQ_GPIO1 57 +#define MADERA_IRQ_GPIO2 58 +#define MADERA_IRQ_GPIO3 59 +#define MADERA_IRQ_GPIO4 60 +#define MADERA_IRQ_GPIO5 61 +#define MADERA_IRQ_GPIO6 62 +#define MADERA_IRQ_GPIO7 63 +#define MADERA_IRQ_GPIO8 64 +#define MADERA_IRQ_DSP1_BUS_ERR 65 +#define MADERA_IRQ_DSP2_BUS_ERR 66 +#define MADERA_IRQ_DSP3_BUS_ERR 67 +#define MADERA_IRQ_DSP4_BUS_ERR 68 +#define MADERA_IRQ_DSP5_BUS_ERR 69 +#define MADERA_IRQ_DSP6_BUS_ERR 70 +#define MADERA_IRQ_DSP7_BUS_ERR 71 + +#define MADERA_NUM_IRQ 72 + +/* + * These wrapper functions are for use by other child drivers of the + * same parent MFD. + */ +static inline int madera_get_irq_mapping(struct madera *madera, int irq) +{ + if (!madera->irq_dev) + return -ENODEV; + + return regmap_irq_get_virq(madera->irq_data, irq); +} + +static inline int madera_request_irq(struct madera *madera, int irq, + const char *name, + irq_handler_t handler, void *data) +{ + irq = madera_get_irq_mapping(madera, irq); + if (irq < 0) + return irq; + + return request_threaded_irq(irq, NULL, handler, IRQF_ONESHOT, name, + data); +} + +static inline void madera_free_irq(struct madera *madera, int irq, void *data) +{ + irq = madera_get_irq_mapping(madera, irq); + if (irq < 0) + return; + + free_irq(irq, data); +} + +static inline int madera_set_irq_wake(struct madera *madera, int irq, int on) +{ + irq = madera_get_irq_mapping(madera, irq); + if (irq < 0) + return irq; + + return irq_set_irq_wake(irq, on); +} + +#endif diff --git a/include/linux/msi.h b/include/linux/msi.h index 0e9c50052ff3..eb213b87617c 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -116,6 +116,8 @@ struct msi_desc { list_first_entry(dev_to_msi_list((dev)), struct msi_desc, list) #define for_each_msi_entry(desc, dev) \ list_for_each_entry((desc), dev_to_msi_list((dev)), list) +#define for_each_msi_entry_safe(desc, tmp, dev) \ + list_for_each_entry_safe((desc), (tmp), dev_to_msi_list((dev)), list) #ifdef CONFIG_PCI_MSI #define first_pci_msi_entry(pdev) first_msi_entry(&(pdev)->dev) diff --git a/kernel/irq/irq_sim.c b/kernel/irq/irq_sim.c index dd20d0d528d4..98a20e1594ce 100644 --- a/kernel/irq/irq_sim.c +++ b/kernel/irq/irq_sim.c @@ -34,9 +34,20 @@ static struct irq_chip irq_sim_irqchip = { static void irq_sim_handle_irq(struct irq_work *work) { struct irq_sim_work_ctx *work_ctx; + unsigned int offset = 0; + struct irq_sim *sim; + int irqnum; work_ctx = container_of(work, struct irq_sim_work_ctx, work); - handle_simple_irq(irq_to_desc(work_ctx->irq)); + sim = container_of(work_ctx, struct irq_sim, work_ctx); + + while (!bitmap_empty(work_ctx->pending, sim->irq_count)) { + offset = find_next_bit(work_ctx->pending, + sim->irq_count, offset); + clear_bit(offset, work_ctx->pending); + irqnum = irq_sim_irqnum(sim, offset); + handle_simple_irq(irq_to_desc(irqnum)); + } } /** @@ -63,6 +74,13 @@ int irq_sim_init(struct irq_sim *sim, unsigned int num_irqs) return sim->irq_base; } + sim->work_ctx.pending = bitmap_zalloc(num_irqs, GFP_KERNEL); + if (!sim->work_ctx.pending) { + kfree(sim->irqs); + irq_free_descs(sim->irq_base, num_irqs); + return -ENOMEM; + } + for (i = 0; i < num_irqs; i++) { sim->irqs[i].irqnum = sim->irq_base + i; sim->irqs[i].enabled = false; @@ -89,6 +107,7 @@ EXPORT_SYMBOL_GPL(irq_sim_init); void irq_sim_fini(struct irq_sim *sim) { irq_work_sync(&sim->work_ctx.work); + bitmap_free(sim->work_ctx.pending); irq_free_descs(sim->irq_base, sim->irq_count); kfree(sim->irqs); } @@ -143,7 +162,7 @@ EXPORT_SYMBOL_GPL(devm_irq_sim_init); void irq_sim_fire(struct irq_sim *sim, unsigned int offset) { if (sim->irqs[offset].enabled) { - sim->work_ctx.irq = irq_sim_irqnum(sim, offset); + set_bit(offset, sim->work_ctx.pending); irq_work_queue(&sim->work_ctx.work); } } |