From 9773c540441c6ae15aefb49e67142e94369dbbc0 Mon Sep 17 00:00:00 2001 From: Caleb James DeLisle Date: Sun, 30 Mar 2025 17:02:58 +0000 Subject: dt-bindings: interrupt-controller: Add EcoNet EN751221 INTC Document the device tree binding for the interrupt controller in the EcoNet EN751221 MIPS SoC. Signed-off-by: Caleb James DeLisle Signed-off-by: Thomas Gleixner Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/all/20250330170306.2584136-3-cjd@cjdns.fr --- .../interrupt-controller/econet,en751221-intc.yaml | 78 ++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 Documentation/devicetree/bindings/interrupt-controller/econet,en751221-intc.yaml diff --git a/Documentation/devicetree/bindings/interrupt-controller/econet,en751221-intc.yaml b/Documentation/devicetree/bindings/interrupt-controller/econet,en751221-intc.yaml new file mode 100644 index 000000000000..5536319c49c3 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/econet,en751221-intc.yaml @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interrupt-controller/econet,en751221-intc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: EcoNet EN751221 Interrupt Controller + +maintainers: + - Caleb James DeLisle + +description: + The EcoNet EN751221 Interrupt Controller is a simple interrupt controller + designed for the MIPS 34Kc MT SMP processor with 2 VPEs. Each interrupt can + be routed to either VPE but not both, so to support per-CPU interrupts, a + secondary IRQ number is allocated to control masking/unmasking on VPE#1. For + lack of a better term we call these "shadow interrupts". The assignment of + shadow interrupts is defined by the SoC integrator when wiring the interrupt + lines, so they are configurable in the device tree. + +allOf: + - $ref: /schemas/interrupt-controller.yaml# + +properties: + compatible: + const: econet,en751221-intc + + reg: + maxItems: 1 + + "#interrupt-cells": + const: 1 + + interrupt-controller: true + + interrupts: + maxItems: 1 + description: Interrupt line connecting this controller to its parent. + + econet,shadow-interrupts: + $ref: /schemas/types.yaml#/definitions/uint32-matrix + description: + An array of interrupt number pairs where each pair represents a shadow + interrupt relationship. The first number in each pair is the primary IRQ, + and the second is its shadow IRQ used for VPE#1 control. For example, + <8 3> means IRQ 8 is shadowed by IRQ 3, so IRQ 3 cannot be mapped, but + when VPE#1 requests IRQ 8, it will manipulate the IRQ 3 mask bit. + minItems: 1 + maxItems: 20 + items: + items: + - description: primary per-CPU IRQ + - description: shadow IRQ number + +required: + - compatible + - reg + - interrupt-controller + - "#interrupt-cells" + - interrupts + +additionalProperties: false + +examples: + - | + interrupt-controller@1fb40000 { + compatible = "econet,en751221-intc"; + reg = <0x1fb40000 0x100>; + + interrupt-controller; + #interrupt-cells = <1>; + + interrupt-parent = <&cpuintc>; + interrupts = <2>; + + econet,shadow-interrupts = <7 2>, <8 3>, <13 12>, <30 29>; + }; +... -- cgit v1.2.3 From 1902a59cf5f9d8b99ecf0cb8f122cb00ef7a3f13 Mon Sep 17 00:00:00 2001 From: Caleb James DeLisle Date: Sun, 30 Mar 2025 17:02:59 +0000 Subject: irqchip: Add EcoNet EN751221 INTC Add a driver for the interrupt controller in the EcoNet EN751221 MIPS SoC. Signed-off-by: Caleb James DeLisle Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/all/20250330170306.2584136-4-cjd@cjdns.fr --- drivers/irqchip/Kconfig | 5 + drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-econet-en751221.c | 309 ++++++++++++++++++++++++++++++++++ 3 files changed, 315 insertions(+) create mode 100644 drivers/irqchip/irq-econet-en751221.c diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index cec05e443083..4b438708cdd6 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -166,6 +166,11 @@ config DW_APB_ICTL select GENERIC_IRQ_CHIP select IRQ_DOMAIN_HIERARCHY +config ECONET_EN751221_INTC + bool + select GENERIC_IRQ_CHIP + select IRQ_DOMAIN + config FARADAY_FTINTC010 bool select IRQ_DOMAIN diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 365bcea9a61f..23ca4959e6ce 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2836.o obj-$(CONFIG_ARCH_ACTIONS) += irq-owl-sirq.o obj-$(CONFIG_DAVINCI_CP_INTC) += irq-davinci-cp-intc.o obj-$(CONFIG_EXYNOS_IRQ_COMBINER) += exynos-combiner.o +obj-$(CONFIG_ECONET_EN751221_INTC) += irq-econet-en751221.o obj-$(CONFIG_FARADAY_FTINTC010) += irq-ftintc010.o obj-$(CONFIG_ARCH_HIP04) += irq-hip04.o obj-$(CONFIG_ARCH_LPC32XX) += irq-lpc32xx.o diff --git a/drivers/irqchip/irq-econet-en751221.c b/drivers/irqchip/irq-econet-en751221.c new file mode 100644 index 000000000000..886d60c6f8a0 --- /dev/null +++ b/drivers/irqchip/irq-econet-en751221.c @@ -0,0 +1,309 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * EN751221 Interrupt Controller Driver. + * + * The EcoNet EN751221 Interrupt Controller is a simple interrupt controller + * designed for the MIPS 34Kc MT SMP processor with 2 VPEs. Each interrupt can + * be routed to either VPE but not both, so to support per-CPU interrupts, a + * secondary IRQ number is allocated to control masking/unmasking on VPE#1. In + * this driver, these are called "shadow interrupts". The assignment of shadow + * interrupts is defined by the SoC integrator when wiring the interrupt lines, + * so they are configurable in the device tree. + * + * If an interrupt (say 30) needs per-CPU capability, the SoC integrator + * allocates another IRQ number (say 29) to be its shadow. The device tree + * reflects this by adding the pair <30 29> to the "econet,shadow-interrupts" + * property. + * + * When VPE#1 requests IRQ 30, the driver manipulates the mask bit for IRQ 29, + * telling the hardware to mask VPE#1's view of IRQ 30. + * + * Copyright (C) 2025 Caleb James DeLisle + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define IRQ_COUNT 40 + +#define NOT_PERCPU 0xff +#define IS_SHADOW 0xfe + +#define REG_MASK0 0x04 +#define REG_MASK1 0x50 +#define REG_PENDING0 0x08 +#define REG_PENDING1 0x54 + +/** + * @membase: Base address of the interrupt controller registers + * @interrupt_shadows: Array of all interrupts, for each value, + * - NOT_PERCPU: This interrupt is not per-cpu, so it has no shadow + * - IS_SHADOW: This interrupt is a shadow of another per-cpu interrupt + * - else: This is a per-cpu interrupt whose shadow is the value + */ +static struct { + void __iomem *membase; + u8 interrupt_shadows[IRQ_COUNT]; +} econet_intc __ro_after_init; + +static DEFINE_RAW_SPINLOCK(irq_lock); + +/* IRQs must be disabled */ +static void econet_wreg(u32 reg, u32 val, u32 mask) +{ + u32 v; + + guard(raw_spinlock)(&irq_lock); + + v = ioread32(econet_intc.membase + reg); + v &= ~mask; + v |= val & mask; + iowrite32(v, econet_intc.membase + reg); +} + +/* IRQs must be disabled */ +static void econet_chmask(u32 hwirq, bool unmask) +{ + u32 reg, mask; + u8 shadow; + + /* + * If the IRQ is a shadow, it should never be manipulated directly. + * It should only be masked/unmasked as a result of the "real" per-cpu + * irq being manipulated by a thread running on VPE#1. + * If it is per-cpu (has a shadow), and we're on VPE#1, the shadow is what we mask. + * This is single processor only, so smp_processor_id() never exceeds 1. + */ + shadow = econet_intc.interrupt_shadows[hwirq]; + if (WARN_ON_ONCE(shadow == IS_SHADOW)) + return; + else if (shadow != NOT_PERCPU && smp_processor_id() == 1) + hwirq = shadow; + + if (hwirq >= 32) { + reg = REG_MASK1; + mask = BIT(hwirq - 32); + } else { + reg = REG_MASK0; + mask = BIT(hwirq); + } + + econet_wreg(reg, unmask ? mask : 0, mask); +} + +/* IRQs must be disabled */ +static void econet_intc_mask(struct irq_data *d) +{ + econet_chmask(d->hwirq, false); +} + +/* IRQs must be disabled */ +static void econet_intc_unmask(struct irq_data *d) +{ + econet_chmask(d->hwirq, true); +} + +static void econet_mask_all(void) +{ + /* IRQs are generally disabled during init, but guarding here makes it non-obligatory. */ + guard(irqsave)(); + econet_wreg(REG_MASK0, 0, ~0); + econet_wreg(REG_MASK1, 0, ~0); +} + +static void econet_intc_handle_pending(struct irq_domain *d, u32 pending, u32 offset) +{ + int hwirq; + + while (pending) { + hwirq = fls(pending) - 1; + generic_handle_domain_irq(d, hwirq + offset); + pending &= ~BIT(hwirq); + } +} + +static void econet_intc_from_parent(struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + struct irq_domain *domain; + u32 pending0, pending1; + + chained_irq_enter(chip, desc); + + pending0 = ioread32(econet_intc.membase + REG_PENDING0); + pending1 = ioread32(econet_intc.membase + REG_PENDING1); + + if (unlikely(!(pending0 | pending1))) { + spurious_interrupt(); + } else { + domain = irq_desc_get_handler_data(desc); + econet_intc_handle_pending(domain, pending0, 0); + econet_intc_handle_pending(domain, pending1, 32); + } + + chained_irq_exit(chip, desc); +} + +static const struct irq_chip econet_irq_chip; + +static int econet_intc_map(struct irq_domain *d, u32 irq, irq_hw_number_t hwirq) +{ + int ret; + + if (hwirq >= IRQ_COUNT) { + pr_err("%s: hwirq %lu out of range\n", __func__, hwirq); + return -EINVAL; + } else if (econet_intc.interrupt_shadows[hwirq] == IS_SHADOW) { + pr_err("%s: can't map hwirq %lu, it is a shadow interrupt\n", __func__, hwirq); + return -EINVAL; + } + + if (econet_intc.interrupt_shadows[hwirq] == NOT_PERCPU) { + irq_set_chip_and_handler(irq, &econet_irq_chip, handle_level_irq); + } else { + irq_set_chip_and_handler(irq, &econet_irq_chip, handle_percpu_devid_irq); + ret = irq_set_percpu_devid(irq); + if (ret) + pr_warn("%s: Failed irq_set_percpu_devid for %u: %d\n", d->name, irq, ret); + } + + irq_set_chip_data(irq, NULL); + return 0; +} + +static const struct irq_chip econet_irq_chip = { + .name = "en751221-intc", + .irq_unmask = econet_intc_unmask, + .irq_mask = econet_intc_mask, + .irq_mask_ack = econet_intc_mask, +}; + +static const struct irq_domain_ops econet_domain_ops = { + .xlate = irq_domain_xlate_onecell, + .map = econet_intc_map +}; + +static int __init get_shadow_interrupts(struct device_node *node) +{ + const char *field = "econet,shadow-interrupts"; + int num_shadows; + + num_shadows = of_property_count_u32_elems(node, field); + + memset(econet_intc.interrupt_shadows, NOT_PERCPU, + sizeof(econet_intc.interrupt_shadows)); + + if (num_shadows <= 0) { + return 0; + } else if (num_shadows % 2) { + pr_err("%pOF: %s count is odd, ignoring\n", node, field); + return 0; + } + + u32 *shadows __free(kfree) = kmalloc_array(num_shadows, sizeof(u32), GFP_KERNEL); + if (!shadows) + return -ENOMEM; + + if (of_property_read_u32_array(node, field, shadows, num_shadows)) { + pr_err("%pOF: Failed to read %s\n", node, field); + return -EINVAL; + } + + for (int i = 0; i < num_shadows; i += 2) { + u32 shadow = shadows[i + 1]; + u32 target = shadows[i]; + + if (shadow > IRQ_COUNT) { + pr_err("%pOF: %s[%d] shadow(%d) out of range\n", + node, field, i + 1, shadow); + continue; + } + + if (target >= IRQ_COUNT) { + pr_err("%pOF: %s[%d] target(%d) out of range\n", node, field, i, target); + continue; + } + + if (econet_intc.interrupt_shadows[target] != NOT_PERCPU) { + pr_err("%pOF: %s[%d] target(%d) already has a shadow\n", + node, field, i, target); + continue; + } + + if (econet_intc.interrupt_shadows[shadow] != NOT_PERCPU) { + pr_err("%pOF: %s[%d] shadow(%d) already has a target\n", + node, field, i + 1, shadow); + continue; + } + + econet_intc.interrupt_shadows[target] = shadow; + econet_intc.interrupt_shadows[shadow] = IS_SHADOW; + } + + return 0; +} + +static int __init econet_intc_of_init(struct device_node *node, struct device_node *parent) +{ + struct irq_domain *domain; + struct resource res; + int ret, irq; + + ret = get_shadow_interrupts(node); + if (ret) + return ret; + + irq = irq_of_parse_and_map(node, 0); + if (!irq) { + pr_err("%pOF: DT: Failed to get IRQ from 'interrupts'\n", node); + return -EINVAL; + } + + if (of_address_to_resource(node, 0, &res)) { + pr_err("%pOF: DT: Failed to get 'reg'\n", node); + ret = -EINVAL; + goto err_dispose_mapping; + } + + if (!request_mem_region(res.start, resource_size(&res), res.name)) { + pr_err("%pOF: Failed to request memory\n", node); + ret = -EBUSY; + goto err_dispose_mapping; + } + + econet_intc.membase = ioremap(res.start, resource_size(&res)); + if (!econet_intc.membase) { + pr_err("%pOF: Failed to remap membase\n", node); + ret = -ENOMEM; + goto err_release; + } + + econet_mask_all(); + + domain = irq_domain_add_linear(node, IRQ_COUNT, &econet_domain_ops, NULL); + if (!domain) { + pr_err("%pOF: Failed to add irqdomain\n", node); + ret = -ENOMEM; + goto err_unmap; + } + + irq_set_chained_handler_and_data(irq, econet_intc_from_parent, domain); + + return 0; + +err_unmap: + iounmap(econet_intc.membase); +err_release: + release_mem_region(res.start, resource_size(&res)); +err_dispose_mapping: + irq_dispose_mapping(irq); + return ret; +} + +IRQCHIP_DECLARE(econet_en751221_intc, "econet,en751221-intc", econet_intc_of_init); -- cgit v1.2.3 From 06f2f68a670aae28b825065439301831e74da880 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 13 Mar 2025 15:31:15 +0100 Subject: genirq/generic-chip: Make locking unconditional The SMP conditional wrappers around raw_spin_[un]lock() have no real value. On !SMP kernels the lock operations are NOOPs except for a preempt_disable/enable() pair on PREEMPT enabled kernels, which are not really worth to optimize for. Aside of that this evades lockdep on !SMP kernels. Remove the !SMP stubs and make it unconditional. No functional change. Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/all/20250313142524.011345765@linutronix.de --- include/linux/irq.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/include/linux/irq.h b/include/linux/irq.h index dd5df1e2d032..500772943207 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -1222,7 +1222,6 @@ static inline struct irq_chip_type *irq_data_get_chip_type(struct irq_data *d) #define IRQ_MSK(n) (u32)((n) < 32 ? ((1 << (n)) - 1) : UINT_MAX) -#ifdef CONFIG_SMP static inline void irq_gc_lock(struct irq_chip_generic *gc) { raw_spin_lock(&gc->lock); @@ -1232,10 +1231,6 @@ static inline void irq_gc_unlock(struct irq_chip_generic *gc) { raw_spin_unlock(&gc->lock); } -#else -static inline void irq_gc_lock(struct irq_chip_generic *gc) { } -static inline void irq_gc_unlock(struct irq_chip_generic *gc) { } -#endif /* * The irqsave variants are for usage in non interrupt code. Do not use -- cgit v1.2.3 From 195298c3b11628a6c52c515c31470e673cf259a9 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 13 Mar 2025 15:31:17 +0100 Subject: genirq/generic-chip: Convert core code to lock guards Replace the irq_gc_lock/unlock() pairs with guards. There is no point to implement a guard wrapper for them as they just wrap around raw_spin_lock*(). Switch the other lock instances in the core code to guards as well. Conversion was done with Coccinelle plus manual fixups. No functional change. Signed-off-by: Thomas Gleixner Reviewed-by: Linus Walleij Link: https://lore.kernel.org/all/20250313142524.073826193@linutronix.de --- kernel/irq/generic-chip.c | 47 ++++++++++++++++------------------------------- 1 file changed, 16 insertions(+), 31 deletions(-) diff --git a/kernel/irq/generic-chip.c b/kernel/irq/generic-chip.c index c4a8bca5f2b0..8014bfecb948 100644 --- a/kernel/irq/generic-chip.c +++ b/kernel/irq/generic-chip.c @@ -40,10 +40,9 @@ void irq_gc_mask_disable_reg(struct irq_data *d) struct irq_chip_type *ct = irq_data_get_chip_type(d); u32 mask = d->mask; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); irq_reg_writel(gc, mask, ct->regs.disable); *ct->mask_cache &= ~mask; - irq_gc_unlock(gc); } EXPORT_SYMBOL_GPL(irq_gc_mask_disable_reg); @@ -60,10 +59,9 @@ void irq_gc_mask_set_bit(struct irq_data *d) struct irq_chip_type *ct = irq_data_get_chip_type(d); u32 mask = d->mask; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); *ct->mask_cache |= mask; irq_reg_writel(gc, *ct->mask_cache, ct->regs.mask); - irq_gc_unlock(gc); } EXPORT_SYMBOL_GPL(irq_gc_mask_set_bit); @@ -80,10 +78,9 @@ void irq_gc_mask_clr_bit(struct irq_data *d) struct irq_chip_type *ct = irq_data_get_chip_type(d); u32 mask = d->mask; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); *ct->mask_cache &= ~mask; irq_reg_writel(gc, *ct->mask_cache, ct->regs.mask); - irq_gc_unlock(gc); } EXPORT_SYMBOL_GPL(irq_gc_mask_clr_bit); @@ -100,10 +97,9 @@ void irq_gc_unmask_enable_reg(struct irq_data *d) struct irq_chip_type *ct = irq_data_get_chip_type(d); u32 mask = d->mask; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); irq_reg_writel(gc, mask, ct->regs.enable); *ct->mask_cache |= mask; - irq_gc_unlock(gc); } EXPORT_SYMBOL_GPL(irq_gc_unmask_enable_reg); @@ -117,9 +113,8 @@ void irq_gc_ack_set_bit(struct irq_data *d) struct irq_chip_type *ct = irq_data_get_chip_type(d); u32 mask = d->mask; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); irq_reg_writel(gc, mask, ct->regs.ack); - irq_gc_unlock(gc); } EXPORT_SYMBOL_GPL(irq_gc_ack_set_bit); @@ -133,9 +128,8 @@ void irq_gc_ack_clr_bit(struct irq_data *d) struct irq_chip_type *ct = irq_data_get_chip_type(d); u32 mask = ~d->mask; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); irq_reg_writel(gc, mask, ct->regs.ack); - irq_gc_unlock(gc); } /** @@ -156,11 +150,10 @@ void irq_gc_mask_disable_and_ack_set(struct irq_data *d) struct irq_chip_type *ct = irq_data_get_chip_type(d); u32 mask = d->mask; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); irq_reg_writel(gc, mask, ct->regs.disable); *ct->mask_cache &= ~mask; irq_reg_writel(gc, mask, ct->regs.ack); - irq_gc_unlock(gc); } EXPORT_SYMBOL_GPL(irq_gc_mask_disable_and_ack_set); @@ -174,9 +167,8 @@ void irq_gc_eoi(struct irq_data *d) struct irq_chip_type *ct = irq_data_get_chip_type(d); u32 mask = d->mask; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); irq_reg_writel(gc, mask, ct->regs.eoi); - irq_gc_unlock(gc); } /** @@ -196,12 +188,11 @@ int irq_gc_set_wake(struct irq_data *d, unsigned int on) if (!(mask & gc->wake_enabled)) return -EINVAL; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); if (on) gc->wake_active |= mask; else gc->wake_active &= ~mask; - irq_gc_unlock(gc); return 0; } EXPORT_SYMBOL_GPL(irq_gc_set_wake); @@ -288,7 +279,6 @@ int irq_domain_alloc_generic_chips(struct irq_domain *d, { struct irq_domain_chip_generic *dgc; struct irq_chip_generic *gc; - unsigned long flags; int numchips, i; size_t dgc_sz; size_t gc_sz; @@ -340,9 +330,8 @@ int irq_domain_alloc_generic_chips(struct irq_domain *d, goto err; } - raw_spin_lock_irqsave(&gc_lock, flags); - list_add_tail(&gc->list, &gc_list); - raw_spin_unlock_irqrestore(&gc_lock, flags); + scoped_guard (raw_spinlock, &gc_lock) + list_add_tail(&gc->list, &gc_list); /* Calc pointer to the next generic chip */ tmp += gc_sz; } @@ -459,7 +448,6 @@ int irq_map_generic_chip(struct irq_domain *d, unsigned int virq, struct irq_chip_generic *gc; struct irq_chip_type *ct; struct irq_chip *chip; - unsigned long flags; int idx; gc = __irq_get_domain_generic_chip(d, hw_irq); @@ -479,9 +467,8 @@ int irq_map_generic_chip(struct irq_domain *d, unsigned int virq, /* We only init the cache for the first mapping of a generic chip */ if (!gc->installed) { - raw_spin_lock_irqsave(&gc->lock, flags); + guard(raw_spinlock_irq)(&gc->lock); irq_gc_init_mask_cache(gc, dgc->gc_flags); - raw_spin_unlock_irqrestore(&gc->lock, flags); } /* Mark the interrupt as installed */ @@ -548,9 +535,8 @@ void irq_setup_generic_chip(struct irq_chip_generic *gc, u32 msk, struct irq_chip *chip = &ct->chip; unsigned int i; - raw_spin_lock(&gc_lock); - list_add_tail(&gc->list, &gc_list); - raw_spin_unlock(&gc_lock); + scoped_guard (raw_spinlock, &gc_lock) + list_add_tail(&gc->list, &gc_list); irq_gc_init_mask_cache(gc, flags); @@ -616,9 +602,8 @@ void irq_remove_generic_chip(struct irq_chip_generic *gc, u32 msk, { unsigned int i, virq; - raw_spin_lock(&gc_lock); - list_del(&gc->list); - raw_spin_unlock(&gc_lock); + scoped_guard (raw_spinlock, &gc_lock) + list_del(&gc->list); for (i = 0; msk; msk >>= 1, i++) { if (!(msk & 0x01)) -- cgit v1.2.3 From b54bd5a29b428afff4a37c7b6e1df67e43c327c3 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 13 Mar 2025 15:31:21 +0100 Subject: soc: dove: Convert generic irqchip locking to guard() Conversion was done with Coccinelle. No functional change. Signed-off-by: Thomas Gleixner Reviewed-by: Andrew Lunn Reviewed-by: Linus Walleij Link: https://lore.kernel.org/all/20250313142524.137040686@linutronix.de --- drivers/soc/dove/pmu.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/soc/dove/pmu.c b/drivers/soc/dove/pmu.c index 6202dbcd20a8..795802bb3e7d 100644 --- a/drivers/soc/dove/pmu.c +++ b/drivers/soc/dove/pmu.c @@ -257,10 +257,9 @@ static void pmu_irq_handler(struct irq_desc *desc) * So, let's structure the code so that the window is as small as * possible. */ - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); done &= readl_relaxed(base + PMC_IRQ_CAUSE); writel_relaxed(done, base + PMC_IRQ_CAUSE); - irq_gc_unlock(gc); } static int __init dove_init_pmu_irq(struct pmu_data *pmu, int irq) -- cgit v1.2.3 From 73989a38268dd80f7f2c945b8e3097b7c9ee95f5 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 13 Mar 2025 15:31:23 +0100 Subject: ARM: orion/gpio:: Convert generic irqchip locking to guard() Conversion was done with Coccinelle. No functional change. Signed-off-by: Thomas Gleixner Reviewed-by: Linus Walleij Link: https://lore.kernel.org/all/20250313142524.200515896@linutronix.de --- arch/arm/plat-orion/gpio.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/arch/arm/plat-orion/gpio.c b/arch/arm/plat-orion/gpio.c index 595e9cb33c1d..47d411d80a94 100644 --- a/arch/arm/plat-orion/gpio.c +++ b/arch/arm/plat-orion/gpio.c @@ -496,11 +496,10 @@ static void orion_gpio_unmask_irq(struct irq_data *d) u32 reg_val; u32 mask = d->mask; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); reg_val = irq_reg_readl(gc, ct->regs.mask); reg_val |= mask; irq_reg_writel(gc, reg_val, ct->regs.mask); - irq_gc_unlock(gc); } static void orion_gpio_mask_irq(struct irq_data *d) @@ -510,11 +509,10 @@ static void orion_gpio_mask_irq(struct irq_data *d) u32 mask = d->mask; u32 reg_val; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); reg_val = irq_reg_readl(gc, ct->regs.mask); reg_val &= ~mask; irq_reg_writel(gc, reg_val, ct->regs.mask); - irq_gc_unlock(gc); } void __init orion_gpio_init(int gpio_base, int ngpio, -- cgit v1.2.3 From 9949aec666eb3e55522409f243fa6e873424fdc5 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 13 Mar 2025 15:31:25 +0100 Subject: gpio: mvebu: Convert generic irqchip locking to guard() Conversion was done with coccinelle. Signed-off-by: Thomas Gleixner Reviewed-by: Linus Walleij Link: https://lore.kernel.org/all/20250313142524.262678485@linutronix.de --- drivers/gpio/gpio-mvebu.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index 3604abcb6fec..2581fbbfbad3 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -408,9 +408,8 @@ static void mvebu_gpio_irq_ack(struct irq_data *d) struct mvebu_gpio_chip *mvchip = gc->private; u32 mask = d->mask; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); mvebu_gpio_write_edge_cause(mvchip, ~mask); - irq_gc_unlock(gc); } static void mvebu_gpio_edge_irq_mask(struct irq_data *d) @@ -420,10 +419,9 @@ static void mvebu_gpio_edge_irq_mask(struct irq_data *d) struct irq_chip_type *ct = irq_data_get_chip_type(d); u32 mask = d->mask; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); ct->mask_cache_priv &= ~mask; mvebu_gpio_write_edge_mask(mvchip, ct->mask_cache_priv); - irq_gc_unlock(gc); } static void mvebu_gpio_edge_irq_unmask(struct irq_data *d) @@ -433,11 +431,10 @@ static void mvebu_gpio_edge_irq_unmask(struct irq_data *d) struct irq_chip_type *ct = irq_data_get_chip_type(d); u32 mask = d->mask; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); mvebu_gpio_write_edge_cause(mvchip, ~mask); ct->mask_cache_priv |= mask; mvebu_gpio_write_edge_mask(mvchip, ct->mask_cache_priv); - irq_gc_unlock(gc); } static void mvebu_gpio_level_irq_mask(struct irq_data *d) @@ -447,10 +444,9 @@ static void mvebu_gpio_level_irq_mask(struct irq_data *d) struct irq_chip_type *ct = irq_data_get_chip_type(d); u32 mask = d->mask; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); ct->mask_cache_priv &= ~mask; mvebu_gpio_write_level_mask(mvchip, ct->mask_cache_priv); - irq_gc_unlock(gc); } static void mvebu_gpio_level_irq_unmask(struct irq_data *d) @@ -460,10 +456,9 @@ static void mvebu_gpio_level_irq_unmask(struct irq_data *d) struct irq_chip_type *ct = irq_data_get_chip_type(d); u32 mask = d->mask; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); ct->mask_cache_priv |= mask; mvebu_gpio_write_level_mask(mvchip, ct->mask_cache_priv); - irq_gc_unlock(gc); } /***************************************************************************** -- cgit v1.2.3 From b00bee8afaca47fd4f716488eb3663ac1f0abc31 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 13 Mar 2025 15:31:27 +0100 Subject: irqchip: Convert generic irqchip locking to guards Conversion was done with Coccinelle and a few manual fixups. In a few interrupt chip callbacks this changes replaces raw_spin_lock_irqsave() with a guard(raw_spinlock). That's intended and correct because those interrupt chip callbacks are invoked with the interrupt descriptor lock held and interrupts disabled. No point in using the irqsave variant. No functional change. Signed-off-by: Thomas Gleixner Reviewed-by: Linus Walleij Link: https://lore.kernel.org/all/20250313142524.325627746@linutronix.de --- drivers/irqchip/irq-al-fic.c | 18 +++++------------- drivers/irqchip/irq-atmel-aic.c | 19 ++++++------------- drivers/irqchip/irq-atmel-aic5.c | 28 ++++++++-------------------- drivers/irqchip/irq-bcm7120-l2.c | 22 +++++++++------------- drivers/irqchip/irq-brcmstb-l2.c | 8 ++------ drivers/irqchip/irq-csky-apb-intc.c | 3 +-- drivers/irqchip/irq-dw-apb-ictl.c | 3 +-- drivers/irqchip/irq-ingenic-tcu.c | 9 +++------ drivers/irqchip/irq-lan966x-oic.c | 18 +++++++----------- drivers/irqchip/irq-loongson-liointc.c | 9 ++------- drivers/irqchip/irq-mscc-ocelot.c | 3 +-- drivers/irqchip/irq-stm32-exti.c | 21 ++++++--------------- drivers/irqchip/irq-sunxi-nmi.c | 9 ++------- drivers/irqchip/irq-tb10x.c | 13 +++---------- 14 files changed, 56 insertions(+), 127 deletions(-) diff --git a/drivers/irqchip/irq-al-fic.c b/drivers/irqchip/irq-al-fic.c index dfb761e86c9c..7308b8dadecc 100644 --- a/drivers/irqchip/irq-al-fic.c +++ b/drivers/irqchip/irq-al-fic.c @@ -65,15 +65,13 @@ static int al_fic_irq_set_type(struct irq_data *data, unsigned int flow_type) struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); struct al_fic *fic = gc->private; enum al_fic_state new_state; - int ret = 0; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); if (((flow_type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_LEVEL_HIGH) && ((flow_type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_EDGE_RISING)) { pr_debug("fic doesn't support flow type %d\n", flow_type); - ret = -EINVAL; - goto err; + return -EINVAL; } new_state = (flow_type & IRQ_TYPE_LEVEL_HIGH) ? @@ -91,16 +89,10 @@ static int al_fic_irq_set_type(struct irq_data *data, unsigned int flow_type) if (fic->state == AL_FIC_UNCONFIGURED) { al_fic_set_trigger(fic, gc, new_state); } else if (fic->state != new_state) { - pr_debug("fic %s state already configured to %d\n", - fic->name, fic->state); - ret = -EINVAL; - goto err; + pr_debug("fic %s state already configured to %d\n", fic->name, fic->state); + return -EINVAL; } - -err: - irq_gc_unlock(gc); - - return ret; + return 0; } static void al_fic_irq_handler(struct irq_desc *desc) diff --git a/drivers/irqchip/irq-atmel-aic.c b/drivers/irqchip/irq-atmel-aic.c index 3839ad79ad31..03aeed39a4d2 100644 --- a/drivers/irqchip/irq-atmel-aic.c +++ b/drivers/irqchip/irq-atmel-aic.c @@ -78,9 +78,8 @@ static int aic_retrigger(struct irq_data *d) struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); /* Enable interrupt on AIC5 */ - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); irq_reg_writel(gc, d->mask, AT91_AIC_ISCR); - irq_gc_unlock(gc); return 1; } @@ -106,30 +105,27 @@ static void aic_suspend(struct irq_data *d) { struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); irq_reg_writel(gc, gc->mask_cache, AT91_AIC_IDCR); irq_reg_writel(gc, gc->wake_active, AT91_AIC_IECR); - irq_gc_unlock(gc); } static void aic_resume(struct irq_data *d) { struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); irq_reg_writel(gc, gc->wake_active, AT91_AIC_IDCR); irq_reg_writel(gc, gc->mask_cache, AT91_AIC_IECR); - irq_gc_unlock(gc); } static void aic_pm_shutdown(struct irq_data *d) { struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); irq_reg_writel(gc, 0xffffffff, AT91_AIC_IDCR); irq_reg_writel(gc, 0xffffffff, AT91_AIC_ICCR); - irq_gc_unlock(gc); } #else #define aic_suspend NULL @@ -175,10 +171,8 @@ static int aic_irq_domain_xlate(struct irq_domain *d, { struct irq_domain_chip_generic *dgc = d->gc; struct irq_chip_generic *gc; - unsigned long flags; unsigned smr; - int idx; - int ret; + int idx, ret; if (!dgc) return -EINVAL; @@ -194,11 +188,10 @@ static int aic_irq_domain_xlate(struct irq_domain *d, gc = dgc->gc[idx]; - irq_gc_lock_irqsave(gc, flags); + guard(raw_spinlock_irq)(&gc->lock); smr = irq_reg_readl(gc, AT91_AIC_SMR(*out_hwirq)); aic_common_set_priority(intspec[2], &smr); irq_reg_writel(gc, smr, AT91_AIC_SMR(*out_hwirq)); - irq_gc_unlock_irqrestore(gc, flags); return ret; } diff --git a/drivers/irqchip/irq-atmel-aic5.c b/drivers/irqchip/irq-atmel-aic5.c index e98c2875af9e..60b00d2c3d7a 100644 --- a/drivers/irqchip/irq-atmel-aic5.c +++ b/drivers/irqchip/irq-atmel-aic5.c @@ -92,11 +92,10 @@ static void aic5_mask(struct irq_data *d) * Disable interrupt on AIC5. We always take the lock of the * first irq chip as all chips share the same registers. */ - irq_gc_lock(bgc); + guard(raw_spinlock)(&bgc->lock); irq_reg_writel(gc, d->hwirq, AT91_AIC5_SSR); irq_reg_writel(gc, 1, AT91_AIC5_IDCR); gc->mask_cache &= ~d->mask; - irq_gc_unlock(bgc); } static void aic5_unmask(struct irq_data *d) @@ -109,11 +108,10 @@ static void aic5_unmask(struct irq_data *d) * Enable interrupt on AIC5. We always take the lock of the * first irq chip as all chips share the same registers. */ - irq_gc_lock(bgc); + guard(raw_spinlock)(&bgc->lock); irq_reg_writel(gc, d->hwirq, AT91_AIC5_SSR); irq_reg_writel(gc, 1, AT91_AIC5_IECR); gc->mask_cache |= d->mask; - irq_gc_unlock(bgc); } static int aic5_retrigger(struct irq_data *d) @@ -122,11 +120,9 @@ static int aic5_retrigger(struct irq_data *d) struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0); /* Enable interrupt on AIC5 */ - irq_gc_lock(bgc); + guard(raw_spinlock)(&bgc->lock); irq_reg_writel(bgc, d->hwirq, AT91_AIC5_SSR); irq_reg_writel(bgc, 1, AT91_AIC5_ISCR); - irq_gc_unlock(bgc); - return 1; } @@ -137,14 +133,12 @@ static int aic5_set_type(struct irq_data *d, unsigned type) unsigned int smr; int ret; - irq_gc_lock(bgc); + guard(raw_spinlock)(&bgc->lock); irq_reg_writel(bgc, d->hwirq, AT91_AIC5_SSR); smr = irq_reg_readl(bgc, AT91_AIC5_SMR); ret = aic_common_set_type(d, type, &smr); if (!ret) irq_reg_writel(bgc, smr, AT91_AIC5_SMR); - irq_gc_unlock(bgc); - return ret; } @@ -166,7 +160,7 @@ static void aic5_suspend(struct irq_data *d) smr_cache[i] = irq_reg_readl(bgc, AT91_AIC5_SMR); } - irq_gc_lock(bgc); + guard(raw_spinlock)(&bgc->lock); for (i = 0; i < dgc->irqs_per_chip; i++) { mask = 1 << i; if ((mask & gc->mask_cache) == (mask & gc->wake_active)) @@ -178,7 +172,6 @@ static void aic5_suspend(struct irq_data *d) else irq_reg_writel(bgc, 1, AT91_AIC5_IDCR); } - irq_gc_unlock(bgc); } static void aic5_resume(struct irq_data *d) @@ -190,7 +183,7 @@ static void aic5_resume(struct irq_data *d) int i; u32 mask; - irq_gc_lock(bgc); + guard(raw_spinlock)(&bgc->lock); if (smr_cache) { irq_reg_writel(bgc, 0xffffffff, AT91_AIC5_SPU); @@ -214,7 +207,6 @@ static void aic5_resume(struct irq_data *d) else irq_reg_writel(bgc, 1, AT91_AIC5_IDCR); } - irq_gc_unlock(bgc); } static void aic5_pm_shutdown(struct irq_data *d) @@ -225,13 +217,12 @@ static void aic5_pm_shutdown(struct irq_data *d) struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); int i; - irq_gc_lock(bgc); + guard(raw_spinlock)(&bgc->lock); for (i = 0; i < dgc->irqs_per_chip; i++) { irq_reg_writel(bgc, i + gc->irq_base, AT91_AIC5_SSR); irq_reg_writel(bgc, 1, AT91_AIC5_IDCR); irq_reg_writel(bgc, 1, AT91_AIC5_ICCR); } - irq_gc_unlock(bgc); } #else #define aic5_suspend NULL @@ -277,7 +268,6 @@ static int aic5_irq_domain_xlate(struct irq_domain *d, unsigned int *out_type) { struct irq_chip_generic *bgc = irq_get_domain_generic_chip(d, 0); - unsigned long flags; unsigned smr; int ret; @@ -289,13 +279,11 @@ static int aic5_irq_domain_xlate(struct irq_domain *d, if (ret) return ret; - irq_gc_lock_irqsave(bgc, flags); + guard(raw_spinlock_irq)(&bgc->lock); irq_reg_writel(bgc, *out_hwirq, AT91_AIC5_SSR); smr = irq_reg_readl(bgc, AT91_AIC5_SMR); aic_common_set_priority(intspec[2], &smr); irq_reg_writel(bgc, smr, AT91_AIC5_SMR); - irq_gc_unlock_irqrestore(bgc, flags); - return ret; } diff --git a/drivers/irqchip/irq-bcm7120-l2.c b/drivers/irqchip/irq-bcm7120-l2.c index 1e9dab6e0d86..90909f8c6e84 100644 --- a/drivers/irqchip/irq-bcm7120-l2.c +++ b/drivers/irqchip/irq-bcm7120-l2.c @@ -63,16 +63,15 @@ static void bcm7120_l2_intc_irq_handle(struct irq_desc *desc) for (idx = 0; idx < b->n_words; idx++) { int base = idx * IRQS_PER_WORD; - struct irq_chip_generic *gc = - irq_get_domain_generic_chip(b->domain, base); + struct irq_chip_generic *gc; unsigned long pending; int hwirq; - irq_gc_lock(gc); - pending = irq_reg_readl(gc, b->stat_offset[idx]) & - gc->mask_cache & - data->irq_map_mask[idx]; - irq_gc_unlock(gc); + gc = irq_get_domain_generic_chip(b->domain, base); + scoped_guard (raw_spinlock, &gc->lock) { + pending = irq_reg_readl(gc, b->stat_offset[idx]) & gc->mask_cache & + data->irq_map_mask[idx]; + } for_each_set_bit(hwirq, &pending, IRQS_PER_WORD) generic_handle_domain_irq(b->domain, base + hwirq); @@ -86,11 +85,9 @@ static void bcm7120_l2_intc_suspend(struct irq_chip_generic *gc) struct bcm7120_l2_intc_data *b = gc->private; struct irq_chip_type *ct = gc->chip_types; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); if (b->can_wake) - irq_reg_writel(gc, gc->mask_cache | gc->wake_active, - ct->regs.mask); - irq_gc_unlock(gc); + irq_reg_writel(gc, gc->mask_cache | gc->wake_active, ct->regs.mask); } static void bcm7120_l2_intc_resume(struct irq_chip_generic *gc) @@ -98,9 +95,8 @@ static void bcm7120_l2_intc_resume(struct irq_chip_generic *gc) struct irq_chip_type *ct = gc->chip_types; /* Restore the saved mask */ - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); irq_reg_writel(gc, gc->mask_cache, ct->regs.mask); - irq_gc_unlock(gc); } static int bcm7120_l2_intc_init_one(struct device_node *dn, diff --git a/drivers/irqchip/irq-brcmstb-l2.c b/drivers/irqchip/irq-brcmstb-l2.c index db4c9721fcf2..aa4e1232fd62 100644 --- a/drivers/irqchip/irq-brcmstb-l2.c +++ b/drivers/irqchip/irq-brcmstb-l2.c @@ -97,9 +97,8 @@ static void __brcmstb_l2_intc_suspend(struct irq_data *d, bool save) struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); struct irq_chip_type *ct = irq_data_get_chip_type(d); struct brcmstb_l2_intc_data *b = gc->private; - unsigned long flags; - irq_gc_lock_irqsave(gc, flags); + guard(raw_spinlock_irqsave)(&gc->lock); /* Save the current mask */ if (save) b->saved_mask = irq_reg_readl(gc, ct->regs.mask); @@ -109,7 +108,6 @@ static void __brcmstb_l2_intc_suspend(struct irq_data *d, bool save) irq_reg_writel(gc, ~gc->wake_active, ct->regs.disable); irq_reg_writel(gc, gc->wake_active, ct->regs.enable); } - irq_gc_unlock_irqrestore(gc, flags); } static void brcmstb_l2_intc_shutdown(struct irq_data *d) @@ -127,9 +125,8 @@ static void brcmstb_l2_intc_resume(struct irq_data *d) struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); struct irq_chip_type *ct = irq_data_get_chip_type(d); struct brcmstb_l2_intc_data *b = gc->private; - unsigned long flags; - irq_gc_lock_irqsave(gc, flags); + guard(raw_spinlock_irqsave)(&gc->lock); if (ct->chip.irq_ack) { /* Clear unmasked non-wakeup interrupts */ irq_reg_writel(gc, ~b->saved_mask & ~gc->wake_active, @@ -139,7 +136,6 @@ static void brcmstb_l2_intc_resume(struct irq_data *d) /* Restore the saved mask */ irq_reg_writel(gc, b->saved_mask, ct->regs.disable); irq_reg_writel(gc, ~b->saved_mask, ct->regs.enable); - irq_gc_unlock_irqrestore(gc, flags); } static int __init brcmstb_l2_intc_of_init(struct device_node *np, diff --git a/drivers/irqchip/irq-csky-apb-intc.c b/drivers/irqchip/irq-csky-apb-intc.c index 6710691e4c25..6e17186db1cb 100644 --- a/drivers/irqchip/irq-csky-apb-intc.c +++ b/drivers/irqchip/irq-csky-apb-intc.c @@ -50,11 +50,10 @@ static void irq_ck_mask_set_bit(struct irq_data *d) unsigned long ifr = ct->regs.mask - 8; u32 mask = d->mask; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); *ct->mask_cache |= mask; irq_reg_writel(gc, *ct->mask_cache, ct->regs.mask); irq_reg_writel(gc, irq_reg_readl(gc, ifr) & ~mask, ifr); - irq_gc_unlock(gc); } static void __init ck_set_gc(struct device_node *node, void __iomem *reg_base, diff --git a/drivers/irqchip/irq-dw-apb-ictl.c b/drivers/irqchip/irq-dw-apb-ictl.c index d5c1c750c8d2..6874ec82f6da 100644 --- a/drivers/irqchip/irq-dw-apb-ictl.c +++ b/drivers/irqchip/irq-dw-apb-ictl.c @@ -101,10 +101,9 @@ static void dw_apb_ictl_resume(struct irq_data *d) struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); struct irq_chip_type *ct = irq_data_get_chip_type(d); - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); writel_relaxed(~0, gc->reg_base + ct->regs.enable); writel_relaxed(*ct->mask_cache, gc->reg_base + ct->regs.mask); - irq_gc_unlock(gc); } #else #define dw_apb_ictl_resume NULL diff --git a/drivers/irqchip/irq-ingenic-tcu.c b/drivers/irqchip/irq-ingenic-tcu.c index 3363f83bd7e9..945fd57bcc73 100644 --- a/drivers/irqchip/irq-ingenic-tcu.c +++ b/drivers/irqchip/irq-ingenic-tcu.c @@ -52,11 +52,10 @@ static void ingenic_tcu_gc_unmask_enable_reg(struct irq_data *d) struct regmap *map = gc->private; u32 mask = d->mask; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); regmap_write(map, ct->regs.ack, mask); regmap_write(map, ct->regs.enable, mask); *ct->mask_cache |= mask; - irq_gc_unlock(gc); } static void ingenic_tcu_gc_mask_disable_reg(struct irq_data *d) @@ -66,10 +65,9 @@ static void ingenic_tcu_gc_mask_disable_reg(struct irq_data *d) struct regmap *map = gc->private; u32 mask = d->mask; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); regmap_write(map, ct->regs.disable, mask); *ct->mask_cache &= ~mask; - irq_gc_unlock(gc); } static void ingenic_tcu_gc_mask_disable_reg_and_ack(struct irq_data *d) @@ -79,10 +77,9 @@ static void ingenic_tcu_gc_mask_disable_reg_and_ack(struct irq_data *d) struct regmap *map = gc->private; u32 mask = d->mask; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); regmap_write(map, ct->regs.ack, mask); regmap_write(map, ct->regs.disable, mask); - irq_gc_unlock(gc); } static int __init ingenic_tcu_irq_init(struct device_node *np, diff --git a/drivers/irqchip/irq-lan966x-oic.c b/drivers/irqchip/irq-lan966x-oic.c index 41ac880e3b87..f77bd2a2e5e5 100644 --- a/drivers/irqchip/irq-lan966x-oic.c +++ b/drivers/irqchip/irq-lan966x-oic.c @@ -71,14 +71,12 @@ static unsigned int lan966x_oic_irq_startup(struct irq_data *data) struct lan966x_oic_chip_regs *chip_regs = gc->private; u32 map; - irq_gc_lock(gc); - - /* Map the source interrupt to the destination */ - map = irq_reg_readl(gc, chip_regs->reg_off_map); - map |= data->mask; - irq_reg_writel(gc, map, chip_regs->reg_off_map); - - irq_gc_unlock(gc); + scoped_guard (raw_spinlock, &gc->lock) { + /* Map the source interrupt to the destination */ + map = irq_reg_readl(gc, chip_regs->reg_off_map); + map |= data->mask; + irq_reg_writel(gc, map, chip_regs->reg_off_map); + } ct->chip.irq_ack(data); ct->chip.irq_unmask(data); @@ -95,14 +93,12 @@ static void lan966x_oic_irq_shutdown(struct irq_data *data) ct->chip.irq_mask(data); - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); /* Unmap the interrupt */ map = irq_reg_readl(gc, chip_regs->reg_off_map); map &= ~data->mask; irq_reg_writel(gc, map, chip_regs->reg_off_map); - - irq_gc_unlock(gc); } static int lan966x_oic_irq_set_type(struct irq_data *data, diff --git a/drivers/irqchip/irq-loongson-liointc.c b/drivers/irqchip/irq-loongson-liointc.c index 2b1bd4a96665..61c0b479bfe5 100644 --- a/drivers/irqchip/irq-loongson-liointc.c +++ b/drivers/irqchip/irq-loongson-liointc.c @@ -116,9 +116,8 @@ static int liointc_set_type(struct irq_data *data, unsigned int type) { struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); u32 mask = data->mask; - unsigned long flags; - irq_gc_lock_irqsave(gc, flags); + guard(raw_spinlock)(&gc->lock); switch (type) { case IRQ_TYPE_LEVEL_HIGH: liointc_set_bit(gc, LIOINTC_REG_INTC_EDGE, mask, false); @@ -137,10 +136,8 @@ static int liointc_set_type(struct irq_data *data, unsigned int type) liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, true); break; default: - irq_gc_unlock_irqrestore(gc, flags); return -EINVAL; } - irq_gc_unlock_irqrestore(gc, flags); irqd_set_trigger_type(data, type); return 0; @@ -157,10 +154,9 @@ static void liointc_suspend(struct irq_chip_generic *gc) static void liointc_resume(struct irq_chip_generic *gc) { struct liointc_priv *priv = gc->private; - unsigned long flags; int i; - irq_gc_lock_irqsave(gc, flags); + guard(raw_spinlock_irqsave)(&gc->lock); /* Disable all at first */ writel(0xffffffff, gc->reg_base + LIOINTC_REG_INTC_DISABLE); /* Restore map cache */ @@ -170,7 +166,6 @@ static void liointc_resume(struct irq_chip_generic *gc) writel(priv->int_edge, gc->reg_base + LIOINTC_REG_INTC_EDGE); /* Restore mask cache */ writel(gc->mask_cache, gc->reg_base + LIOINTC_REG_INTC_ENABLE); - irq_gc_unlock_irqrestore(gc, flags); } static int parent_irq[LIOINTC_NUM_PARENT]; diff --git a/drivers/irqchip/irq-mscc-ocelot.c b/drivers/irqchip/irq-mscc-ocelot.c index 3dc745b14caf..d69c5f112a7e 100644 --- a/drivers/irqchip/irq-mscc-ocelot.c +++ b/drivers/irqchip/irq-mscc-ocelot.c @@ -83,7 +83,7 @@ static void ocelot_irq_unmask(struct irq_data *data) unsigned int mask = data->mask; u32 val; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); /* * Clear sticky bits for edge mode interrupts. * Serval has only one trigger register replication, but the adjacent @@ -97,7 +97,6 @@ static void ocelot_irq_unmask(struct irq_data *data) *ct->mask_cache &= ~mask; irq_reg_writel(gc, mask, p->reg_off_ena_set); - irq_gc_unlock(gc); } static void ocelot_irq_handler(struct irq_desc *desc) diff --git a/drivers/irqchip/irq-stm32-exti.c b/drivers/irqchip/irq-stm32-exti.c index 7c6a0080c330..477046d4db0f 100644 --- a/drivers/irqchip/irq-stm32-exti.c +++ b/drivers/irqchip/irq-stm32-exti.c @@ -169,22 +169,18 @@ static int stm32_irq_set_type(struct irq_data *d, unsigned int type) u32 rtsr, ftsr; int err; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); 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) - goto unlock; + return err; irq_reg_writel(gc, rtsr, stm32_bank->rtsr_ofst); irq_reg_writel(gc, ftsr, stm32_bank->ftsr_ofst); - -unlock: - irq_gc_unlock(gc); - - return err; + return 0; } static void stm32_chip_suspend(struct stm32_exti_chip_data *chip_data, @@ -217,18 +213,16 @@ static void stm32_irq_suspend(struct irq_chip_generic *gc) { struct stm32_exti_chip_data *chip_data = gc->private; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); stm32_chip_suspend(chip_data, gc->wake_active); - irq_gc_unlock(gc); } static void stm32_irq_resume(struct irq_chip_generic *gc) { struct stm32_exti_chip_data *chip_data = gc->private; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); stm32_chip_resume(chip_data, gc->mask_cache); - irq_gc_unlock(gc); } static int stm32_exti_alloc(struct irq_domain *d, unsigned int virq, @@ -265,11 +259,8 @@ static void stm32_irq_ack(struct irq_data *d) struct stm32_exti_chip_data *chip_data = gc->private; const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; - irq_gc_lock(gc); - + guard(raw_spinlock)(&gc->lock); irq_reg_writel(gc, d->mask, stm32_bank->rpr_ofst); - - irq_gc_unlock(gc); } static struct diff --git a/drivers/irqchip/irq-sunxi-nmi.c b/drivers/irqchip/irq-sunxi-nmi.c index 01b0d8321728..4c977ac10ddc 100644 --- a/drivers/irqchip/irq-sunxi-nmi.c +++ b/drivers/irqchip/irq-sunxi-nmi.c @@ -111,7 +111,7 @@ static int sunxi_sc_nmi_set_type(struct irq_data *data, unsigned int flow_type) unsigned int src_type; unsigned int i; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); switch (flow_type & IRQF_TRIGGER_MASK) { case IRQ_TYPE_EDGE_FALLING: @@ -128,9 +128,7 @@ static int sunxi_sc_nmi_set_type(struct irq_data *data, unsigned int flow_type) src_type = SUNXI_SRC_TYPE_LEVEL_LOW; break; default: - irq_gc_unlock(gc); - pr_err("Cannot assign multiple trigger modes to IRQ %d.\n", - data->irq); + pr_err("Cannot assign multiple trigger modes to IRQ %d.\n", data->irq); return -EBADR; } @@ -145,9 +143,6 @@ static int sunxi_sc_nmi_set_type(struct irq_data *data, unsigned int flow_type) src_type_reg &= ~SUNXI_NMI_SRC_TYPE_MASK; src_type_reg |= src_type; sunxi_sc_nmi_write(gc, ctrl_off, src_type_reg); - - irq_gc_unlock(gc); - return IRQ_SET_MASK_OK; } diff --git a/drivers/irqchip/irq-tb10x.c b/drivers/irqchip/irq-tb10x.c index d59bfbe8c6d0..aa80944695e6 100644 --- a/drivers/irqchip/irq-tb10x.c +++ b/drivers/irqchip/irq-tb10x.c @@ -41,11 +41,9 @@ static inline u32 ab_irqctl_readreg(struct irq_chip_generic *gc, u32 reg) static int tb10x_irq_set_type(struct irq_data *data, unsigned int flow_type) { struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); - uint32_t im, mod, pol; + uint32_t mod, pol, im = data->mask; - im = data->mask; - - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); mod = ab_irqctl_readreg(gc, AB_IRQCTL_SRC_MODE) | im; pol = ab_irqctl_readreg(gc, AB_IRQCTL_SRC_POLARITY) | im; @@ -67,9 +65,7 @@ static int tb10x_irq_set_type(struct irq_data *data, unsigned int flow_type) case IRQ_TYPE_EDGE_RISING: break; default: - irq_gc_unlock(gc); - pr_err("%s: Cannot assign multiple trigger modes to IRQ %d.\n", - __func__, data->irq); + pr_err("%s: Cannot assign multiple trigger modes to IRQ %d.\n", __func__, data->irq); return -EBADR; } @@ -79,9 +75,6 @@ static int tb10x_irq_set_type(struct irq_data *data, unsigned int flow_type) ab_irqctl_writereg(gc, AB_IRQCTL_SRC_MODE, mod); ab_irqctl_writereg(gc, AB_IRQCTL_SRC_POLARITY, pol); ab_irqctl_writereg(gc, AB_IRQCTL_INT_STATUS, im); - - irq_gc_unlock(gc); - return IRQ_SET_MASK_OK; } -- cgit v1.2.3 From 7ae844a6650c5c15ccfbf76ed767e7f2cc61ec1d Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 13 Mar 2025 15:31:28 +0100 Subject: genirq/generic-chip: Remove unused lock wrappers All users are converted to lock guards. Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/all/20250313142524.388478168@linutronix.de --- include/linux/irq.h | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/include/linux/irq.h b/include/linux/irq.h index 500772943207..d896d3a471ec 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -1222,26 +1222,6 @@ static inline struct irq_chip_type *irq_data_get_chip_type(struct irq_data *d) #define IRQ_MSK(n) (u32)((n) < 32 ? ((1 << (n)) - 1) : UINT_MAX) -static inline void irq_gc_lock(struct irq_chip_generic *gc) -{ - raw_spin_lock(&gc->lock); -} - -static inline void irq_gc_unlock(struct irq_chip_generic *gc) -{ - raw_spin_unlock(&gc->lock); -} - -/* - * The irqsave variants are for usage in non interrupt code. Do not use - * them in irq_chip callbacks. Use irq_gc_lock() instead. - */ -#define irq_gc_lock_irqsave(gc, flags) \ - raw_spin_lock_irqsave(&(gc)->lock, flags) - -#define irq_gc_unlock_irqrestore(gc, flags) \ - raw_spin_unlock_irqrestore(&(gc)->lock, flags) - static inline void irq_reg_writel(struct irq_chip_generic *gc, u32 val, int reg_offset) { -- cgit v1.2.3 From 771487050f83b030090079133b192de6e3cf5909 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 8 Apr 2025 20:07:02 +0200 Subject: genirq/generic-chip: Fix incorrect lock guard conversions When booting BeagleBone Black: ------------[ cut here ]------------ WARNING: CPU: 0 PID: 0 at kernel/locking/lockdep.c:4398 lockdep_hardirqs_on_prepare+0x23c/0x280 DEBUG_LOCKS_WARN_ON(early_boot_irqs_disabled) CPU: 0 UID: 0 PID: 0 Comm: swapper/0 Not tainted 6.15.0-rc1-boneblack-00004-g195298c3b116 #209 NONE Hardware name: Generic AM33XX (Flattened Device Tree) Call trace: _raw_spin_unlock_irq from irq_map_generic_chip+0x144/0x190 irq_map_generic_chip from irq_domain_associate_locked+0x68/0x164 irq_domain_associate_locked from irq_create_fwspec_mapping+0x34c/0x43c irq_create_fwspec_mapping from irq_create_of_mapping+0x64/0x8c irq_create_of_mapping from irq_of_parse_and_map+0x54/0x7c irq_of_parse_and_map from dmtimer_clkevt_init_common+0x54/0x15c dmtimer_clkevt_init_common from dmtimer_systimer_init+0x41c/0x5b8 dmtimer_systimer_init from timer_probe+0x68/0xf0 timer_probe from start_kernel+0x4a4/0x6bc start_kernel from 0x0 irq event stamp: 0 hardirqs last enabled at (0): [<00000000>] 0x0 hardirqs last disabled at (0): [<00000000>] 0x0 softirqs last enabled at (0): [<00000000>] 0x0 softirqs last disabled at (0): [<00000000>] 0x0 ---[ end trace 0000000000000000 ]--- and: ------------[ cut here ]------------ WARNING: CPU: 0 PID: 0 at init/main.c:1022 start_kernel+0x4e8/0x6bc Interrupts were enabled early CPU: 0 UID: 0 PID: 0 Comm: swapper/0 Tainted: G W 6.15.0-rc1-boneblack-00004-g195298c3b116 #209 NONE Tainted: [W]=WARN Hardware name: Generic AM33XX (Flattened Device Tree) Call trace: unwind_backtrace from show_stack+0x10/0x14 show_stack from dump_stack_lvl+0x6c/0x90 dump_stack_lvl from __warn+0x70/0x1b0 __warn from warn_slowpath_fmt+0x1d4/0x1ec warn_slowpath_fmt from start_kernel+0x4e8/0x6bc start_kernel from 0x0 irq event stamp: 0 hardirqs last enabled at (0): [<00000000>] 0x0 hardirqs last disabled at (0): [<00000000>] 0x0 softirqs last enabled at (0): [<00000000>] 0x0 softirqs last disabled at (0): [<00000000>] 0x0 ---[ end trace 0000000000000000 ]--- Fix this by correcting two misconversions of raw_spin_{,un}lock_irq{save,restore}() to lock guards. Fixes: 195298c3b11628a6 ("genirq/generic-chip: Convert core code to lock guards") Signed-off-by: Geert Uytterhoeven Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/all/514f94c5891c61ac0a4a7fdad113e75db1eea367.1744135467.git.geert+renesas@glider.be --- kernel/irq/generic-chip.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/irq/generic-chip.c b/kernel/irq/generic-chip.c index 8014bfecb948..bf59e37d650a 100644 --- a/kernel/irq/generic-chip.c +++ b/kernel/irq/generic-chip.c @@ -330,7 +330,7 @@ int irq_domain_alloc_generic_chips(struct irq_domain *d, goto err; } - scoped_guard (raw_spinlock, &gc_lock) + scoped_guard (raw_spinlock_irqsave, &gc_lock) list_add_tail(&gc->list, &gc_list); /* Calc pointer to the next generic chip */ tmp += gc_sz; @@ -467,7 +467,7 @@ int irq_map_generic_chip(struct irq_domain *d, unsigned int virq, /* We only init the cache for the first mapping of a generic chip */ if (!gc->installed) { - guard(raw_spinlock_irq)(&gc->lock); + guard(raw_spinlock_irqsave)(&gc->lock); irq_gc_init_mask_cache(gc, dgc->gc_flags); } -- cgit v1.2.3 From 9fe5a0790af6af619940a2819aabe8f6fb30700c Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Mon, 14 Apr 2025 06:49:12 +0800 Subject: dt-bindings: interrupt-controller: Add Sophgo SG2044 MSI controller Like SG2042, SG2044 also uses an external MSI controller to provide MSI interrupt for PCIe controllers. The difference between these two MSI controllers are: 1. SG2044 acks the interrupt by writing 0, SG2042 by setting the bit related to the interrupt. 2. SG2044 uses interrupt number modulo 32 as MSI message data, but SG2042 uses the bit related to the interrupt. Add support for the SG2044 MSI controller. Signed-off-by: Inochi Amaoto Signed-off-by: Thomas Gleixner Tested-by: Chen Wang # SG2042 Reviewed-by: Chen Wang Acked-by: Conor Dooley Link: https://lore.kernel.org/all/20250413224922.69719-2-inochiama@gmail.com --- .../devicetree/bindings/interrupt-controller/sophgo,sg2042-msi.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/interrupt-controller/sophgo,sg2042-msi.yaml b/Documentation/devicetree/bindings/interrupt-controller/sophgo,sg2042-msi.yaml index e1ffd55fa7bf..f6b8b1d92f79 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/sophgo,sg2042-msi.yaml +++ b/Documentation/devicetree/bindings/interrupt-controller/sophgo,sg2042-msi.yaml @@ -18,7 +18,9 @@ allOf: properties: compatible: - const: sophgo,sg2042-msi + enum: + - sophgo,sg2042-msi + - sophgo,sg2044-msi reg: items: -- cgit v1.2.3 From bced55494c23dca674c3c18b4a07ffe7c15000e2 Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Mon, 14 Apr 2025 06:49:13 +0800 Subject: irqchip/sg2042-msi: Rename functions and data structures to be SG2042 agnostic As the driver logic can be used in both SG2042 and SG2044 SoCs, rename functions and data structures, which are not SG2042 specific, to SG204x*. Signed-off-by: Inochi Amaoto Signed-off-by: Thomas Gleixner Tested-by: Chen Wang # SG2042 Reviewed-by: Chen Wang Link: https://lore.kernel.org/all/20250413224922.69719-3-inochiama@gmail.com --- drivers/irqchip/irq-sg2042-msi.c | 46 ++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/irqchip/irq-sg2042-msi.c b/drivers/irqchip/irq-sg2042-msi.c index ee682e87eb8b..5f7fae456bd4 100644 --- a/drivers/irqchip/irq-sg2042-msi.c +++ b/drivers/irqchip/irq-sg2042-msi.c @@ -21,7 +21,7 @@ #define SG2042_MAX_MSI_VECTOR 32 -struct sg2042_msi_chipdata { +struct sg204x_msi_chipdata { void __iomem *reg_clr; // clear reg, see TRM, 10.1.33, GP_INTR0_CLR phys_addr_t doorbell_addr; // see TRM, 10.1.32, GP_INTR0_SET @@ -33,7 +33,7 @@ struct sg2042_msi_chipdata { struct mutex msi_map_lock; // lock for msi_map }; -static int sg2042_msi_allocate_hwirq(struct sg2042_msi_chipdata *data, int num_req) +static int sg204x_msi_allocate_hwirq(struct sg204x_msi_chipdata *data, int num_req) { int first; @@ -43,7 +43,7 @@ static int sg2042_msi_allocate_hwirq(struct sg2042_msi_chipdata *data, int num_r return first >= 0 ? first : -ENOSPC; } -static void sg2042_msi_free_hwirq(struct sg2042_msi_chipdata *data, int hwirq, int num_req) +static void sg204x_msi_free_hwirq(struct sg204x_msi_chipdata *data, int hwirq, int num_req) { guard(mutex)(&data->msi_map_lock); bitmap_release_region(data->msi_map, hwirq, get_count_order(num_req)); @@ -51,7 +51,7 @@ static void sg2042_msi_free_hwirq(struct sg2042_msi_chipdata *data, int hwirq, i static void sg2042_msi_irq_ack(struct irq_data *d) { - struct sg2042_msi_chipdata *data = irq_data_get_irq_chip_data(d); + struct sg204x_msi_chipdata *data = irq_data_get_irq_chip_data(d); int bit_off = d->hwirq; writel(1 << bit_off, data->reg_clr); @@ -61,7 +61,7 @@ static void sg2042_msi_irq_ack(struct irq_data *d) static void sg2042_msi_irq_compose_msi_msg(struct irq_data *d, struct msi_msg *msg) { - struct sg2042_msi_chipdata *data = irq_data_get_irq_chip_data(d); + struct sg204x_msi_chipdata *data = irq_data_get_irq_chip_data(d); msg->address_hi = upper_32_bits(data->doorbell_addr); msg->address_lo = lower_32_bits(data->doorbell_addr); @@ -79,9 +79,9 @@ static const struct irq_chip sg2042_msi_middle_irq_chip = { .irq_compose_msi_msg = sg2042_msi_irq_compose_msi_msg, }; -static int sg2042_msi_parent_domain_alloc(struct irq_domain *domain, unsigned int virq, int hwirq) +static int sg204x_msi_parent_domain_alloc(struct irq_domain *domain, unsigned int virq, int hwirq) { - struct sg2042_msi_chipdata *data = domain->host_data; + struct sg204x_msi_chipdata *data = domain->host_data; struct irq_fwspec fwspec; struct irq_data *d; int ret; @@ -99,18 +99,18 @@ static int sg2042_msi_parent_domain_alloc(struct irq_domain *domain, unsigned in return d->chip->irq_set_type(d, IRQ_TYPE_EDGE_RISING); } -static int sg2042_msi_middle_domain_alloc(struct irq_domain *domain, unsigned int virq, +static int sg204x_msi_middle_domain_alloc(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs, void *args) { - struct sg2042_msi_chipdata *data = domain->host_data; + struct sg204x_msi_chipdata *data = domain->host_data; int hwirq, err, i; - hwirq = sg2042_msi_allocate_hwirq(data, nr_irqs); + hwirq = sg204x_msi_allocate_hwirq(data, nr_irqs); if (hwirq < 0) return hwirq; for (i = 0; i < nr_irqs; i++) { - err = sg2042_msi_parent_domain_alloc(domain, virq + i, hwirq + i); + err = sg204x_msi_parent_domain_alloc(domain, virq + i, hwirq + i); if (err) goto err_hwirq; @@ -121,25 +121,25 @@ static int sg2042_msi_middle_domain_alloc(struct irq_domain *domain, unsigned in return 0; err_hwirq: - sg2042_msi_free_hwirq(data, hwirq, nr_irqs); + sg204x_msi_free_hwirq(data, hwirq, nr_irqs); irq_domain_free_irqs_parent(domain, virq, i); return err; } -static void sg2042_msi_middle_domain_free(struct irq_domain *domain, unsigned int virq, +static void sg204x_msi_middle_domain_free(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs) { struct irq_data *d = irq_domain_get_irq_data(domain, virq); - struct sg2042_msi_chipdata *data = irq_data_get_irq_chip_data(d); + struct sg204x_msi_chipdata *data = irq_data_get_irq_chip_data(d); irq_domain_free_irqs_parent(domain, virq, nr_irqs); - sg2042_msi_free_hwirq(data, d->hwirq, nr_irqs); + sg204x_msi_free_hwirq(data, d->hwirq, nr_irqs); } -static const struct irq_domain_ops sg2042_msi_middle_domain_ops = { - .alloc = sg2042_msi_middle_domain_alloc, - .free = sg2042_msi_middle_domain_free, +static const struct irq_domain_ops sg204x_msi_middle_domain_ops = { + .alloc = sg204x_msi_middle_domain_alloc, + .free = sg204x_msi_middle_domain_free, .select = msi_lib_irq_domain_select, }; @@ -157,14 +157,14 @@ static const struct msi_parent_ops sg2042_msi_parent_ops = { .init_dev_msi_info = msi_lib_init_dev_msi_info, }; -static int sg2042_msi_init_domains(struct sg2042_msi_chipdata *data, +static int sg204x_msi_init_domains(struct sg204x_msi_chipdata *data, struct irq_domain *plic_domain, struct device *dev) { struct fwnode_handle *fwnode = dev_fwnode(dev); struct irq_domain *middle_domain; middle_domain = irq_domain_create_hierarchy(plic_domain, 0, data->num_irqs, fwnode, - &sg2042_msi_middle_domain_ops, data); + &sg204x_msi_middle_domain_ops, data); if (!middle_domain) { pr_err("Failed to create the MSI middle domain\n"); return -ENOMEM; @@ -181,13 +181,13 @@ static int sg2042_msi_init_domains(struct sg2042_msi_chipdata *data, static int sg2042_msi_probe(struct platform_device *pdev) { struct fwnode_reference_args args = { }; - struct sg2042_msi_chipdata *data; + struct sg204x_msi_chipdata *data; struct device *dev = &pdev->dev; struct irq_domain *plic_domain; struct resource *res; int ret; - data = devm_kzalloc(dev, sizeof(struct sg2042_msi_chipdata), GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(struct sg204x_msi_chipdata), GFP_KERNEL); if (!data) return -ENOMEM; @@ -231,7 +231,7 @@ static int sg2042_msi_probe(struct platform_device *pdev) mutex_init(&data->msi_map_lock); - return sg2042_msi_init_domains(data, plic_domain, dev); + return sg204x_msi_init_domains(data, plic_domain, dev); } static const struct of_device_id sg2042_msi_of_match[] = { -- cgit v1.2.3 From bad2094e3b1c37c683f183a2a885ea0232ba31e9 Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Mon, 14 Apr 2025 06:49:14 +0800 Subject: irqchip/sg2042-msi: Introduce configurable chipinfo for SG2042 As the controller on SG2044 uses different msi_parent_ops and a difffernt irq_chip, it is necessary to provide that information to the probe function. Add a new chipinfo structure to hold that information, implement the necessary logic and make SG2042 use it. Signed-off-by: Inochi Amaoto Signed-off-by: Thomas Gleixner Tested-by: Chen Wang # SG2042 Reviewed-by: Chen Wang Link: https://lore.kernel.org/all/20250413224922.69719-4-inochiama@gmail.com --- drivers/irqchip/irq-sg2042-msi.c | 53 +++++++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 11 deletions(-) diff --git a/drivers/irqchip/irq-sg2042-msi.c b/drivers/irqchip/irq-sg2042-msi.c index 5f7fae456bd4..325a83c17573 100644 --- a/drivers/irqchip/irq-sg2042-msi.c +++ b/drivers/irqchip/irq-sg2042-msi.c @@ -19,18 +19,33 @@ #include "irq-msi-lib.h" -#define SG2042_MAX_MSI_VECTOR 32 +struct sg204x_msi_chip_info { + const struct irq_chip *irqchip; + const struct msi_parent_ops *parent_ops; +}; +/** + * struct sg204x_msi_chipdata - chip data for the SG204x MSI IRQ controller + * @reg_clr: clear reg, see TRM, 10.1.33, GP_INTR0_CLR + * @doorbell_addr: see TRM, 10.1.32, GP_INTR0_SET + * @irq_first: First vectors number that MSIs starts + * @num_irqs: Number of vectors for MSIs + * @msi_map: mapping for allocated MSI vectors. + * @msi_map_lock: Lock for msi_map + * @chip_info: chip specific infomations + */ struct sg204x_msi_chipdata { - void __iomem *reg_clr; // clear reg, see TRM, 10.1.33, GP_INTR0_CLR + void __iomem *reg_clr; + + phys_addr_t doorbell_addr; - phys_addr_t doorbell_addr; // see TRM, 10.1.32, GP_INTR0_SET + u32 irq_first; + u32 num_irqs; - u32 irq_first; // The vector number that MSIs starts - u32 num_irqs; // The number of vectors for MSIs + unsigned long *msi_map; + struct mutex msi_map_lock; - DECLARE_BITMAP(msi_map, SG2042_MAX_MSI_VECTOR); - struct mutex msi_map_lock; // lock for msi_map + const struct sg204x_msi_chip_info *chip_info; }; static int sg204x_msi_allocate_hwirq(struct sg204x_msi_chipdata *data, int num_req) @@ -115,7 +130,7 @@ static int sg204x_msi_middle_domain_alloc(struct irq_domain *domain, unsigned in goto err_hwirq; irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, - &sg2042_msi_middle_irq_chip, data); + data->chip_info->irqchip, data); } return 0; @@ -173,8 +188,7 @@ static int sg204x_msi_init_domains(struct sg204x_msi_chipdata *data, irq_domain_update_bus_token(middle_domain, DOMAIN_BUS_NEXUS); middle_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT; - middle_domain->msi_parent_ops = &sg2042_msi_parent_ops; - + middle_domain->msi_parent_ops = data->chip_info->parent_ops; return 0; } @@ -191,6 +205,12 @@ static int sg2042_msi_probe(struct platform_device *pdev) if (!data) return -ENOMEM; + data->chip_info = device_get_match_data(&pdev->dev); + if (!data->chip_info) { + dev_err(&pdev->dev, "Failed to get irqchip\n"); + return -EINVAL; + } + data->reg_clr = devm_platform_ioremap_resource_byname(pdev, "clr"); if (IS_ERR(data->reg_clr)) { dev_err(dev, "Failed to map clear register\n"); @@ -231,11 +251,22 @@ static int sg2042_msi_probe(struct platform_device *pdev) mutex_init(&data->msi_map_lock); + data->msi_map = devm_bitmap_zalloc(&pdev->dev, data->num_irqs, GFP_KERNEL); + if (!data->msi_map) { + dev_err(&pdev->dev, "Unable to allocate msi mapping\n"); + return -ENOMEM; + } + return sg204x_msi_init_domains(data, plic_domain, dev); } +static const struct sg204x_msi_chip_info sg2042_chip_info = { + .irqchip = &sg2042_msi_middle_irq_chip, + .parent_ops = &sg2042_msi_parent_ops, +}; + static const struct of_device_id sg2042_msi_of_match[] = { - { .compatible = "sophgo,sg2042-msi" }, + { .compatible = "sophgo,sg2042-msi", .data = &sg2042_chip_info }, { } }; -- cgit v1.2.3 From e96b93a97c90181627cef6e70a0dbc8dbdae4adc Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Mon, 14 Apr 2025 06:49:15 +0800 Subject: irqchip/sg2042-msi: Add the Sophgo SG2044 MSI interrupt controller Add support for Sophgo SG2044 MSI interrupt controller. Signed-off-by: Inochi Amaoto Signed-off-by: Thomas Gleixner Tested-by: Chen Wang # SG2042 Reviewed-by: Chen Wang Link: https://lore.kernel.org/all/20250413224922.69719-5-inochiama@gmail.com --- drivers/irqchip/irq-sg2042-msi.c | 53 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/drivers/irqchip/irq-sg2042-msi.c b/drivers/irqchip/irq-sg2042-msi.c index 325a83c17573..8a83c690b760 100644 --- a/drivers/irqchip/irq-sg2042-msi.c +++ b/drivers/irqchip/irq-sg2042-msi.c @@ -94,6 +94,35 @@ static const struct irq_chip sg2042_msi_middle_irq_chip = { .irq_compose_msi_msg = sg2042_msi_irq_compose_msi_msg, }; +static void sg2044_msi_irq_ack(struct irq_data *d) +{ + struct sg204x_msi_chipdata *data = irq_data_get_irq_chip_data(d); + + writel(0, (u32 *)data->reg_clr + d->hwirq); + irq_chip_ack_parent(d); +} + +static void sg2044_msi_irq_compose_msi_msg(struct irq_data *d, struct msi_msg *msg) +{ + struct sg204x_msi_chipdata *data = irq_data_get_irq_chip_data(d); + phys_addr_t doorbell = data->doorbell_addr + 4 * (d->hwirq / 32); + + msg->address_lo = lower_32_bits(doorbell); + msg->address_hi = upper_32_bits(doorbell); + msg->data = d->hwirq % 32; +} + +static struct irq_chip sg2044_msi_middle_irq_chip = { + .name = "SG2044 MSI", + .irq_ack = sg2044_msi_irq_ack, + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, +#ifdef CONFIG_SMP + .irq_set_affinity = irq_chip_set_affinity_parent, +#endif + .irq_compose_msi_msg = sg2044_msi_irq_compose_msi_msg, +}; + static int sg204x_msi_parent_domain_alloc(struct irq_domain *domain, unsigned int virq, int hwirq) { struct sg204x_msi_chipdata *data = domain->host_data; @@ -132,13 +161,11 @@ static int sg204x_msi_middle_domain_alloc(struct irq_domain *domain, unsigned in irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, data->chip_info->irqchip, data); } - return 0; err_hwirq: sg204x_msi_free_hwirq(data, hwirq, nr_irqs); irq_domain_free_irqs_parent(domain, virq, i); - return err; } @@ -172,6 +199,22 @@ static const struct msi_parent_ops sg2042_msi_parent_ops = { .init_dev_msi_info = msi_lib_init_dev_msi_info, }; +#define SG2044_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \ + MSI_FLAG_USE_DEF_CHIP_OPS) + +#define SG2044_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK | \ + MSI_FLAG_PCI_MSIX) + +static const struct msi_parent_ops sg2044_msi_parent_ops = { + .required_flags = SG2044_MSI_FLAGS_REQUIRED, + .supported_flags = SG2044_MSI_FLAGS_SUPPORTED, + .chip_flags = MSI_CHIP_FLAG_SET_EOI | MSI_CHIP_FLAG_SET_ACK, + .bus_select_mask = MATCH_PCI_MSI, + .bus_select_token = DOMAIN_BUS_NEXUS, + .prefix = "SG2044-", + .init_dev_msi_info = msi_lib_init_dev_msi_info, +}; + static int sg204x_msi_init_domains(struct sg204x_msi_chipdata *data, struct irq_domain *plic_domain, struct device *dev) { @@ -265,8 +308,14 @@ static const struct sg204x_msi_chip_info sg2042_chip_info = { .parent_ops = &sg2042_msi_parent_ops, }; +static const struct sg204x_msi_chip_info sg2044_chip_info = { + .irqchip = &sg2044_msi_middle_irq_chip, + .parent_ops = &sg2044_msi_parent_ops, +}; + static const struct of_device_id sg2042_msi_of_match[] = { { .compatible = "sophgo,sg2042-msi", .data = &sg2042_chip_info }, + { .compatible = "sophgo,sg2044-msi", .data = &sg2044_chip_info }, { } }; -- cgit v1.2.3 From 76b66e8c9d159eb3d1699e0fa80ceacf9a9ae627 Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Tue, 22 Apr 2025 08:38:03 +0800 Subject: irqchip/sg2042-msi: Fix wrong type cast in sg2044_msi_irq_ack() The type cast in sg2044_msi_irq_ack() lost the __iomem attribute, which makes the pointer type incorrect. Add it back. Fixes: e96b93a97c90 ("irqchip/sg2042-msi: Add the Sophgo SG2044 MSI interrupt controller") Reported-by: kernel test robot Signed-off-by: Inochi Amaoto Signed-off-by: Thomas Gleixner Reviewed-by: Chen Wang Link: https://lore.kernel.org/all/20250422003804.214264-1-inochiama@gmail.com Closes: https://lore.kernel.org/oe-kbuild-all/202504211251.B3aesulq-lkp@intel.com/ --- drivers/irqchip/irq-sg2042-msi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/irqchip/irq-sg2042-msi.c b/drivers/irqchip/irq-sg2042-msi.c index 8a83c690b760..a3e2a26d8495 100644 --- a/drivers/irqchip/irq-sg2042-msi.c +++ b/drivers/irqchip/irq-sg2042-msi.c @@ -98,7 +98,7 @@ static void sg2044_msi_irq_ack(struct irq_data *d) { struct sg204x_msi_chipdata *data = irq_data_get_irq_chip_data(d); - writel(0, (u32 *)data->reg_clr + d->hwirq); + writel(0, (u32 __iomem *)data->reg_clr + d->hwirq); irq_chip_ack_parent(d); } -- cgit v1.2.3 From 22111fdf11666e4ff2bb80481a874a6b958323f2 Mon Sep 17 00:00:00 2001 From: Alexey Charkov Date: Tue, 6 May 2025 16:46:14 +0400 Subject: irqchip/irq-vt8500: Split up ack/mask functions vt8500_irq_mask() really does the ACK for edge triggered interrupts and the MASK for level triggered interrupts. Edge triggered interrupts never really are masked as a result, and there is unnecessary reading of the status register before the ACK even though it's write-one-to-clear. Split it up into a proper standalone vt8500_irq_ack() and an unconditional vt8500_irq_mask(). No Fixes tag added, as it has survived this way for 15 years and nobody complained, so apparently nothing really used edge triggered interrupts anyway. [ tglx: Tabularize the irqchip struct initializer ] Signed-off-by: Alexey Charkov Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/all/20250506-vt8500-intc-updates-v2-1-a3a0606cf92d@gmail.com --- drivers/irqchip/irq-vt8500.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/drivers/irqchip/irq-vt8500.c b/drivers/irqchip/irq-vt8500.c index e17dd3a8c2d5..9fb9d37c81e3 100644 --- a/drivers/irqchip/irq-vt8500.c +++ b/drivers/irqchip/irq-vt8500.c @@ -67,25 +67,25 @@ struct vt8500_irq_data { static struct vt8500_irq_data intc[VT8500_INTC_MAX]; static u32 active_cnt = 0; -static void vt8500_irq_mask(struct irq_data *d) +static void vt8500_irq_ack(struct irq_data *d) { struct vt8500_irq_data *priv = d->domain->host_data; void __iomem *base = priv->base; void __iomem *stat_reg = base + VT8500_ICIS + (d->hwirq < 32 ? 0 : 4); - u8 edge, dctr; - u32 status; - - edge = readb(base + VT8500_ICDC + d->hwirq) & VT8500_EDGE; - if (edge) { - status = readl(stat_reg); - - status |= (1 << (d->hwirq & 0x1f)); - writel(status, stat_reg); - } else { - dctr = readb(base + VT8500_ICDC + d->hwirq); - dctr &= ~VT8500_INT_ENABLE; - writeb(dctr, base + VT8500_ICDC + d->hwirq); - } + u32 status = (1 << (d->hwirq & 0x1f)); + + writel(status, stat_reg); +} + +static void vt8500_irq_mask(struct irq_data *d) +{ + struct vt8500_irq_data *priv = d->domain->host_data; + void __iomem *base = priv->base; + u8 dctr; + + dctr = readb(base + VT8500_ICDC + d->hwirq); + dctr &= ~VT8500_INT_ENABLE; + writeb(dctr, base + VT8500_ICDC + d->hwirq); } static void vt8500_irq_unmask(struct irq_data *d) @@ -130,11 +130,11 @@ static int vt8500_irq_set_type(struct irq_data *d, unsigned int flow_type) } static struct irq_chip vt8500_irq_chip = { - .name = "vt8500", - .irq_ack = vt8500_irq_mask, - .irq_mask = vt8500_irq_mask, - .irq_unmask = vt8500_irq_unmask, - .irq_set_type = vt8500_irq_set_type, + .name = "vt8500", + .irq_ack = vt8500_irq_ack, + .irq_mask = vt8500_irq_mask, + .irq_unmask = vt8500_irq_unmask, + .irq_set_type = vt8500_irq_set_type, }; static void __init vt8500_init_irq_hw(void __iomem *base) -- cgit v1.2.3 From 45453df7f69b0157a14478f635c99216821faeee Mon Sep 17 00:00:00 2001 From: Alexey Charkov Date: Tue, 6 May 2025 16:46:15 +0400 Subject: irqchip/irq-vt8500: Drop redundant copy of the device node pointer Inside vt8500_irq_init(), np is the same as node. Drop it. Signed-off-by: Alexey Charkov Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/all/20250506-vt8500-intc-updates-v2-2-a3a0606cf92d@gmail.com --- drivers/irqchip/irq-vt8500.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/irqchip/irq-vt8500.c b/drivers/irqchip/irq-vt8500.c index 9fb9d37c81e3..c88aa6484afb 100644 --- a/drivers/irqchip/irq-vt8500.c +++ b/drivers/irqchip/irq-vt8500.c @@ -191,7 +191,6 @@ static int __init vt8500_irq_init(struct device_node *node, struct device_node *parent) { int irq, i; - struct device_node *np = node; if (active_cnt == VT8500_INTC_MAX) { pr_err("%s: Interrupt controllers > VT8500_INTC_MAX\n", @@ -199,7 +198,7 @@ static int __init vt8500_irq_init(struct device_node *node, goto out; } - intc[active_cnt].base = of_iomap(np, 0); + intc[active_cnt].base = of_iomap(node, 0); intc[active_cnt].domain = irq_domain_add_linear(node, 64, &vt8500_irq_domain_ops, &intc[active_cnt]); @@ -222,16 +221,16 @@ static int __init vt8500_irq_init(struct device_node *node, active_cnt++; /* check if this is a slaved controller */ - if (of_irq_count(np) != 0) { + if (of_irq_count(node) != 0) { /* check that we have the correct number of interrupts */ - if (of_irq_count(np) != 8) { + if (of_irq_count(node) != 8) { pr_err("%s: Incorrect IRQ map for slaved controller\n", __func__); return -EINVAL; } for (i = 0; i < 8; i++) { - irq = irq_of_parse_and_map(np, i); + irq = irq_of_parse_and_map(node, i); enable_irq(irq); } -- cgit v1.2.3 From 54a1f3eb89ded8114b0bffc3696757cd95665ef9 Mon Sep 17 00:00:00 2001 From: Alexey Charkov Date: Tue, 6 May 2025 16:46:16 +0400 Subject: irqchip/irq-vt8500: Don't require 8 interrupts from a chained controller VT8500 chained controller can route its interrupts to either or all of its 8 interrupt outputs. Current code actually routes all of them to the first output, so there is no need to create mappings for all eight. Drop redundant checks, and only map as many chained controller interrupts as are defined in the device tree. Signed-off-by: Alexey Charkov Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/all/20250506-vt8500-intc-updates-v2-3-a3a0606cf92d@gmail.com --- drivers/irqchip/irq-vt8500.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/irqchip/irq-vt8500.c b/drivers/irqchip/irq-vt8500.c index c88aa6484afb..debca897c1ee 100644 --- a/drivers/irqchip/irq-vt8500.c +++ b/drivers/irqchip/irq-vt8500.c @@ -220,16 +220,9 @@ static int __init vt8500_irq_init(struct device_node *node, active_cnt++; - /* check if this is a slaved controller */ + /* check if this is a chained controller */ if (of_irq_count(node) != 0) { - /* check that we have the correct number of interrupts */ - if (of_irq_count(node) != 8) { - pr_err("%s: Incorrect IRQ map for slaved controller\n", - __func__); - return -EINVAL; - } - - for (i = 0; i < 8; i++) { + for (i = 0; i < of_irq_count(node); i++) { irq = irq_of_parse_and_map(node, i); enable_irq(irq); } -- cgit v1.2.3 From 49f92d3859cdd8534a1cd15037f950c483a5de40 Mon Sep 17 00:00:00 2001 From: Alexey Charkov Date: Tue, 6 May 2025 16:46:17 +0400 Subject: irqchip/irq-vt8500: Use a dedicated chained handler function Current code for the chained interrupt controller maps its interrupts on the parent but doesn't register a separate chained handler, instead needlessly calling enable_irq() on an unactivated parent interrupt, causing a boot time WARN_ON from the common code. The common handler meanwhile loops through all registered interrupt controllers in an arbitrary order and tries to handle active interrupts in each of them, which is fragile. Use common infrastructure for handling chained interrupts instead. Signed-off-by: Alexey Charkov Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/all/20250506-vt8500-intc-updates-v2-4-a3a0606cf92d@gmail.com --- drivers/irqchip/irq-vt8500.c | 59 +++++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/drivers/irqchip/irq-vt8500.c b/drivers/irqchip/irq-vt8500.c index debca897c1ee..15134cb2fbe5 100644 --- a/drivers/irqchip/irq-vt8500.c +++ b/drivers/irqchip/irq-vt8500.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -66,6 +67,8 @@ struct vt8500_irq_data { /* Global variable for accessing io-mem addresses */ static struct vt8500_irq_data intc[VT8500_INTC_MAX]; static u32 active_cnt = 0; +/* Primary interrupt controller data */ +static struct vt8500_irq_data *primary_intc; static void vt8500_irq_ack(struct irq_data *d) { @@ -163,28 +166,38 @@ static const struct irq_domain_ops vt8500_irq_domain_ops = { .xlate = irq_domain_xlate_onecell, }; +static inline void vt8500_handle_irq_common(struct vt8500_irq_data *intc) +{ + unsigned long irqnr = readl_relaxed(intc->base) & 0x3F; + unsigned long stat; + + /* + * Highest Priority register default = 63, so check that this + * is a real interrupt by checking the status register + */ + if (irqnr == 63) { + stat = readl_relaxed(intc->base + VT8500_ICIS + 4); + if (!(stat & BIT(31))) + return; + } + + generic_handle_domain_irq(intc->domain, irqnr); +} + static void __exception_irq_entry vt8500_handle_irq(struct pt_regs *regs) { - u32 stat, i; - int irqnr; - void __iomem *base; - - /* Loop through each active controller */ - for (i=0; ihost_data; + + chained_irq_enter(chip, desc); + vt8500_handle_irq_common(intc); + chained_irq_exit(chip, desc); } static int __init vt8500_irq_init(struct device_node *node, @@ -212,8 +225,6 @@ static int __init vt8500_irq_init(struct device_node *node, goto out; } - set_handle_irq(vt8500_handle_irq); - vt8500_init_irq_hw(intc[active_cnt].base); pr_info("vt8500-irq: Added interrupt controller\n"); @@ -224,10 +235,14 @@ static int __init vt8500_irq_init(struct device_node *node, if (of_irq_count(node) != 0) { for (i = 0; i < of_irq_count(node); i++) { irq = irq_of_parse_and_map(node, i); - enable_irq(irq); + irq_set_chained_handler_and_data(irq, vt8500_handle_irq_chained, + &intc[active_cnt]); } pr_info("vt8500-irq: Enabled slave->parent interrupts\n"); + } else { + primary_intc = &intc[active_cnt]; + set_handle_irq(vt8500_handle_irq); } out: return 0; -- cgit v1.2.3 From 99ad153fbd22fcf7c2bdd774d08fc4bf70029fa6 Mon Sep 17 00:00:00 2001 From: Alexey Charkov Date: Tue, 6 May 2025 16:46:18 +0400 Subject: irqchip/irq-vt8500: Use fewer global variables and add error handling Controller private data doesn't really need to be in a global statically allocated array - kzalloc it per controller instead, keeping only one pointer to the primary controller global. While at that, also add proper error return statuses in the init path and respective cleanup of resources on errors. Signed-off-by: Alexey Charkov Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/all/20250506-vt8500-intc-updates-v2-5-a3a0606cf92d@gmail.com --- drivers/irqchip/irq-vt8500.c | 48 +++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/drivers/irqchip/irq-vt8500.c b/drivers/irqchip/irq-vt8500.c index 15134cb2fbe5..40c2cff0df11 100644 --- a/drivers/irqchip/irq-vt8500.c +++ b/drivers/irqchip/irq-vt8500.c @@ -64,9 +64,6 @@ struct vt8500_irq_data { struct irq_domain *domain; /* Domain for this controller */ }; -/* Global variable for accessing io-mem addresses */ -static struct vt8500_irq_data intc[VT8500_INTC_MAX]; -static u32 active_cnt = 0; /* Primary interrupt controller data */ static struct vt8500_irq_data *primary_intc; @@ -203,49 +200,54 @@ static void vt8500_handle_irq_chained(struct irq_desc *desc) static int __init vt8500_irq_init(struct device_node *node, struct device_node *parent) { - int irq, i; + struct vt8500_irq_data *intc; + int irq, i, ret = 0; - if (active_cnt == VT8500_INTC_MAX) { - pr_err("%s: Interrupt controllers > VT8500_INTC_MAX\n", - __func__); - goto out; - } - - intc[active_cnt].base = of_iomap(node, 0); - intc[active_cnt].domain = irq_domain_add_linear(node, 64, - &vt8500_irq_domain_ops, &intc[active_cnt]); + intc = kzalloc(sizeof(*intc), GFP_KERNEL); + if (!intc) + return -ENOMEM; - if (!intc[active_cnt].base) { + intc->base = of_iomap(node, 0); + if (!intc->base) { pr_err("%s: Unable to map IO memory\n", __func__); - goto out; + ret = -ENOMEM; + goto err_free; } - if (!intc[active_cnt].domain) { + intc->domain = irq_domain_add_linear(node, + 64, + &vt8500_irq_domain_ops, + intc); + if (!intc->domain) { pr_err("%s: Unable to add irq domain!\n", __func__); - goto out; + ret = -ENOMEM; + goto err_unmap; } - vt8500_init_irq_hw(intc[active_cnt].base); + vt8500_init_irq_hw(intc->base); pr_info("vt8500-irq: Added interrupt controller\n"); - active_cnt++; - /* check if this is a chained controller */ if (of_irq_count(node) != 0) { for (i = 0; i < of_irq_count(node); i++) { irq = irq_of_parse_and_map(node, i); irq_set_chained_handler_and_data(irq, vt8500_handle_irq_chained, - &intc[active_cnt]); + intc); } pr_info("vt8500-irq: Enabled slave->parent interrupts\n"); } else { - primary_intc = &intc[active_cnt]; + primary_intc = intc; set_handle_irq(vt8500_handle_irq); } -out: return 0; + +err_unmap: + iounmap(intc->base); +err_free: + kfree(intc); + return ret; } IRQCHIP_DECLARE(vt8500_irq, "via,vt8500-intc", vt8500_irq_init); -- cgit v1.2.3 From 85cf5c63d32f39874d0dc1cd6c12b40e1ba8370e Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 6 May 2025 15:17:46 +0200 Subject: irqchip/econet-en751221: Switch to irq_domain_create_linear() irq_domain_add_linear() is about to be removed. Switch to irq_domain_create_linear(). Signed-off-by: Thomas Gleixner Tested-by: Caleb James DeLisle Link: https://lore.kernel.org/all/877c2top39.ffs@tglx --- drivers/irqchip/irq-econet-en751221.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/irqchip/irq-econet-en751221.c b/drivers/irqchip/irq-econet-en751221.c index 886d60c6f8a0..fd65dfe54ce9 100644 --- a/drivers/irqchip/irq-econet-en751221.c +++ b/drivers/irqchip/irq-econet-en751221.c @@ -286,7 +286,8 @@ static int __init econet_intc_of_init(struct device_node *node, struct device_no econet_mask_all(); - domain = irq_domain_add_linear(node, IRQ_COUNT, &econet_domain_ops, NULL); + domain = irq_domain_create_linear(of_node_to_fwnode(node), IRQ_COUNT, + &econet_domain_ops, NULL); if (!domain) { pr_err("%pOF: Failed to add irqdomain\n", node); ret = -ENOMEM; -- cgit v1.2.3 From 15568ffd59d4e7d8c39286a7159880afe327216d Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Wed, 19 Mar 2025 10:29:14 +0100 Subject: irqchip/irq-vt8500: Switch to irq_domain_create_*() irq_domain_add_*() interfaces are going away as being obsolete now. Switch to the preferred irq_domain_create_*() ones. Those differ in the node parameter: They take more generic struct fwnode_handle instead of struct device_node. Therefore, of_fwnode_handle() is added around the original parameter. Note some of the users can likely use dev->fwnode directly instead of indirect of_fwnode_handle(dev->of_node). But dev->fwnode is not guaranteed to be set for all, so this has to be investigated on case to case basis (by people who can actually test with the HW). [ tglx: Split out from combo patch to avoid merge conflicts ] Signed-off-by: Jiri Slaby (SUSE) Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/all/20250319092951.37667-22-jirislaby@kernel.org --- drivers/irqchip/irq-vt8500.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/irqchip/irq-vt8500.c b/drivers/irqchip/irq-vt8500.c index 40c2cff0df11..3b742590aec8 100644 --- a/drivers/irqchip/irq-vt8500.c +++ b/drivers/irqchip/irq-vt8500.c @@ -214,10 +214,8 @@ static int __init vt8500_irq_init(struct device_node *node, goto err_free; } - intc->domain = irq_domain_add_linear(node, - 64, - &vt8500_irq_domain_ops, - intc); + intc->domain = irq_domain_create_linear(of_fwnode_handle(node), 64, + &vt8500_irq_domain_ops, intc); if (!intc->domain) { pr_err("%s: Unable to add irq domain!\n", __func__); ret = -ENOMEM; -- cgit v1.2.3 From 96a8cb6d28cebd51a286fe4a8782dc8492813ac4 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Tue, 13 May 2025 10:47:39 +0200 Subject: irqchip/econet-en751221: Switch to of_fwnode_handle() of_node_to_fwnode() is an irqdomain's reimplementation of the "officially" defined of_fwnode_handle(). The former is in the process of being removed, so use the latter instead. Signed-off-by: Jiri Slaby (SUSE) Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/all/20250513084739.2611747-1-jirislaby@kernel.org --- drivers/irqchip/irq-econet-en751221.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/irqchip/irq-econet-en751221.c b/drivers/irqchip/irq-econet-en751221.c index fd65dfe54ce9..d83d5eb12795 100644 --- a/drivers/irqchip/irq-econet-en751221.c +++ b/drivers/irqchip/irq-econet-en751221.c @@ -286,7 +286,7 @@ static int __init econet_intc_of_init(struct device_node *node, struct device_no econet_mask_all(); - domain = irq_domain_create_linear(of_node_to_fwnode(node), IRQ_COUNT, + domain = irq_domain_create_linear(of_fwnode_handle(node), IRQ_COUNT, &econet_domain_ops, NULL); if (!domain) { pr_err("%pOF: Failed to add irqdomain\n", node); -- cgit v1.2.3 From f1a3fac4095c7bc0b30e2aa9921c232af8faeae0 Mon Sep 17 00:00:00 2001 From: Nianyao Tang Date: Thu, 15 May 2025 14:53:59 +0000 Subject: irqchip/gic-v4.1: Use local 4_1 ITS to generate VSGI On multi-node GICv4.1 system, VSGI senders always use one certain 4_1 ITS, because find_4_1_its() returns the first its_node in the list, regardless of which node the VSGI sender is on. This brings guest VSGI performance drop when VM is not running on the same node as this returned ITS. On a 2-socket environment, each with one ITS and 32 cpu, GICv4.1 enabled, 4U8G guest, 4 vcpu is running on same socket. When the VM is on socket0, kvm-unit-tests ipi_hw result is 850ns. When the VM is on socket1, it is 750ns. The reason is that the VSGI sender always uses the last reported ITS (that on socket1) to inject VSGI. The access from a CPU to a other-socket ITS will cost 100ns more compared to an access to the local ITS. Using the local ITS results in a 12% reduction in IPI latency. Modify find_4_1_its() to return the first per-CPU local_4_1_its, which is initialized when the VPE table is inherited from the ITS or from another CPU. If it fails to find a local 4_1 ITS, it returns any 4_1 ITS like before. Suggested-by: Marc Zyngier Signed-off-by: Nianyao Tang Signed-off-by: Thomas Gleixner Reviewed-by: Marc Zyngier Link: https://lore.kernel.org/all/20250515145359.2795959-1-tangnianyao@huawei.com --- drivers/irqchip/irq-gic-v3-its.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 0115ad6c8259..81a44ce7619f 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -125,6 +125,8 @@ struct its_node { int vlpi_redist_offset; }; +static DEFINE_PER_CPU(struct its_node *, local_4_1_its); + #define is_v4(its) (!!((its)->typer & GITS_TYPER_VLPIS)) #define is_v4_1(its) (!!((its)->typer & GITS_TYPER_VMAPP)) #define device_ids(its) (FIELD_GET(GITS_TYPER_DEVBITS, (its)->typer) + 1) @@ -2778,6 +2780,7 @@ static u64 inherit_vpe_l1_table_from_its(void) } val |= FIELD_PREP(GICR_VPROPBASER_4_1_SIZE, GITS_BASER_NR_PAGES(baser) - 1); + *this_cpu_ptr(&local_4_1_its) = its; return val; } @@ -2815,6 +2818,7 @@ static u64 inherit_vpe_l1_table_from_rd(cpumask_t **mask) gic_data_rdist()->vpe_l1_base = gic_data_rdist_cpu(cpu)->vpe_l1_base; *mask = gic_data_rdist_cpu(cpu)->vpe_table_mask; + *this_cpu_ptr(&local_4_1_its) = *per_cpu_ptr(&local_4_1_its, cpu); return val; } @@ -4180,7 +4184,7 @@ static struct irq_chip its_vpe_irq_chip = { static struct its_node *find_4_1_its(void) { - static struct its_node *its = NULL; + struct its_node *its = *this_cpu_ptr(&local_4_1_its); if (!its) { list_for_each_entry(its, &its_nodes, entry) { -- cgit v1.2.3 From 3e402acd5c4f9d447849b9bfb5a5c61f10668b7e Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Thu, 15 May 2025 16:34:50 +0800 Subject: irqchip/irq-pruss-intc: Simplify chained interrupt handler setup The chained interrupt handler setup installs the handler and handler data with two function call.s irq_set_chained_handler_and_data() can set both in one operation. Replace the two calls with one. Signed-off-by: Chen Ni Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/all/20250515083450.3811411-1-nichen@iscas.ac.cn --- drivers/irqchip/irq-pruss-intc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/irqchip/irq-pruss-intc.c b/drivers/irqchip/irq-pruss-intc.c index bee01980b463..af7684758049 100644 --- a/drivers/irqchip/irq-pruss-intc.c +++ b/drivers/irqchip/irq-pruss-intc.c @@ -581,8 +581,7 @@ static int pruss_intc_probe(struct platform_device *pdev) host_data->intc = intc; host_data->host_irq = i; - irq_set_handler_data(irq, host_data); - irq_set_chained_handler(irq, pruss_intc_irq_handler); + irq_set_chained_handler_and_data(irq, pruss_intc_irq_handler, host_data); } return 0; -- cgit v1.2.3