irqchip updates for Linux 5.13

New HW support:
 
 - New driver for the Nuvoton WPCM450 interrupt controller
 - New driver for the IDT 79rc3243x interrupt controller
 - Add support for interrupt trigger configuration to the MStar irqchip
 - Add more external interrupt support to the STM32 irqchip
 - Add new compatible strings for QCOM SC7280 to the qcom-pdc binding
 
 Fixes and cleanups:
 
 - Drop irq_create_strict_mappings() and irq_create_identity_mapping()
   from the irqdomain API, with cleanups in a couple of drivers
 - Fix nested NMI issue with spurious interrupts on GICv3
 - Don't allow GICv4.1 vSGIs when the CPU doesn't support them
 - Various cleanups and minor fixes
 -----BEGIN PGP SIGNATURE-----
 
 iQJDBAABCgAtFiEEn9UcU+C1Yxj9lZw9I9DQutE9ekMFAmCD5kwPHG1hekBrZXJu
 ZWwub3JnAAoJECPQ0LrRPXpDCWsQAL5yHXtApf4l3F0W99SJIooumrQh3UR6nENG
 2WVR66g+MiuZ/JQcHAojdLQ6W6K9W8eTcY3hRNFCqlI1lrKffz6ovstuYg3Wphog
 JX1gQYcpqt67WYtb/TVw3JM5D3NLU4XKPKZPhRzSHv5G9utI2QeAv13EBcPoHxZd
 UBRAEdUrv90KIFDe2CxWo8B5ra07xfgOpDvlYYKlee+jQLtf6i4Kj7Tm0XoK3hoW
 w0Mo//5r2SggdXfFLW1sm0BGs0bpJMSNixKCZWRfXbnZLAYIaBynSoLT9XoYT/uC
 FDegtFZ9IG/5NXJ1d3Yl0RjsPp+iPUOOTq/5gAoXI0hRCLZ1f8G1IuDEoIf8ElOg
 kxA1JpYE1fewxNt7oh48BAs3Qa3fdjJ1+k6gFlau4ctJBjxTHMz7v7lr7PmjhPz7
 HgcmzFCu9Wb8pj1IDHMINkOMmAiQhgr3N0WK372wQyNE8Z8iB0ZeCYX9jAV5YTK6
 eQdsDgNW18rv1ks/f7vzJw4EHRUM2tzSYimgf3oW+EJq6xKacMHfDMp9ERtHcnfJ
 +4CCEEafrSOj/KsNpNnA7Bq3Qjh+RdRXDtCPsoGQ3LS1L5/JOaUoSmrCkWNNfXuZ
 kUKTrNzopmMPvvwx6Q1YUypMbKCloNvlO3IgKalKNVP5drWA184abOIU2MGp+yI1
 LAA8SFYU
 =RqVj
 -----END PGP SIGNATURE-----

Merge tag 'irqchip-5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms into irq/core

Pull irqchip and irqdomain updates from Marc Zyngier:

 New HW support:

  - New driver for the Nuvoton WPCM450 interrupt controller
  - New driver for the IDT 79rc3243x interrupt controller
  - Add support for interrupt trigger configuration to the MStar irqchip
  - Add more external interrupt support to the STM32 irqchip
  - Add new compatible strings for QCOM SC7280 to the qcom-pdc binding

 Fixes and cleanups:

  - Drop irq_create_strict_mappings() and irq_create_identity_mapping()
    from the irqdomain API, with cleanups in a couple of drivers
  - Fix nested NMI issue with spurious interrupts on GICv3
  - Don't allow GICv4.1 vSGIs when the CPU doesn't support them
  - Various cleanups and minor fixes

Link: https://lore.kernel.org/r/20210424094640.1731920-1-maz@kernel.org
This commit is contained in:
Thomas Gleixner 2021-04-24 21:18:44 +02:00
commit 765822e156
28 changed files with 578 additions and 137 deletions

View file

@ -0,0 +1,48 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/interrupt-controller/idt,32434-pic.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: IDT 79RC32434 Interrupt Controller Device Tree Bindings
maintainers:
- Thomas Bogendoerfer <tsbogend@alpha.franken.de>
allOf:
- $ref: /schemas/interrupt-controller.yaml#
properties:
"#interrupt-cells":
const: 1
compatible:
const: idt,32434-pic
reg:
maxItems: 1
interrupt-controller: true
required:
- "#interrupt-cells"
- compatible
- reg
- interrupt-controller
additionalProperties: false
examples:
- |
idtpic3: interrupt-controller@3800c {
compatible = "idt,32434-pic";
reg = <0x3800c 0x0c>;
interrupt-controller;
#interrupt-cells = <1>;
interrupt-parent = <&cpuintc>;
interrupts = <3>;
};
...

View file

@ -23,6 +23,7 @@ properties:
- enum:
- ingenic,jz4775-intc
- ingenic,jz4770-intc
- ingenic,jz4760b-intc
- const: ingenic,jz4760-intc
- items:
- const: ingenic,x1000-intc

View file

@ -0,0 +1,39 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/interrupt-controller/nuvoton,wpcm450-aic.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Nuvoton WPCM450 Advanced Interrupt Controller bindings
maintainers:
- Jonathan Neuschäfer <j.neuschaefer@gmx.net>
properties:
'#interrupt-cells':
const: 2
compatible:
const: nuvoton,wpcm450-aic
interrupt-controller: true
reg:
maxItems: 1
additionalProperties: false
required:
- '#interrupt-cells'
- compatible
- reg
- interrupt-controller
examples:
- |
aic: interrupt-controller@b8002000 {
compatible = "nuvoton,wpcm450-aic";
reg = <0xb8002000 0x1000>;
interrupt-controller;
#interrupt-cells = <2>;
};

View file

@ -19,6 +19,7 @@ Properties:
Value type: <string>
Definition: Should contain "qcom,<soc>-pdc" and "qcom,pdc"
- "qcom,sc7180-pdc": For SC7180
- "qcom,sc7280-pdc": For SC7280
- "qcom,sdm845-pdc": For SDM845
- "qcom,sdm8250-pdc": For SM8250
- "qcom,sdm8350-pdc": For SM8350

View file

@ -348,6 +348,7 @@ config ARCH_EP93XX
select ARM_AMBA
imply ARM_PATCH_PHYS_VIRT
select ARM_VIC
select GENERIC_IRQ_MULTI_HANDLER
select AUTO_ZRELADDR
select CLKDEV_LOOKUP
select CLKSRC_MMIO

View file

@ -147,22 +147,20 @@ static int cplds_probe(struct platform_device *pdev)
}
irq_set_irq_wake(fpga->irq, 1);
fpga->irqdomain = irq_domain_add_linear(pdev->dev.of_node,
CPLDS_NB_IRQ,
&cplds_irq_domain_ops, fpga);
if (base_irq)
fpga->irqdomain = irq_domain_add_legacy(pdev->dev.of_node,
CPLDS_NB_IRQ,
base_irq, 0,
&cplds_irq_domain_ops,
fpga);
else
fpga->irqdomain = irq_domain_add_linear(pdev->dev.of_node,
CPLDS_NB_IRQ,
&cplds_irq_domain_ops,
fpga);
if (!fpga->irqdomain)
return -ENODEV;
if (base_irq) {
ret = irq_create_strict_mappings(fpga->irqdomain, base_irq, 0,
CPLDS_NB_IRQ);
if (ret) {
dev_err(&pdev->dev, "couldn't create the irq mapping %d..%d\n",
base_irq, base_irq + CPLDS_NB_IRQ);
return ret;
}
}
return 0;
}

View file

@ -86,7 +86,7 @@ static unsigned long vgic_mmio_read_v3_misc(struct kvm_vcpu *vcpu,
}
break;
case GICD_TYPER2:
if (kvm_vgic_global_state.has_gicv4_1)
if (kvm_vgic_global_state.has_gicv4_1 && gic_cpuif_has_vsgi())
value = GICD_TYPER2_nASSGIcap;
break;
case GICD_IIDR:
@ -119,7 +119,7 @@ static void vgic_mmio_write_v3_misc(struct kvm_vcpu *vcpu,
dist->enabled = val & GICD_CTLR_ENABLE_SS_G1;
/* Not a GICv4.1? No HW SGIs */
if (!kvm_vgic_global_state.has_gicv4_1)
if (!kvm_vgic_global_state.has_gicv4_1 || !gic_cpuif_has_vsgi())
val &= ~GICD_CTLR_nASSGIreq;
/* Dist stays enabled? nASSGIreq is RO */

View file

@ -276,10 +276,6 @@ asmlinkage void plat_irq_dispatch(void)
}
#ifdef CONFIG_CPU_XLP
static const struct irq_domain_ops xlp_pic_irq_domain_ops = {
.xlate = irq_domain_xlate_onetwocell,
};
static int __init xlp_of_pic_init(struct device_node *node,
struct device_node *parent)
{
@ -324,7 +320,7 @@ static int __init xlp_of_pic_init(struct device_node *node,
xlp_pic_domain = irq_domain_add_legacy(node, n_picirqs,
nlm_irq_to_xirq(socid, PIC_IRQ_BASE), PIC_IRQ_BASE,
&xlp_pic_irq_domain_ops, NULL);
&irq_domain_simple_ops, NULL);
if (xlp_pic_domain == NULL) {
pr_err("PIC %pOFn: Creating legacy domain failed!\n", node);
return -EINVAL;

View file

@ -8,7 +8,6 @@ config IRQCHIP
config ARM_GIC
bool
select IRQ_DOMAIN_HIERARCHY
select GENERIC_IRQ_MULTI_HANDLER
select GENERIC_IRQ_EFFECTIVE_AFF_MASK
config ARM_GIC_PM
@ -33,7 +32,6 @@ config GIC_NON_BANKED
config ARM_GIC_V3
bool
select GENERIC_IRQ_MULTI_HANDLER
select IRQ_DOMAIN_HIERARCHY
select PARTITION_PERCPU
select GENERIC_IRQ_EFFECTIVE_AFF_MASK
@ -64,7 +62,6 @@ config ARM_NVIC
config ARM_VIC
bool
select IRQ_DOMAIN
select GENERIC_IRQ_MULTI_HANDLER
config ARM_VIC_NR
int
@ -99,14 +96,12 @@ config ATMEL_AIC_IRQ
bool
select GENERIC_IRQ_CHIP
select IRQ_DOMAIN
select GENERIC_IRQ_MULTI_HANDLER
select SPARSE_IRQ
config ATMEL_AIC5_IRQ
bool
select GENERIC_IRQ_CHIP
select IRQ_DOMAIN
select GENERIC_IRQ_MULTI_HANDLER
select SPARSE_IRQ
config I8259
@ -153,7 +148,6 @@ config DW_APB_ICTL
config FARADAY_FTINTC010
bool
select IRQ_DOMAIN
select GENERIC_IRQ_MULTI_HANDLER
select SPARSE_IRQ
config HISILICON_IRQ_MBIGEN
@ -169,7 +163,6 @@ config IMGPDC_IRQ
config IXP4XX_IRQ
bool
select IRQ_DOMAIN
select GENERIC_IRQ_MULTI_HANDLER
select SPARSE_IRQ
config MADERA_IRQ
@ -186,7 +179,6 @@ config CLPS711X_IRQCHIP
bool
depends on ARCH_CLPS711X
select IRQ_DOMAIN
select GENERIC_IRQ_MULTI_HANDLER
select SPARSE_IRQ
default y
@ -205,7 +197,6 @@ config OMAP_IRQCHIP
config ORION_IRQCHIP
bool
select IRQ_DOMAIN
select GENERIC_IRQ_MULTI_HANDLER
config PIC32_EVIC
bool
@ -288,8 +279,13 @@ config XTENSA_MX
select GENERIC_IRQ_EFFECTIVE_AFF_MASK
config XILINX_INTC
bool
bool "Xilinx Interrupt Controller IP"
depends on MICROBLAZE || ARCH_ZYNQ || ARCH_ZYNQMP
select IRQ_DOMAIN
help
Support for the Xilinx Interrupt Controller IP core.
This is used as a primary controller with MicroBlaze and can also
be used as a secondary chained controller on other platforms.
config IRQ_CROSSBAR
bool
@ -586,4 +582,15 @@ config MST_IRQ
help
Support MStar Interrupt Controller.
config WPCM450_AIC
bool "Nuvoton WPCM450 Advanced Interrupt Controller"
depends on ARCH_WPCM450
help
Support for the interrupt controller in the Nuvoton WPCM450 BMC SoC.
config IRQ_IDT3243X
bool
select GENERIC_IRQ_CHIP
select IRQ_DOMAIN
endmenu

View file

@ -113,3 +113,5 @@ obj-$(CONFIG_LOONGSON_PCH_MSI) += irq-loongson-pch-msi.o
obj-$(CONFIG_MST_IRQ) += irq-mst-intc.o
obj-$(CONFIG_SL28CPLD_INTC) += irq-sl28cpld.o
obj-$(CONFIG_MACH_REALTEK_RTL) += irq-realtek-rtl.o
obj-$(CONFIG_WPCM450_AIC) += irq-wpcm450-aic.o
obj-$(CONFIG_IRQ_IDT3243X) += irq-idt3243x.o

View file

@ -303,7 +303,7 @@ int __init mbi_init(struct fwnode_handle *fwnode, struct irq_domain *parent)
reg = of_get_property(np, "mbi-alias", NULL);
if (reg) {
mbi_phys_base = of_translate_address(np, reg);
if (mbi_phys_base == OF_BAD_ADDR) {
if (mbi_phys_base == (phys_addr_t)OF_BAD_ADDR) {
ret = -ENXIO;
goto err_free_mbi;
}

View file

@ -648,6 +648,10 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs
irqnr = gic_read_iar();
/* Check for special IDs first */
if ((irqnr >= 1020 && irqnr <= 1023))
return;
if (gic_supports_nmi() &&
unlikely(gic_read_rpr() == GICD_INT_NMI_PRI)) {
gic_handle_nmi(irqnr, regs);
@ -659,10 +663,6 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs
gic_arch_enable_irqs();
}
/* Check for special IDs first */
if ((irqnr >= 1020 && irqnr <= 1023))
return;
if (static_branch_likely(&supports_deactivate_key))
gic_write_eoir(irqnr);
else

View file

@ -87,17 +87,40 @@ static struct irq_domain *gic_domain;
static const struct irq_domain_ops *vpe_domain_ops;
static const struct irq_domain_ops *sgi_domain_ops;
#ifdef CONFIG_ARM64
#include <asm/cpufeature.h>
bool gic_cpuif_has_vsgi(void)
{
unsigned long fld, reg = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
fld = cpuid_feature_extract_unsigned_field(reg, ID_AA64PFR0_GIC_SHIFT);
return fld >= 0x3;
}
#else
bool gic_cpuif_has_vsgi(void)
{
return false;
}
#endif
static bool has_v4_1(void)
{
return !!sgi_domain_ops;
}
static bool has_v4_1_sgi(void)
{
return has_v4_1() && gic_cpuif_has_vsgi();
}
static int its_alloc_vcpu_sgis(struct its_vpe *vpe, int idx)
{
char *name;
int sgi_base;
if (!has_v4_1())
if (!has_v4_1_sgi())
return 0;
name = kasprintf(GFP_KERNEL, "GICv4-sgi-%d", task_pid_nr(current));
@ -182,7 +205,7 @@ static void its_free_sgi_irqs(struct its_vm *vm)
{
int i;
if (!has_v4_1())
if (!has_v4_1_sgi())
return;
for (i = 0; i < vm->nr_vpes; i++) {

View file

@ -1,9 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Hisilicon HiP04 INTC
* HiSilicon HiP04 INTC
*
* Copyright (C) 2002-2014 ARM Limited.
* Copyright (c) 2013-2014 Hisilicon Ltd.
* Copyright (c) 2013-2014 HiSilicon Ltd.
* Copyright (c) 2013-2014 Linaro Ltd.
*
* Interrupt architecture for the HIP04 INTC:

View file

@ -0,0 +1,124 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Driver for IDT/Renesas 79RC3243x Interrupt Controller.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqchip.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqdomain.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#define IDT_PIC_NR_IRQS 32
#define IDT_PIC_IRQ_PEND 0x00
#define IDT_PIC_IRQ_MASK 0x08
struct idt_pic_data {
void __iomem *base;
struct irq_domain *irq_domain;
struct irq_chip_generic *gc;
};
static void idt_irq_dispatch(struct irq_desc *desc)
{
struct idt_pic_data *idtpic = irq_desc_get_handler_data(desc);
struct irq_chip *host_chip = irq_desc_get_chip(desc);
u32 pending, hwirq, virq;
chained_irq_enter(host_chip, desc);
pending = irq_reg_readl(idtpic->gc, IDT_PIC_IRQ_PEND);
pending &= ~idtpic->gc->mask_cache;
while (pending) {
hwirq = __fls(pending);
virq = irq_linear_revmap(idtpic->irq_domain, hwirq);
if (virq)
generic_handle_irq(virq);
pending &= ~(1 << hwirq);
}
chained_irq_exit(host_chip, desc);
}
static int idt_pic_init(struct device_node *of_node, struct device_node *parent)
{
struct irq_domain *domain;
struct idt_pic_data *idtpic;
struct irq_chip_generic *gc;
struct irq_chip_type *ct;
unsigned int parent_irq;
int ret = 0;
idtpic = kzalloc(sizeof(*idtpic), GFP_KERNEL);
if (!idtpic) {
ret = -ENOMEM;
goto out_err;
}
parent_irq = irq_of_parse_and_map(of_node, 0);
if (!parent_irq) {
pr_err("Failed to map parent IRQ!\n");
ret = -EINVAL;
goto out_free;
}
idtpic->base = of_iomap(of_node, 0);
if (!idtpic->base) {
pr_err("Failed to map base address!\n");
ret = -ENOMEM;
goto out_unmap_irq;
}
domain = irq_domain_add_linear(of_node, IDT_PIC_NR_IRQS,
&irq_generic_chip_ops, NULL);
if (!domain) {
pr_err("Failed to add irqdomain!\n");
ret = -ENOMEM;
goto out_iounmap;
}
idtpic->irq_domain = domain;
ret = irq_alloc_domain_generic_chips(domain, 32, 1, "IDTPIC",
handle_level_irq, 0,
IRQ_NOPROBE | IRQ_LEVEL, 0);
if (ret)
goto out_domain_remove;
gc = irq_get_domain_generic_chip(domain, 0);
gc->reg_base = idtpic->base;
gc->private = idtpic;
ct = gc->chip_types;
ct->regs.mask = IDT_PIC_IRQ_MASK;
ct->chip.irq_mask = irq_gc_mask_set_bit;
ct->chip.irq_unmask = irq_gc_mask_clr_bit;
idtpic->gc = gc;
/* Mask interrupts. */
writel(0xffffffff, idtpic->base + IDT_PIC_IRQ_MASK);
gc->mask_cache = 0xffffffff;
irq_set_chained_handler_and_data(parent_irq,
idt_irq_dispatch, idtpic);
return 0;
out_domain_remove:
irq_domain_remove(domain);
out_iounmap:
iounmap(idtpic->base);
out_unmap_irq:
irq_dispose_mapping(parent_irq);
out_free:
kfree(idtpic);
out_err:
pr_err("Failed to initialize! (errno = %d)\n", ret);
return ret;
}
IRQCHIP_DECLARE(idt_pic, "idt,32434-pic", idt_pic_init);

View file

@ -179,5 +179,6 @@ static int __init ingenic_tcu_irq_init(struct device_node *np,
}
IRQCHIP_DECLARE(jz4740_tcu_irq, "ingenic,jz4740-tcu", ingenic_tcu_irq_init);
IRQCHIP_DECLARE(jz4725b_tcu_irq, "ingenic,jz4725b-tcu", ingenic_tcu_irq_init);
IRQCHIP_DECLARE(jz4760_tcu_irq, "ingenic,jz4760-tcu", ingenic_tcu_irq_init);
IRQCHIP_DECLARE(jz4770_tcu_irq, "ingenic,jz4770-tcu", ingenic_tcu_irq_init);
IRQCHIP_DECLARE(x1000_tcu_irq, "ingenic,x1000-tcu", ingenic_tcu_irq_init);

View file

@ -155,6 +155,7 @@ static int __init intc_2chip_of_init(struct device_node *node,
{
return ingenic_intc_of_init(node, 2);
}
IRQCHIP_DECLARE(jz4760_intc, "ingenic,jz4760-intc", intc_2chip_of_init);
IRQCHIP_DECLARE(jz4770_intc, "ingenic,jz4770-intc", intc_2chip_of_init);
IRQCHIP_DECLARE(jz4775_intc, "ingenic,jz4775-intc", intc_2chip_of_init);
IRQCHIP_DECLARE(jz4780_intc, "ingenic,jz4780-intc", intc_2chip_of_init);

View file

@ -100,11 +100,11 @@ static int __init aic_irq_of_init(struct device_node *node,
jcore_aic.irq_unmask = noop;
jcore_aic.name = "AIC";
domain = irq_domain_add_linear(node, dom_sz, &jcore_aic_irqdomain_ops,
domain = irq_domain_add_legacy(node, dom_sz - min_irq, min_irq, min_irq,
&jcore_aic_irqdomain_ops,
&jcore_aic);
if (!domain)
return -ENOMEM;
irq_create_strict_mappings(domain, min_irq, min_irq, dom_sz - min_irq);
return 0;
}

View file

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2015 Hisilicon Limited, All Rights Reserved.
* Copyright (C) 2015 HiSilicon Limited, All Rights Reserved.
* Author: Jun Ma <majun258@huawei.com>
* Author: Yun Wu <wuyun.wu@huawei.com>
*/
@ -390,4 +390,4 @@ module_platform_driver(mbigen_platform_driver);
MODULE_AUTHOR("Jun Ma <majun258@huawei.com>");
MODULE_AUTHOR("Yun Wu <wuyun.wu@huawei.com>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Hisilicon MBI Generator driver");
MODULE_DESCRIPTION("HiSilicon MBI Generator driver");

View file

@ -13,15 +13,27 @@
#include <linux/of_irq.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/syscore_ops.h>
#define INTC_MASK 0x0
#define INTC_EOI 0x20
#define MST_INTC_MAX_IRQS 64
#define INTC_MASK 0x0
#define INTC_REV_POLARITY 0x10
#define INTC_EOI 0x20
#ifdef CONFIG_PM_SLEEP
static LIST_HEAD(mst_intc_list);
#endif
struct mst_intc_chip_data {
raw_spinlock_t lock;
unsigned int irq_start, nr_irqs;
void __iomem *base;
bool no_eoi;
#ifdef CONFIG_PM_SLEEP
struct list_head entry;
u16 saved_polarity_conf[DIV_ROUND_UP(MST_INTC_MAX_IRQS, 16)];
#endif
};
static void mst_set_irq(struct irq_data *d, u32 offset)
@ -78,6 +90,24 @@ static void mst_intc_eoi_irq(struct irq_data *d)
irq_chip_eoi_parent(d);
}
static int mst_irq_chip_set_type(struct irq_data *data, unsigned int type)
{
switch (type) {
case IRQ_TYPE_LEVEL_LOW:
case IRQ_TYPE_EDGE_FALLING:
mst_set_irq(data, INTC_REV_POLARITY);
break;
case IRQ_TYPE_LEVEL_HIGH:
case IRQ_TYPE_EDGE_RISING:
mst_clear_irq(data, INTC_REV_POLARITY);
break;
default:
return -EINVAL;
}
return irq_chip_set_type_parent(data, IRQ_TYPE_LEVEL_HIGH);
}
static struct irq_chip mst_intc_chip = {
.name = "mst-intc",
.irq_mask = mst_intc_mask_irq,
@ -87,13 +117,62 @@ static struct irq_chip mst_intc_chip = {
.irq_set_irqchip_state = irq_chip_set_parent_state,
.irq_set_affinity = irq_chip_set_affinity_parent,
.irq_set_vcpu_affinity = irq_chip_set_vcpu_affinity_parent,
.irq_set_type = irq_chip_set_type_parent,
.irq_set_type = mst_irq_chip_set_type,
.irq_retrigger = irq_chip_retrigger_hierarchy,
.flags = IRQCHIP_SET_TYPE_MASKED |
IRQCHIP_SKIP_SET_WAKE |
IRQCHIP_MASK_ON_SUSPEND,
};
#ifdef CONFIG_PM_SLEEP
static void mst_intc_polarity_save(struct mst_intc_chip_data *cd)
{
int i;
void __iomem *addr = cd->base + INTC_REV_POLARITY;
for (i = 0; i < DIV_ROUND_UP(cd->nr_irqs, 16); i++)
cd->saved_polarity_conf[i] = readw_relaxed(addr + i * 4);
}
static void mst_intc_polarity_restore(struct mst_intc_chip_data *cd)
{
int i;
void __iomem *addr = cd->base + INTC_REV_POLARITY;
for (i = 0; i < DIV_ROUND_UP(cd->nr_irqs, 16); i++)
writew_relaxed(cd->saved_polarity_conf[i], addr + i * 4);
}
static void mst_irq_resume(void)
{
struct mst_intc_chip_data *cd;
list_for_each_entry(cd, &mst_intc_list, entry)
mst_intc_polarity_restore(cd);
}
static int mst_irq_suspend(void)
{
struct mst_intc_chip_data *cd;
list_for_each_entry(cd, &mst_intc_list, entry)
mst_intc_polarity_save(cd);
return 0;
}
static struct syscore_ops mst_irq_syscore_ops = {
.suspend = mst_irq_suspend,
.resume = mst_irq_resume,
};
static int __init mst_irq_pm_init(void)
{
register_syscore_ops(&mst_irq_syscore_ops);
return 0;
}
late_initcall(mst_irq_pm_init);
#endif
static int mst_intc_domain_translate(struct irq_domain *d,
struct irq_fwspec *fwspec,
unsigned long *hwirq,
@ -145,6 +224,15 @@ static int mst_intc_domain_alloc(struct irq_domain *domain, unsigned int virq,
parent_fwspec = *fwspec;
parent_fwspec.fwnode = domain->parent->fwnode;
parent_fwspec.param[1] = cd->irq_start + hwirq;
/*
* mst-intc latch the interrupt request if it's edge triggered,
* so the output signal to parent GIC is always level sensitive.
* And if the irq signal is active low, configure it to active high
* to meet GIC SPI spec in mst_irq_chip_set_type via REV_POLARITY bit.
*/
parent_fwspec.param[2] = IRQ_TYPE_LEVEL_HIGH;
return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &parent_fwspec);
}
@ -193,6 +281,10 @@ static int __init mst_intc_of_init(struct device_node *dn,
return -ENOMEM;
}
#ifdef CONFIG_PM_SLEEP
INIT_LIST_HEAD(&cd->entry);
list_add_tail(&cd->entry, &mst_intc_list);
#endif
return 0;
}

View file

@ -77,8 +77,8 @@ struct plic_handler {
void __iomem *enable_base;
struct plic_priv *priv;
};
static int plic_parent_irq;
static bool plic_cpuhp_setup_done;
static int plic_parent_irq __ro_after_init;
static bool plic_cpuhp_setup_done __ro_after_init;
static DEFINE_PER_CPU(struct plic_handler, plic_handlers);
static inline void plic_toggle(struct plic_handler *handler,

View file

@ -193,7 +193,14 @@ static const struct stm32_desc_irq stm32mp1_desc_irq[] = {
{ .exti = 23, .irq_parent = 72, .chip = &stm32_exti_h_chip_direct },
{ .exti = 24, .irq_parent = 95, .chip = &stm32_exti_h_chip_direct },
{ .exti = 25, .irq_parent = 107, .chip = &stm32_exti_h_chip_direct },
{ .exti = 26, .irq_parent = 37, .chip = &stm32_exti_h_chip_direct },
{ .exti = 27, .irq_parent = 38, .chip = &stm32_exti_h_chip_direct },
{ .exti = 28, .irq_parent = 39, .chip = &stm32_exti_h_chip_direct },
{ .exti = 29, .irq_parent = 71, .chip = &stm32_exti_h_chip_direct },
{ .exti = 30, .irq_parent = 52, .chip = &stm32_exti_h_chip_direct },
{ .exti = 31, .irq_parent = 53, .chip = &stm32_exti_h_chip_direct },
{ .exti = 32, .irq_parent = 82, .chip = &stm32_exti_h_chip_direct },
{ .exti = 33, .irq_parent = 83, .chip = &stm32_exti_h_chip_direct },
{ .exti = 47, .irq_parent = 93, .chip = &stm32_exti_h_chip_direct },
{ .exti = 48, .irq_parent = 138, .chip = &stm32_exti_h_chip_direct },
{ .exti = 50, .irq_parent = 139, .chip = &stm32_exti_h_chip_direct },

View file

@ -60,6 +60,7 @@ static int tb10x_irq_set_type(struct irq_data *data, unsigned int flow_type)
break;
case IRQ_TYPE_NONE:
flow_type = IRQ_TYPE_LEVEL_LOW;
fallthrough;
case IRQ_TYPE_LEVEL_LOW:
mod ^= im;
pol ^= im;

View file

@ -0,0 +1,161 @@
// SPDX-License-Identifier: GPL-2.0-only
// Copyright 2021 Jonathan Neuschäfer
#include <linux/irqchip.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/printk.h>
#include <asm/exception.h>
#define AIC_SCR(x) ((x)*4) /* Source control registers */
#define AIC_GEN 0x84 /* Interrupt group enable control register */
#define AIC_GRSR 0x88 /* Interrupt group raw status register */
#define AIC_IRSR 0x100 /* Interrupt raw status register */
#define AIC_IASR 0x104 /* Interrupt active status register */
#define AIC_ISR 0x108 /* Interrupt status register */
#define AIC_IPER 0x10c /* Interrupt priority encoding register */
#define AIC_ISNR 0x110 /* Interrupt source number register */
#define AIC_IMR 0x114 /* Interrupt mask register */
#define AIC_OISR 0x118 /* Output interrupt status register */
#define AIC_MECR 0x120 /* Mask enable command register */
#define AIC_MDCR 0x124 /* Mask disable command register */
#define AIC_SSCR 0x128 /* Source set command register */
#define AIC_SCCR 0x12c /* Source clear command register */
#define AIC_EOSCR 0x130 /* End of service command register */
#define AIC_SCR_SRCTYPE_LOW_LEVEL (0 << 6)
#define AIC_SCR_SRCTYPE_HIGH_LEVEL (1 << 6)
#define AIC_SCR_SRCTYPE_NEG_EDGE (2 << 6)
#define AIC_SCR_SRCTYPE_POS_EDGE (3 << 6)
#define AIC_SCR_PRIORITY(x) (x)
#define AIC_SCR_PRIORITY_MASK 0x7
#define AIC_NUM_IRQS 32
struct wpcm450_aic {
void __iomem *regs;
struct irq_domain *domain;
};
static struct wpcm450_aic *aic;
static void wpcm450_aic_init_hw(void)
{
int i;
/* Disable (mask) all interrupts */
writel(0xffffffff, aic->regs + AIC_MDCR);
/*
* Make sure the interrupt controller is ready to serve new interrupts.
* Reading from IPER indicates that the nIRQ signal may be deasserted,
* and writing to EOSCR indicates that interrupt handling has finished.
*/
readl(aic->regs + AIC_IPER);
writel(0, aic->regs + AIC_EOSCR);
/* Initialize trigger mode and priority of each interrupt source */
for (i = 0; i < AIC_NUM_IRQS; i++)
writel(AIC_SCR_SRCTYPE_HIGH_LEVEL | AIC_SCR_PRIORITY(7),
aic->regs + AIC_SCR(i));
}
static void __exception_irq_entry wpcm450_aic_handle_irq(struct pt_regs *regs)
{
int hwirq;
/* Determine the interrupt source */
/* Read IPER to signal that nIRQ can be de-asserted */
hwirq = readl(aic->regs + AIC_IPER) / 4;
handle_domain_irq(aic->domain, hwirq, regs);
}
static void wpcm450_aic_eoi(struct irq_data *d)
{
/* Signal end-of-service */
writel(0, aic->regs + AIC_EOSCR);
}
static void wpcm450_aic_mask(struct irq_data *d)
{
unsigned int mask = BIT(d->hwirq);
/* Disable (mask) the interrupt */
writel(mask, aic->regs + AIC_MDCR);
}
static void wpcm450_aic_unmask(struct irq_data *d)
{
unsigned int mask = BIT(d->hwirq);
/* Enable (unmask) the interrupt */
writel(mask, aic->regs + AIC_MECR);
}
static int wpcm450_aic_set_type(struct irq_data *d, unsigned int flow_type)
{
/*
* The hardware supports high/low level, as well as rising/falling edge
* modes, and the DT binding accommodates for that, but as long as
* other modes than high level mode are not used and can't be tested,
* they are rejected in this driver.
*/
if ((flow_type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_LEVEL_HIGH)
return -EINVAL;
return 0;
}
static struct irq_chip wpcm450_aic_chip = {
.name = "wpcm450-aic",
.irq_eoi = wpcm450_aic_eoi,
.irq_mask = wpcm450_aic_mask,
.irq_unmask = wpcm450_aic_unmask,
.irq_set_type = wpcm450_aic_set_type,
};
static int wpcm450_aic_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hwirq)
{
if (hwirq >= AIC_NUM_IRQS)
return -EPERM;
irq_set_chip_and_handler(irq, &wpcm450_aic_chip, handle_fasteoi_irq);
irq_set_chip_data(irq, aic);
irq_set_probe(irq);
return 0;
}
static const struct irq_domain_ops wpcm450_aic_ops = {
.map = wpcm450_aic_map,
.xlate = irq_domain_xlate_twocell,
};
static int __init wpcm450_aic_of_init(struct device_node *node,
struct device_node *parent)
{
if (parent)
return -EINVAL;
aic = kzalloc(sizeof(*aic), GFP_KERNEL);
if (!aic)
return -ENOMEM;
aic->regs = of_iomap(node, 0);
if (!aic->regs) {
pr_err("Failed to map WPCM450 AIC registers\n");
return -ENOMEM;
}
wpcm450_aic_init_hw();
set_handle_irq(wpcm450_aic_handle_irq);
aic->domain = irq_domain_add_linear(node, AIC_NUM_IRQS, &wpcm450_aic_ops, aic);
return 0;
}
IRQCHIP_DECLARE(wpcm450_aic, "nuvoton,wpcm450-aic", wpcm450_aic_of_init);

View file

@ -179,6 +179,21 @@ static unsigned int __init save_reg(struct intc_desc_int *d,
return 0;
}
static bool __init intc_map(struct irq_domain *domain, int irq)
{
if (!irq_to_desc(irq) && irq_alloc_desc_at(irq, NUMA_NO_NODE) != irq) {
pr_err("uname to allocate IRQ %d\n", irq);
return false;
}
if (irq_domain_associate(domain, irq, irq)) {
pr_err("domain association failure\n");
return false;
}
return true;
}
int __init register_intc_controller(struct intc_desc *desc)
{
unsigned int i, k, smp;
@ -311,24 +326,12 @@ int __init register_intc_controller(struct intc_desc *desc)
for (i = 0; i < hw->nr_vectors; i++) {
struct intc_vect *vect = hw->vectors + i;
unsigned int irq = evt2irq(vect->vect);
int res;
if (!vect->enum_id)
continue;
res = irq_create_identity_mapping(d->domain, irq);
if (unlikely(res)) {
if (res == -EEXIST) {
res = irq_domain_associate(d->domain, irq, irq);
if (unlikely(res)) {
pr_err("domain association failure\n");
continue;
}
} else {
pr_err("can't identity map IRQ %d\n", irq);
continue;
}
}
if (!intc_map(d->domain, irq))
continue;
intc_irq_xlate_set(irq, vect->enum_id, d);
intc_register_irq(desc, d, vect->enum_id, irq);
@ -345,22 +348,8 @@ int __init register_intc_controller(struct intc_desc *desc)
* IRQ support, each vector still needs to have
* its own backing irq_desc.
*/
res = irq_create_identity_mapping(d->domain, irq2);
if (unlikely(res)) {
if (res == -EEXIST) {
res = irq_domain_associate(d->domain,
irq2, irq2);
if (unlikely(res)) {
pr_err("domain association "
"failure\n");
continue;
}
} else {
pr_err("can't identity map IRQ %d\n",
irq);
continue;
}
}
if (!intc_map(d->domain, irq2))
continue;
vect2->enum_id = 0;

View file

@ -145,4 +145,6 @@ int its_init_v4(struct irq_domain *domain,
const struct irq_domain_ops *vpe_ops,
const struct irq_domain_ops *sgi_ops);
bool gic_cpuif_has_vsgi(void);
#endif

View file

@ -150,7 +150,6 @@ struct irq_domain_chip_generic;
* setting up one or more generic chips for interrupt controllers
* drivers using the generic chip library which uses this pointer.
* @parent: Pointer to parent irq_domain to support hierarchy irq_domains
* @debugfs_file: dentry for the domain debugfs file
*
* Revmap data, used internally by irq_domain
* @revmap_direct_max_irq: The largest hwirq that can be set for controllers that
@ -174,9 +173,6 @@ struct irq_domain {
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
struct irq_domain *parent;
#endif
#ifdef CONFIG_GENERIC_IRQ_DEBUGFS
struct dentry *debugfs_file;
#endif
/* reverse map data. The linear map gets appended to the irq_domain */
irq_hw_number_t hwirq_max;
@ -419,15 +415,6 @@ static inline unsigned int irq_linear_revmap(struct irq_domain *domain,
extern unsigned int irq_find_mapping(struct irq_domain *host,
irq_hw_number_t hwirq);
extern unsigned int irq_create_direct_mapping(struct irq_domain *host);
extern int irq_create_strict_mappings(struct irq_domain *domain,
unsigned int irq_base,
irq_hw_number_t hwirq_base, int count);
static inline int irq_create_identity_mapping(struct irq_domain *host,
irq_hw_number_t hwirq)
{
return irq_create_strict_mappings(host, hwirq, hwirq, 1);
}
extern const struct irq_domain_ops irq_domain_simple_ops;

View file

@ -703,41 +703,6 @@ unsigned int irq_create_mapping_affinity(struct irq_domain *domain,
}
EXPORT_SYMBOL_GPL(irq_create_mapping_affinity);
/**
* irq_create_strict_mappings() - Map a range of hw irqs to fixed linux irqs
* @domain: domain owning the interrupt range
* @irq_base: beginning of linux IRQ range
* @hwirq_base: beginning of hardware IRQ range
* @count: Number of interrupts to map
*
* This routine is used for allocating and mapping a range of hardware
* irqs to linux irqs where the linux irq numbers are at pre-defined
* locations. For use by controllers that already have static mappings
* to insert in to the domain.
*
* Non-linear users can use irq_create_identity_mapping() for IRQ-at-a-time
* domain insertion.
*
* 0 is returned upon success, while any failure to establish a static
* mapping is treated as an error.
*/
int irq_create_strict_mappings(struct irq_domain *domain, unsigned int irq_base,
irq_hw_number_t hwirq_base, int count)
{
struct device_node *of_node;
int ret;
of_node = irq_domain_get_of_node(domain);
ret = irq_alloc_descs(irq_base, irq_base, count,
of_node_to_nid(of_node));
if (unlikely(ret < 0))
return ret;
irq_domain_associate_many(domain, irq_base, hwirq_base, count);
return 0;
}
EXPORT_SYMBOL_GPL(irq_create_strict_mappings);
static int irq_domain_translate(struct irq_domain *d,
struct irq_fwspec *fwspec,
irq_hw_number_t *hwirq, unsigned int *type)
@ -1694,12 +1659,10 @@ void irq_domain_free_irqs(unsigned int virq, unsigned int nr_irqs)
/**
* irq_domain_alloc_irqs_parent - Allocate interrupts from parent domain
* @domain: Domain below which interrupts must be allocated
* @irq_base: Base IRQ number
* @nr_irqs: Number of IRQs to allocate
* @arg: Allocation data (arch/domain specific)
*
* Check whether the domain has been setup recursive. If not allocate
* through the parent domain.
*/
int irq_domain_alloc_irqs_parent(struct irq_domain *domain,
unsigned int irq_base, unsigned int nr_irqs,
@ -1715,11 +1678,9 @@ EXPORT_SYMBOL_GPL(irq_domain_alloc_irqs_parent);
/**
* irq_domain_free_irqs_parent - Free interrupts from parent domain
* @domain: Domain below which interrupts must be freed
* @irq_base: Base IRQ number
* @nr_irqs: Number of IRQs to free
*
* Check whether the domain has been setup recursive. If not free
* through the parent domain.
*/
void irq_domain_free_irqs_parent(struct irq_domain *domain,
unsigned int irq_base, unsigned int nr_irqs)
@ -1898,16 +1859,15 @@ DEFINE_SHOW_ATTRIBUTE(irq_domain_debug);
static void debugfs_add_domain_dir(struct irq_domain *d)
{
if (!d->name || !domain_dir || d->debugfs_file)
if (!d->name || !domain_dir)
return;
d->debugfs_file = debugfs_create_file(d->name, 0444, domain_dir, d,
&irq_domain_debug_fops);
debugfs_create_file(d->name, 0444, domain_dir, d,
&irq_domain_debug_fops);
}
static void debugfs_remove_domain_dir(struct irq_domain *d)
{
debugfs_remove(d->debugfs_file);
d->debugfs_file = NULL;
debugfs_remove(debugfs_lookup(d->name, domain_dir));
}
void __init irq_domain_debugfs_init(struct dentry *root)