From d011c022efe275791897668aa421e2db9f2e6450 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Tue, 4 Jun 2024 18:37:10 +0100 Subject: irqchip/renesas-rzg2l: Add support for RZ/Five SoC The IX45 block has additional mask registers (NMSK/IMSK/TMSK) compared to the RZ/G2L (family) SoC. A new rzfive_irqc_chip irq_chip is introduced for RZ/Five, where function pointers for irq_[un]mask() and irq_[dis|en]able() handle the ([un]masking of the interrupts. The irq_chip pointer is now passed as an init callback and stored in the priv pointer to differentiate between RZ/G2L and RZ/Five. Signed-off-by: Lad Prabhakar Signed-off-by: Thomas Gleixner Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20240604173710.534132-3-prabhakar.mahadev-lad.rj@bp.renesas.com --- drivers/irqchip/irq-renesas-rzg2l.c | 148 +++++++++++++++++++++++++++++++++++- 1 file changed, 145 insertions(+), 3 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-renesas-rzg2l.c b/drivers/irqchip/irq-renesas-rzg2l.c index f6484bf15e0b..861a0e5a3e97 100644 --- a/drivers/irqchip/irq-renesas-rzg2l.c +++ b/drivers/irqchip/irq-renesas-rzg2l.c @@ -37,6 +37,8 @@ #define TSSEL_SHIFT(n) (8 * (n)) #define TSSEL_MASK GENMASK(7, 0) #define IRQ_MASK 0x3 +#define IMSK 0x10010 +#define TMSK 0x10020 #define TSSR_OFFSET(n) ((n) % 4) #define TSSR_INDEX(n) ((n) / 4) @@ -69,12 +71,14 @@ struct rzg2l_irqc_reg_cache { /** * struct rzg2l_irqc_priv - IRQ controller private data structure * @base: Controller's base address + * @irqchip: Pointer to struct irq_chip * @fwspec: IRQ firmware specific data * @lock: Lock to serialize access to hardware registers * @cache: Registers cache for suspend/resume */ static struct rzg2l_irqc_priv { void __iomem *base; + const struct irq_chip *irqchip; struct irq_fwspec fwspec[IRQC_NUM_IRQ]; raw_spinlock_t lock; struct rzg2l_irqc_reg_cache cache; @@ -138,6 +142,111 @@ static void rzg2l_irqc_eoi(struct irq_data *d) irq_chip_eoi_parent(d); } +static void rzfive_irqc_mask_irq_interrupt(struct rzg2l_irqc_priv *priv, + unsigned int hwirq) +{ + u32 bit = BIT(hwirq - IRQC_IRQ_START); + + writel_relaxed(readl_relaxed(priv->base + IMSK) | bit, priv->base + IMSK); +} + +static void rzfive_irqc_unmask_irq_interrupt(struct rzg2l_irqc_priv *priv, + unsigned int hwirq) +{ + u32 bit = BIT(hwirq - IRQC_IRQ_START); + + writel_relaxed(readl_relaxed(priv->base + IMSK) & ~bit, priv->base + IMSK); +} + +static void rzfive_irqc_mask_tint_interrupt(struct rzg2l_irqc_priv *priv, + unsigned int hwirq) +{ + u32 bit = BIT(hwirq - IRQC_TINT_START); + + writel_relaxed(readl_relaxed(priv->base + TMSK) | bit, priv->base + TMSK); +} + +static void rzfive_irqc_unmask_tint_interrupt(struct rzg2l_irqc_priv *priv, + unsigned int hwirq) +{ + u32 bit = BIT(hwirq - IRQC_TINT_START); + + writel_relaxed(readl_relaxed(priv->base + TMSK) & ~bit, priv->base + TMSK); +} + +static void rzfive_irqc_mask(struct irq_data *d) +{ + struct rzg2l_irqc_priv *priv = irq_data_to_priv(d); + unsigned int hwirq = irqd_to_hwirq(d); + + raw_spin_lock(&priv->lock); + if (hwirq >= IRQC_IRQ_START && hwirq <= IRQC_IRQ_COUNT) + rzfive_irqc_mask_irq_interrupt(priv, hwirq); + else if (hwirq >= IRQC_TINT_START && hwirq < IRQC_NUM_IRQ) + rzfive_irqc_mask_tint_interrupt(priv, hwirq); + raw_spin_unlock(&priv->lock); + irq_chip_mask_parent(d); +} + +static void rzfive_irqc_unmask(struct irq_data *d) +{ + struct rzg2l_irqc_priv *priv = irq_data_to_priv(d); + unsigned int hwirq = irqd_to_hwirq(d); + + raw_spin_lock(&priv->lock); + if (hwirq >= IRQC_IRQ_START && hwirq <= IRQC_IRQ_COUNT) + rzfive_irqc_unmask_irq_interrupt(priv, hwirq); + else if (hwirq >= IRQC_TINT_START && hwirq < IRQC_NUM_IRQ) + rzfive_irqc_unmask_tint_interrupt(priv, hwirq); + raw_spin_unlock(&priv->lock); + irq_chip_unmask_parent(d); +} + +static void rzfive_tint_irq_endisable(struct irq_data *d, bool enable) +{ + struct rzg2l_irqc_priv *priv = irq_data_to_priv(d); + unsigned int hwirq = irqd_to_hwirq(d); + + if (hwirq >= IRQC_TINT_START && hwirq < IRQC_NUM_IRQ) { + u32 offset = hwirq - IRQC_TINT_START; + u32 tssr_offset = TSSR_OFFSET(offset); + u8 tssr_index = TSSR_INDEX(offset); + u32 reg; + + raw_spin_lock(&priv->lock); + if (enable) + rzfive_irqc_unmask_tint_interrupt(priv, hwirq); + else + rzfive_irqc_mask_tint_interrupt(priv, hwirq); + reg = readl_relaxed(priv->base + TSSR(tssr_index)); + if (enable) + reg |= TIEN << TSSEL_SHIFT(tssr_offset); + else + reg &= ~(TIEN << TSSEL_SHIFT(tssr_offset)); + writel_relaxed(reg, priv->base + TSSR(tssr_index)); + raw_spin_unlock(&priv->lock); + } else { + raw_spin_lock(&priv->lock); + if (enable) + rzfive_irqc_unmask_irq_interrupt(priv, hwirq); + else + rzfive_irqc_mask_irq_interrupt(priv, hwirq); + raw_spin_unlock(&priv->lock); + } +} + +static void rzfive_irqc_irq_disable(struct irq_data *d) +{ + irq_chip_disable_parent(d); + rzfive_tint_irq_endisable(d, false); +} + +static void rzfive_irqc_irq_enable(struct irq_data *d) +{ + rzfive_tint_irq_endisable(d, true); + irq_chip_enable_parent(d); +} + static void rzg2l_tint_irq_endisable(struct irq_data *d, bool enable) { unsigned int hw_irq = irqd_to_hwirq(d); @@ -321,7 +430,7 @@ static struct syscore_ops rzg2l_irqc_syscore_ops = { .resume = rzg2l_irqc_irq_resume, }; -static const struct irq_chip irqc_chip = { +static const struct irq_chip rzg2l_irqc_chip = { .name = "rzg2l-irqc", .irq_eoi = rzg2l_irqc_eoi, .irq_mask = irq_chip_mask_parent, @@ -338,6 +447,23 @@ static const struct irq_chip irqc_chip = { IRQCHIP_SKIP_SET_WAKE, }; +static const struct irq_chip rzfive_irqc_chip = { + .name = "rzfive-irqc", + .irq_eoi = rzg2l_irqc_eoi, + .irq_mask = rzfive_irqc_mask, + .irq_unmask = rzfive_irqc_unmask, + .irq_disable = rzfive_irqc_irq_disable, + .irq_enable = rzfive_irqc_irq_enable, + .irq_get_irqchip_state = irq_chip_get_parent_state, + .irq_set_irqchip_state = irq_chip_set_parent_state, + .irq_retrigger = irq_chip_retrigger_hierarchy, + .irq_set_type = rzg2l_irqc_set_type, + .irq_set_affinity = irq_chip_set_affinity_parent, + .flags = IRQCHIP_MASK_ON_SUSPEND | + IRQCHIP_SET_TYPE_MASKED | + IRQCHIP_SKIP_SET_WAKE, +}; + static int rzg2l_irqc_alloc(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs, void *arg) { @@ -369,7 +495,7 @@ static int rzg2l_irqc_alloc(struct irq_domain *domain, unsigned int virq, if (hwirq > (IRQC_NUM_IRQ - 1)) return -EINVAL; - ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, &irqc_chip, + ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, priv->irqchip, (void *)(uintptr_t)tint); if (ret) return ret; @@ -401,7 +527,8 @@ static int rzg2l_irqc_parse_interrupts(struct rzg2l_irqc_priv *priv, return 0; } -static int rzg2l_irqc_init(struct device_node *node, struct device_node *parent) +static int rzg2l_irqc_common_init(struct device_node *node, struct device_node *parent, + const struct irq_chip *irq_chip) { struct irq_domain *irq_domain, *parent_domain; struct platform_device *pdev; @@ -422,6 +549,8 @@ static int rzg2l_irqc_init(struct device_node *node, struct device_node *parent) if (!rzg2l_irqc_data) return -ENOMEM; + rzg2l_irqc_data->irqchip = irq_chip; + rzg2l_irqc_data->base = devm_of_iomap(&pdev->dev, pdev->dev.of_node, 0, NULL); if (IS_ERR(rzg2l_irqc_data->base)) return PTR_ERR(rzg2l_irqc_data->base); @@ -472,8 +601,21 @@ pm_disable: return ret; } +static int __init rzg2l_irqc_init(struct device_node *node, + struct device_node *parent) +{ + return rzg2l_irqc_common_init(node, parent, &rzg2l_irqc_chip); +} + +static int __init rzfive_irqc_init(struct device_node *node, + struct device_node *parent) +{ + return rzg2l_irqc_common_init(node, parent, &rzfive_irqc_chip); +} + IRQCHIP_PLATFORM_DRIVER_BEGIN(rzg2l_irqc) IRQCHIP_MATCH("renesas,rzg2l-irqc", rzg2l_irqc_init) +IRQCHIP_MATCH("renesas,r9a07g043f-irqc", rzfive_irqc_init) IRQCHIP_PLATFORM_DRIVER_END(rzg2l_irqc) MODULE_AUTHOR("Lad Prabhakar "); MODULE_DESCRIPTION("Renesas RZ/G2L IRQC Driver"); -- cgit v1.2.3 From ababa16fd9bd0e2727a1c31c4fb68d6be053bddc Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Thu, 6 Jun 2024 11:42:38 +0200 Subject: irqchip/gic-v3: Enable non-coherent redistributors/ITSes ACPI probing The GIC architecture specification defines a set of registers for redistributors and ITSes that control the sharebility and cacheability attributes of redistributors/ITSes initiator ports on the interconnect (GICR_[V]PROPBASER, GICR_[V]PENDBASER, GITS_BASER). Architecturally the GIC provides a means to drive shareability and cacheability attributes signals but it is not mandatory for designs to wire up the corresponding interconnect signals that control the cacheability/shareability of transactions. Redistributors and ITSes interconnect ports can be connected to non-coherent interconnects that are not able to manage the shareability/cacheability attributes; this implicitly makes the redistributors and ITSes non-coherent observers. To enable non-coherent GIC designs on ACPI based systems, parse the MADT GICC/GICR/ITS subtables non-coherent flags to determine whether the respective components are non-coherent observers and force the shareability attributes to be programmed into the redistributors and ITSes registers. An ACPI global function (acpi_get_madt_revision()) is added to retrieve the MADT revision, in that it is essential to check the MADT revision before checking for flags that were added with MADT revision 7 so that if the kernel is booted with an ACPI MADT table with revision < 7 it skips parsing the newly added flags (that should be zeroed reserved values for MADT versions < 7 but they could turn out to be buggy and should be ignored). Signed-off-by: Lorenzo Pieralisi Signed-off-by: Thomas Gleixner Reviewed-by: Robin Murphy Acked-by: Marc Zyngier Cc: "Rafael J. Wysocki" Link: https://lore.kernel.org/r/20240606094238.757649-2-lpieralisi@kernel.org --- drivers/acpi/processor_core.c | 15 +++++++++++++++ drivers/irqchip/irq-gic-v3-its.c | 4 ++++ drivers/irqchip/irq-gic-v3.c | 9 +++++++++ include/linux/acpi.h | 3 +++ 4 files changed, 31 insertions(+) (limited to 'drivers/irqchip') diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index b203cfe28550..915713c0e9b7 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -215,6 +215,21 @@ phys_cpuid_t __init acpi_map_madt_entry(u32 acpi_id) return rv; } +int __init acpi_get_madt_revision(void) +{ + struct acpi_table_header *madt = NULL; + int revision; + + if (ACPI_FAILURE(acpi_get_table(ACPI_SIG_MADT, 0, &madt))) + return -EINVAL; + + revision = madt->revision; + + acpi_put_table(madt); + + return revision; +} + static phys_cpuid_t map_mat_entry(acpi_handle handle, int type, u32 acpi_id) { struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 40ebf1726393..af5297ef2293 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -5600,6 +5600,10 @@ static int __init gic_acpi_parse_madt_its(union acpi_subtable_headers *header, goto node_err; } + if (acpi_get_madt_revision() >= 7 && + (its_entry->flags & ACPI_MADT_ITS_NON_COHERENT)) + its->flags |= ITS_FLAGS_FORCE_NON_SHAREABLE; + err = its_probe_one(its); if (!err) return 0; diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 6fb276504bcc..e4bc5f04226e 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -2349,6 +2349,11 @@ gic_acpi_parse_madt_redist(union acpi_subtable_headers *header, pr_err("Couldn't map GICR region @%llx\n", redist->base_address); return -ENOMEM; } + + if (acpi_get_madt_revision() >= 7 && + (redist->flags & ACPI_MADT_GICR_NON_COHERENT)) + gic_data.rdists.flags |= RDIST_FLAGS_FORCE_NON_SHAREABLE; + gic_request_region(redist->base_address, redist->length, "GICR"); gic_acpi_register_redist(redist->base_address, redist_base); @@ -2373,6 +2378,10 @@ gic_acpi_parse_madt_gicc(union acpi_subtable_headers *header, return -ENOMEM; gic_request_region(gicc->gicr_base_address, size, "GICR"); + if (acpi_get_madt_revision() >= 7 && + (gicc->flags & ACPI_MADT_GICC_NON_COHERENT)) + gic_data.rdists.flags |= RDIST_FLAGS_FORCE_NON_SHAREABLE; + gic_acpi_register_redist(gicc->gicr_base_address, redist_base); return 0; } diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 28c3fb2bef0d..000d339e1596 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -279,6 +279,9 @@ static inline bool invalid_phys_cpuid(phys_cpuid_t phys_id) return phys_id == PHYS_CPUID_INVALID; } + +int __init acpi_get_madt_revision(void); + /* Validate the processor object's proc_id */ bool acpi_duplicate_processor_id(int proc_id); /* Processor _CTS control */ -- cgit v1.2.3 From 3e3a7b35332924c8ade696c4b24735561ee6aea3 Mon Sep 17 00:00:00 2001 From: Herve Codina Date: Fri, 14 Jun 2024 19:32:23 +0200 Subject: irqchip: Add support for LAN966x OIC The Microchip LAN966x outband interrupt controller (OIC) maps the internal interrupt sources of the LAN966x device to an external interrupt. When the LAN966x device is used as a PCI device, the external interrupt is routed to the PCI interrupt. Signed-off-by: Herve Codina Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/r/20240614173232.1184015-23-herve.codina@bootlin.com --- drivers/irqchip/Kconfig | 12 ++ drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-lan966x-oic.c | 278 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 291 insertions(+) create mode 100644 drivers/irqchip/irq-lan966x-oic.c (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 14464716bacb..348f34525d23 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -169,6 +169,18 @@ config IXP4XX_IRQ select IRQ_DOMAIN select SPARSE_IRQ +config LAN966X_OIC + tristate "Microchip LAN966x OIC Support" + select GENERIC_IRQ_CHIP + select IRQ_DOMAIN + help + Enable support for the LAN966x Outbound Interrupt Controller. + This controller is present on the Microchip LAN966x PCI device and + maps the internal interrupts sources to PCIe interrupt. + + To compile this driver as a module, choose M here: the module + will be called irq-lan966x-oic. + config MADERA_IRQ tristate diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index d9dc3d99aaa8..9f6f88274bec 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -104,6 +104,7 @@ obj-$(CONFIG_IMX_IRQSTEER) += irq-imx-irqsteer.o obj-$(CONFIG_IMX_INTMUX) += irq-imx-intmux.o obj-$(CONFIG_IMX_MU_MSI) += irq-imx-mu-msi.o obj-$(CONFIG_MADERA_IRQ) += irq-madera.o +obj-$(CONFIG_LAN966X_OIC) += irq-lan966x-oic.o obj-$(CONFIG_LS1X_IRQ) += irq-ls1x.o obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) += irq-ti-sci-intr.o obj-$(CONFIG_TI_SCI_INTA_IRQCHIP) += irq-ti-sci-inta.o diff --git a/drivers/irqchip/irq-lan966x-oic.c b/drivers/irqchip/irq-lan966x-oic.c new file mode 100644 index 000000000000..41ac880e3b87 --- /dev/null +++ b/drivers/irqchip/irq-lan966x-oic.c @@ -0,0 +1,278 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the Microchip LAN966x outbound interrupt controller + * + * Copyright (c) 2024 Technology Inc. and its subsidiaries. + * + * Authors: + * Horatiu Vultur + * Clément Léger + * Herve Codina + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct lan966x_oic_chip_regs { + int reg_off_ena_set; + int reg_off_ena_clr; + int reg_off_sticky; + int reg_off_ident; + int reg_off_map; +}; + +struct lan966x_oic_data { + void __iomem *regs; + int irq; +}; + +#define LAN966X_OIC_NR_IRQ 86 + +/* Interrupt sticky status */ +#define LAN966X_OIC_INTR_STICKY 0x30 +#define LAN966X_OIC_INTR_STICKY1 0x34 +#define LAN966X_OIC_INTR_STICKY2 0x38 + +/* Interrupt enable */ +#define LAN966X_OIC_INTR_ENA 0x48 +#define LAN966X_OIC_INTR_ENA1 0x4c +#define LAN966X_OIC_INTR_ENA2 0x50 + +/* Atomic clear of interrupt enable */ +#define LAN966X_OIC_INTR_ENA_CLR 0x54 +#define LAN966X_OIC_INTR_ENA_CLR1 0x58 +#define LAN966X_OIC_INTR_ENA_CLR2 0x5c + +/* Atomic set of interrupt */ +#define LAN966X_OIC_INTR_ENA_SET 0x60 +#define LAN966X_OIC_INTR_ENA_SET1 0x64 +#define LAN966X_OIC_INTR_ENA_SET2 0x68 + +/* Mapping of source to destination interrupts (_n = 0..8) */ +#define LAN966X_OIC_DST_INTR_MAP(_n) (0x78 + (_n) * 4) +#define LAN966X_OIC_DST_INTR_MAP1(_n) (0x9c + (_n) * 4) +#define LAN966X_OIC_DST_INTR_MAP2(_n) (0xc0 + (_n) * 4) + +/* Currently active interrupt sources per destination (_n = 0..8) */ +#define LAN966X_OIC_DST_INTR_IDENT(_n) (0xe4 + (_n) * 4) +#define LAN966X_OIC_DST_INTR_IDENT1(_n) (0x108 + (_n) * 4) +#define LAN966X_OIC_DST_INTR_IDENT2(_n) (0x12c + (_n) * 4) + +static unsigned int lan966x_oic_irq_startup(struct irq_data *data) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); + struct irq_chip_type *ct = irq_data_get_chip_type(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); + + ct->chip.irq_ack(data); + ct->chip.irq_unmask(data); + + return 0; +} + +static void lan966x_oic_irq_shutdown(struct irq_data *data) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); + struct irq_chip_type *ct = irq_data_get_chip_type(data); + struct lan966x_oic_chip_regs *chip_regs = gc->private; + u32 map; + + ct->chip.irq_mask(data); + + irq_gc_lock(gc); + + /* 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, + unsigned int flow_type) +{ + if (flow_type != IRQ_TYPE_LEVEL_HIGH) { + pr_err("lan966x oic doesn't support flow type %d\n", flow_type); + return -EINVAL; + } + + return 0; +} + +static void lan966x_oic_irq_handler_domain(struct irq_domain *d, u32 first_irq) +{ + struct irq_chip_generic *gc = irq_get_domain_generic_chip(d, first_irq); + struct lan966x_oic_chip_regs *chip_regs = gc->private; + unsigned long ident; + unsigned int hwirq; + + ident = irq_reg_readl(gc, chip_regs->reg_off_ident); + if (!ident) + return; + + for_each_set_bit(hwirq, &ident, 32) + generic_handle_domain_irq(d, hwirq + first_irq); +} + +static void lan966x_oic_irq_handler(struct irq_desc *desc) +{ + struct irq_domain *d = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + + chained_irq_enter(chip, desc); + lan966x_oic_irq_handler_domain(d, 0); + lan966x_oic_irq_handler_domain(d, 32); + lan966x_oic_irq_handler_domain(d, 64); + chained_irq_exit(chip, desc); +} + +static struct lan966x_oic_chip_regs lan966x_oic_chip_regs[3] = { + { + .reg_off_ena_set = LAN966X_OIC_INTR_ENA_SET, + .reg_off_ena_clr = LAN966X_OIC_INTR_ENA_CLR, + .reg_off_sticky = LAN966X_OIC_INTR_STICKY, + .reg_off_ident = LAN966X_OIC_DST_INTR_IDENT(0), + .reg_off_map = LAN966X_OIC_DST_INTR_MAP(0), + }, { + .reg_off_ena_set = LAN966X_OIC_INTR_ENA_SET1, + .reg_off_ena_clr = LAN966X_OIC_INTR_ENA_CLR1, + .reg_off_sticky = LAN966X_OIC_INTR_STICKY1, + .reg_off_ident = LAN966X_OIC_DST_INTR_IDENT1(0), + .reg_off_map = LAN966X_OIC_DST_INTR_MAP1(0), + }, { + .reg_off_ena_set = LAN966X_OIC_INTR_ENA_SET2, + .reg_off_ena_clr = LAN966X_OIC_INTR_ENA_CLR2, + .reg_off_sticky = LAN966X_OIC_INTR_STICKY2, + .reg_off_ident = LAN966X_OIC_DST_INTR_IDENT2(0), + .reg_off_map = LAN966X_OIC_DST_INTR_MAP2(0), + } +}; + +static int lan966x_oic_chip_init(struct irq_chip_generic *gc) +{ + struct lan966x_oic_data *lan966x_oic = gc->domain->host_data; + struct lan966x_oic_chip_regs *chip_regs; + + chip_regs = &lan966x_oic_chip_regs[gc->irq_base / 32]; + + gc->reg_base = lan966x_oic->regs; + gc->chip_types[0].regs.enable = chip_regs->reg_off_ena_set; + gc->chip_types[0].regs.disable = chip_regs->reg_off_ena_clr; + gc->chip_types[0].regs.ack = chip_regs->reg_off_sticky; + gc->chip_types[0].chip.irq_startup = lan966x_oic_irq_startup; + gc->chip_types[0].chip.irq_shutdown = lan966x_oic_irq_shutdown; + gc->chip_types[0].chip.irq_set_type = lan966x_oic_irq_set_type; + gc->chip_types[0].chip.irq_mask = irq_gc_mask_disable_reg; + gc->chip_types[0].chip.irq_unmask = irq_gc_unmask_enable_reg; + gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit; + gc->private = chip_regs; + + /* Disable all interrupts handled by this chip */ + irq_reg_writel(gc, ~0U, chip_regs->reg_off_ena_clr); + + return 0; +} + +static void lan966x_oic_chip_exit(struct irq_chip_generic *gc) +{ + /* Disable and ack all interrupts handled by this chip */ + irq_reg_writel(gc, ~0U, gc->chip_types[0].regs.disable); + irq_reg_writel(gc, ~0U, gc->chip_types[0].regs.ack); +} + +static int lan966x_oic_domain_init(struct irq_domain *d) +{ + struct lan966x_oic_data *lan966x_oic = d->host_data; + + irq_set_chained_handler_and_data(lan966x_oic->irq, lan966x_oic_irq_handler, d); + + return 0; +} + +static void lan966x_oic_domain_exit(struct irq_domain *d) +{ + struct lan966x_oic_data *lan966x_oic = d->host_data; + + irq_set_chained_handler_and_data(lan966x_oic->irq, NULL, NULL); +} + +static int lan966x_oic_probe(struct platform_device *pdev) +{ + struct irq_domain_chip_generic_info dgc_info = { + .name = "lan966x-oic", + .handler = handle_level_irq, + .irqs_per_chip = 32, + .num_ct = 1, + .init = lan966x_oic_chip_init, + .exit = lan966x_oic_chip_exit, + }; + struct irq_domain_info d_info = { + .fwnode = of_node_to_fwnode(pdev->dev.of_node), + .domain_flags = IRQ_DOMAIN_FLAG_DESTROY_GC, + .size = LAN966X_OIC_NR_IRQ, + .hwirq_max = LAN966X_OIC_NR_IRQ, + .ops = &irq_generic_chip_ops, + .dgc_info = &dgc_info, + .init = lan966x_oic_domain_init, + .exit = lan966x_oic_domain_exit, + }; + struct lan966x_oic_data *lan966x_oic; + struct device *dev = &pdev->dev; + struct irq_domain *domain; + + lan966x_oic = devm_kmalloc(dev, sizeof(*lan966x_oic), GFP_KERNEL); + if (!lan966x_oic) + return -ENOMEM; + + lan966x_oic->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(lan966x_oic->regs)) + return dev_err_probe(dev, PTR_ERR(lan966x_oic->regs), + "failed to map resource\n"); + + lan966x_oic->irq = platform_get_irq(pdev, 0); + if (lan966x_oic->irq < 0) + return dev_err_probe(dev, lan966x_oic->irq, "failed to get the IRQ\n"); + + d_info.host_data = lan966x_oic; + domain = devm_irq_domain_instantiate(dev, &d_info); + if (IS_ERR(domain)) + return dev_err_probe(dev, PTR_ERR(domain), + "failed to instantiate the IRQ domain\n"); + return 0; +} + +static const struct of_device_id lan966x_oic_of_match[] = { + { .compatible = "microchip,lan966x-oic" }, + {} /* sentinel */ +}; +MODULE_DEVICE_TABLE(of, lan966x_oic_of_match); + +static struct platform_driver lan966x_oic_driver = { + .probe = lan966x_oic_probe, + .driver = { + .name = "lan966x-oic", + .of_match_table = lan966x_oic_of_match, + }, +}; +module_platform_driver(lan966x_oic_driver); + +MODULE_AUTHOR("Herve Codina "); +MODULE_DESCRIPTION("Microchip LAN966x OIC driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 722c9389c7fa91d1b6c665252f655b352b3a32b8 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Fri, 14 Jun 2024 23:19:55 +0800 Subject: irqchip/riscv-intc: Remove asmlinkage The two functions riscv_intc_aia_irq() and riscv_intc_irq() are only called by C functions. Signed-off-by: Jisheng Zhang Signed-off-by: Thomas Gleixner Reviewed-by: Anup Patel Link: https://lore.kernel.org/r/20240614151955.1949-1-jszhang@kernel.org --- drivers/irqchip/irq-riscv-intc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-riscv-intc.c b/drivers/irqchip/irq-riscv-intc.c index 9e71c4428814..983538a89e65 100644 --- a/drivers/irqchip/irq-riscv-intc.c +++ b/drivers/irqchip/irq-riscv-intc.c @@ -26,7 +26,7 @@ static unsigned int riscv_intc_nr_irqs __ro_after_init = BITS_PER_LONG; static unsigned int riscv_intc_custom_base __ro_after_init = BITS_PER_LONG; static unsigned int riscv_intc_custom_nr_irqs __ro_after_init; -static asmlinkage void riscv_intc_irq(struct pt_regs *regs) +static void riscv_intc_irq(struct pt_regs *regs) { unsigned long cause = regs->cause & ~CAUSE_IRQ_FLAG; @@ -34,7 +34,7 @@ static asmlinkage void riscv_intc_irq(struct pt_regs *regs) pr_warn_ratelimited("Failed to handle interrupt (cause: %ld)\n", cause); } -static asmlinkage void riscv_intc_aia_irq(struct pt_regs *regs) +static void riscv_intc_aia_irq(struct pt_regs *regs) { unsigned long topi; -- cgit v1.2.3 From 492eee82574b163fbb3f099c74ce3b4322d0af28 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Thu, 6 Jun 2024 20:48:13 +0100 Subject: irqchip/renesas-rzg2l: Reorder function calls in rzg2l_irqc_irq_disable() The order of function calls in the disable operation should be the reverse of that in the enable operation. Thus, reorder the function calls to first disable the parent IRQ chip before disabling the TINT IRQ. Reported-by: Geert Uytterhoeven Signed-off-by: Lad Prabhakar Signed-off-by: Thomas Gleixner Tested-by: Claudiu Beznea # on RZ/G3S Link: https://lore.kernel.org/r/20240606194813.676823-1-prabhakar.mahadev-lad.rj@bp.renesas.com --- drivers/irqchip/irq-renesas-rzg2l.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-renesas-rzg2l.c b/drivers/irqchip/irq-renesas-rzg2l.c index 861a0e5a3e97..693ff285ca2c 100644 --- a/drivers/irqchip/irq-renesas-rzg2l.c +++ b/drivers/irqchip/irq-renesas-rzg2l.c @@ -271,8 +271,8 @@ static void rzg2l_tint_irq_endisable(struct irq_data *d, bool enable) static void rzg2l_irqc_irq_disable(struct irq_data *d) { - rzg2l_tint_irq_endisable(d, false); irq_chip_disable_parent(d); + rzg2l_tint_irq_endisable(d, false); } static void rzg2l_irqc_irq_enable(struct irq_data *d) -- cgit v1.2.3 From b1c387506d2738b655f0806d3ee3e6fc94ecb910 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Sat, 8 Jun 2024 09:14:37 -0700 Subject: irqchip: Add missing MODULE_DESCRIPTION() macros On x86, make allmodconfig && make W=1 C=1 reports: WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/irqchip/irq-ts4800.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/irqchip/irq-meson-gpio.o Add the missing invocation of the MODULE_DESCRIPTION() macro to all files which have a MODULE_LICENSE(). This includes a 3rd file, irq-mvebu-pic.c, which did not produce a warning with the x86 allmodconfig, but which may cause this warning with other kernel configurations. Signed-off-by: Jeff Johnson Signed-off-by: Thomas Gleixner Reviewed-by: Andrew Lunn Link: https://lore.kernel.org/r/20240608-md-drivers-irqchip-v1-1-dd02c3229277@quicinc.com --- drivers/irqchip/irq-meson-gpio.c | 1 + drivers/irqchip/irq-mvebu-pic.c | 1 + drivers/irqchip/irq-ts4800.c | 1 + 3 files changed, 3 insertions(+) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-meson-gpio.c b/drivers/irqchip/irq-meson-gpio.c index 9a1791908598..27e30ce41db3 100644 --- a/drivers/irqchip/irq-meson-gpio.c +++ b/drivers/irqchip/irq-meson-gpio.c @@ -608,5 +608,6 @@ IRQCHIP_MATCH("amlogic,meson-gpio-intc", meson_gpio_irq_of_init) IRQCHIP_PLATFORM_DRIVER_END(meson_gpio_intc) MODULE_AUTHOR("Jerome Brunet "); +MODULE_DESCRIPTION("Meson GPIO Interrupt Multiplexer driver"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:meson-gpio-intc"); diff --git a/drivers/irqchip/irq-mvebu-pic.c b/drivers/irqchip/irq-mvebu-pic.c index d17d9c0e2880..08b0cc862adf 100644 --- a/drivers/irqchip/irq-mvebu-pic.c +++ b/drivers/irqchip/irq-mvebu-pic.c @@ -193,6 +193,7 @@ module_platform_driver(mvebu_pic_driver); MODULE_AUTHOR("Yehuda Yitschak "); MODULE_AUTHOR("Thomas Petazzoni "); +MODULE_DESCRIPTION("Marvell Armada 7K/8K PIC driver"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:mvebu_pic"); diff --git a/drivers/irqchip/irq-ts4800.c b/drivers/irqchip/irq-ts4800.c index 57f610dab6b8..b5dddb3c1568 100644 --- a/drivers/irqchip/irq-ts4800.c +++ b/drivers/irqchip/irq-ts4800.c @@ -163,5 +163,6 @@ static struct platform_driver ts4800_ic_driver = { module_platform_driver(ts4800_ic_driver); MODULE_AUTHOR("Damien Riegel "); +MODULE_DESCRIPTION("Multiplexed-IRQs driver for TS-4800's FPGA"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:ts4800_irqc"); -- cgit v1.2.3 From 50c43447f71c6d0eb9e320c4cc69822d396e52bc Mon Sep 17 00:00:00 2001 From: Marek Behún Date: Thu, 20 Jun 2024 11:52:33 +0200 Subject: irqchip/armada-370-xp: Use atomic_io_modify() instead of another spinlock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the dedicated atomic_io_modify() instead of a open coded spin_lock() + readl() + writel() + spin_unlock() sequence. This allows to drop the irq_controller_lock spinlock from the driver. Signed-off-by: Marek Behún Signed-off-by: Thomas Gleixner Reviewed-by: Andrew Lunn --- drivers/irqchip/irq-armada-370-xp.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c index 4b021a67bdfe..676df716a3aa 100644 --- a/drivers/irqchip/irq-armada-370-xp.c +++ b/drivers/irqchip/irq-armada-370-xp.c @@ -461,24 +461,18 @@ static __init void armada_xp_ipi_init(struct device_node *node) set_smp_ipi_range(base_ipi, IPI_DOORBELL_END); } -static DEFINE_RAW_SPINLOCK(irq_controller_lock); - static int armada_xp_set_affinity(struct irq_data *d, const struct cpumask *mask_val, bool force) { irq_hw_number_t hwirq = irqd_to_hwirq(d); - unsigned long reg, mask; int cpu; /* Select a single core from the affinity mask which is online */ cpu = cpumask_any_and(mask_val, cpu_online_mask); - mask = 1UL << cpu_logical_map(cpu); - raw_spin_lock(&irq_controller_lock); - reg = readl(main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(hwirq)); - reg = (reg & (~ARMADA_370_XP_INT_SOURCE_CPU_MASK)) | mask; - writel(reg, main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(hwirq)); - raw_spin_unlock(&irq_controller_lock); + atomic_io_modify(main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(hwirq), + ARMADA_370_XP_INT_SOURCE_CPU_MASK, + BIT(cpu_logical_map(cpu))); irq_data_update_effective_affinity(d, cpumask_of(cpu)); -- cgit v1.2.3 From 3cef738208e5c3cb7084e208caf9bbf684f24feb Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Fri, 21 Jun 2024 11:38:28 +0200 Subject: irqchip/armada-370-xp: Do not allow mapping IRQ 0 and 1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IRQs 0 (IPI) and 1 (MSI) are handled internally by this driver, generic_handle_domain_irq() is never called for these IRQs. Disallow mapping these IRQs. [ Marek: changed commit message ] Signed-off-by: Pali Rohár Signed-off-by: Marek Behún Signed-off-by: Thomas Gleixner Reviewed-by: Andrew Lunn --- drivers/irqchip/irq-armada-370-xp.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c index 676df716a3aa..526077df1c84 100644 --- a/drivers/irqchip/irq-armada-370-xp.c +++ b/drivers/irqchip/irq-armada-370-xp.c @@ -560,6 +560,10 @@ static struct irq_chip armada_370_xp_irq_chip = { static int armada_370_xp_mpic_irq_map(struct irq_domain *h, unsigned int virq, irq_hw_number_t hw) { + /* IRQs 0 and 1 cannot be mapped, they are handled internally */ + if (hw <= 1) + return -EINVAL; + armada_370_xp_irq_mask(irq_get_irq_data(virq)); if (!is_percpu_irq(hw)) writel(hw, per_cpu_int_base + -- cgit v1.2.3 From de796fc184179de86fb87f94178459b5b20b1b1b Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Fri, 21 Jun 2024 11:38:29 +0200 Subject: irqchip/armada-370-xp: Only call ipi_resume() if IPI is available MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IPI is available only on systems where the mpic controller does not have a parent interrupt defined (e.g. on Armada XP). If a parent interrupt is defined, inter-processor interrupts are handled by an interrupt controller higher in the hierarchy (most probably a parent GIC). Only call ipi_resume() on systems where IPI is available in the mpic controller. Signed-off-by: Pali Rohár Signed-off-by: Marek Behún Signed-off-by: Thomas Gleixner Reviewed-by: Andrew Lunn [ refactored a little and changed commit message ] --- drivers/irqchip/irq-armada-370-xp.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c index 526077df1c84..deb4c9b0080c 100644 --- a/drivers/irqchip/irq-armada-370-xp.c +++ b/drivers/irqchip/irq-armada-370-xp.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -156,6 +157,17 @@ static DEFINE_MUTEX(msi_used_lock); static phys_addr_t msi_doorbell_addr; #endif +static inline bool is_ipi_available(void) +{ + /* + * We distinguish IPI availability in the IC by the IC not having a + * parent irq defined. If a parent irq is defined, there is a parent + * interrupt controller (e.g. GIC) that takes care of inter-processor + * interrupts. + */ + return parent_irq <= 0; +} + static inline bool is_percpu_irq(irq_hw_number_t irq) { if (irq <= ARMADA_370_XP_MAX_PER_CPU_IRQS) @@ -521,7 +533,8 @@ static void armada_xp_mpic_reenable_percpu(void) armada_370_xp_irq_unmask(data); } - ipi_resume(); + if (is_ipi_available()) + ipi_resume(); armada_370_xp_msi_reenable_percpu(); } @@ -744,7 +757,8 @@ static void armada_370_xp_mpic_resume(void) if (doorbell_mask_reg & PCI_MSI_DOORBELL_MASK) writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); - ipi_resume(); + if (is_ipi_available()) + ipi_resume(); } static struct syscore_ops armada_370_xp_mpic_syscore_ops = { -- cgit v1.2.3 From 9d80f6bd3ad8f2b49651d4685bb391399ecf80a2 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Fri, 21 Jun 2024 11:38:30 +0200 Subject: irqchip/armada-370-xp: Do not touch IPI registers on platforms without IPI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On platforms where IPI is not available in the MPIC, the IPI registers instead represent an additional set of MSI interrupt registers (currently unused by the driver). Do not touch these registers on platforms where IPI is not available in the MPIC. [ Marek: refactored, changed commit message ] Signed-off-by: Pali Rohár Signed-off-by: Marek Behún Signed-off-by: Thomas Gleixner Reviewed-by: Andrew Lunn --- drivers/irqchip/irq-armada-370-xp.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c index deb4c9b0080c..94a81c551c0c 100644 --- a/drivers/irqchip/irq-armada-370-xp.c +++ b/drivers/irqchip/irq-armada-370-xp.c @@ -502,6 +502,9 @@ static void armada_xp_mpic_smp_cpu_init(void) for (i = 0; i < nr_irqs; i++) writel(i, per_cpu_int_base + ARMADA_370_XP_INT_SET_MASK_OFFS); + if (!is_ipi_available()) + return; + /* Disable all IPIs */ writel(0, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); @@ -752,7 +755,7 @@ static void armada_370_xp_mpic_resume(void) /* Reconfigure doorbells for IPIs and MSIs */ writel(doorbell_mask_reg, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); - if (doorbell_mask_reg & IPI_DOORBELL_MASK) + if (is_ipi_available() && (doorbell_mask_reg & IPI_DOORBELL_MASK)) writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); if (doorbell_mask_reg & PCI_MSI_DOORBELL_MASK) writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); @@ -803,13 +806,18 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node, BUG_ON(!armada_370_xp_mpic_domain); irq_domain_update_bus_token(armada_370_xp_mpic_domain, DOMAIN_BUS_WIRED); + /* + * Initialize parent_irq before calling any other functions, since it is + * used to distinguish between IPI and non-IPI platforms. + */ + parent_irq = irq_of_parse_and_map(node, 0); + /* Setup for the boot CPU */ armada_xp_mpic_perf_init(); armada_xp_mpic_smp_cpu_init(); armada_370_xp_msi_init(node, main_int_res.start); - parent_irq = irq_of_parse_and_map(node, 0); if (parent_irq <= 0) { irq_set_default_host(armada_370_xp_mpic_domain); set_handle_irq(armada_370_xp_handle_irq); -- cgit v1.2.3 From 986b6ad0c4c653940fab7e5decf0d847670bf407 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Fri, 21 Jun 2024 11:38:31 +0200 Subject: irqchip/armada-370-xp: Add support for 32 MSI interrupts on non-IPI platforms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The doorbell interrupts have the following layout on IPI vs no-IPI platforms: | 0...7 | 8...15 | 16...31 | ------------------+---------+----------+---------------------+ IPI platform | IPI | n/a | MSI | ------------------+---------+----------+---------------------+ non-IPI platform | MSI | ------------------+------------------------------------------+ Currently the driver only allows for the upper 16...31 interrupts for MSI domain (i.e. the MSI domain has only 16 interrupts). On platforms where IPI is not available, we can use whole 32 MSI interrupts. Implement support also for the lower 16 MSI interrupts on non-IPI platforms. [ Marek: refactored, changed commit message ] Signed-off-by: Pali Rohár Signed-off-by: Marek Behún Signed-off-by: Thomas Gleixner Reviewed-by: Andrew Lunn --- drivers/irqchip/irq-armada-370-xp.c | 77 ++++++++++++++++++++++++++++++------- 1 file changed, 63 insertions(+), 14 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c index 94a81c551c0c..dce2b80bf439 100644 --- a/drivers/irqchip/irq-armada-370-xp.c +++ b/drivers/irqchip/irq-armada-370-xp.c @@ -13,6 +13,7 @@ * warranty of any kind, whether express or implied. */ +#include #include #include #include @@ -136,6 +137,7 @@ #define ARMADA_370_XP_MAX_PER_CPU_IRQS (28) +/* IPI and MSI interrupt definitions for IPI platforms */ #define IPI_DOORBELL_START (0) #define IPI_DOORBELL_END (8) #define IPI_DOORBELL_MASK 0xFF @@ -144,6 +146,14 @@ #define PCI_MSI_DOORBELL_END (32) #define PCI_MSI_DOORBELL_MASK 0xFFFF0000 +/* MSI interrupt definitions for non-IPI platforms */ +#define PCI_MSI_FULL_DOORBELL_START 0 +#define PCI_MSI_FULL_DOORBELL_NR 32 +#define PCI_MSI_FULL_DOORBELL_END 32 +#define PCI_MSI_FULL_DOORBELL_MASK GENMASK(31, 0) +#define PCI_MSI_FULL_DOORBELL_SRC0_MASK GENMASK(15, 0) +#define PCI_MSI_FULL_DOORBELL_SRC1_MASK GENMASK(31, 16) + static void __iomem *per_cpu_int_base; static void __iomem *main_int_base; static struct irq_domain *armada_370_xp_mpic_domain; @@ -152,7 +162,7 @@ static int parent_irq; #ifdef CONFIG_PCI_MSI static struct irq_domain *armada_370_xp_msi_domain; static struct irq_domain *armada_370_xp_msi_inner_domain; -static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR); +static DECLARE_BITMAP(msi_used, PCI_MSI_FULL_DOORBELL_NR); static DEFINE_MUTEX(msi_used_lock); static phys_addr_t msi_doorbell_addr; #endif @@ -168,6 +178,30 @@ static inline bool is_ipi_available(void) return parent_irq <= 0; } +static inline u32 msi_doorbell_mask(void) +{ + return is_ipi_available() ? PCI_MSI_DOORBELL_MASK : + PCI_MSI_FULL_DOORBELL_MASK; +} + +static inline unsigned int msi_doorbell_start(void) +{ + return is_ipi_available() ? PCI_MSI_DOORBELL_START : + PCI_MSI_FULL_DOORBELL_START; +} + +static inline unsigned int msi_doorbell_size(void) +{ + return is_ipi_available() ? PCI_MSI_DOORBELL_NR : + PCI_MSI_FULL_DOORBELL_NR; +} + +static inline unsigned int msi_doorbell_end(void) +{ + return is_ipi_available() ? PCI_MSI_DOORBELL_END : + PCI_MSI_FULL_DOORBELL_END; +} + static inline bool is_percpu_irq(irq_hw_number_t irq) { if (irq <= ARMADA_370_XP_MAX_PER_CPU_IRQS) @@ -225,7 +259,7 @@ static void armada_370_xp_compose_msi_msg(struct irq_data *data, struct msi_msg msg->address_lo = lower_32_bits(msi_doorbell_addr); msg->address_hi = upper_32_bits(msi_doorbell_addr); - msg->data = BIT(cpu + 8) | (data->hwirq + PCI_MSI_DOORBELL_START); + msg->data = BIT(cpu + 8) | (data->hwirq + msi_doorbell_start()); } static int armada_370_xp_msi_set_affinity(struct irq_data *irq_data, @@ -258,7 +292,7 @@ static int armada_370_xp_msi_alloc(struct irq_domain *domain, unsigned int virq, int hwirq, i; mutex_lock(&msi_used_lock); - hwirq = bitmap_find_free_region(msi_used, PCI_MSI_DOORBELL_NR, + hwirq = bitmap_find_free_region(msi_used, msi_doorbell_size(), order_base_2(nr_irqs)); mutex_unlock(&msi_used_lock); @@ -295,9 +329,10 @@ static void armada_370_xp_msi_reenable_percpu(void) u32 reg; /* Enable MSI doorbell mask and combined cpu local interrupt */ - reg = readl(per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS) - | PCI_MSI_DOORBELL_MASK; + reg = readl(per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); + reg |= msi_doorbell_mask(); writel(reg, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); + /* Unmask local doorbell interrupt */ writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); } @@ -309,7 +344,7 @@ static int armada_370_xp_msi_init(struct device_node *node, ARMADA_370_XP_SW_TRIG_INT_OFFS; armada_370_xp_msi_inner_domain = - irq_domain_add_linear(NULL, PCI_MSI_DOORBELL_NR, + irq_domain_add_linear(NULL, msi_doorbell_size(), &armada_370_xp_msi_domain_ops, NULL); if (!armada_370_xp_msi_inner_domain) return -ENOMEM; @@ -325,6 +360,10 @@ static int armada_370_xp_msi_init(struct device_node *node, armada_370_xp_msi_reenable_percpu(); + /* Unmask low 16 MSI irqs on non-IPI platforms */ + if (!is_ipi_available()) + writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); + return 0; } #else @@ -613,20 +652,20 @@ static void armada_370_xp_handle_msi_irq(struct pt_regs *regs, bool is_chained) u32 msimask, msinr; msimask = readl_relaxed(per_cpu_int_base + - ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS) - & PCI_MSI_DOORBELL_MASK; + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); + msimask &= msi_doorbell_mask(); writel(~msimask, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); - for (msinr = PCI_MSI_DOORBELL_START; - msinr < PCI_MSI_DOORBELL_END; msinr++) { + for (msinr = msi_doorbell_start(); + msinr < msi_doorbell_end(); msinr++) { unsigned int irq; if (!(msimask & BIT(msinr))) continue; - irq = msinr - PCI_MSI_DOORBELL_START; + irq = msinr - msi_doorbell_start(); generic_handle_domain_irq(armada_370_xp_msi_inner_domain, irq); } @@ -655,7 +694,7 @@ static void armada_370_xp_mpic_handle_cascade_irq(struct irq_desc *desc) if (!(irqsrc & ARMADA_370_XP_INT_IRQ_FIQ_MASK(cpuid))) continue; - if (irqn == 1) { + if (irqn == 0 || irqn == 1) { armada_370_xp_handle_msi_irq(NULL, true); continue; } @@ -716,6 +755,7 @@ static int armada_370_xp_mpic_suspend(void) static void armada_370_xp_mpic_resume(void) { + bool src0, src1; int nirqs; irq_hw_number_t irq; @@ -755,9 +795,18 @@ static void armada_370_xp_mpic_resume(void) /* Reconfigure doorbells for IPIs and MSIs */ writel(doorbell_mask_reg, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); - if (is_ipi_available() && (doorbell_mask_reg & IPI_DOORBELL_MASK)) + + if (is_ipi_available()) { + src0 = doorbell_mask_reg & IPI_DOORBELL_MASK; + src1 = doorbell_mask_reg & PCI_MSI_DOORBELL_MASK; + } else { + src0 = doorbell_mask_reg & PCI_MSI_FULL_DOORBELL_SRC0_MASK; + src1 = doorbell_mask_reg & PCI_MSI_FULL_DOORBELL_SRC1_MASK; + } + + if (src0) writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); - if (doorbell_mask_reg & PCI_MSI_DOORBELL_MASK) + if (src1) writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); if (is_ipi_available()) -- cgit v1.2.3 From 760d7e719499d64beea62bfcf53938fb233bb6e7 Mon Sep 17 00:00:00 2001 From: Tianyang Zhang Date: Tue, 4 Jun 2024 20:50:26 +0800 Subject: Loongarch: Support loongarch avec Introduce the advanced extended interrupt controllers. This feature will allow each core to have 256 independent interrupt vectors and MSI interrupts can be independently routed to any vector on any CPU. [ tglx: Fixed up coding style. Made on/offline functions void ] Co-developed-by: Jianmin Lv Signed-off-by: Jianmin Lv Co-developed-by: Liupu Wang Signed-off-by: Liupu Wang Signed-off-by: Tianyang Zhang Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/r/20240604125026.18745-1-zhangtianyang@loongson.cn --- arch/loongarch/Kconfig | 1 + arch/loongarch/include/asm/cpu-features.h | 1 + arch/loongarch/include/asm/cpu.h | 2 + arch/loongarch/include/asm/hw_irq.h | 10 + arch/loongarch/include/asm/irq.h | 12 +- arch/loongarch/include/asm/loongarch.h | 20 +- arch/loongarch/include/asm/smp.h | 2 + arch/loongarch/kernel/cpu-probe.c | 3 +- arch/loongarch/kernel/smp.c | 5 + drivers/irqchip/Makefile | 2 +- drivers/irqchip/irq-loongarch-avec.c | 419 ++++++++++++++++++++++++++++++ drivers/irqchip/irq-loongarch-cpu.c | 4 +- drivers/irqchip/irq-loongson-eiointc.c | 3 + drivers/irqchip/irq-loongson-pch-msi.c | 43 ++- 14 files changed, 517 insertions(+), 10 deletions(-) create mode 100644 drivers/irqchip/irq-loongarch-avec.c (limited to 'drivers/irqchip') diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index e38139c576ee..a66e49b5a68c 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -83,6 +83,7 @@ config LOONGARCH select GENERIC_ENTRY select GENERIC_GETTIMEOFDAY select GENERIC_IOREMAP if !ARCH_IOREMAP + select GENERIC_IRQ_MATRIX_ALLOCATOR select GENERIC_IRQ_MULTI_HANDLER select GENERIC_IRQ_PROBE select GENERIC_IRQ_SHOW diff --git a/arch/loongarch/include/asm/cpu-features.h b/arch/loongarch/include/asm/cpu-features.h index 2eafe6a6aca8..16a716f88a5c 100644 --- a/arch/loongarch/include/asm/cpu-features.h +++ b/arch/loongarch/include/asm/cpu-features.h @@ -65,5 +65,6 @@ #define cpu_has_guestid cpu_opt(LOONGARCH_CPU_GUESTID) #define cpu_has_hypervisor cpu_opt(LOONGARCH_CPU_HYPERVISOR) #define cpu_has_ptw cpu_opt(LOONGARCH_CPU_PTW) +#define cpu_has_avecint cpu_opt(LOONGARCH_CPU_AVECINT) #endif /* __ASM_CPU_FEATURES_H */ diff --git a/arch/loongarch/include/asm/cpu.h b/arch/loongarch/include/asm/cpu.h index 48b9f7168bcc..843f9c4ec980 100644 --- a/arch/loongarch/include/asm/cpu.h +++ b/arch/loongarch/include/asm/cpu.h @@ -99,6 +99,7 @@ enum cpu_type_enum { #define CPU_FEATURE_GUESTID 24 /* CPU has GuestID feature */ #define CPU_FEATURE_HYPERVISOR 25 /* CPU has hypervisor (running in VM) */ #define CPU_FEATURE_PTW 26 /* CPU has hardware page table walker */ +#define CPU_FEATURE_AVECINT 27 /* CPU has avec interrupt */ #define LOONGARCH_CPU_CPUCFG BIT_ULL(CPU_FEATURE_CPUCFG) #define LOONGARCH_CPU_LAM BIT_ULL(CPU_FEATURE_LAM) @@ -127,5 +128,6 @@ enum cpu_type_enum { #define LOONGARCH_CPU_GUESTID BIT_ULL(CPU_FEATURE_GUESTID) #define LOONGARCH_CPU_HYPERVISOR BIT_ULL(CPU_FEATURE_HYPERVISOR) #define LOONGARCH_CPU_PTW BIT_ULL(CPU_FEATURE_PTW) +#define LOONGARCH_CPU_AVECINT BIT_ULL(CPU_FEATURE_AVECINT) #endif /* _ASM_CPU_H */ diff --git a/arch/loongarch/include/asm/hw_irq.h b/arch/loongarch/include/asm/hw_irq.h index af4f4e8fbd85..772692e765c0 100644 --- a/arch/loongarch/include/asm/hw_irq.h +++ b/arch/loongarch/include/asm/hw_irq.h @@ -9,6 +9,16 @@ extern atomic_t irq_err_count; +/* + * 256 vectors Map: + * + * 0 - 15: mapping legacy IPs, e.g. IP0-12. + * 16 - 255: mapping a vector for external IRQ. + * + */ +#define NR_VECTORS 256 +#define IRQ_MATRIX_BITS NR_VECTORS +#define NR_LEGACY_VECTORS 16 /* * interrupt-retrigger: NOP for now. This may not be appropriate for all * machines, we'll see ... diff --git a/arch/loongarch/include/asm/irq.h b/arch/loongarch/include/asm/irq.h index 480418bc5071..cf3b635a9b86 100644 --- a/arch/loongarch/include/asm/irq.h +++ b/arch/loongarch/include/asm/irq.h @@ -65,7 +65,7 @@ extern struct acpi_vector_group msi_group[MAX_IO_PICS]; #define LOONGSON_LPC_LAST_IRQ (LOONGSON_LPC_IRQ_BASE + 15) #define LOONGSON_CPU_IRQ_BASE 16 -#define LOONGSON_CPU_LAST_IRQ (LOONGSON_CPU_IRQ_BASE + 14) +#define LOONGSON_CPU_LAST_IRQ (LOONGSON_CPU_IRQ_BASE + 15) #define LOONGSON_PCH_IRQ_BASE 64 #define LOONGSON_PCH_ACPI_IRQ (LOONGSON_PCH_IRQ_BASE + 47) @@ -101,6 +101,16 @@ int pch_msi_acpi_init(struct irq_domain *parent, struct acpi_madt_msi_pic *acpi_pchmsi); int pch_pic_acpi_init(struct irq_domain *parent, struct acpi_madt_bio_pic *acpi_pchpic); + +#ifdef CONFIG_ACPI +int __init pch_msi_acpi_init_v2(struct irq_domain *parent, + struct acpi_madt_msi_pic *pch_msi_entry); +int __init loongarch_avec_acpi_init(struct irq_domain *parent); +void complete_irq_moving(void); +void loongarch_avec_offline_cpu(unsigned int cpu); +void loongarch_avec_online_cpu(unsigned int cpu); +#endif + int find_pch_pic(u32 gsi); struct fwnode_handle *get_pch_msi_handle(int pci_segment); diff --git a/arch/loongarch/include/asm/loongarch.h b/arch/loongarch/include/asm/loongarch.h index eb09adda54b7..16a910359977 100644 --- a/arch/loongarch/include/asm/loongarch.h +++ b/arch/loongarch/include/asm/loongarch.h @@ -72,7 +72,6 @@ #define CPUCFG1_RPLV BIT(23) #define CPUCFG1_HUGEPG BIT(24) #define CPUCFG1_CRC32 BIT(25) -#define CPUCFG1_MSGINT BIT(26) #define LOONGARCH_CPUCFG2 0x2 #define CPUCFG2_FP BIT(0) @@ -252,8 +251,8 @@ #define CSR_ESTAT_EXC_WIDTH 6 #define CSR_ESTAT_EXC (_ULCAST_(0x3f) << CSR_ESTAT_EXC_SHIFT) #define CSR_ESTAT_IS_SHIFT 0 -#define CSR_ESTAT_IS_WIDTH 14 -#define CSR_ESTAT_IS (_ULCAST_(0x3fff) << CSR_ESTAT_IS_SHIFT) +#define CSR_ESTAT_IS_WIDTH 15 +#define CSR_ESTAT_IS (_ULCAST_(0x7fff) << CSR_ESTAT_IS_SHIFT) #define LOONGARCH_CSR_ERA 0x6 /* ERA */ @@ -999,10 +998,18 @@ #define CSR_FWPC_SKIP_SHIFT 16 #define CSR_FWPC_SKIP (_ULCAST_(1) << CSR_FWPC_SKIP_SHIFT) +#define LOONGARCH_CSR_IRR0 0xa0 +#define LOONGARCH_CSR_IRR1 0xa1 +#define LOONGARCH_CSR_IRR2 0xa2 +#define LOONGARCH_CSR_IRR3 0xa3 +#define LOONGARCH_CSR_IRR_BASE LOONGARCH_CSR_IRR0 + +#define LOONGARCH_CSR_ILR 0xa4 + /* * CSR_ECFG IM */ -#define ECFG0_IM 0x00001fff +#define ECFG0_IM 0x00005fff #define ECFGB_SIP0 0 #define ECFGF_SIP0 (_ULCAST_(1) << ECFGB_SIP0) #define ECFGB_SIP1 1 @@ -1045,6 +1052,7 @@ #define IOCSRF_EIODECODE BIT_ULL(9) #define IOCSRF_FLATMODE BIT_ULL(10) #define IOCSRF_VM BIT_ULL(11) +#define IOCSRF_AVEC BIT_ULL(15) #define LOONGARCH_IOCSR_VENDOR 0x10 @@ -1055,6 +1063,7 @@ #define LOONGARCH_IOCSR_MISC_FUNC 0x420 #define IOCSR_MISC_FUNC_TIMER_RESET BIT_ULL(21) #define IOCSR_MISC_FUNC_EXT_IOI_EN BIT_ULL(48) +#define IOCSR_MISC_FUNC_AVEC_EN BIT_ULL(51) #define LOONGARCH_IOCSR_CPUTEMP 0x428 @@ -1375,9 +1384,10 @@ __BUILD_CSR_OP(tlbidx) #define INT_TI 11 /* Timer */ #define INT_IPI 12 #define INT_NMI 13 +#define INT_AVEC 14 /* ExcCodes corresponding to interrupts */ -#define EXCCODE_INT_NUM (INT_NMI + 1) +#define EXCCODE_INT_NUM (INT_AVEC + 1) #define EXCCODE_INT_START 64 #define EXCCODE_INT_END (EXCCODE_INT_START + EXCCODE_INT_NUM - 1) diff --git a/arch/loongarch/include/asm/smp.h b/arch/loongarch/include/asm/smp.h index 278700cfee88..2399004596a3 100644 --- a/arch/loongarch/include/asm/smp.h +++ b/arch/loongarch/include/asm/smp.h @@ -69,9 +69,11 @@ extern int __cpu_logical_map[NR_CPUS]; #define ACTION_BOOT_CPU 0 #define ACTION_RESCHEDULE 1 #define ACTION_CALL_FUNCTION 2 +#define ACTION_CLEAR_VECT 3 #define SMP_BOOT_CPU BIT(ACTION_BOOT_CPU) #define SMP_RESCHEDULE BIT(ACTION_RESCHEDULE) #define SMP_CALL_FUNCTION BIT(ACTION_CALL_FUNCTION) +#define SMP_CLEAR_VECT BIT(ACTION_CLEAR_VECT) struct secondary_data { unsigned long stack; diff --git a/arch/loongarch/kernel/cpu-probe.c b/arch/loongarch/kernel/cpu-probe.c index 55320813ee08..3b2e72e8f9bd 100644 --- a/arch/loongarch/kernel/cpu-probe.c +++ b/arch/loongarch/kernel/cpu-probe.c @@ -106,7 +106,6 @@ static void cpu_probe_common(struct cpuinfo_loongarch *c) elf_hwcap |= HWCAP_LOONGARCH_CRC32; } - config = read_cpucfg(LOONGARCH_CPUCFG2); if (config & CPUCFG2_LAM) { c->options |= LOONGARCH_CPU_LAM; @@ -176,6 +175,8 @@ static void cpu_probe_common(struct cpuinfo_loongarch *c) c->options |= LOONGARCH_CPU_EIODECODE; if (config & IOCSRF_VM) c->options |= LOONGARCH_CPU_HYPERVISOR; + if (config & IOCSRF_AVEC) + c->options |= LOONGARCH_CPU_AVECINT; config = csr_read32(LOONGARCH_CSR_ASID); config = (config & CSR_ASID_BIT) >> CSR_ASID_BIT_SHIFT; diff --git a/arch/loongarch/kernel/smp.c b/arch/loongarch/kernel/smp.c index 0dfe2388ef41..6dfedef306f3 100644 --- a/arch/loongarch/kernel/smp.c +++ b/arch/loongarch/kernel/smp.c @@ -234,6 +234,9 @@ static irqreturn_t loongson_ipi_interrupt(int irq, void *dev) per_cpu(irq_stat, cpu).ipi_irqs[IPI_CALL_FUNCTION]++; } + if (action & SMP_CLEAR_VECT) + complete_irq_moving(); + return IRQ_HANDLED; } @@ -388,6 +391,7 @@ int loongson_cpu_disable(void) irq_migrate_all_off_this_cpu(); clear_csr_ecfg(ECFG0_IM); local_irq_restore(flags); + loongarch_avec_offline_cpu(cpu); local_flush_tlb_all(); return 0; @@ -566,6 +570,7 @@ asmlinkage void start_secondary(void) * early is dangerous. */ WARN_ON_ONCE(!irqs_disabled()); + loongarch_avec_online_cpu(cpu); loongson_smp_finish(); cpu_startup_entry(CPUHP_AP_ONLINE_IDLE); diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 9f6f88274bec..1062e713cea4 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -109,7 +109,7 @@ obj-$(CONFIG_LS1X_IRQ) += irq-ls1x.o obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) += irq-ti-sci-intr.o obj-$(CONFIG_TI_SCI_INTA_IRQCHIP) += irq-ti-sci-inta.o obj-$(CONFIG_TI_PRUSS_INTC) += irq-pruss-intc.o -obj-$(CONFIG_IRQ_LOONGARCH_CPU) += irq-loongarch-cpu.o +obj-$(CONFIG_IRQ_LOONGARCH_CPU) += irq-loongarch-cpu.o irq-loongarch-avec.o obj-$(CONFIG_LOONGSON_LIOINTC) += irq-loongson-liointc.o obj-$(CONFIG_LOONGSON_EIOINTC) += irq-loongson-eiointc.o obj-$(CONFIG_LOONGSON_HTPIC) += irq-loongson-htpic.o diff --git a/drivers/irqchip/irq-loongarch-avec.c b/drivers/irqchip/irq-loongarch-avec.c new file mode 100644 index 000000000000..4cd9079f2814 --- /dev/null +++ b/drivers/irqchip/irq-loongarch-avec.c @@ -0,0 +1,419 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Loongson Technologies, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define VECTORS_PER_REG 64 +#define ILR_INVALID_MASK 0x80000000UL +#define ILR_VECTOR_MASK 0xffUL +#define AVEC_MSG_OFFSET 0x100000 + +static phys_addr_t msi_base_v2; +static DEFINE_PER_CPU(struct irq_desc * [NR_VECTORS], irq_map); + +struct pending_list { + struct list_head head; +}; + +static DEFINE_PER_CPU(struct pending_list, pending_list); + +struct loongarch_avec_chip { + struct fwnode_handle *fwnode; + struct irq_domain *domain; + struct irq_matrix *vector_matrix; + raw_spinlock_t lock; +}; + +static struct loongarch_avec_chip loongarch_avec; + +struct loongarch_avec_data { + struct list_head entry; + unsigned int cpu; + unsigned int vec; + unsigned int prev_cpu; + unsigned int prev_vec; + unsigned int moving : 1, + managed : 1; +}; + +static struct cpumask intersect_mask; + +static int assign_irq_vector(struct irq_data *irqd, const struct cpumask *dest, + unsigned int *cpu) +{ + return irq_matrix_alloc(loongarch_avec.vector_matrix, dest, false, cpu); +} + +static inline void loongarch_avec_ack_irq(struct irq_data *d) +{ +} + +static inline void loongarch_avec_unmask_irq(struct irq_data *d) +{ +} + +static inline void loongarch_avec_mask_irq(struct irq_data *d) +{ +} + +static void loongarch_avec_sync(struct loongarch_avec_data *adata) +{ + struct pending_list *plist; + + if (cpu_online(adata->prev_cpu)) { + plist = per_cpu_ptr(&pending_list, adata->prev_cpu); + list_add_tail(&adata->entry, &plist->head); + adata->moving = true; + loongson_send_ipi_single(adata->prev_cpu, SMP_CLEAR_VECT); + } + adata->prev_cpu = adata->cpu; + adata->prev_vec = adata->vec; +} + +static int loongarch_avec_set_affinity(struct irq_data *data, const struct cpumask *dest, + bool force) +{ + struct loongarch_avec_data *adata; + unsigned int cpu, vector; + unsigned long flags; + int ret; + + raw_spin_lock_irqsave(&loongarch_avec.lock, flags); + adata = irq_data_get_irq_chip_data(data); + + if (adata->vec && cpu_online(adata->cpu) && cpumask_test_cpu(adata->cpu, dest)) { + raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags); + return 0; + } + if (adata->moving) + return -EBUSY; + + cpumask_and(&intersect_mask, dest, cpu_online_mask); + + ret = assign_irq_vector(data, &intersect_mask, &cpu); + if (ret < 0) { + raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags); + return ret; + } + vector = ret; + adata->cpu = cpu; + adata->vec = vector; + per_cpu_ptr(irq_map, adata->cpu)[adata->vec] = irq_data_to_desc(data); + loongarch_avec_sync(adata); + + raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags); + irq_data_update_effective_affinity(data, cpumask_of(cpu)); + + return IRQ_SET_MASK_OK; +} + +static void loongarch_avec_compose_msg(struct irq_data *d, + struct msi_msg *msg) +{ + struct loongarch_avec_data *avec_data; + + avec_data = irq_data_get_irq_chip_data(d); + + msg->address_hi = 0x0; + msg->address_lo = msi_base_v2 | ((avec_data->vec & 0xff) << 4) | + ((cpu_logical_map(avec_data->cpu & 0xffff)) << 12); + msg->data = 0x0; + +} + +static struct irq_chip loongarch_avec_controller = { + .name = "CORE_AVEC", + .irq_ack = loongarch_avec_ack_irq, + .irq_mask = loongarch_avec_mask_irq, + .irq_unmask = loongarch_avec_unmask_irq, + .irq_set_affinity = loongarch_avec_set_affinity, + .irq_compose_msi_msg = loongarch_avec_compose_msg, +}; + +void complete_irq_moving(void) +{ + struct pending_list *plist = this_cpu_ptr(&pending_list); + struct loongarch_avec_data *adata, *tmp; + int cpu, vector, bias; + u64 irr; + + raw_spin_lock(&loongarch_avec.lock); + + list_for_each_entry_safe(adata, tmp, &plist->head, entry) { + cpu = adata->prev_cpu; + vector = adata->prev_vec; + bias = vector / VECTORS_PER_REG; + switch (bias) { + case 0: + irr = csr_read64(LOONGARCH_CSR_IRR0); + case 1: + irr = csr_read64(LOONGARCH_CSR_IRR1); + case 2: + irr = csr_read64(LOONGARCH_CSR_IRR2); + case 3: + irr = csr_read64(LOONGARCH_CSR_IRR3); + } + + if (irr & (1UL << (vector % VECTORS_PER_REG))) { + loongson_send_ipi_single(cpu, SMP_CLEAR_VECT); + continue; + } + list_del(&adata->entry); + irq_matrix_free(loongarch_avec.vector_matrix, cpu, vector, adata->managed); + this_cpu_write(irq_map[vector], NULL); + adata->moving = 0; + } + raw_spin_unlock(&loongarch_avec.lock); +} + +static void loongarch_avec_dispatch(struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + unsigned long vector; + struct irq_desc *d; + + chained_irq_enter(chip, desc); + vector = csr_read64(LOONGARCH_CSR_ILR); + if (vector & ILR_INVALID_MASK) + return; + + vector &= ILR_VECTOR_MASK; + + d = this_cpu_read(irq_map[vector]); + if (d) { + generic_handle_irq_desc(d); + } else { + pr_warn("IRQ ERROR:Unexpected irq occur on cpu %d[vector %ld]\n", + smp_processor_id(), vector); + } + + chained_irq_exit(chip, desc); +} + +static int loongarch_avec_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *arg) +{ + struct loongarch_avec_data *adata; + struct irq_data *irqd; + unsigned int cpu, vector, i, ret; + unsigned long flags; + + raw_spin_lock_irqsave(&loongarch_avec.lock, flags); + for (i = 0; i < nr_irqs; i++) { + irqd = irq_domain_get_irq_data(domain, virq + i); + adata = kzalloc(sizeof(*adata), GFP_KERNEL); + if (!adata) { + raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags); + return -ENOMEM; + } + ret = assign_irq_vector(irqd, cpu_online_mask, &cpu); + if (ret < 0) { + raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags); + return ret; + } + vector = ret; + adata->prev_cpu = adata->cpu = cpu; + adata->prev_vec = adata->vec = vector; + adata->managed = irqd_affinity_is_managed(irqd); + irq_domain_set_info(domain, virq + i, virq + i, &loongarch_avec_controller, + adata, handle_edge_irq, NULL, NULL); + adata->moving = 0; + irqd_set_single_target(irqd); + irqd_set_affinity_on_activate(irqd); + + per_cpu_ptr(irq_map, adata->cpu)[adata->vec] = irq_data_to_desc(irqd); + } + raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags); + + return 0; +} + +static void clear_free_vector(struct irq_data *irqd) +{ + struct loongarch_avec_data *adata = irq_data_get_irq_chip_data(irqd); + bool managed = irqd_affinity_is_managed(irqd); + + per_cpu(irq_map, adata->cpu)[adata->vec] = NULL; + irq_matrix_free(loongarch_avec.vector_matrix, adata->cpu, adata->vec, managed); + adata->cpu = 0; + adata->vec = 0; + if (!adata->moving) + return; + + per_cpu(irq_map, adata->prev_cpu)[adata->prev_vec] = 0; + irq_matrix_free(loongarch_avec.vector_matrix, adata->prev_cpu, + adata->prev_vec, adata->managed); + adata->prev_vec = 0; + adata->prev_cpu = 0; + adata->moving = 0; + list_del_init(&adata->entry); +} + +static void loongarch_avec_free(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs) +{ + struct irq_data *d; + unsigned long flags; + unsigned int i; + + raw_spin_lock_irqsave(&loongarch_avec.lock, flags); + for (i = 0; i < nr_irqs; i++) { + d = irq_domain_get_irq_data(domain, virq + i); + if (d) { + clear_free_vector(d); + irq_domain_reset_irq_data(d); + + } + } + + raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags); +} + +static const struct irq_domain_ops loongarch_avec_domain_ops = { + .alloc = loongarch_avec_alloc, + .free = loongarch_avec_free, +}; + +static int __init irq_matrix_init(void) +{ + int i; + + loongarch_avec.vector_matrix = irq_alloc_matrix(NR_VECTORS, 0, NR_VECTORS - 1); + if (!loongarch_avec.vector_matrix) + return -ENOMEM; + for (i = 0; i < NR_LEGACY_VECTORS; i++) + irq_matrix_assign_system(loongarch_avec.vector_matrix, i, false); + + irq_matrix_online(loongarch_avec.vector_matrix); + + return 0; +} + +static int __init loongarch_avec_init(struct irq_domain *parent) +{ + struct pending_list *plist = per_cpu_ptr(&pending_list, 0); + int ret = 0, parent_irq; + unsigned long tmp; + + raw_spin_lock_init(&loongarch_avec.lock); + + loongarch_avec.fwnode = irq_domain_alloc_named_fwnode("CORE_AVEC"); + if (!loongarch_avec.fwnode) { + pr_err("Unable to allocate domain handle\n"); + ret = -ENOMEM; + goto out; + } + + loongarch_avec.domain = irq_domain_create_tree(loongarch_avec.fwnode, + &loongarch_avec_domain_ops, NULL); + if (!loongarch_avec.domain) { + pr_err("core-vec: cannot create IRQ domain\n"); + ret = -ENOMEM; + goto out_free_handle; + } + + parent_irq = irq_create_mapping(parent, INT_AVEC); + if (!parent_irq) { + pr_err("Failed to mapping hwirq\n"); + ret = -EINVAL; + goto out_remove_domain; + } + irq_set_chained_handler_and_data(parent_irq, loongarch_avec_dispatch, NULL); + + ret = irq_matrix_init(); + if (ret) { + pr_err("Failed to init irq matrix\n"); + goto out_free_matrix; + } + + INIT_LIST_HEAD(&plist->head); + tmp = iocsr_read64(LOONGARCH_IOCSR_MISC_FUNC); + tmp |= IOCSR_MISC_FUNC_AVEC_EN; + iocsr_write64(tmp, LOONGARCH_IOCSR_MISC_FUNC); + + return ret; + +out_free_matrix: + kfree(loongarch_avec.vector_matrix); +out_remove_domain: + irq_domain_remove(loongarch_avec.domain); +out_free_handle: + irq_domain_free_fwnode(loongarch_avec.fwnode); +out: + return ret; +} + +void loongarch_avec_offline_cpu(unsigned int cpu) +{ + struct pending_list *plist = per_cpu_ptr(&pending_list, cpu); + unsigned long flags; + + raw_spin_lock_irqsave(&loongarch_avec.lock, flags); + if (list_empty(&plist->head)) + irq_matrix_offline(loongarch_avec.vector_matrix); + else + pr_warn("cpu %d advanced extioi is busy\n", cpu); + raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags); +} + +void loongarch_avec_online_cpu(unsigned int cpu) +{ + struct pending_list *plist = per_cpu_ptr(&pending_list, cpu); + unsigned long flags; + + raw_spin_lock_irqsave(&loongarch_avec.lock, flags); + + irq_matrix_online(loongarch_avec.vector_matrix); + + INIT_LIST_HEAD(&plist->head); + + raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags); +} + +static int __init pch_msi_parse_madt(union acpi_subtable_headers *header, + const unsigned long end) +{ + struct acpi_madt_msi_pic *pchmsi_entry = (struct acpi_madt_msi_pic *)header; + + msi_base_v2 = pchmsi_entry->msg_address - AVEC_MSG_OFFSET; + return pch_msi_acpi_init_v2(loongarch_avec.domain, pchmsi_entry); +} + +static inline int __init acpi_cascade_irqdomain_init(void) +{ + return acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, pch_msi_parse_madt, 1); +} + +int __init loongarch_avec_acpi_init(struct irq_domain *parent) +{ + int ret = 0; + + ret = loongarch_avec_init(parent); + if (ret) { + pr_err("Failed to init irq domain\n"); + return ret; + } + + ret = acpi_cascade_irqdomain_init(); + if (ret) { + pr_err("Failed to cascade IRQ domain\n"); + return ret; + } + + return ret; +} diff --git a/drivers/irqchip/irq-loongarch-cpu.c b/drivers/irqchip/irq-loongarch-cpu.c index 9d8f2c406043..1ecac59925c6 100644 --- a/drivers/irqchip/irq-loongarch-cpu.c +++ b/drivers/irqchip/irq-loongarch-cpu.c @@ -138,7 +138,9 @@ static int __init acpi_cascade_irqdomain_init(void) if (r < 0) return r; - return 0; + if (cpu_has_avecint) + r = loongarch_avec_acpi_init(irq_domain); + return r; } static int __init cpuintc_acpi_init(union acpi_subtable_headers *header, diff --git a/drivers/irqchip/irq-loongson-eiointc.c b/drivers/irqchip/irq-loongson-eiointc.c index c7ddebf312ad..1f9a30488137 100644 --- a/drivers/irqchip/irq-loongson-eiointc.c +++ b/drivers/irqchip/irq-loongson-eiointc.c @@ -359,6 +359,9 @@ static int __init acpi_cascade_irqdomain_init(void) if (r < 0) return r; + if (cpu_has_avecint) + return 0; + r = acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, pch_msi_parse_madt, 1); if (r < 0) return r; diff --git a/drivers/irqchip/irq-loongson-pch-msi.c b/drivers/irqchip/irq-loongson-pch-msi.c index dd4d699170f4..1926857f9a41 100644 --- a/drivers/irqchip/irq-loongson-pch-msi.c +++ b/drivers/irqchip/irq-loongson-pch-msi.c @@ -16,7 +16,6 @@ #include static int nr_pics; - struct pch_msi_data { struct mutex msi_map_lock; phys_addr_t doorbell; @@ -100,6 +99,17 @@ static struct irq_chip middle_irq_chip = { .irq_compose_msi_msg = pch_msi_compose_msi_msg, }; +static struct irq_chip pch_msi_irq_chip_v2 = { + .name = "MSI", + .irq_ack = irq_chip_ack_parent, +}; + +static struct msi_domain_info pch_msi_domain_info_v2 = { + .flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | + MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX, + .chip = &pch_msi_irq_chip_v2, +}; + static int pch_msi_parent_domain_alloc(struct irq_domain *domain, unsigned int virq, int hwirq) { @@ -268,6 +278,9 @@ struct fwnode_handle *get_pch_msi_handle(int pci_segment) { int i; + if (cpu_has_avecint) + return pch_msi_handle[0]; + for (i = 0; i < MAX_IO_PICS; i++) { if (msi_group[i].pci_segment == pci_segment) return pch_msi_handle[i]; @@ -289,4 +302,32 @@ int __init pch_msi_acpi_init(struct irq_domain *parent, return ret; } + +int __init pch_msi_acpi_init_v2(struct irq_domain *parent, + struct acpi_madt_msi_pic *msi_entry) +{ + struct irq_domain *msi_domain; + + if (pch_msi_handle[0]) + return 0; + + pch_msi_handle[0] = irq_domain_alloc_named_fwnode("msipic-v2"); + if (!pch_msi_handle[0]) { + pr_err("Unable to allocate domain handle\n"); + kfree(pch_msi_handle[0]); + return -ENOMEM; + } + + msi_domain = pci_msi_create_irq_domain(pch_msi_handle[0], + &pch_msi_domain_info_v2, + parent); + if (!msi_domain) { + pr_err("Failed to create PCI MSI domain\n"); + kfree(pch_msi_handle[0]); + return -ENOMEM; + } + + pr_info("IRQ domain MSIPIC-V2 init done.\n"); + return 0; +} #endif -- cgit v1.2.3 From ef7080bd30bab81a1c4dd7c0afd942d2ab43081d Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Mon, 3 Jun 2024 12:56:52 +0000 Subject: irqchip/riscv-aplic: Simplify the initialization code The initialization code has an is_of_node() check and invokes to_of_node() for every of_property_*() invocation. to_of_node() has a is_of_node() check already, so simplify the code by invoking to_of_node() and checking that for NULL. If not NULL hand in the node pointer to of_property_*(). The same applies to of_property_*() which fails when invoked with a NULL node pointer. [ tglx: Massaged change log ] Signed-off-by: Jinjie Ruan Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/all/20240603125652.791601-1-ruanjinjie@huawei.com --- drivers/irqchip/irq-riscv-aplic-main.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-riscv-aplic-main.c b/drivers/irqchip/irq-riscv-aplic-main.c index 774a0c97fdab..28dd175b5764 100644 --- a/drivers/irqchip/irq-riscv-aplic-main.c +++ b/drivers/irqchip/irq-riscv-aplic-main.c @@ -127,6 +127,7 @@ static void aplic_init_hw_irqs(struct aplic_priv *priv) int aplic_setup_priv(struct aplic_priv *priv, struct device *dev, void __iomem *regs) { + struct device_node *np = to_of_node(dev->fwnode); struct of_phandle_args parent; int rc; @@ -134,7 +135,7 @@ int aplic_setup_priv(struct aplic_priv *priv, struct device *dev, void __iomem * * Currently, only OF fwnode is supported so extend this * function for ACPI support. */ - if (!is_of_node(dev->fwnode)) + if (!np) return -EINVAL; /* Save device pointer and register base */ @@ -142,8 +143,7 @@ int aplic_setup_priv(struct aplic_priv *priv, struct device *dev, void __iomem * priv->regs = regs; /* Find out number of interrupt sources */ - rc = of_property_read_u32(to_of_node(dev->fwnode), "riscv,num-sources", - &priv->nr_irqs); + rc = of_property_read_u32(np, "riscv,num-sources", &priv->nr_irqs); if (rc) { dev_err(dev, "failed to get number of interrupt sources\n"); return rc; @@ -155,8 +155,8 @@ int aplic_setup_priv(struct aplic_priv *priv, struct device *dev, void __iomem * * If "msi-parent" property is present then we ignore the * APLIC IDCs which forces the APLIC driver to use MSI mode. */ - if (!of_property_present(to_of_node(dev->fwnode), "msi-parent")) { - while (!of_irq_parse_one(to_of_node(dev->fwnode), priv->nr_idcs, &parent)) + if (!of_property_present(np, "msi-parent")) { + while (!of_irq_parse_one(np, priv->nr_idcs, &parent)) priv->nr_idcs++; } @@ -184,8 +184,7 @@ static int aplic_probe(struct platform_device *pdev) * If msi-parent property is present then setup APLIC MSI * mode otherwise setup APLIC direct mode. */ - if (is_of_node(dev->fwnode)) - msi_mode = of_property_present(to_of_node(dev->fwnode), "msi-parent"); + msi_mode = of_property_present(to_of_node(dev->fwnode), "msi-parent"); if (msi_mode) rc = aplic_msi_setup(dev, regs); else -- cgit v1.2.3 From 7cc4f309c933ec5d64eea31066fe86bbf9e48819 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Fri, 14 Jun 2024 23:34:49 +0800 Subject: irqchip/dw-apb-ictl: Support building as module The driver is now always built in. In some synaptics ARM64 SoCs it is used as a second level interrupt controller hanging off the ARM GIC and is therefore loadable during boot. Enable it to be built as a module and handle built-in usage correctly, so that it continues working on systems where it is the main interrupt controller. [ tglx: Massage changelog ] Signed-off-by: Jisheng Zhang Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/r/20240614153449.2083-1-jszhang@kernel.org --- drivers/irqchip/Kconfig | 2 +- drivers/irqchip/irq-dw-apb-ictl.c | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 348f34525d23..aaf84530386a 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -145,7 +145,7 @@ config DAVINCI_CP_INTC select IRQ_DOMAIN config DW_APB_ICTL - bool + tristate "DesignWare APB Interrupt Controller" select GENERIC_IRQ_CHIP select IRQ_DOMAIN_HIERARCHY diff --git a/drivers/irqchip/irq-dw-apb-ictl.c b/drivers/irqchip/irq-dw-apb-ictl.c index d5c1c750c8d2..5eda6c4689cf 100644 --- a/drivers/irqchip/irq-dw-apb-ictl.c +++ b/drivers/irqchip/irq-dw-apb-ictl.c @@ -122,7 +122,7 @@ static int __init dw_apb_ictl_init(struct device_node *np, int ret, nrirqs, parent_irq, i; u32 reg; - if (!parent) { + if (!parent && IS_BUILTIN(CONFIG_DW_APB_ICTL)) { /* Used as the primary interrupt controller */ parent_irq = 0; domain_ops = &dw_apb_ictl_irq_domain_ops; @@ -214,5 +214,12 @@ err_release: release_mem_region(r.start, resource_size(&r)); return ret; } -IRQCHIP_DECLARE(dw_apb_ictl, - "snps,dw-apb-ictl", dw_apb_ictl_init); +#if IS_BUILTIN(CONFIG_DW_APB_ICTL) +IRQCHIP_DECLARE(dw_apb_ictl, "snps,dw-apb-ictl", dw_apb_ictl_init); +#else +IRQCHIP_PLATFORM_DRIVER_BEGIN(dw_apb_ictl) +IRQCHIP_MATCH("snps,dw-apb-ictl", dw_apb_ictl_init) +IRQCHIP_PLATFORM_DRIVER_END(dw_apb_ictl) +MODULE_DESCRIPTION("DesignWare APB Interrupt Controller"); +MODULE_LICENSE("GPL v2"); +#endif -- cgit v1.2.3 From b20cf2dcbe8b77afb4fcbe7af9349dfca6b7f22a Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Thu, 20 Jun 2024 10:31:08 +0200 Subject: irqchip/stm32-exti: Add CONFIG_STM32MP_EXTI To guarantee bisect-ability during the split of stm32-exti in MCU and MPU code, introduce CONFIG_STM32MP_EXTI. Signed-off-by: Antonio Borneo Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/r/20240620083115.204362-2-antonio.borneo@foss.st.com --- drivers/irqchip/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index aaf84530386a..bc5e19177280 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -404,6 +404,10 @@ config LS_SCFG_MSI config PARTITION_PERCPU bool +config STM32MP_EXTI + bool + select STM32_EXTI + config STM32_EXTI bool select IRQ_DOMAIN -- cgit v1.2.3 From 350755e2e548ccbe941d045900b57233efa906cb Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Thu, 20 Jun 2024 10:31:11 +0200 Subject: irqchip/stm32-exti: Split MCU and MPU code Keep only the code for ARMv7m STM32 MCUs in in stm32-exti.c and split out the code for ARMv7a & ARMv8a STM32MPxxx MPUs into stm32mp-exti.c Signed-off-by: Antonio Borneo Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/r/20240620083115.204362-5-antonio.borneo@foss.st.com --- drivers/irqchip/Kconfig | 3 +- drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-stm32-exti.c | 670 +-------------------------------- drivers/irqchip/irq-stm32mp-exti.c | 744 +++++++++++++++++++++++++++++++++++++ 4 files changed, 751 insertions(+), 667 deletions(-) create mode 100644 drivers/irqchip/irq-stm32mp-exti.c (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index bc5e19177280..978639d10d75 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -406,7 +406,8 @@ config PARTITION_PERCPU config STM32MP_EXTI bool - select STM32_EXTI + select IRQ_DOMAIN + select GENERIC_IRQ_CHIP config STM32_EXTI bool diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 1062e713cea4..de091a9f7c88 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -84,6 +84,7 @@ obj-$(CONFIG_MVEBU_SEI) += irq-mvebu-sei.o obj-$(CONFIG_LS_EXTIRQ) += irq-ls-extirq.o obj-$(CONFIG_LS_SCFG_MSI) += irq-ls-scfg-msi.o obj-$(CONFIG_ARCH_ASPEED) += irq-aspeed-vic.o irq-aspeed-i2c-ic.o irq-aspeed-scu-ic.o +obj-$(CONFIG_STM32MP_EXTI) += irq-stm32mp-exti.o obj-$(CONFIG_STM32_EXTI) += irq-stm32-exti.o obj-$(CONFIG_QCOM_IRQ_COMBINER) += qcom-irq-combiner.o obj-$(CONFIG_IRQ_UNIPHIER_AIDET) += irq-uniphier-aidet.o diff --git a/drivers/irqchip/irq-stm32-exti.c b/drivers/irqchip/irq-stm32-exti.c index 2cc9f3b7d669..7c6a0080c330 100644 --- a/drivers/irqchip/irq-stm32-exti.c +++ b/drivers/irqchip/irq-stm32-exti.c @@ -1,45 +1,22 @@ // SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) Maxime Coquelin 2015 - * Copyright (C) STMicroelectronics 2017 + * Copyright (C) STMicroelectronics 2017-2024 * Author: Maxime Coquelin */ #include -#include -#include #include #include #include #include #include #include -#include -#include #include #include -#include -#include - -#include #define IRQS_PER_BANK 32 -#define HWSPNLCK_TIMEOUT 1000 /* usec */ - -#define EXTI_EnCIDCFGR(n) (0x180 + (n) * 4) -#define EXTI_HWCFGR1 0x3f0 - -/* Register: EXTI_EnCIDCFGR(n) */ -#define EXTI_CIDCFGR_CFEN_MASK BIT(0) -#define EXTI_CIDCFGR_CID_MASK GENMASK(6, 4) -#define EXTI_CIDCFGR_CID_SHIFT 4 - -/* Register: EXTI_HWCFGR1 */ -#define EXTI_HWCFGR1_CIDWIDTH_MASK GENMASK(27, 24) - -#define EXTI_CID1 1 - struct stm32_exti_bank { u32 imr_ofst; u32 emr_ofst; @@ -47,13 +24,8 @@ struct stm32_exti_bank { u32 ftsr_ofst; u32 swier_ofst; u32 rpr_ofst; - u32 fpr_ofst; - u32 trg_ofst; - u32 seccfgr_ofst; }; -#define UNDEF_REG ~0 - struct stm32_exti_drv_data { const struct stm32_exti_bank **exti_banks; const u8 *desc_irqs; @@ -63,7 +35,6 @@ struct stm32_exti_drv_data { struct stm32_exti_chip_data { struct stm32_exti_host_data *host_data; const struct stm32_exti_bank *reg_bank; - struct raw_spinlock rlock; u32 wake_active; u32 mask_cache; u32 rtsr_cache; @@ -76,8 +47,6 @@ struct stm32_exti_host_data { struct device *dev; struct stm32_exti_chip_data *chips_data; const struct stm32_exti_drv_data *drv_data; - struct hwspinlock *hwlock; - bool dt_has_irqs_desc; /* skip internal desc_irqs array and get it from DT */ }; static const struct stm32_exti_bank stm32f4xx_exti_b1 = { @@ -87,9 +56,6 @@ static const struct stm32_exti_bank stm32f4xx_exti_b1 = { .ftsr_ofst = 0x0C, .swier_ofst = 0x10, .rpr_ofst = 0x14, - .fpr_ofst = UNDEF_REG, - .trg_ofst = UNDEF_REG, - .seccfgr_ofst = UNDEF_REG, }; static const struct stm32_exti_bank *stm32f4xx_exti_banks[] = { @@ -108,9 +74,6 @@ static const struct stm32_exti_bank stm32h7xx_exti_b1 = { .ftsr_ofst = 0x04, .swier_ofst = 0x08, .rpr_ofst = 0x88, - .fpr_ofst = UNDEF_REG, - .trg_ofst = UNDEF_REG, - .seccfgr_ofst = UNDEF_REG, }; static const struct stm32_exti_bank stm32h7xx_exti_b2 = { @@ -120,9 +83,6 @@ static const struct stm32_exti_bank stm32h7xx_exti_b2 = { .ftsr_ofst = 0x24, .swier_ofst = 0x28, .rpr_ofst = 0x98, - .fpr_ofst = UNDEF_REG, - .trg_ofst = UNDEF_REG, - .seccfgr_ofst = UNDEF_REG, }; static const struct stm32_exti_bank stm32h7xx_exti_b3 = { @@ -132,9 +92,6 @@ static const struct stm32_exti_bank stm32h7xx_exti_b3 = { .ftsr_ofst = 0x44, .swier_ofst = 0x48, .rpr_ofst = 0xA8, - .fpr_ofst = UNDEF_REG, - .trg_ofst = UNDEF_REG, - .seccfgr_ofst = UNDEF_REG, }; static const struct stm32_exti_bank *stm32h7xx_exti_banks[] = { @@ -148,183 +105,12 @@ static const struct stm32_exti_drv_data stm32h7xx_drv_data = { .bank_nr = ARRAY_SIZE(stm32h7xx_exti_banks), }; -static const struct stm32_exti_bank stm32mp1_exti_b1 = { - .imr_ofst = 0x80, - .emr_ofst = UNDEF_REG, - .rtsr_ofst = 0x00, - .ftsr_ofst = 0x04, - .swier_ofst = 0x08, - .rpr_ofst = 0x0C, - .fpr_ofst = 0x10, - .trg_ofst = 0x3EC, - .seccfgr_ofst = 0x14, -}; - -static const struct stm32_exti_bank stm32mp1_exti_b2 = { - .imr_ofst = 0x90, - .emr_ofst = UNDEF_REG, - .rtsr_ofst = 0x20, - .ftsr_ofst = 0x24, - .swier_ofst = 0x28, - .rpr_ofst = 0x2C, - .fpr_ofst = 0x30, - .trg_ofst = 0x3E8, - .seccfgr_ofst = 0x34, -}; - -static const struct stm32_exti_bank stm32mp1_exti_b3 = { - .imr_ofst = 0xA0, - .emr_ofst = UNDEF_REG, - .rtsr_ofst = 0x40, - .ftsr_ofst = 0x44, - .swier_ofst = 0x48, - .rpr_ofst = 0x4C, - .fpr_ofst = 0x50, - .trg_ofst = 0x3E4, - .seccfgr_ofst = 0x54, -}; - -static const struct stm32_exti_bank *stm32mp1_exti_banks[] = { - &stm32mp1_exti_b1, - &stm32mp1_exti_b2, - &stm32mp1_exti_b3, -}; - -static struct irq_chip stm32_exti_h_chip; -static struct irq_chip stm32_exti_h_chip_direct; - -#define EXTI_INVALID_IRQ U8_MAX -#define STM32MP1_DESC_IRQ_SIZE (ARRAY_SIZE(stm32mp1_exti_banks) * IRQS_PER_BANK) - -/* - * Use some intentionally tricky logic here to initialize the whole array to - * EXTI_INVALID_IRQ, but then override certain fields, requiring us to indicate - * that we "know" that there are overrides in this structure, and we'll need to - * disable that warning from W=1 builds. - */ -__diag_push(); -__diag_ignore_all("-Woverride-init", - "logic to initialize all and then override some is OK"); - -static const u8 stm32mp1_desc_irq[] = { - /* default value */ - [0 ... (STM32MP1_DESC_IRQ_SIZE - 1)] = EXTI_INVALID_IRQ, - - [0] = 6, - [1] = 7, - [2] = 8, - [3] = 9, - [4] = 10, - [5] = 23, - [6] = 64, - [7] = 65, - [8] = 66, - [9] = 67, - [10] = 40, - [11] = 42, - [12] = 76, - [13] = 77, - [14] = 121, - [15] = 127, - [16] = 1, - [19] = 3, - [21] = 31, - [22] = 33, - [23] = 72, - [24] = 95, - [25] = 107, - [26] = 37, - [27] = 38, - [28] = 39, - [29] = 71, - [30] = 52, - [31] = 53, - [32] = 82, - [33] = 83, - [46] = 151, - [47] = 93, - [48] = 138, - [50] = 139, - [52] = 140, - [53] = 141, - [54] = 135, - [61] = 100, - [65] = 144, - [68] = 143, - [70] = 62, - [73] = 129, -}; - -static const u8 stm32mp13_desc_irq[] = { - /* default value */ - [0 ... (STM32MP1_DESC_IRQ_SIZE - 1)] = EXTI_INVALID_IRQ, - - [0] = 6, - [1] = 7, - [2] = 8, - [3] = 9, - [4] = 10, - [5] = 24, - [6] = 65, - [7] = 66, - [8] = 67, - [9] = 68, - [10] = 41, - [11] = 43, - [12] = 77, - [13] = 78, - [14] = 106, - [15] = 109, - [16] = 1, - [19] = 3, - [21] = 32, - [22] = 34, - [23] = 73, - [24] = 93, - [25] = 114, - [26] = 38, - [27] = 39, - [28] = 40, - [29] = 72, - [30] = 53, - [31] = 54, - [32] = 83, - [33] = 84, - [44] = 96, - [47] = 92, - [48] = 116, - [50] = 117, - [52] = 118, - [53] = 119, - [68] = 63, - [70] = 98, -}; - -__diag_pop(); - -static const struct stm32_exti_drv_data stm32mp1_drv_data = { - .exti_banks = stm32mp1_exti_banks, - .bank_nr = ARRAY_SIZE(stm32mp1_exti_banks), - .desc_irqs = stm32mp1_desc_irq, -}; - -static const struct stm32_exti_drv_data stm32mp13_drv_data = { - .exti_banks = stm32mp1_exti_banks, - .bank_nr = ARRAY_SIZE(stm32mp1_exti_banks), - .desc_irqs = stm32mp13_desc_irq, -}; - static unsigned long stm32_exti_pending(struct irq_chip_generic *gc) { struct stm32_exti_chip_data *chip_data = gc->private; const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; - unsigned long pending; - pending = irq_reg_readl(gc, stm32_bank->rpr_ofst); - if (stm32_bank->fpr_ofst != UNDEF_REG) - pending |= irq_reg_readl(gc, stm32_bank->fpr_ofst); - - return pending; + return irq_reg_readl(gc, stm32_bank->rpr_ofst); } static void stm32_irq_handler(struct irq_desc *desc) @@ -380,33 +166,21 @@ static int stm32_irq_set_type(struct irq_data *d, unsigned int type) struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); struct stm32_exti_chip_data *chip_data = gc->private; const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; - struct hwspinlock *hwlock = chip_data->host_data->hwlock; u32 rtsr, ftsr; int err; irq_gc_lock(gc); - if (hwlock) { - err = hwspin_lock_timeout_in_atomic(hwlock, HWSPNLCK_TIMEOUT); - if (err) { - pr_err("%s can't get hwspinlock (%d)\n", __func__, err); - goto unlock; - } - } - rtsr = irq_reg_readl(gc, stm32_bank->rtsr_ofst); ftsr = irq_reg_readl(gc, stm32_bank->ftsr_ofst); err = stm32_exti_set_type(d, type, &rtsr, &ftsr); if (err) - goto unspinlock; + goto unlock; irq_reg_writel(gc, rtsr, stm32_bank->rtsr_ofst); irq_reg_writel(gc, ftsr, stm32_bank->ftsr_ofst); -unspinlock: - if (hwlock) - hwspin_unlock_in_atomic(hwlock); unlock: irq_gc_unlock(gc); @@ -494,287 +268,10 @@ static void stm32_irq_ack(struct irq_data *d) irq_gc_lock(gc); irq_reg_writel(gc, d->mask, stm32_bank->rpr_ofst); - if (stm32_bank->fpr_ofst != UNDEF_REG) - irq_reg_writel(gc, d->mask, stm32_bank->fpr_ofst); irq_gc_unlock(gc); } -/* directly set the target bit without reading first. */ -static inline void stm32_exti_write_bit(struct irq_data *d, u32 reg) -{ - struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); - void __iomem *base = chip_data->host_data->base; - u32 val = BIT(d->hwirq % IRQS_PER_BANK); - - writel_relaxed(val, base + reg); -} - -static inline u32 stm32_exti_set_bit(struct irq_data *d, u32 reg) -{ - struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); - void __iomem *base = chip_data->host_data->base; - u32 val; - - val = readl_relaxed(base + reg); - val |= BIT(d->hwirq % IRQS_PER_BANK); - writel_relaxed(val, base + reg); - - return val; -} - -static inline u32 stm32_exti_clr_bit(struct irq_data *d, u32 reg) -{ - struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); - void __iomem *base = chip_data->host_data->base; - u32 val; - - val = readl_relaxed(base + reg); - val &= ~BIT(d->hwirq % IRQS_PER_BANK); - writel_relaxed(val, base + reg); - - return val; -} - -static void stm32_exti_h_eoi(struct irq_data *d) -{ - struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); - const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; - - raw_spin_lock(&chip_data->rlock); - - stm32_exti_write_bit(d, stm32_bank->rpr_ofst); - if (stm32_bank->fpr_ofst != UNDEF_REG) - stm32_exti_write_bit(d, stm32_bank->fpr_ofst); - - raw_spin_unlock(&chip_data->rlock); - - if (d->parent_data->chip) - irq_chip_eoi_parent(d); -} - -static void stm32_exti_h_mask(struct irq_data *d) -{ - struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); - const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; - - raw_spin_lock(&chip_data->rlock); - chip_data->mask_cache = stm32_exti_clr_bit(d, stm32_bank->imr_ofst); - raw_spin_unlock(&chip_data->rlock); - - if (d->parent_data->chip) - irq_chip_mask_parent(d); -} - -static void stm32_exti_h_unmask(struct irq_data *d) -{ - struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); - const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; - - raw_spin_lock(&chip_data->rlock); - chip_data->mask_cache = stm32_exti_set_bit(d, stm32_bank->imr_ofst); - raw_spin_unlock(&chip_data->rlock); - - if (d->parent_data->chip) - irq_chip_unmask_parent(d); -} - -static int stm32_exti_h_set_type(struct irq_data *d, unsigned int type) -{ - struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); - const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; - struct hwspinlock *hwlock = chip_data->host_data->hwlock; - void __iomem *base = chip_data->host_data->base; - u32 rtsr, ftsr; - int err; - - raw_spin_lock(&chip_data->rlock); - - if (hwlock) { - err = hwspin_lock_timeout_in_atomic(hwlock, HWSPNLCK_TIMEOUT); - if (err) { - pr_err("%s can't get hwspinlock (%d)\n", __func__, err); - goto unlock; - } - } - - rtsr = readl_relaxed(base + stm32_bank->rtsr_ofst); - ftsr = readl_relaxed(base + stm32_bank->ftsr_ofst); - - err = stm32_exti_set_type(d, type, &rtsr, &ftsr); - if (err) - goto unspinlock; - - writel_relaxed(rtsr, base + stm32_bank->rtsr_ofst); - writel_relaxed(ftsr, base + stm32_bank->ftsr_ofst); - -unspinlock: - if (hwlock) - hwspin_unlock_in_atomic(hwlock); -unlock: - raw_spin_unlock(&chip_data->rlock); - - return err; -} - -static int stm32_exti_h_set_wake(struct irq_data *d, unsigned int on) -{ - struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); - u32 mask = BIT(d->hwirq % IRQS_PER_BANK); - - raw_spin_lock(&chip_data->rlock); - - if (on) - chip_data->wake_active |= mask; - else - chip_data->wake_active &= ~mask; - - raw_spin_unlock(&chip_data->rlock); - - return 0; -} - -static int stm32_exti_h_set_affinity(struct irq_data *d, - const struct cpumask *dest, bool force) -{ - if (d->parent_data->chip) - return irq_chip_set_affinity_parent(d, dest, force); - - return IRQ_SET_MASK_OK_DONE; -} - -static int stm32_exti_h_suspend(struct device *dev) -{ - struct stm32_exti_host_data *host_data = dev_get_drvdata(dev); - struct stm32_exti_chip_data *chip_data; - int i; - - for (i = 0; i < host_data->drv_data->bank_nr; i++) { - chip_data = &host_data->chips_data[i]; - stm32_chip_suspend(chip_data, chip_data->wake_active); - } - - return 0; -} - -static int stm32_exti_h_resume(struct device *dev) -{ - struct stm32_exti_host_data *host_data = dev_get_drvdata(dev); - struct stm32_exti_chip_data *chip_data; - int i; - - for (i = 0; i < host_data->drv_data->bank_nr; i++) { - chip_data = &host_data->chips_data[i]; - stm32_chip_resume(chip_data, chip_data->mask_cache); - } - - return 0; -} - -static int stm32_exti_h_retrigger(struct irq_data *d) -{ - struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); - const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; - void __iomem *base = chip_data->host_data->base; - u32 mask = BIT(d->hwirq % IRQS_PER_BANK); - - writel_relaxed(mask, base + stm32_bank->swier_ofst); - - return 0; -} - -static struct irq_chip stm32_exti_h_chip = { - .name = "stm32-exti-h", - .irq_eoi = stm32_exti_h_eoi, - .irq_mask = stm32_exti_h_mask, - .irq_unmask = stm32_exti_h_unmask, - .irq_retrigger = stm32_exti_h_retrigger, - .irq_set_type = stm32_exti_h_set_type, - .irq_set_wake = stm32_exti_h_set_wake, - .flags = IRQCHIP_MASK_ON_SUSPEND, - .irq_set_affinity = IS_ENABLED(CONFIG_SMP) ? stm32_exti_h_set_affinity : NULL, -}; - -static struct irq_chip stm32_exti_h_chip_direct = { - .name = "stm32-exti-h-direct", - .irq_eoi = irq_chip_eoi_parent, - .irq_ack = irq_chip_ack_parent, - .irq_mask = stm32_exti_h_mask, - .irq_unmask = stm32_exti_h_unmask, - .irq_retrigger = irq_chip_retrigger_hierarchy, - .irq_set_type = irq_chip_set_type_parent, - .irq_set_wake = stm32_exti_h_set_wake, - .flags = IRQCHIP_MASK_ON_SUSPEND, - .irq_set_affinity = IS_ENABLED(CONFIG_SMP) ? irq_chip_set_affinity_parent : NULL, -}; - -static int stm32_exti_h_domain_alloc(struct irq_domain *dm, - unsigned int virq, - unsigned int nr_irqs, void *data) -{ - struct stm32_exti_host_data *host_data = dm->host_data; - struct stm32_exti_chip_data *chip_data; - u8 desc_irq; - struct irq_fwspec *fwspec = data; - struct irq_fwspec p_fwspec; - irq_hw_number_t hwirq; - int bank; - u32 event_trg; - struct irq_chip *chip; - - hwirq = fwspec->param[0]; - if (hwirq >= host_data->drv_data->bank_nr * IRQS_PER_BANK) - return -EINVAL; - - bank = hwirq / IRQS_PER_BANK; - chip_data = &host_data->chips_data[bank]; - - /* Check if event is reserved (Secure) */ - if (chip_data->event_reserved & BIT(hwirq % IRQS_PER_BANK)) { - dev_err(host_data->dev, "event %lu is reserved, secure\n", hwirq); - return -EPERM; - } - - event_trg = readl_relaxed(host_data->base + chip_data->reg_bank->trg_ofst); - chip = (event_trg & BIT(hwirq % IRQS_PER_BANK)) ? - &stm32_exti_h_chip : &stm32_exti_h_chip_direct; - - irq_domain_set_hwirq_and_chip(dm, virq, hwirq, chip, chip_data); - - if (host_data->dt_has_irqs_desc) { - struct of_phandle_args out_irq; - int ret; - - ret = of_irq_parse_one(host_data->dev->of_node, hwirq, &out_irq); - if (ret) - return ret; - /* we only support one parent, so far */ - if (of_node_to_fwnode(out_irq.np) != dm->parent->fwnode) - return -EINVAL; - - of_phandle_args_to_fwspec(out_irq.np, out_irq.args, - out_irq.args_count, &p_fwspec); - - return irq_domain_alloc_irqs_parent(dm, virq, 1, &p_fwspec); - } - - if (!host_data->drv_data->desc_irqs) - return -EINVAL; - - desc_irq = host_data->drv_data->desc_irqs[hwirq]; - if (desc_irq != EXTI_INVALID_IRQ) { - p_fwspec.fwnode = dm->parent->fwnode; - p_fwspec.param_count = 3; - p_fwspec.param[0] = GIC_SPI; - p_fwspec.param[1] = desc_irq; - p_fwspec.param[2] = IRQ_TYPE_LEVEL_HIGH; - - return irq_domain_alloc_irqs_parent(dm, virq, 1, &p_fwspec); - } - - return 0; -} - static struct stm32_exti_host_data *stm32_exti_host_init(const struct stm32_exti_drv_data *dd, struct device_node *node) @@ -822,19 +319,12 @@ stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data, chip_data->host_data = h_data; chip_data->reg_bank = stm32_bank; - raw_spin_lock_init(&chip_data->rlock); - /* * This IP has no reset, so after hot reboot we should * clear registers to avoid residue */ writel_relaxed(0, base + stm32_bank->imr_ofst); - if (stm32_bank->emr_ofst != UNDEF_REG) - writel_relaxed(0, base + stm32_bank->emr_ofst); - - /* reserve Secure events */ - if (stm32_bank->seccfgr_ofst != UNDEF_REG) - chip_data->event_reserved = readl_relaxed(base + stm32_bank->seccfgr_ofst); + writel_relaxed(0, base + stm32_bank->emr_ofst); pr_info("%pOF: bank%d\n", node, bank_idx); @@ -914,158 +404,6 @@ out_unmap: return ret; } -static const struct irq_domain_ops stm32_exti_h_domain_ops = { - .alloc = stm32_exti_h_domain_alloc, - .free = irq_domain_free_irqs_common, - .xlate = irq_domain_xlate_twocell, -}; - -static void stm32_exti_check_rif(struct stm32_exti_host_data *host_data) -{ - unsigned int bank, i, event; - u32 cid, cidcfgr, hwcfgr1; - - /* quit on CID not supported */ - hwcfgr1 = readl_relaxed(host_data->base + EXTI_HWCFGR1); - if ((hwcfgr1 & EXTI_HWCFGR1_CIDWIDTH_MASK) == 0) - return; - - for (bank = 0; bank < host_data->drv_data->bank_nr; bank++) { - for (i = 0; i < IRQS_PER_BANK; i++) { - event = bank * IRQS_PER_BANK + i; - cidcfgr = readl_relaxed(host_data->base + EXTI_EnCIDCFGR(event)); - cid = (cidcfgr & EXTI_CIDCFGR_CID_MASK) >> EXTI_CIDCFGR_CID_SHIFT; - if ((cidcfgr & EXTI_CIDCFGR_CFEN_MASK) && cid != EXTI_CID1) - host_data->chips_data[bank].event_reserved |= BIT(i); - } - } -} - -static void stm32_exti_remove_irq(void *data) -{ - struct irq_domain *domain = data; - - irq_domain_remove(domain); -} - -static int stm32_exti_probe(struct platform_device *pdev) -{ - int ret, i; - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - struct irq_domain *parent_domain, *domain; - struct stm32_exti_host_data *host_data; - const struct stm32_exti_drv_data *drv_data; - - host_data = devm_kzalloc(dev, sizeof(*host_data), GFP_KERNEL); - if (!host_data) - return -ENOMEM; - - dev_set_drvdata(dev, host_data); - host_data->dev = dev; - - /* check for optional hwspinlock which may be not available yet */ - ret = of_hwspin_lock_get_id(np, 0); - if (ret == -EPROBE_DEFER) - /* hwspinlock framework not yet ready */ - return ret; - - if (ret >= 0) { - host_data->hwlock = devm_hwspin_lock_request_specific(dev, ret); - if (!host_data->hwlock) { - dev_err(dev, "Failed to request hwspinlock\n"); - return -EINVAL; - } - } else if (ret != -ENOENT) { - /* note: ENOENT is a valid case (means 'no hwspinlock') */ - dev_err(dev, "Failed to get hwspinlock\n"); - return ret; - } - - /* initialize host_data */ - drv_data = of_device_get_match_data(dev); - if (!drv_data) { - dev_err(dev, "no of match data\n"); - return -ENODEV; - } - host_data->drv_data = drv_data; - - host_data->chips_data = devm_kcalloc(dev, drv_data->bank_nr, - sizeof(*host_data->chips_data), - GFP_KERNEL); - if (!host_data->chips_data) - return -ENOMEM; - - host_data->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(host_data->base)) - return PTR_ERR(host_data->base); - - for (i = 0; i < drv_data->bank_nr; i++) - stm32_exti_chip_init(host_data, i, np); - - stm32_exti_check_rif(host_data); - - parent_domain = irq_find_host(of_irq_find_parent(np)); - if (!parent_domain) { - dev_err(dev, "GIC interrupt-parent not found\n"); - return -EINVAL; - } - - domain = irq_domain_add_hierarchy(parent_domain, 0, - drv_data->bank_nr * IRQS_PER_BANK, - np, &stm32_exti_h_domain_ops, - host_data); - - if (!domain) { - dev_err(dev, "Could not register exti domain\n"); - return -ENOMEM; - } - - ret = devm_add_action_or_reset(dev, stm32_exti_remove_irq, domain); - if (ret) - return ret; - - if (of_property_read_bool(np, "interrupts-extended")) - host_data->dt_has_irqs_desc = true; - - return 0; -} - -/* platform driver only for MP1 */ -static const struct of_device_id stm32_exti_ids[] = { - { .compatible = "st,stm32mp1-exti", .data = &stm32mp1_drv_data}, - { .compatible = "st,stm32mp13-exti", .data = &stm32mp13_drv_data}, - {}, -}; -MODULE_DEVICE_TABLE(of, stm32_exti_ids); - -static const struct dev_pm_ops stm32_exti_dev_pm_ops = { - NOIRQ_SYSTEM_SLEEP_PM_OPS(stm32_exti_h_suspend, stm32_exti_h_resume) -}; - -static struct platform_driver stm32_exti_driver = { - .probe = stm32_exti_probe, - .driver = { - .name = "stm32_exti", - .of_match_table = stm32_exti_ids, - .pm = &stm32_exti_dev_pm_ops, - }, -}; - -static int __init stm32_exti_arch_init(void) -{ - return platform_driver_register(&stm32_exti_driver); -} - -static void __exit stm32_exti_arch_exit(void) -{ - return platform_driver_unregister(&stm32_exti_driver); -} - -arch_initcall(stm32_exti_arch_init); -module_exit(stm32_exti_arch_exit); - -/* no platform driver for F4 and H7 */ static int __init stm32f4_exti_of_init(struct device_node *np, struct device_node *parent) { diff --git a/drivers/irqchip/irq-stm32mp-exti.c b/drivers/irqchip/irq-stm32mp-exti.c new file mode 100644 index 000000000000..8a45ece2e198 --- /dev/null +++ b/drivers/irqchip/irq-stm32mp-exti.c @@ -0,0 +1,744 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Maxime Coquelin 2015 + * Copyright (C) STMicroelectronics 2017-2024 + * Author: Maxime Coquelin + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define IRQS_PER_BANK 32 + +#define HWSPNLCK_TIMEOUT 1000 /* usec */ + +#define EXTI_EnCIDCFGR(n) (0x180 + (n) * 4) +#define EXTI_HWCFGR1 0x3f0 + +/* Register: EXTI_EnCIDCFGR(n) */ +#define EXTI_CIDCFGR_CFEN_MASK BIT(0) +#define EXTI_CIDCFGR_CID_MASK GENMASK(6, 4) +#define EXTI_CIDCFGR_CID_SHIFT 4 + +/* Register: EXTI_HWCFGR1 */ +#define EXTI_HWCFGR1_CIDWIDTH_MASK GENMASK(27, 24) + +#define EXTI_CID1 1 + +struct stm32_exti_bank { + u32 imr_ofst; + u32 rtsr_ofst; + u32 ftsr_ofst; + u32 swier_ofst; + u32 rpr_ofst; + u32 fpr_ofst; + u32 trg_ofst; + u32 seccfgr_ofst; +}; + +struct stm32_exti_drv_data { + const struct stm32_exti_bank **exti_banks; + const u8 *desc_irqs; + u32 bank_nr; +}; + +struct stm32_exti_chip_data { + struct stm32_exti_host_data *host_data; + const struct stm32_exti_bank *reg_bank; + struct raw_spinlock rlock; + u32 wake_active; + u32 mask_cache; + u32 rtsr_cache; + u32 ftsr_cache; + u32 event_reserved; +}; + +struct stm32_exti_host_data { + void __iomem *base; + struct device *dev; + struct stm32_exti_chip_data *chips_data; + const struct stm32_exti_drv_data *drv_data; + struct hwspinlock *hwlock; + bool dt_has_irqs_desc; /* skip internal desc_irqs array and get it from DT */ +}; + +static const struct stm32_exti_bank stm32mp1_exti_b1 = { + .imr_ofst = 0x80, + .rtsr_ofst = 0x00, + .ftsr_ofst = 0x04, + .swier_ofst = 0x08, + .rpr_ofst = 0x0C, + .fpr_ofst = 0x10, + .trg_ofst = 0x3EC, + .seccfgr_ofst = 0x14, +}; + +static const struct stm32_exti_bank stm32mp1_exti_b2 = { + .imr_ofst = 0x90, + .rtsr_ofst = 0x20, + .ftsr_ofst = 0x24, + .swier_ofst = 0x28, + .rpr_ofst = 0x2C, + .fpr_ofst = 0x30, + .trg_ofst = 0x3E8, + .seccfgr_ofst = 0x34, +}; + +static const struct stm32_exti_bank stm32mp1_exti_b3 = { + .imr_ofst = 0xA0, + .rtsr_ofst = 0x40, + .ftsr_ofst = 0x44, + .swier_ofst = 0x48, + .rpr_ofst = 0x4C, + .fpr_ofst = 0x50, + .trg_ofst = 0x3E4, + .seccfgr_ofst = 0x54, +}; + +static const struct stm32_exti_bank *stm32mp1_exti_banks[] = { + &stm32mp1_exti_b1, + &stm32mp1_exti_b2, + &stm32mp1_exti_b3, +}; + +static struct irq_chip stm32_exti_h_chip; +static struct irq_chip stm32_exti_h_chip_direct; + +#define EXTI_INVALID_IRQ U8_MAX +#define STM32MP1_DESC_IRQ_SIZE (ARRAY_SIZE(stm32mp1_exti_banks) * IRQS_PER_BANK) + +/* + * Use some intentionally tricky logic here to initialize the whole array to + * EXTI_INVALID_IRQ, but then override certain fields, requiring us to indicate + * that we "know" that there are overrides in this structure, and we'll need to + * disable that warning from W=1 builds. + */ +__diag_push(); +__diag_ignore_all("-Woverride-init", + "logic to initialize all and then override some is OK"); + +static const u8 stm32mp1_desc_irq[] = { + /* default value */ + [0 ... (STM32MP1_DESC_IRQ_SIZE - 1)] = EXTI_INVALID_IRQ, + + [0] = 6, + [1] = 7, + [2] = 8, + [3] = 9, + [4] = 10, + [5] = 23, + [6] = 64, + [7] = 65, + [8] = 66, + [9] = 67, + [10] = 40, + [11] = 42, + [12] = 76, + [13] = 77, + [14] = 121, + [15] = 127, + [16] = 1, + [19] = 3, + [21] = 31, + [22] = 33, + [23] = 72, + [24] = 95, + [25] = 107, + [26] = 37, + [27] = 38, + [28] = 39, + [29] = 71, + [30] = 52, + [31] = 53, + [32] = 82, + [33] = 83, + [46] = 151, + [47] = 93, + [48] = 138, + [50] = 139, + [52] = 140, + [53] = 141, + [54] = 135, + [61] = 100, + [65] = 144, + [68] = 143, + [70] = 62, + [73] = 129, +}; + +static const u8 stm32mp13_desc_irq[] = { + /* default value */ + [0 ... (STM32MP1_DESC_IRQ_SIZE - 1)] = EXTI_INVALID_IRQ, + + [0] = 6, + [1] = 7, + [2] = 8, + [3] = 9, + [4] = 10, + [5] = 24, + [6] = 65, + [7] = 66, + [8] = 67, + [9] = 68, + [10] = 41, + [11] = 43, + [12] = 77, + [13] = 78, + [14] = 106, + [15] = 109, + [16] = 1, + [19] = 3, + [21] = 32, + [22] = 34, + [23] = 73, + [24] = 93, + [25] = 114, + [26] = 38, + [27] = 39, + [28] = 40, + [29] = 72, + [30] = 53, + [31] = 54, + [32] = 83, + [33] = 84, + [44] = 96, + [47] = 92, + [48] = 116, + [50] = 117, + [52] = 118, + [53] = 119, + [68] = 63, + [70] = 98, +}; + +__diag_pop(); + +static const struct stm32_exti_drv_data stm32mp1_drv_data = { + .exti_banks = stm32mp1_exti_banks, + .bank_nr = ARRAY_SIZE(stm32mp1_exti_banks), + .desc_irqs = stm32mp1_desc_irq, +}; + +static const struct stm32_exti_drv_data stm32mp13_drv_data = { + .exti_banks = stm32mp1_exti_banks, + .bank_nr = ARRAY_SIZE(stm32mp1_exti_banks), + .desc_irqs = stm32mp13_desc_irq, +}; + +static int stm32_exti_set_type(struct irq_data *d, + unsigned int type, u32 *rtsr, u32 *ftsr) +{ + u32 mask = BIT(d->hwirq % IRQS_PER_BANK); + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + *rtsr |= mask; + *ftsr &= ~mask; + break; + case IRQ_TYPE_EDGE_FALLING: + *rtsr &= ~mask; + *ftsr |= mask; + break; + case IRQ_TYPE_EDGE_BOTH: + *rtsr |= mask; + *ftsr |= mask; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void stm32_chip_suspend(struct stm32_exti_chip_data *chip_data, + u32 wake_active) +{ + const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; + void __iomem *base = chip_data->host_data->base; + + /* save rtsr, ftsr registers */ + chip_data->rtsr_cache = readl_relaxed(base + stm32_bank->rtsr_ofst); + chip_data->ftsr_cache = readl_relaxed(base + stm32_bank->ftsr_ofst); + + writel_relaxed(wake_active, base + stm32_bank->imr_ofst); +} + +static void stm32_chip_resume(struct stm32_exti_chip_data *chip_data, + u32 mask_cache) +{ + const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; + void __iomem *base = chip_data->host_data->base; + + /* restore rtsr, ftsr, registers */ + writel_relaxed(chip_data->rtsr_cache, base + stm32_bank->rtsr_ofst); + writel_relaxed(chip_data->ftsr_cache, base + stm32_bank->ftsr_ofst); + + writel_relaxed(mask_cache, base + stm32_bank->imr_ofst); +} + +/* directly set the target bit without reading first. */ +static inline void stm32_exti_write_bit(struct irq_data *d, u32 reg) +{ + struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); + void __iomem *base = chip_data->host_data->base; + u32 val = BIT(d->hwirq % IRQS_PER_BANK); + + writel_relaxed(val, base + reg); +} + +static inline u32 stm32_exti_set_bit(struct irq_data *d, u32 reg) +{ + struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); + void __iomem *base = chip_data->host_data->base; + u32 val; + + val = readl_relaxed(base + reg); + val |= BIT(d->hwirq % IRQS_PER_BANK); + writel_relaxed(val, base + reg); + + return val; +} + +static inline u32 stm32_exti_clr_bit(struct irq_data *d, u32 reg) +{ + struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); + void __iomem *base = chip_data->host_data->base; + u32 val; + + val = readl_relaxed(base + reg); + val &= ~BIT(d->hwirq % IRQS_PER_BANK); + writel_relaxed(val, base + reg); + + return val; +} + +static void stm32_exti_h_eoi(struct irq_data *d) +{ + struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); + const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; + + raw_spin_lock(&chip_data->rlock); + + stm32_exti_write_bit(d, stm32_bank->rpr_ofst); + stm32_exti_write_bit(d, stm32_bank->fpr_ofst); + + raw_spin_unlock(&chip_data->rlock); + + if (d->parent_data->chip) + irq_chip_eoi_parent(d); +} + +static void stm32_exti_h_mask(struct irq_data *d) +{ + struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); + const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; + + raw_spin_lock(&chip_data->rlock); + chip_data->mask_cache = stm32_exti_clr_bit(d, stm32_bank->imr_ofst); + raw_spin_unlock(&chip_data->rlock); + + if (d->parent_data->chip) + irq_chip_mask_parent(d); +} + +static void stm32_exti_h_unmask(struct irq_data *d) +{ + struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); + const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; + + raw_spin_lock(&chip_data->rlock); + chip_data->mask_cache = stm32_exti_set_bit(d, stm32_bank->imr_ofst); + raw_spin_unlock(&chip_data->rlock); + + if (d->parent_data->chip) + irq_chip_unmask_parent(d); +} + +static int stm32_exti_h_set_type(struct irq_data *d, unsigned int type) +{ + struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); + const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; + struct hwspinlock *hwlock = chip_data->host_data->hwlock; + void __iomem *base = chip_data->host_data->base; + u32 rtsr, ftsr; + int err; + + raw_spin_lock(&chip_data->rlock); + + if (hwlock) { + err = hwspin_lock_timeout_in_atomic(hwlock, HWSPNLCK_TIMEOUT); + if (err) { + pr_err("%s can't get hwspinlock (%d)\n", __func__, err); + goto unlock; + } + } + + rtsr = readl_relaxed(base + stm32_bank->rtsr_ofst); + ftsr = readl_relaxed(base + stm32_bank->ftsr_ofst); + + err = stm32_exti_set_type(d, type, &rtsr, &ftsr); + if (err) + goto unspinlock; + + writel_relaxed(rtsr, base + stm32_bank->rtsr_ofst); + writel_relaxed(ftsr, base + stm32_bank->ftsr_ofst); + +unspinlock: + if (hwlock) + hwspin_unlock_in_atomic(hwlock); +unlock: + raw_spin_unlock(&chip_data->rlock); + + return err; +} + +static int stm32_exti_h_set_wake(struct irq_data *d, unsigned int on) +{ + struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); + u32 mask = BIT(d->hwirq % IRQS_PER_BANK); + + raw_spin_lock(&chip_data->rlock); + + if (on) + chip_data->wake_active |= mask; + else + chip_data->wake_active &= ~mask; + + raw_spin_unlock(&chip_data->rlock); + + return 0; +} + +static int stm32_exti_h_set_affinity(struct irq_data *d, + const struct cpumask *dest, bool force) +{ + if (d->parent_data->chip) + return irq_chip_set_affinity_parent(d, dest, force); + + return IRQ_SET_MASK_OK_DONE; +} + +static int stm32_exti_h_suspend(struct device *dev) +{ + struct stm32_exti_host_data *host_data = dev_get_drvdata(dev); + struct stm32_exti_chip_data *chip_data; + int i; + + for (i = 0; i < host_data->drv_data->bank_nr; i++) { + chip_data = &host_data->chips_data[i]; + stm32_chip_suspend(chip_data, chip_data->wake_active); + } + + return 0; +} + +static int stm32_exti_h_resume(struct device *dev) +{ + struct stm32_exti_host_data *host_data = dev_get_drvdata(dev); + struct stm32_exti_chip_data *chip_data; + int i; + + for (i = 0; i < host_data->drv_data->bank_nr; i++) { + chip_data = &host_data->chips_data[i]; + stm32_chip_resume(chip_data, chip_data->mask_cache); + } + + return 0; +} + +static int stm32_exti_h_retrigger(struct irq_data *d) +{ + struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); + const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; + void __iomem *base = chip_data->host_data->base; + u32 mask = BIT(d->hwirq % IRQS_PER_BANK); + + writel_relaxed(mask, base + stm32_bank->swier_ofst); + + return 0; +} + +static struct irq_chip stm32_exti_h_chip = { + .name = "stm32-exti-h", + .irq_eoi = stm32_exti_h_eoi, + .irq_mask = stm32_exti_h_mask, + .irq_unmask = stm32_exti_h_unmask, + .irq_retrigger = stm32_exti_h_retrigger, + .irq_set_type = stm32_exti_h_set_type, + .irq_set_wake = stm32_exti_h_set_wake, + .flags = IRQCHIP_MASK_ON_SUSPEND, + .irq_set_affinity = IS_ENABLED(CONFIG_SMP) ? stm32_exti_h_set_affinity : NULL, +}; + +static struct irq_chip stm32_exti_h_chip_direct = { + .name = "stm32-exti-h-direct", + .irq_eoi = irq_chip_eoi_parent, + .irq_ack = irq_chip_ack_parent, + .irq_mask = stm32_exti_h_mask, + .irq_unmask = stm32_exti_h_unmask, + .irq_retrigger = irq_chip_retrigger_hierarchy, + .irq_set_type = irq_chip_set_type_parent, + .irq_set_wake = stm32_exti_h_set_wake, + .flags = IRQCHIP_MASK_ON_SUSPEND, + .irq_set_affinity = IS_ENABLED(CONFIG_SMP) ? irq_chip_set_affinity_parent : NULL, +}; + +static int stm32_exti_h_domain_alloc(struct irq_domain *dm, + unsigned int virq, + unsigned int nr_irqs, void *data) +{ + struct stm32_exti_host_data *host_data = dm->host_data; + struct stm32_exti_chip_data *chip_data; + u8 desc_irq; + struct irq_fwspec *fwspec = data; + struct irq_fwspec p_fwspec; + irq_hw_number_t hwirq; + int bank; + u32 event_trg; + struct irq_chip *chip; + + hwirq = fwspec->param[0]; + if (hwirq >= host_data->drv_data->bank_nr * IRQS_PER_BANK) + return -EINVAL; + + bank = hwirq / IRQS_PER_BANK; + chip_data = &host_data->chips_data[bank]; + + /* Check if event is reserved (Secure) */ + if (chip_data->event_reserved & BIT(hwirq % IRQS_PER_BANK)) { + dev_err(host_data->dev, "event %lu is reserved, secure\n", hwirq); + return -EPERM; + } + + event_trg = readl_relaxed(host_data->base + chip_data->reg_bank->trg_ofst); + chip = (event_trg & BIT(hwirq % IRQS_PER_BANK)) ? + &stm32_exti_h_chip : &stm32_exti_h_chip_direct; + + irq_domain_set_hwirq_and_chip(dm, virq, hwirq, chip, chip_data); + + if (host_data->dt_has_irqs_desc) { + struct of_phandle_args out_irq; + int ret; + + ret = of_irq_parse_one(host_data->dev->of_node, hwirq, &out_irq); + if (ret) + return ret; + /* we only support one parent, so far */ + if (of_node_to_fwnode(out_irq.np) != dm->parent->fwnode) + return -EINVAL; + + of_phandle_args_to_fwspec(out_irq.np, out_irq.args, + out_irq.args_count, &p_fwspec); + + return irq_domain_alloc_irqs_parent(dm, virq, 1, &p_fwspec); + } + + if (!host_data->drv_data->desc_irqs) + return -EINVAL; + + desc_irq = host_data->drv_data->desc_irqs[hwirq]; + if (desc_irq != EXTI_INVALID_IRQ) { + p_fwspec.fwnode = dm->parent->fwnode; + p_fwspec.param_count = 3; + p_fwspec.param[0] = GIC_SPI; + p_fwspec.param[1] = desc_irq; + p_fwspec.param[2] = IRQ_TYPE_LEVEL_HIGH; + + return irq_domain_alloc_irqs_parent(dm, virq, 1, &p_fwspec); + } + + return 0; +} + +static struct +stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data, + u32 bank_idx, + struct device_node *node) +{ + const struct stm32_exti_bank *stm32_bank; + struct stm32_exti_chip_data *chip_data; + void __iomem *base = h_data->base; + + stm32_bank = h_data->drv_data->exti_banks[bank_idx]; + chip_data = &h_data->chips_data[bank_idx]; + chip_data->host_data = h_data; + chip_data->reg_bank = stm32_bank; + + raw_spin_lock_init(&chip_data->rlock); + + /* + * This IP has no reset, so after hot reboot we should + * clear registers to avoid residue + */ + writel_relaxed(0, base + stm32_bank->imr_ofst); + + /* reserve Secure events */ + chip_data->event_reserved = readl_relaxed(base + stm32_bank->seccfgr_ofst); + + pr_info("%pOF: bank%d\n", node, bank_idx); + + return chip_data; +} + +static const struct irq_domain_ops stm32_exti_h_domain_ops = { + .alloc = stm32_exti_h_domain_alloc, + .free = irq_domain_free_irqs_common, + .xlate = irq_domain_xlate_twocell, +}; + +static void stm32_exti_check_rif(struct stm32_exti_host_data *host_data) +{ + unsigned int bank, i, event; + u32 cid, cidcfgr, hwcfgr1; + + /* quit on CID not supported */ + hwcfgr1 = readl_relaxed(host_data->base + EXTI_HWCFGR1); + if ((hwcfgr1 & EXTI_HWCFGR1_CIDWIDTH_MASK) == 0) + return; + + for (bank = 0; bank < host_data->drv_data->bank_nr; bank++) { + for (i = 0; i < IRQS_PER_BANK; i++) { + event = bank * IRQS_PER_BANK + i; + cidcfgr = readl_relaxed(host_data->base + EXTI_EnCIDCFGR(event)); + cid = (cidcfgr & EXTI_CIDCFGR_CID_MASK) >> EXTI_CIDCFGR_CID_SHIFT; + if ((cidcfgr & EXTI_CIDCFGR_CFEN_MASK) && cid != EXTI_CID1) + host_data->chips_data[bank].event_reserved |= BIT(i); + } + } +} + +static void stm32_exti_remove_irq(void *data) +{ + struct irq_domain *domain = data; + + irq_domain_remove(domain); +} + +static int stm32_exti_probe(struct platform_device *pdev) +{ + int ret, i; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct irq_domain *parent_domain, *domain; + struct stm32_exti_host_data *host_data; + const struct stm32_exti_drv_data *drv_data; + + host_data = devm_kzalloc(dev, sizeof(*host_data), GFP_KERNEL); + if (!host_data) + return -ENOMEM; + + dev_set_drvdata(dev, host_data); + host_data->dev = dev; + + /* check for optional hwspinlock which may be not available yet */ + ret = of_hwspin_lock_get_id(np, 0); + if (ret == -EPROBE_DEFER) + /* hwspinlock framework not yet ready */ + return ret; + + if (ret >= 0) { + host_data->hwlock = devm_hwspin_lock_request_specific(dev, ret); + if (!host_data->hwlock) { + dev_err(dev, "Failed to request hwspinlock\n"); + return -EINVAL; + } + } else if (ret != -ENOENT) { + /* note: ENOENT is a valid case (means 'no hwspinlock') */ + dev_err(dev, "Failed to get hwspinlock\n"); + return ret; + } + + /* initialize host_data */ + drv_data = of_device_get_match_data(dev); + if (!drv_data) { + dev_err(dev, "no of match data\n"); + return -ENODEV; + } + host_data->drv_data = drv_data; + + host_data->chips_data = devm_kcalloc(dev, drv_data->bank_nr, + sizeof(*host_data->chips_data), + GFP_KERNEL); + if (!host_data->chips_data) + return -ENOMEM; + + host_data->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(host_data->base)) + return PTR_ERR(host_data->base); + + for (i = 0; i < drv_data->bank_nr; i++) + stm32_exti_chip_init(host_data, i, np); + + stm32_exti_check_rif(host_data); + + parent_domain = irq_find_host(of_irq_find_parent(np)); + if (!parent_domain) { + dev_err(dev, "GIC interrupt-parent not found\n"); + return -EINVAL; + } + + domain = irq_domain_add_hierarchy(parent_domain, 0, + drv_data->bank_nr * IRQS_PER_BANK, + np, &stm32_exti_h_domain_ops, + host_data); + + if (!domain) { + dev_err(dev, "Could not register exti domain\n"); + return -ENOMEM; + } + + ret = devm_add_action_or_reset(dev, stm32_exti_remove_irq, domain); + if (ret) + return ret; + + if (of_property_read_bool(np, "interrupts-extended")) + host_data->dt_has_irqs_desc = true; + + return 0; +} + +static const struct of_device_id stm32_exti_ids[] = { + { .compatible = "st,stm32mp1-exti", .data = &stm32mp1_drv_data}, + { .compatible = "st,stm32mp13-exti", .data = &stm32mp13_drv_data}, + {}, +}; +MODULE_DEVICE_TABLE(of, stm32_exti_ids); + +static const struct dev_pm_ops stm32_exti_dev_pm_ops = { + NOIRQ_SYSTEM_SLEEP_PM_OPS(stm32_exti_h_suspend, stm32_exti_h_resume) +}; + +static struct platform_driver stm32_exti_driver = { + .probe = stm32_exti_probe, + .driver = { + .name = "stm32_exti", + .of_match_table = stm32_exti_ids, + .pm = &stm32_exti_dev_pm_ops, + }, +}; + +static int __init stm32_exti_arch_init(void) +{ + return platform_driver_register(&stm32_exti_driver); +} + +static void __exit stm32_exti_arch_exit(void) +{ + return platform_driver_unregister(&stm32_exti_driver); +} + +arch_initcall(stm32_exti_arch_init); +module_exit(stm32_exti_arch_exit); -- cgit v1.2.3 From c9d269469d2b9a06559cdc84d12dd3fb4d552581 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Thu, 20 Jun 2024 10:31:12 +0200 Subject: irqchip/stm32mp-exti: Rename internal symbols Rename all the internal symbols accordingly to the new name of the driver. Renaming done automatically through sed rules: s/stm32_exti_set_type/stm32mp_exti_convert_type/g s/stm32_exti_h_/stm32mp_exti_/g s/stm32_exti/stm32mp_exti/g s/stm32_bank/bank/g s/stm32_/stm32mp_/g s/STM32_/STM32MP_/g s/STM32MP1_/STM32MP_/g s/stm32mp1_exti_/stm32mp_exti_/g s/stm32-exti-h/stm32mp-exti/g Manually fix some indentation after the rename. [ tglx: Mop up more coding style issues while at it ] Signed-off-by: Antonio Borneo Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/r/20240620083115.204362-6-antonio.borneo@foss.st.com --- drivers/irqchip/irq-stm32mp-exti.c | 312 ++++++++++++++++++------------------- 1 file changed, 152 insertions(+), 160 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-stm32mp-exti.c b/drivers/irqchip/irq-stm32mp-exti.c index 8a45ece2e198..727859e073ea 100644 --- a/drivers/irqchip/irq-stm32mp-exti.c +++ b/drivers/irqchip/irq-stm32mp-exti.c @@ -38,7 +38,7 @@ #define EXTI_CID1 1 -struct stm32_exti_bank { +struct stm32mp_exti_bank { u32 imr_ofst; u32 rtsr_ofst; u32 ftsr_ofst; @@ -49,33 +49,34 @@ struct stm32_exti_bank { u32 seccfgr_ofst; }; -struct stm32_exti_drv_data { - const struct stm32_exti_bank **exti_banks; - const u8 *desc_irqs; - u32 bank_nr; +struct stm32mp_exti_drv_data { + const struct stm32mp_exti_bank **exti_banks; + const u8 *desc_irqs; + u32 bank_nr; }; -struct stm32_exti_chip_data { - struct stm32_exti_host_data *host_data; - const struct stm32_exti_bank *reg_bank; - struct raw_spinlock rlock; - u32 wake_active; - u32 mask_cache; - u32 rtsr_cache; - u32 ftsr_cache; - u32 event_reserved; +struct stm32mp_exti_chip_data { + struct stm32mp_exti_host_data *host_data; + const struct stm32mp_exti_bank *reg_bank; + struct raw_spinlock rlock; + u32 wake_active; + u32 mask_cache; + u32 rtsr_cache; + u32 ftsr_cache; + u32 event_reserved; }; -struct stm32_exti_host_data { - void __iomem *base; - struct device *dev; - struct stm32_exti_chip_data *chips_data; - const struct stm32_exti_drv_data *drv_data; - struct hwspinlock *hwlock; - bool dt_has_irqs_desc; /* skip internal desc_irqs array and get it from DT */ +struct stm32mp_exti_host_data { + void __iomem *base; + struct device *dev; + struct stm32mp_exti_chip_data *chips_data; + const struct stm32mp_exti_drv_data *drv_data; + struct hwspinlock *hwlock; + /* skip internal desc_irqs array and get it from DT */ + bool dt_has_irqs_desc; }; -static const struct stm32_exti_bank stm32mp1_exti_b1 = { +static const struct stm32mp_exti_bank stm32mp_exti_b1 = { .imr_ofst = 0x80, .rtsr_ofst = 0x00, .ftsr_ofst = 0x04, @@ -86,7 +87,7 @@ static const struct stm32_exti_bank stm32mp1_exti_b1 = { .seccfgr_ofst = 0x14, }; -static const struct stm32_exti_bank stm32mp1_exti_b2 = { +static const struct stm32mp_exti_bank stm32mp_exti_b2 = { .imr_ofst = 0x90, .rtsr_ofst = 0x20, .ftsr_ofst = 0x24, @@ -97,7 +98,7 @@ static const struct stm32_exti_bank stm32mp1_exti_b2 = { .seccfgr_ofst = 0x34, }; -static const struct stm32_exti_bank stm32mp1_exti_b3 = { +static const struct stm32mp_exti_bank stm32mp_exti_b3 = { .imr_ofst = 0xA0, .rtsr_ofst = 0x40, .ftsr_ofst = 0x44, @@ -108,17 +109,17 @@ static const struct stm32_exti_bank stm32mp1_exti_b3 = { .seccfgr_ofst = 0x54, }; -static const struct stm32_exti_bank *stm32mp1_exti_banks[] = { - &stm32mp1_exti_b1, - &stm32mp1_exti_b2, - &stm32mp1_exti_b3, +static const struct stm32mp_exti_bank *stm32mp_exti_banks[] = { + &stm32mp_exti_b1, + &stm32mp_exti_b2, + &stm32mp_exti_b3, }; -static struct irq_chip stm32_exti_h_chip; -static struct irq_chip stm32_exti_h_chip_direct; +static struct irq_chip stm32mp_exti_chip; +static struct irq_chip stm32mp_exti_chip_direct; #define EXTI_INVALID_IRQ U8_MAX -#define STM32MP1_DESC_IRQ_SIZE (ARRAY_SIZE(stm32mp1_exti_banks) * IRQS_PER_BANK) +#define STM32MP_DESC_IRQ_SIZE (ARRAY_SIZE(stm32mp_exti_banks) * IRQS_PER_BANK) /* * Use some intentionally tricky logic here to initialize the whole array to @@ -132,7 +133,7 @@ __diag_ignore_all("-Woverride-init", static const u8 stm32mp1_desc_irq[] = { /* default value */ - [0 ... (STM32MP1_DESC_IRQ_SIZE - 1)] = EXTI_INVALID_IRQ, + [0 ... (STM32MP_DESC_IRQ_SIZE - 1)] = EXTI_INVALID_IRQ, [0] = 6, [1] = 7, @@ -181,7 +182,7 @@ static const u8 stm32mp1_desc_irq[] = { static const u8 stm32mp13_desc_irq[] = { /* default value */ - [0 ... (STM32MP1_DESC_IRQ_SIZE - 1)] = EXTI_INVALID_IRQ, + [0 ... (STM32MP_DESC_IRQ_SIZE - 1)] = EXTI_INVALID_IRQ, [0] = 6, [1] = 7, @@ -226,20 +227,19 @@ static const u8 stm32mp13_desc_irq[] = { __diag_pop(); -static const struct stm32_exti_drv_data stm32mp1_drv_data = { - .exti_banks = stm32mp1_exti_banks, - .bank_nr = ARRAY_SIZE(stm32mp1_exti_banks), +static const struct stm32mp_exti_drv_data stm32mp1_drv_data = { + .exti_banks = stm32mp_exti_banks, + .bank_nr = ARRAY_SIZE(stm32mp_exti_banks), .desc_irqs = stm32mp1_desc_irq, }; -static const struct stm32_exti_drv_data stm32mp13_drv_data = { - .exti_banks = stm32mp1_exti_banks, - .bank_nr = ARRAY_SIZE(stm32mp1_exti_banks), +static const struct stm32mp_exti_drv_data stm32mp13_drv_data = { + .exti_banks = stm32mp_exti_banks, + .bank_nr = ARRAY_SIZE(stm32mp_exti_banks), .desc_irqs = stm32mp13_desc_irq, }; -static int stm32_exti_set_type(struct irq_data *d, - unsigned int type, u32 *rtsr, u32 *ftsr) +static int stm32mp_exti_convert_type(struct irq_data *d, unsigned int type, u32 *rtsr, u32 *ftsr) { u32 mask = BIT(d->hwirq % IRQS_PER_BANK); @@ -263,45 +263,43 @@ static int stm32_exti_set_type(struct irq_data *d, return 0; } -static void stm32_chip_suspend(struct stm32_exti_chip_data *chip_data, - u32 wake_active) +static void stm32mp_chip_suspend(struct stm32mp_exti_chip_data *chip_data, u32 wake_active) { - const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; + const struct stm32mp_exti_bank *bank = chip_data->reg_bank; void __iomem *base = chip_data->host_data->base; /* save rtsr, ftsr registers */ - chip_data->rtsr_cache = readl_relaxed(base + stm32_bank->rtsr_ofst); - chip_data->ftsr_cache = readl_relaxed(base + stm32_bank->ftsr_ofst); + chip_data->rtsr_cache = readl_relaxed(base + bank->rtsr_ofst); + chip_data->ftsr_cache = readl_relaxed(base + bank->ftsr_ofst); - writel_relaxed(wake_active, base + stm32_bank->imr_ofst); + writel_relaxed(wake_active, base + bank->imr_ofst); } -static void stm32_chip_resume(struct stm32_exti_chip_data *chip_data, - u32 mask_cache) +static void stm32mp_chip_resume(struct stm32mp_exti_chip_data *chip_data, u32 mask_cache) { - const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; + const struct stm32mp_exti_bank *bank = chip_data->reg_bank; void __iomem *base = chip_data->host_data->base; /* restore rtsr, ftsr, registers */ - writel_relaxed(chip_data->rtsr_cache, base + stm32_bank->rtsr_ofst); - writel_relaxed(chip_data->ftsr_cache, base + stm32_bank->ftsr_ofst); + writel_relaxed(chip_data->rtsr_cache, base + bank->rtsr_ofst); + writel_relaxed(chip_data->ftsr_cache, base + bank->ftsr_ofst); - writel_relaxed(mask_cache, base + stm32_bank->imr_ofst); + writel_relaxed(mask_cache, base + bank->imr_ofst); } /* directly set the target bit without reading first. */ -static inline void stm32_exti_write_bit(struct irq_data *d, u32 reg) +static inline void stm32mp_exti_write_bit(struct irq_data *d, u32 reg) { - struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); + struct stm32mp_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); void __iomem *base = chip_data->host_data->base; u32 val = BIT(d->hwirq % IRQS_PER_BANK); writel_relaxed(val, base + reg); } -static inline u32 stm32_exti_set_bit(struct irq_data *d, u32 reg) +static inline u32 stm32mp_exti_set_bit(struct irq_data *d, u32 reg) { - struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); + struct stm32mp_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); void __iomem *base = chip_data->host_data->base; u32 val; @@ -312,9 +310,9 @@ static inline u32 stm32_exti_set_bit(struct irq_data *d, u32 reg) return val; } -static inline u32 stm32_exti_clr_bit(struct irq_data *d, u32 reg) +static inline u32 stm32mp_exti_clr_bit(struct irq_data *d, u32 reg) { - struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); + struct stm32mp_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); void __iomem *base = chip_data->host_data->base; u32 val; @@ -325,15 +323,15 @@ static inline u32 stm32_exti_clr_bit(struct irq_data *d, u32 reg) return val; } -static void stm32_exti_h_eoi(struct irq_data *d) +static void stm32mp_exti_eoi(struct irq_data *d) { - struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); - const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; + struct stm32mp_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); + const struct stm32mp_exti_bank *bank = chip_data->reg_bank; raw_spin_lock(&chip_data->rlock); - stm32_exti_write_bit(d, stm32_bank->rpr_ofst); - stm32_exti_write_bit(d, stm32_bank->fpr_ofst); + stm32mp_exti_write_bit(d, bank->rpr_ofst); + stm32mp_exti_write_bit(d, bank->fpr_ofst); raw_spin_unlock(&chip_data->rlock); @@ -341,36 +339,36 @@ static void stm32_exti_h_eoi(struct irq_data *d) irq_chip_eoi_parent(d); } -static void stm32_exti_h_mask(struct irq_data *d) +static void stm32mp_exti_mask(struct irq_data *d) { - struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); - const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; + struct stm32mp_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); + const struct stm32mp_exti_bank *bank = chip_data->reg_bank; raw_spin_lock(&chip_data->rlock); - chip_data->mask_cache = stm32_exti_clr_bit(d, stm32_bank->imr_ofst); + chip_data->mask_cache = stm32mp_exti_clr_bit(d, bank->imr_ofst); raw_spin_unlock(&chip_data->rlock); if (d->parent_data->chip) irq_chip_mask_parent(d); } -static void stm32_exti_h_unmask(struct irq_data *d) +static void stm32mp_exti_unmask(struct irq_data *d) { - struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); - const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; + struct stm32mp_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); + const struct stm32mp_exti_bank *bank = chip_data->reg_bank; raw_spin_lock(&chip_data->rlock); - chip_data->mask_cache = stm32_exti_set_bit(d, stm32_bank->imr_ofst); + chip_data->mask_cache = stm32mp_exti_set_bit(d, bank->imr_ofst); raw_spin_unlock(&chip_data->rlock); if (d->parent_data->chip) irq_chip_unmask_parent(d); } -static int stm32_exti_h_set_type(struct irq_data *d, unsigned int type) +static int stm32mp_exti_set_type(struct irq_data *d, unsigned int type) { - struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); - const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; + struct stm32mp_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); + const struct stm32mp_exti_bank *bank = chip_data->reg_bank; struct hwspinlock *hwlock = chip_data->host_data->hwlock; void __iomem *base = chip_data->host_data->base; u32 rtsr, ftsr; @@ -386,28 +384,25 @@ static int stm32_exti_h_set_type(struct irq_data *d, unsigned int type) } } - rtsr = readl_relaxed(base + stm32_bank->rtsr_ofst); - ftsr = readl_relaxed(base + stm32_bank->ftsr_ofst); + rtsr = readl_relaxed(base + bank->rtsr_ofst); + ftsr = readl_relaxed(base + bank->ftsr_ofst); - err = stm32_exti_set_type(d, type, &rtsr, &ftsr); - if (err) - goto unspinlock; - - writel_relaxed(rtsr, base + stm32_bank->rtsr_ofst); - writel_relaxed(ftsr, base + stm32_bank->ftsr_ofst); + err = stm32mp_exti_convert_type(d, type, &rtsr, &ftsr); + if (!err) { + writel_relaxed(rtsr, base + bank->rtsr_ofst); + writel_relaxed(ftsr, base + bank->ftsr_ofst); + } -unspinlock: if (hwlock) hwspin_unlock_in_atomic(hwlock); unlock: raw_spin_unlock(&chip_data->rlock); - return err; } -static int stm32_exti_h_set_wake(struct irq_data *d, unsigned int on) +static int stm32mp_exti_set_wake(struct irq_data *d, unsigned int on) { - struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); + struct stm32mp_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); u32 mask = BIT(d->hwirq % IRQS_PER_BANK); raw_spin_lock(&chip_data->rlock); @@ -422,8 +417,7 @@ static int stm32_exti_h_set_wake(struct irq_data *d, unsigned int on) return 0; } -static int stm32_exti_h_set_affinity(struct irq_data *d, - const struct cpumask *dest, bool force) +static int stm32mp_exti_set_affinity(struct irq_data *d, const struct cpumask *dest, bool force) { if (d->parent_data->chip) return irq_chip_set_affinity_parent(d, dest, force); @@ -431,84 +425,84 @@ static int stm32_exti_h_set_affinity(struct irq_data *d, return IRQ_SET_MASK_OK_DONE; } -static int stm32_exti_h_suspend(struct device *dev) +static int stm32mp_exti_suspend(struct device *dev) { - struct stm32_exti_host_data *host_data = dev_get_drvdata(dev); - struct stm32_exti_chip_data *chip_data; + struct stm32mp_exti_host_data *host_data = dev_get_drvdata(dev); + struct stm32mp_exti_chip_data *chip_data; int i; for (i = 0; i < host_data->drv_data->bank_nr; i++) { chip_data = &host_data->chips_data[i]; - stm32_chip_suspend(chip_data, chip_data->wake_active); + stm32mp_chip_suspend(chip_data, chip_data->wake_active); } return 0; } -static int stm32_exti_h_resume(struct device *dev) +static int stm32mp_exti_resume(struct device *dev) { - struct stm32_exti_host_data *host_data = dev_get_drvdata(dev); - struct stm32_exti_chip_data *chip_data; + struct stm32mp_exti_host_data *host_data = dev_get_drvdata(dev); + struct stm32mp_exti_chip_data *chip_data; int i; for (i = 0; i < host_data->drv_data->bank_nr; i++) { chip_data = &host_data->chips_data[i]; - stm32_chip_resume(chip_data, chip_data->mask_cache); + stm32mp_chip_resume(chip_data, chip_data->mask_cache); } return 0; } -static int stm32_exti_h_retrigger(struct irq_data *d) +static int stm32mp_exti_retrigger(struct irq_data *d) { - struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); - const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; + struct stm32mp_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); + const struct stm32mp_exti_bank *bank = chip_data->reg_bank; void __iomem *base = chip_data->host_data->base; u32 mask = BIT(d->hwirq % IRQS_PER_BANK); - writel_relaxed(mask, base + stm32_bank->swier_ofst); + writel_relaxed(mask, base + bank->swier_ofst); return 0; } -static struct irq_chip stm32_exti_h_chip = { - .name = "stm32-exti-h", - .irq_eoi = stm32_exti_h_eoi, - .irq_mask = stm32_exti_h_mask, - .irq_unmask = stm32_exti_h_unmask, - .irq_retrigger = stm32_exti_h_retrigger, - .irq_set_type = stm32_exti_h_set_type, - .irq_set_wake = stm32_exti_h_set_wake, +static struct irq_chip stm32mp_exti_chip = { + .name = "stm32mp-exti", + .irq_eoi = stm32mp_exti_eoi, + .irq_mask = stm32mp_exti_mask, + .irq_unmask = stm32mp_exti_unmask, + .irq_retrigger = stm32mp_exti_retrigger, + .irq_set_type = stm32mp_exti_set_type, + .irq_set_wake = stm32mp_exti_set_wake, .flags = IRQCHIP_MASK_ON_SUSPEND, - .irq_set_affinity = IS_ENABLED(CONFIG_SMP) ? stm32_exti_h_set_affinity : NULL, + .irq_set_affinity = IS_ENABLED(CONFIG_SMP) ? stm32mp_exti_set_affinity : NULL, }; -static struct irq_chip stm32_exti_h_chip_direct = { - .name = "stm32-exti-h-direct", +static struct irq_chip stm32mp_exti_chip_direct = { + .name = "stm32mp-exti-direct", .irq_eoi = irq_chip_eoi_parent, .irq_ack = irq_chip_ack_parent, - .irq_mask = stm32_exti_h_mask, - .irq_unmask = stm32_exti_h_unmask, + .irq_mask = stm32mp_exti_mask, + .irq_unmask = stm32mp_exti_unmask, .irq_retrigger = irq_chip_retrigger_hierarchy, .irq_set_type = irq_chip_set_type_parent, - .irq_set_wake = stm32_exti_h_set_wake, + .irq_set_wake = stm32mp_exti_set_wake, .flags = IRQCHIP_MASK_ON_SUSPEND, .irq_set_affinity = IS_ENABLED(CONFIG_SMP) ? irq_chip_set_affinity_parent : NULL, }; -static int stm32_exti_h_domain_alloc(struct irq_domain *dm, +static int stm32mp_exti_domain_alloc(struct irq_domain *dm, unsigned int virq, unsigned int nr_irqs, void *data) { - struct stm32_exti_host_data *host_data = dm->host_data; - struct stm32_exti_chip_data *chip_data; - u8 desc_irq; + struct stm32mp_exti_host_data *host_data = dm->host_data; + struct stm32mp_exti_chip_data *chip_data; struct irq_fwspec *fwspec = data; struct irq_fwspec p_fwspec; irq_hw_number_t hwirq; - int bank; - u32 event_trg; struct irq_chip *chip; + u32 event_trg; + u8 desc_irq; + int bank; hwirq = fwspec->param[0]; if (hwirq >= host_data->drv_data->bank_nr * IRQS_PER_BANK) @@ -525,7 +519,7 @@ static int stm32_exti_h_domain_alloc(struct irq_domain *dm, event_trg = readl_relaxed(host_data->base + chip_data->reg_bank->trg_ofst); chip = (event_trg & BIT(hwirq % IRQS_PER_BANK)) ? - &stm32_exti_h_chip : &stm32_exti_h_chip_direct; + &stm32mp_exti_chip : &stm32mp_exti_chip_direct; irq_domain_set_hwirq_and_chip(dm, virq, hwirq, chip, chip_data); @@ -563,19 +557,17 @@ static int stm32_exti_h_domain_alloc(struct irq_domain *dm, return 0; } -static struct -stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data, - u32 bank_idx, - struct device_node *node) +static struct stm32mp_exti_chip_data *stm32mp_exti_chip_init(struct stm32mp_exti_host_data *h_data, + u32 bank_idx, struct device_node *node) { - const struct stm32_exti_bank *stm32_bank; - struct stm32_exti_chip_data *chip_data; + struct stm32mp_exti_chip_data *chip_data; + const struct stm32mp_exti_bank *bank; void __iomem *base = h_data->base; - stm32_bank = h_data->drv_data->exti_banks[bank_idx]; + bank = h_data->drv_data->exti_banks[bank_idx]; chip_data = &h_data->chips_data[bank_idx]; chip_data->host_data = h_data; - chip_data->reg_bank = stm32_bank; + chip_data->reg_bank = bank; raw_spin_lock_init(&chip_data->rlock); @@ -583,23 +575,23 @@ stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data, * This IP has no reset, so after hot reboot we should * clear registers to avoid residue */ - writel_relaxed(0, base + stm32_bank->imr_ofst); + writel_relaxed(0, base + bank->imr_ofst); /* reserve Secure events */ - chip_data->event_reserved = readl_relaxed(base + stm32_bank->seccfgr_ofst); + chip_data->event_reserved = readl_relaxed(base + bank->seccfgr_ofst); pr_info("%pOF: bank%d\n", node, bank_idx); return chip_data; } -static const struct irq_domain_ops stm32_exti_h_domain_ops = { - .alloc = stm32_exti_h_domain_alloc, +static const struct irq_domain_ops stm32mp_exti_domain_ops = { + .alloc = stm32mp_exti_domain_alloc, .free = irq_domain_free_irqs_common, .xlate = irq_domain_xlate_twocell, }; -static void stm32_exti_check_rif(struct stm32_exti_host_data *host_data) +static void stm32mp_exti_check_rif(struct stm32mp_exti_host_data *host_data) { unsigned int bank, i, event; u32 cid, cidcfgr, hwcfgr1; @@ -620,21 +612,21 @@ static void stm32_exti_check_rif(struct stm32_exti_host_data *host_data) } } -static void stm32_exti_remove_irq(void *data) +static void stm32mp_exti_remove_irq(void *data) { struct irq_domain *domain = data; irq_domain_remove(domain); } -static int stm32_exti_probe(struct platform_device *pdev) +static int stm32mp_exti_probe(struct platform_device *pdev) { - int ret, i; + const struct stm32mp_exti_drv_data *drv_data; + struct irq_domain *parent_domain, *domain; + struct stm32mp_exti_host_data *host_data; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; - struct irq_domain *parent_domain, *domain; - struct stm32_exti_host_data *host_data; - const struct stm32_exti_drv_data *drv_data; + int ret, i; host_data = devm_kzalloc(dev, sizeof(*host_data), GFP_KERNEL); if (!host_data) @@ -680,9 +672,9 @@ static int stm32_exti_probe(struct platform_device *pdev) return PTR_ERR(host_data->base); for (i = 0; i < drv_data->bank_nr; i++) - stm32_exti_chip_init(host_data, i, np); + stm32mp_exti_chip_init(host_data, i, np); - stm32_exti_check_rif(host_data); + stm32mp_exti_check_rif(host_data); parent_domain = irq_find_host(of_irq_find_parent(np)); if (!parent_domain) { @@ -692,7 +684,7 @@ static int stm32_exti_probe(struct platform_device *pdev) domain = irq_domain_add_hierarchy(parent_domain, 0, drv_data->bank_nr * IRQS_PER_BANK, - np, &stm32_exti_h_domain_ops, + np, &stm32mp_exti_domain_ops, host_data); if (!domain) { @@ -700,7 +692,7 @@ static int stm32_exti_probe(struct platform_device *pdev) return -ENOMEM; } - ret = devm_add_action_or_reset(dev, stm32_exti_remove_irq, domain); + ret = devm_add_action_or_reset(dev, stm32mp_exti_remove_irq, domain); if (ret) return ret; @@ -710,35 +702,35 @@ static int stm32_exti_probe(struct platform_device *pdev) return 0; } -static const struct of_device_id stm32_exti_ids[] = { +static const struct of_device_id stm32mp_exti_ids[] = { { .compatible = "st,stm32mp1-exti", .data = &stm32mp1_drv_data}, { .compatible = "st,stm32mp13-exti", .data = &stm32mp13_drv_data}, {}, }; -MODULE_DEVICE_TABLE(of, stm32_exti_ids); +MODULE_DEVICE_TABLE(of, stm32mp_exti_ids); -static const struct dev_pm_ops stm32_exti_dev_pm_ops = { - NOIRQ_SYSTEM_SLEEP_PM_OPS(stm32_exti_h_suspend, stm32_exti_h_resume) +static const struct dev_pm_ops stm32mp_exti_dev_pm_ops = { + NOIRQ_SYSTEM_SLEEP_PM_OPS(stm32mp_exti_suspend, stm32mp_exti_resume) }; -static struct platform_driver stm32_exti_driver = { - .probe = stm32_exti_probe, +static struct platform_driver stm32mp_exti_driver = { + .probe = stm32mp_exti_probe, .driver = { - .name = "stm32_exti", - .of_match_table = stm32_exti_ids, - .pm = &stm32_exti_dev_pm_ops, + .name = "stm32mp_exti", + .of_match_table = stm32mp_exti_ids, + .pm = &stm32mp_exti_dev_pm_ops, }, }; -static int __init stm32_exti_arch_init(void) +static int __init stm32mp_exti_arch_init(void) { - return platform_driver_register(&stm32_exti_driver); + return platform_driver_register(&stm32mp_exti_driver); } -static void __exit stm32_exti_arch_exit(void) +static void __exit stm32mp_exti_arch_exit(void) { - return platform_driver_unregister(&stm32_exti_driver); + return platform_driver_unregister(&stm32mp_exti_driver); } -arch_initcall(stm32_exti_arch_init); -module_exit(stm32_exti_arch_exit); +arch_initcall(stm32mp_exti_arch_init); +module_exit(stm32mp_exti_arch_exit); -- cgit v1.2.3 From 0be58e0553812fcbd37c0c2d89e2b5bc296f04ea Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Thu, 20 Jun 2024 10:31:13 +0200 Subject: irqchip/stm32mp-exti: Allow building as module Allow to build the driver as a module by adding the necessarily hooks in Kconfig and in the driver's code. Since all the probe dependencies linked to this driver have already been fixed, remove the not longer relevant 'arch_initcall'. Signed-off-by: Antonio Borneo Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/r/20240620083115.204362-7-antonio.borneo@foss.st.com --- drivers/irqchip/Kconfig | 8 ++++++-- drivers/irqchip/irq-stm32mp-exti.c | 15 ++++----------- 2 files changed, 10 insertions(+), 13 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 978639d10d75..cbf49b6404b8 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -405,9 +405,13 @@ config PARTITION_PERCPU bool config STM32MP_EXTI - bool - select IRQ_DOMAIN + tristate "STM32MP extended interrupts and event controller" + depends on (ARCH_STM32 && !ARM_SINGLE_ARMV7M) || COMPILE_TEST + default y + select IRQ_DOMAIN_HIERARCHY select GENERIC_IRQ_CHIP + help + Support STM32MP EXTI (extended interrupts and event) controller. config STM32_EXTI bool diff --git a/drivers/irqchip/irq-stm32mp-exti.c b/drivers/irqchip/irq-stm32mp-exti.c index 727859e073ea..33e0cfdea654 100644 --- a/drivers/irqchip/irq-stm32mp-exti.c +++ b/drivers/irqchip/irq-stm32mp-exti.c @@ -722,15 +722,8 @@ static struct platform_driver stm32mp_exti_driver = { }, }; -static int __init stm32mp_exti_arch_init(void) -{ - return platform_driver_register(&stm32mp_exti_driver); -} - -static void __exit stm32mp_exti_arch_exit(void) -{ - return platform_driver_unregister(&stm32mp_exti_driver); -} +module_platform_driver(stm32mp_exti_driver); -arch_initcall(stm32mp_exti_arch_init); -module_exit(stm32mp_exti_arch_exit); +MODULE_AUTHOR("Maxime Coquelin "); +MODULE_DESCRIPTION("STM32MP EXTI driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 18db1b6dbbe576c283f69f3e925fbeb57de40855 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 25 Jun 2024 07:40:08 +0200 Subject: Revert "Loongarch: Support loongarch avec" This reverts commit 760d7e719499d64beea62bfcf53938fb233bb6e7. This results in build failures and has other issues according to Tianyang. Reported-by: kernel test robot Signed-off-by: Thomas Gleixner Cc: Tianyang Zhang Closes: https://lore.kernel.org/oe-kbuild-all/202406240451.ygBFNyJ3-lkp@intel.com/ --- arch/loongarch/Kconfig | 1 - arch/loongarch/include/asm/cpu-features.h | 1 - arch/loongarch/include/asm/cpu.h | 2 - arch/loongarch/include/asm/hw_irq.h | 10 - arch/loongarch/include/asm/irq.h | 12 +- arch/loongarch/include/asm/loongarch.h | 20 +- arch/loongarch/include/asm/smp.h | 2 - arch/loongarch/kernel/cpu-probe.c | 3 +- arch/loongarch/kernel/smp.c | 5 - drivers/irqchip/Makefile | 2 +- drivers/irqchip/irq-loongarch-avec.c | 419 ------------------------------ drivers/irqchip/irq-loongarch-cpu.c | 4 +- drivers/irqchip/irq-loongson-eiointc.c | 3 - drivers/irqchip/irq-loongson-pch-msi.c | 43 +-- 14 files changed, 10 insertions(+), 517 deletions(-) delete mode 100644 drivers/irqchip/irq-loongarch-avec.c (limited to 'drivers/irqchip') diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index a66e49b5a68c..e38139c576ee 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -83,7 +83,6 @@ config LOONGARCH select GENERIC_ENTRY select GENERIC_GETTIMEOFDAY select GENERIC_IOREMAP if !ARCH_IOREMAP - select GENERIC_IRQ_MATRIX_ALLOCATOR select GENERIC_IRQ_MULTI_HANDLER select GENERIC_IRQ_PROBE select GENERIC_IRQ_SHOW diff --git a/arch/loongarch/include/asm/cpu-features.h b/arch/loongarch/include/asm/cpu-features.h index 16a716f88a5c..2eafe6a6aca8 100644 --- a/arch/loongarch/include/asm/cpu-features.h +++ b/arch/loongarch/include/asm/cpu-features.h @@ -65,6 +65,5 @@ #define cpu_has_guestid cpu_opt(LOONGARCH_CPU_GUESTID) #define cpu_has_hypervisor cpu_opt(LOONGARCH_CPU_HYPERVISOR) #define cpu_has_ptw cpu_opt(LOONGARCH_CPU_PTW) -#define cpu_has_avecint cpu_opt(LOONGARCH_CPU_AVECINT) #endif /* __ASM_CPU_FEATURES_H */ diff --git a/arch/loongarch/include/asm/cpu.h b/arch/loongarch/include/asm/cpu.h index 843f9c4ec980..48b9f7168bcc 100644 --- a/arch/loongarch/include/asm/cpu.h +++ b/arch/loongarch/include/asm/cpu.h @@ -99,7 +99,6 @@ enum cpu_type_enum { #define CPU_FEATURE_GUESTID 24 /* CPU has GuestID feature */ #define CPU_FEATURE_HYPERVISOR 25 /* CPU has hypervisor (running in VM) */ #define CPU_FEATURE_PTW 26 /* CPU has hardware page table walker */ -#define CPU_FEATURE_AVECINT 27 /* CPU has avec interrupt */ #define LOONGARCH_CPU_CPUCFG BIT_ULL(CPU_FEATURE_CPUCFG) #define LOONGARCH_CPU_LAM BIT_ULL(CPU_FEATURE_LAM) @@ -128,6 +127,5 @@ enum cpu_type_enum { #define LOONGARCH_CPU_GUESTID BIT_ULL(CPU_FEATURE_GUESTID) #define LOONGARCH_CPU_HYPERVISOR BIT_ULL(CPU_FEATURE_HYPERVISOR) #define LOONGARCH_CPU_PTW BIT_ULL(CPU_FEATURE_PTW) -#define LOONGARCH_CPU_AVECINT BIT_ULL(CPU_FEATURE_AVECINT) #endif /* _ASM_CPU_H */ diff --git a/arch/loongarch/include/asm/hw_irq.h b/arch/loongarch/include/asm/hw_irq.h index 772692e765c0..af4f4e8fbd85 100644 --- a/arch/loongarch/include/asm/hw_irq.h +++ b/arch/loongarch/include/asm/hw_irq.h @@ -9,16 +9,6 @@ extern atomic_t irq_err_count; -/* - * 256 vectors Map: - * - * 0 - 15: mapping legacy IPs, e.g. IP0-12. - * 16 - 255: mapping a vector for external IRQ. - * - */ -#define NR_VECTORS 256 -#define IRQ_MATRIX_BITS NR_VECTORS -#define NR_LEGACY_VECTORS 16 /* * interrupt-retrigger: NOP for now. This may not be appropriate for all * machines, we'll see ... diff --git a/arch/loongarch/include/asm/irq.h b/arch/loongarch/include/asm/irq.h index cf3b635a9b86..480418bc5071 100644 --- a/arch/loongarch/include/asm/irq.h +++ b/arch/loongarch/include/asm/irq.h @@ -65,7 +65,7 @@ extern struct acpi_vector_group msi_group[MAX_IO_PICS]; #define LOONGSON_LPC_LAST_IRQ (LOONGSON_LPC_IRQ_BASE + 15) #define LOONGSON_CPU_IRQ_BASE 16 -#define LOONGSON_CPU_LAST_IRQ (LOONGSON_CPU_IRQ_BASE + 15) +#define LOONGSON_CPU_LAST_IRQ (LOONGSON_CPU_IRQ_BASE + 14) #define LOONGSON_PCH_IRQ_BASE 64 #define LOONGSON_PCH_ACPI_IRQ (LOONGSON_PCH_IRQ_BASE + 47) @@ -101,16 +101,6 @@ int pch_msi_acpi_init(struct irq_domain *parent, struct acpi_madt_msi_pic *acpi_pchmsi); int pch_pic_acpi_init(struct irq_domain *parent, struct acpi_madt_bio_pic *acpi_pchpic); - -#ifdef CONFIG_ACPI -int __init pch_msi_acpi_init_v2(struct irq_domain *parent, - struct acpi_madt_msi_pic *pch_msi_entry); -int __init loongarch_avec_acpi_init(struct irq_domain *parent); -void complete_irq_moving(void); -void loongarch_avec_offline_cpu(unsigned int cpu); -void loongarch_avec_online_cpu(unsigned int cpu); -#endif - int find_pch_pic(u32 gsi); struct fwnode_handle *get_pch_msi_handle(int pci_segment); diff --git a/arch/loongarch/include/asm/loongarch.h b/arch/loongarch/include/asm/loongarch.h index 16a910359977..eb09adda54b7 100644 --- a/arch/loongarch/include/asm/loongarch.h +++ b/arch/loongarch/include/asm/loongarch.h @@ -72,6 +72,7 @@ #define CPUCFG1_RPLV BIT(23) #define CPUCFG1_HUGEPG BIT(24) #define CPUCFG1_CRC32 BIT(25) +#define CPUCFG1_MSGINT BIT(26) #define LOONGARCH_CPUCFG2 0x2 #define CPUCFG2_FP BIT(0) @@ -251,8 +252,8 @@ #define CSR_ESTAT_EXC_WIDTH 6 #define CSR_ESTAT_EXC (_ULCAST_(0x3f) << CSR_ESTAT_EXC_SHIFT) #define CSR_ESTAT_IS_SHIFT 0 -#define CSR_ESTAT_IS_WIDTH 15 -#define CSR_ESTAT_IS (_ULCAST_(0x7fff) << CSR_ESTAT_IS_SHIFT) +#define CSR_ESTAT_IS_WIDTH 14 +#define CSR_ESTAT_IS (_ULCAST_(0x3fff) << CSR_ESTAT_IS_SHIFT) #define LOONGARCH_CSR_ERA 0x6 /* ERA */ @@ -998,18 +999,10 @@ #define CSR_FWPC_SKIP_SHIFT 16 #define CSR_FWPC_SKIP (_ULCAST_(1) << CSR_FWPC_SKIP_SHIFT) -#define LOONGARCH_CSR_IRR0 0xa0 -#define LOONGARCH_CSR_IRR1 0xa1 -#define LOONGARCH_CSR_IRR2 0xa2 -#define LOONGARCH_CSR_IRR3 0xa3 -#define LOONGARCH_CSR_IRR_BASE LOONGARCH_CSR_IRR0 - -#define LOONGARCH_CSR_ILR 0xa4 - /* * CSR_ECFG IM */ -#define ECFG0_IM 0x00005fff +#define ECFG0_IM 0x00001fff #define ECFGB_SIP0 0 #define ECFGF_SIP0 (_ULCAST_(1) << ECFGB_SIP0) #define ECFGB_SIP1 1 @@ -1052,7 +1045,6 @@ #define IOCSRF_EIODECODE BIT_ULL(9) #define IOCSRF_FLATMODE BIT_ULL(10) #define IOCSRF_VM BIT_ULL(11) -#define IOCSRF_AVEC BIT_ULL(15) #define LOONGARCH_IOCSR_VENDOR 0x10 @@ -1063,7 +1055,6 @@ #define LOONGARCH_IOCSR_MISC_FUNC 0x420 #define IOCSR_MISC_FUNC_TIMER_RESET BIT_ULL(21) #define IOCSR_MISC_FUNC_EXT_IOI_EN BIT_ULL(48) -#define IOCSR_MISC_FUNC_AVEC_EN BIT_ULL(51) #define LOONGARCH_IOCSR_CPUTEMP 0x428 @@ -1384,10 +1375,9 @@ __BUILD_CSR_OP(tlbidx) #define INT_TI 11 /* Timer */ #define INT_IPI 12 #define INT_NMI 13 -#define INT_AVEC 14 /* ExcCodes corresponding to interrupts */ -#define EXCCODE_INT_NUM (INT_AVEC + 1) +#define EXCCODE_INT_NUM (INT_NMI + 1) #define EXCCODE_INT_START 64 #define EXCCODE_INT_END (EXCCODE_INT_START + EXCCODE_INT_NUM - 1) diff --git a/arch/loongarch/include/asm/smp.h b/arch/loongarch/include/asm/smp.h index 2399004596a3..278700cfee88 100644 --- a/arch/loongarch/include/asm/smp.h +++ b/arch/loongarch/include/asm/smp.h @@ -69,11 +69,9 @@ extern int __cpu_logical_map[NR_CPUS]; #define ACTION_BOOT_CPU 0 #define ACTION_RESCHEDULE 1 #define ACTION_CALL_FUNCTION 2 -#define ACTION_CLEAR_VECT 3 #define SMP_BOOT_CPU BIT(ACTION_BOOT_CPU) #define SMP_RESCHEDULE BIT(ACTION_RESCHEDULE) #define SMP_CALL_FUNCTION BIT(ACTION_CALL_FUNCTION) -#define SMP_CLEAR_VECT BIT(ACTION_CLEAR_VECT) struct secondary_data { unsigned long stack; diff --git a/arch/loongarch/kernel/cpu-probe.c b/arch/loongarch/kernel/cpu-probe.c index 3b2e72e8f9bd..55320813ee08 100644 --- a/arch/loongarch/kernel/cpu-probe.c +++ b/arch/loongarch/kernel/cpu-probe.c @@ -106,6 +106,7 @@ static void cpu_probe_common(struct cpuinfo_loongarch *c) elf_hwcap |= HWCAP_LOONGARCH_CRC32; } + config = read_cpucfg(LOONGARCH_CPUCFG2); if (config & CPUCFG2_LAM) { c->options |= LOONGARCH_CPU_LAM; @@ -175,8 +176,6 @@ static void cpu_probe_common(struct cpuinfo_loongarch *c) c->options |= LOONGARCH_CPU_EIODECODE; if (config & IOCSRF_VM) c->options |= LOONGARCH_CPU_HYPERVISOR; - if (config & IOCSRF_AVEC) - c->options |= LOONGARCH_CPU_AVECINT; config = csr_read32(LOONGARCH_CSR_ASID); config = (config & CSR_ASID_BIT) >> CSR_ASID_BIT_SHIFT; diff --git a/arch/loongarch/kernel/smp.c b/arch/loongarch/kernel/smp.c index 6dfedef306f3..0dfe2388ef41 100644 --- a/arch/loongarch/kernel/smp.c +++ b/arch/loongarch/kernel/smp.c @@ -234,9 +234,6 @@ static irqreturn_t loongson_ipi_interrupt(int irq, void *dev) per_cpu(irq_stat, cpu).ipi_irqs[IPI_CALL_FUNCTION]++; } - if (action & SMP_CLEAR_VECT) - complete_irq_moving(); - return IRQ_HANDLED; } @@ -391,7 +388,6 @@ int loongson_cpu_disable(void) irq_migrate_all_off_this_cpu(); clear_csr_ecfg(ECFG0_IM); local_irq_restore(flags); - loongarch_avec_offline_cpu(cpu); local_flush_tlb_all(); return 0; @@ -570,7 +566,6 @@ asmlinkage void start_secondary(void) * early is dangerous. */ WARN_ON_ONCE(!irqs_disabled()); - loongarch_avec_online_cpu(cpu); loongson_smp_finish(); cpu_startup_entry(CPUHP_AP_ONLINE_IDLE); diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index de091a9f7c88..2df72b7b165b 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -110,7 +110,7 @@ obj-$(CONFIG_LS1X_IRQ) += irq-ls1x.o obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) += irq-ti-sci-intr.o obj-$(CONFIG_TI_SCI_INTA_IRQCHIP) += irq-ti-sci-inta.o obj-$(CONFIG_TI_PRUSS_INTC) += irq-pruss-intc.o -obj-$(CONFIG_IRQ_LOONGARCH_CPU) += irq-loongarch-cpu.o irq-loongarch-avec.o +obj-$(CONFIG_IRQ_LOONGARCH_CPU) += irq-loongarch-cpu.o obj-$(CONFIG_LOONGSON_LIOINTC) += irq-loongson-liointc.o obj-$(CONFIG_LOONGSON_EIOINTC) += irq-loongson-eiointc.o obj-$(CONFIG_LOONGSON_HTPIC) += irq-loongson-htpic.o diff --git a/drivers/irqchip/irq-loongarch-avec.c b/drivers/irqchip/irq-loongarch-avec.c deleted file mode 100644 index 4cd9079f2814..000000000000 --- a/drivers/irqchip/irq-loongarch-avec.c +++ /dev/null @@ -1,419 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2020 Loongson Technologies, Inc. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define VECTORS_PER_REG 64 -#define ILR_INVALID_MASK 0x80000000UL -#define ILR_VECTOR_MASK 0xffUL -#define AVEC_MSG_OFFSET 0x100000 - -static phys_addr_t msi_base_v2; -static DEFINE_PER_CPU(struct irq_desc * [NR_VECTORS], irq_map); - -struct pending_list { - struct list_head head; -}; - -static DEFINE_PER_CPU(struct pending_list, pending_list); - -struct loongarch_avec_chip { - struct fwnode_handle *fwnode; - struct irq_domain *domain; - struct irq_matrix *vector_matrix; - raw_spinlock_t lock; -}; - -static struct loongarch_avec_chip loongarch_avec; - -struct loongarch_avec_data { - struct list_head entry; - unsigned int cpu; - unsigned int vec; - unsigned int prev_cpu; - unsigned int prev_vec; - unsigned int moving : 1, - managed : 1; -}; - -static struct cpumask intersect_mask; - -static int assign_irq_vector(struct irq_data *irqd, const struct cpumask *dest, - unsigned int *cpu) -{ - return irq_matrix_alloc(loongarch_avec.vector_matrix, dest, false, cpu); -} - -static inline void loongarch_avec_ack_irq(struct irq_data *d) -{ -} - -static inline void loongarch_avec_unmask_irq(struct irq_data *d) -{ -} - -static inline void loongarch_avec_mask_irq(struct irq_data *d) -{ -} - -static void loongarch_avec_sync(struct loongarch_avec_data *adata) -{ - struct pending_list *plist; - - if (cpu_online(adata->prev_cpu)) { - plist = per_cpu_ptr(&pending_list, adata->prev_cpu); - list_add_tail(&adata->entry, &plist->head); - adata->moving = true; - loongson_send_ipi_single(adata->prev_cpu, SMP_CLEAR_VECT); - } - adata->prev_cpu = adata->cpu; - adata->prev_vec = adata->vec; -} - -static int loongarch_avec_set_affinity(struct irq_data *data, const struct cpumask *dest, - bool force) -{ - struct loongarch_avec_data *adata; - unsigned int cpu, vector; - unsigned long flags; - int ret; - - raw_spin_lock_irqsave(&loongarch_avec.lock, flags); - adata = irq_data_get_irq_chip_data(data); - - if (adata->vec && cpu_online(adata->cpu) && cpumask_test_cpu(adata->cpu, dest)) { - raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags); - return 0; - } - if (adata->moving) - return -EBUSY; - - cpumask_and(&intersect_mask, dest, cpu_online_mask); - - ret = assign_irq_vector(data, &intersect_mask, &cpu); - if (ret < 0) { - raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags); - return ret; - } - vector = ret; - adata->cpu = cpu; - adata->vec = vector; - per_cpu_ptr(irq_map, adata->cpu)[adata->vec] = irq_data_to_desc(data); - loongarch_avec_sync(adata); - - raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags); - irq_data_update_effective_affinity(data, cpumask_of(cpu)); - - return IRQ_SET_MASK_OK; -} - -static void loongarch_avec_compose_msg(struct irq_data *d, - struct msi_msg *msg) -{ - struct loongarch_avec_data *avec_data; - - avec_data = irq_data_get_irq_chip_data(d); - - msg->address_hi = 0x0; - msg->address_lo = msi_base_v2 | ((avec_data->vec & 0xff) << 4) | - ((cpu_logical_map(avec_data->cpu & 0xffff)) << 12); - msg->data = 0x0; - -} - -static struct irq_chip loongarch_avec_controller = { - .name = "CORE_AVEC", - .irq_ack = loongarch_avec_ack_irq, - .irq_mask = loongarch_avec_mask_irq, - .irq_unmask = loongarch_avec_unmask_irq, - .irq_set_affinity = loongarch_avec_set_affinity, - .irq_compose_msi_msg = loongarch_avec_compose_msg, -}; - -void complete_irq_moving(void) -{ - struct pending_list *plist = this_cpu_ptr(&pending_list); - struct loongarch_avec_data *adata, *tmp; - int cpu, vector, bias; - u64 irr; - - raw_spin_lock(&loongarch_avec.lock); - - list_for_each_entry_safe(adata, tmp, &plist->head, entry) { - cpu = adata->prev_cpu; - vector = adata->prev_vec; - bias = vector / VECTORS_PER_REG; - switch (bias) { - case 0: - irr = csr_read64(LOONGARCH_CSR_IRR0); - case 1: - irr = csr_read64(LOONGARCH_CSR_IRR1); - case 2: - irr = csr_read64(LOONGARCH_CSR_IRR2); - case 3: - irr = csr_read64(LOONGARCH_CSR_IRR3); - } - - if (irr & (1UL << (vector % VECTORS_PER_REG))) { - loongson_send_ipi_single(cpu, SMP_CLEAR_VECT); - continue; - } - list_del(&adata->entry); - irq_matrix_free(loongarch_avec.vector_matrix, cpu, vector, adata->managed); - this_cpu_write(irq_map[vector], NULL); - adata->moving = 0; - } - raw_spin_unlock(&loongarch_avec.lock); -} - -static void loongarch_avec_dispatch(struct irq_desc *desc) -{ - struct irq_chip *chip = irq_desc_get_chip(desc); - unsigned long vector; - struct irq_desc *d; - - chained_irq_enter(chip, desc); - vector = csr_read64(LOONGARCH_CSR_ILR); - if (vector & ILR_INVALID_MASK) - return; - - vector &= ILR_VECTOR_MASK; - - d = this_cpu_read(irq_map[vector]); - if (d) { - generic_handle_irq_desc(d); - } else { - pr_warn("IRQ ERROR:Unexpected irq occur on cpu %d[vector %ld]\n", - smp_processor_id(), vector); - } - - chained_irq_exit(chip, desc); -} - -static int loongarch_avec_alloc(struct irq_domain *domain, unsigned int virq, - unsigned int nr_irqs, void *arg) -{ - struct loongarch_avec_data *adata; - struct irq_data *irqd; - unsigned int cpu, vector, i, ret; - unsigned long flags; - - raw_spin_lock_irqsave(&loongarch_avec.lock, flags); - for (i = 0; i < nr_irqs; i++) { - irqd = irq_domain_get_irq_data(domain, virq + i); - adata = kzalloc(sizeof(*adata), GFP_KERNEL); - if (!adata) { - raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags); - return -ENOMEM; - } - ret = assign_irq_vector(irqd, cpu_online_mask, &cpu); - if (ret < 0) { - raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags); - return ret; - } - vector = ret; - adata->prev_cpu = adata->cpu = cpu; - adata->prev_vec = adata->vec = vector; - adata->managed = irqd_affinity_is_managed(irqd); - irq_domain_set_info(domain, virq + i, virq + i, &loongarch_avec_controller, - adata, handle_edge_irq, NULL, NULL); - adata->moving = 0; - irqd_set_single_target(irqd); - irqd_set_affinity_on_activate(irqd); - - per_cpu_ptr(irq_map, adata->cpu)[adata->vec] = irq_data_to_desc(irqd); - } - raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags); - - return 0; -} - -static void clear_free_vector(struct irq_data *irqd) -{ - struct loongarch_avec_data *adata = irq_data_get_irq_chip_data(irqd); - bool managed = irqd_affinity_is_managed(irqd); - - per_cpu(irq_map, adata->cpu)[adata->vec] = NULL; - irq_matrix_free(loongarch_avec.vector_matrix, adata->cpu, adata->vec, managed); - adata->cpu = 0; - adata->vec = 0; - if (!adata->moving) - return; - - per_cpu(irq_map, adata->prev_cpu)[adata->prev_vec] = 0; - irq_matrix_free(loongarch_avec.vector_matrix, adata->prev_cpu, - adata->prev_vec, adata->managed); - adata->prev_vec = 0; - adata->prev_cpu = 0; - adata->moving = 0; - list_del_init(&adata->entry); -} - -static void loongarch_avec_free(struct irq_domain *domain, unsigned int virq, - unsigned int nr_irqs) -{ - struct irq_data *d; - unsigned long flags; - unsigned int i; - - raw_spin_lock_irqsave(&loongarch_avec.lock, flags); - for (i = 0; i < nr_irqs; i++) { - d = irq_domain_get_irq_data(domain, virq + i); - if (d) { - clear_free_vector(d); - irq_domain_reset_irq_data(d); - - } - } - - raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags); -} - -static const struct irq_domain_ops loongarch_avec_domain_ops = { - .alloc = loongarch_avec_alloc, - .free = loongarch_avec_free, -}; - -static int __init irq_matrix_init(void) -{ - int i; - - loongarch_avec.vector_matrix = irq_alloc_matrix(NR_VECTORS, 0, NR_VECTORS - 1); - if (!loongarch_avec.vector_matrix) - return -ENOMEM; - for (i = 0; i < NR_LEGACY_VECTORS; i++) - irq_matrix_assign_system(loongarch_avec.vector_matrix, i, false); - - irq_matrix_online(loongarch_avec.vector_matrix); - - return 0; -} - -static int __init loongarch_avec_init(struct irq_domain *parent) -{ - struct pending_list *plist = per_cpu_ptr(&pending_list, 0); - int ret = 0, parent_irq; - unsigned long tmp; - - raw_spin_lock_init(&loongarch_avec.lock); - - loongarch_avec.fwnode = irq_domain_alloc_named_fwnode("CORE_AVEC"); - if (!loongarch_avec.fwnode) { - pr_err("Unable to allocate domain handle\n"); - ret = -ENOMEM; - goto out; - } - - loongarch_avec.domain = irq_domain_create_tree(loongarch_avec.fwnode, - &loongarch_avec_domain_ops, NULL); - if (!loongarch_avec.domain) { - pr_err("core-vec: cannot create IRQ domain\n"); - ret = -ENOMEM; - goto out_free_handle; - } - - parent_irq = irq_create_mapping(parent, INT_AVEC); - if (!parent_irq) { - pr_err("Failed to mapping hwirq\n"); - ret = -EINVAL; - goto out_remove_domain; - } - irq_set_chained_handler_and_data(parent_irq, loongarch_avec_dispatch, NULL); - - ret = irq_matrix_init(); - if (ret) { - pr_err("Failed to init irq matrix\n"); - goto out_free_matrix; - } - - INIT_LIST_HEAD(&plist->head); - tmp = iocsr_read64(LOONGARCH_IOCSR_MISC_FUNC); - tmp |= IOCSR_MISC_FUNC_AVEC_EN; - iocsr_write64(tmp, LOONGARCH_IOCSR_MISC_FUNC); - - return ret; - -out_free_matrix: - kfree(loongarch_avec.vector_matrix); -out_remove_domain: - irq_domain_remove(loongarch_avec.domain); -out_free_handle: - irq_domain_free_fwnode(loongarch_avec.fwnode); -out: - return ret; -} - -void loongarch_avec_offline_cpu(unsigned int cpu) -{ - struct pending_list *plist = per_cpu_ptr(&pending_list, cpu); - unsigned long flags; - - raw_spin_lock_irqsave(&loongarch_avec.lock, flags); - if (list_empty(&plist->head)) - irq_matrix_offline(loongarch_avec.vector_matrix); - else - pr_warn("cpu %d advanced extioi is busy\n", cpu); - raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags); -} - -void loongarch_avec_online_cpu(unsigned int cpu) -{ - struct pending_list *plist = per_cpu_ptr(&pending_list, cpu); - unsigned long flags; - - raw_spin_lock_irqsave(&loongarch_avec.lock, flags); - - irq_matrix_online(loongarch_avec.vector_matrix); - - INIT_LIST_HEAD(&plist->head); - - raw_spin_unlock_irqrestore(&loongarch_avec.lock, flags); -} - -static int __init pch_msi_parse_madt(union acpi_subtable_headers *header, - const unsigned long end) -{ - struct acpi_madt_msi_pic *pchmsi_entry = (struct acpi_madt_msi_pic *)header; - - msi_base_v2 = pchmsi_entry->msg_address - AVEC_MSG_OFFSET; - return pch_msi_acpi_init_v2(loongarch_avec.domain, pchmsi_entry); -} - -static inline int __init acpi_cascade_irqdomain_init(void) -{ - return acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, pch_msi_parse_madt, 1); -} - -int __init loongarch_avec_acpi_init(struct irq_domain *parent) -{ - int ret = 0; - - ret = loongarch_avec_init(parent); - if (ret) { - pr_err("Failed to init irq domain\n"); - return ret; - } - - ret = acpi_cascade_irqdomain_init(); - if (ret) { - pr_err("Failed to cascade IRQ domain\n"); - return ret; - } - - return ret; -} diff --git a/drivers/irqchip/irq-loongarch-cpu.c b/drivers/irqchip/irq-loongarch-cpu.c index 1ecac59925c6..9d8f2c406043 100644 --- a/drivers/irqchip/irq-loongarch-cpu.c +++ b/drivers/irqchip/irq-loongarch-cpu.c @@ -138,9 +138,7 @@ static int __init acpi_cascade_irqdomain_init(void) if (r < 0) return r; - if (cpu_has_avecint) - r = loongarch_avec_acpi_init(irq_domain); - return r; + return 0; } static int __init cpuintc_acpi_init(union acpi_subtable_headers *header, diff --git a/drivers/irqchip/irq-loongson-eiointc.c b/drivers/irqchip/irq-loongson-eiointc.c index 1f9a30488137..c7ddebf312ad 100644 --- a/drivers/irqchip/irq-loongson-eiointc.c +++ b/drivers/irqchip/irq-loongson-eiointc.c @@ -359,9 +359,6 @@ static int __init acpi_cascade_irqdomain_init(void) if (r < 0) return r; - if (cpu_has_avecint) - return 0; - r = acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, pch_msi_parse_madt, 1); if (r < 0) return r; diff --git a/drivers/irqchip/irq-loongson-pch-msi.c b/drivers/irqchip/irq-loongson-pch-msi.c index 1926857f9a41..dd4d699170f4 100644 --- a/drivers/irqchip/irq-loongson-pch-msi.c +++ b/drivers/irqchip/irq-loongson-pch-msi.c @@ -16,6 +16,7 @@ #include static int nr_pics; + struct pch_msi_data { struct mutex msi_map_lock; phys_addr_t doorbell; @@ -99,17 +100,6 @@ static struct irq_chip middle_irq_chip = { .irq_compose_msi_msg = pch_msi_compose_msi_msg, }; -static struct irq_chip pch_msi_irq_chip_v2 = { - .name = "MSI", - .irq_ack = irq_chip_ack_parent, -}; - -static struct msi_domain_info pch_msi_domain_info_v2 = { - .flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | - MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX, - .chip = &pch_msi_irq_chip_v2, -}; - static int pch_msi_parent_domain_alloc(struct irq_domain *domain, unsigned int virq, int hwirq) { @@ -278,9 +268,6 @@ struct fwnode_handle *get_pch_msi_handle(int pci_segment) { int i; - if (cpu_has_avecint) - return pch_msi_handle[0]; - for (i = 0; i < MAX_IO_PICS; i++) { if (msi_group[i].pci_segment == pci_segment) return pch_msi_handle[i]; @@ -302,32 +289,4 @@ int __init pch_msi_acpi_init(struct irq_domain *parent, return ret; } - -int __init pch_msi_acpi_init_v2(struct irq_domain *parent, - struct acpi_madt_msi_pic *msi_entry) -{ - struct irq_domain *msi_domain; - - if (pch_msi_handle[0]) - return 0; - - pch_msi_handle[0] = irq_domain_alloc_named_fwnode("msipic-v2"); - if (!pch_msi_handle[0]) { - pr_err("Unable to allocate domain handle\n"); - kfree(pch_msi_handle[0]); - return -ENOMEM; - } - - msi_domain = pci_msi_create_irq_domain(pch_msi_handle[0], - &pch_msi_domain_info_v2, - parent); - if (!msi_domain) { - pr_err("Failed to create PCI MSI domain\n"); - kfree(pch_msi_handle[0]); - return -ENOMEM; - } - - pr_info("IRQ domain MSIPIC-V2 init done.\n"); - return 0; -} #endif -- cgit v1.2.3 From be5e5f3a1120bada0cff1bc84c2a1805da308f6e Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 25 Jun 2024 21:30:48 +0200 Subject: Revert "irqchip/dw-apb-ictl: Support building as module" This reverts commit 7cc4f309c933ec5d64eea31066fe86bbf9e48819. Causes build fails. Reported-by: Mark Brown Reported-by: kernel test robot Signed-off-by: Thomas Gleixner Cc: Jisheng Zhang https://lore.kernel.org/oe-kbuild-all/202406250214.WZEjWnnU-lkp@intel.com/ --- drivers/irqchip/Kconfig | 2 +- drivers/irqchip/irq-dw-apb-ictl.c | 13 +++---------- 2 files changed, 4 insertions(+), 11 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index cbf49b6404b8..344c484736af 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -145,7 +145,7 @@ config DAVINCI_CP_INTC select IRQ_DOMAIN config DW_APB_ICTL - tristate "DesignWare APB Interrupt Controller" + bool select GENERIC_IRQ_CHIP select IRQ_DOMAIN_HIERARCHY diff --git a/drivers/irqchip/irq-dw-apb-ictl.c b/drivers/irqchip/irq-dw-apb-ictl.c index 5eda6c4689cf..d5c1c750c8d2 100644 --- a/drivers/irqchip/irq-dw-apb-ictl.c +++ b/drivers/irqchip/irq-dw-apb-ictl.c @@ -122,7 +122,7 @@ static int __init dw_apb_ictl_init(struct device_node *np, int ret, nrirqs, parent_irq, i; u32 reg; - if (!parent && IS_BUILTIN(CONFIG_DW_APB_ICTL)) { + if (!parent) { /* Used as the primary interrupt controller */ parent_irq = 0; domain_ops = &dw_apb_ictl_irq_domain_ops; @@ -214,12 +214,5 @@ err_release: release_mem_region(r.start, resource_size(&r)); return ret; } -#if IS_BUILTIN(CONFIG_DW_APB_ICTL) -IRQCHIP_DECLARE(dw_apb_ictl, "snps,dw-apb-ictl", dw_apb_ictl_init); -#else -IRQCHIP_PLATFORM_DRIVER_BEGIN(dw_apb_ictl) -IRQCHIP_MATCH("snps,dw-apb-ictl", dw_apb_ictl_init) -IRQCHIP_PLATFORM_DRIVER_END(dw_apb_ictl) -MODULE_DESCRIPTION("DesignWare APB Interrupt Controller"); -MODULE_LICENSE("GPL v2"); -#endif +IRQCHIP_DECLARE(dw_apb_ictl, + "snps,dw-apb-ictl", dw_apb_ictl_init); -- cgit v1.2.3 From 7d2c2048a86477461f7bc75d064579ed349472bc Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 5 Jul 2024 10:31:53 +0100 Subject: irqchip/gic-v4: Always configure affinity on VPE activation There are currently two paths to set the initial affinity of a VPE: - at activation time on GICv4 without the stupid VMOVP list, and on GICv4.1 - at map time for GICv4 with VMOVP list The latter location may end-up modifying the affinity of VPE that is currently running, making the results unpredictible. Instead, unify the two paths, making sure to set the initial affinity only at activation time. Reported-by: Nianyao Tang Signed-off-by: Marc Zyngier Signed-off-by: Thomas Gleixner Tested-by: Nianyao Tang Link: https://lore.kernel.org/r/20240705093155.871070-2-maz@kernel.org --- drivers/irqchip/irq-gic-v3-its.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index af5297ef2293..387ac5a31d1d 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -1809,13 +1809,9 @@ static void its_map_vm(struct its_node *its, struct its_vm *vm) for (i = 0; i < vm->nr_vpes; i++) { struct its_vpe *vpe = vm->vpes[i]; - struct irq_data *d = irq_get_irq_data(vpe->irq); - /* Map the VPE to the first possible CPU */ - vpe->col_idx = cpumask_first(cpu_online_mask); its_send_vmapp(its, vpe, true); its_send_vinvall(its, vpe); - irq_data_update_effective_affinity(d, cpumask_of(vpe->col_idx)); } } @@ -4582,6 +4578,10 @@ static int its_vpe_irq_domain_activate(struct irq_domain *domain, struct its_vpe *vpe = irq_data_get_irq_chip_data(d); struct its_node *its; + /* Map the VPE to the first possible CPU */ + vpe->col_idx = cpumask_first(cpu_online_mask); + irq_data_update_effective_affinity(d, cpumask_of(vpe->col_idx)); + /* * If we use the list map, we issue VMAPP on demand... Unless * we're on a GICv4.1 and we eagerly map the VPE on all ITSs @@ -4590,9 +4590,6 @@ static int its_vpe_irq_domain_activate(struct irq_domain *domain, if (!gic_requires_eager_mapping()) return 0; - /* Map the VPE to the first possible CPU */ - vpe->col_idx = cpumask_first(cpu_online_mask); - list_for_each_entry(its, &its_nodes, entry) { if (!is_v4(its)) continue; @@ -4601,8 +4598,6 @@ static int its_vpe_irq_domain_activate(struct irq_domain *domain, its_send_vinvall(its, vpe); } - irq_data_update_effective_affinity(d, cpumask_of(vpe->col_idx)); - return 0; } -- cgit v1.2.3 From f0eb154c39471bf881422e8ac23e4c037289ece9 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 5 Jul 2024 10:31:54 +0100 Subject: irqchip/gic-v4: Substitute vmovp_lock for a per-VM lock vmovp_lock is abused in a number of cases to serialise updates to vlpi_count[] and deal with map/unmap of a VM to ITSs. Instead, provide a per-VM lock and revisit the use of vlpi_count[] so that it is always wrapped in this per-VM vmapp_lock. This reduces the potential contention on a concurrent VMOVP command, and paves the way for subsequent VPE locking that holding vmovp_lock actively prevents due to the lock ordering. Signed-off-by: Marc Zyngier Signed-off-by: Thomas Gleixner Tested-by: Nianyao Tang Link: https://lore.kernel.org/r/20240705093155.871070-3-maz@kernel.org --- drivers/irqchip/irq-gic-v3-its.c | 27 ++++++++++++--------------- include/linux/irqchip/arm-gic-v4.h | 8 ++++++++ 2 files changed, 20 insertions(+), 15 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 387ac5a31d1d..215c7ab74e3a 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -1317,7 +1317,6 @@ static void its_send_vmovp(struct its_vpe *vpe) { struct its_cmd_desc desc = {}; struct its_node *its; - unsigned long flags; int col_id = vpe->col_idx; desc.its_vmovp_cmd.vpe = vpe; @@ -1329,6 +1328,12 @@ static void its_send_vmovp(struct its_vpe *vpe) return; } + /* + * Protect against concurrent updates of the mapping state on + * individual VMs. + */ + guard(raw_spinlock_irqsave)(&vpe->its_vm->vmapp_lock); + /* * Yet another marvel of the architecture. If using the * its_list "feature", we need to make sure that all ITSs @@ -1337,8 +1342,7 @@ static void its_send_vmovp(struct its_vpe *vpe) * * Wall <-- Head. */ - raw_spin_lock_irqsave(&vmovp_lock, flags); - + guard(raw_spinlock)(&vmovp_lock); desc.its_vmovp_cmd.seq_num = vmovp_seq_num++; desc.its_vmovp_cmd.its_list = get_its_list(vpe->its_vm); @@ -1353,8 +1357,6 @@ static void its_send_vmovp(struct its_vpe *vpe) desc.its_vmovp_cmd.col = &its->collections[col_id]; its_send_single_vcommand(its, its_build_vmovp_cmd, &desc); } - - raw_spin_unlock_irqrestore(&vmovp_lock, flags); } static void its_send_vinvall(struct its_node *its, struct its_vpe *vpe) @@ -1791,12 +1793,10 @@ static bool gic_requires_eager_mapping(void) static void its_map_vm(struct its_node *its, struct its_vm *vm) { - unsigned long flags; - if (gic_requires_eager_mapping()) return; - raw_spin_lock_irqsave(&vmovp_lock, flags); + guard(raw_spinlock_irqsave)(&vm->vmapp_lock); /* * If the VM wasn't mapped yet, iterate over the vpes and get @@ -1814,19 +1814,15 @@ static void its_map_vm(struct its_node *its, struct its_vm *vm) its_send_vinvall(its, vpe); } } - - raw_spin_unlock_irqrestore(&vmovp_lock, flags); } static void its_unmap_vm(struct its_node *its, struct its_vm *vm) { - unsigned long flags; - /* Not using the ITS list? Everything is always mapped. */ if (gic_requires_eager_mapping()) return; - raw_spin_lock_irqsave(&vmovp_lock, flags); + guard(raw_spinlock_irqsave)(&vm->vmapp_lock); if (!--vm->vlpi_count[its->list_nr]) { int i; @@ -1834,8 +1830,6 @@ static void its_unmap_vm(struct its_node *its, struct its_vm *vm) for (i = 0; i < vm->nr_vpes; i++) its_send_vmapp(its, vm->vpes[i], false); } - - raw_spin_unlock_irqrestore(&vmovp_lock, flags); } static int its_vlpi_map(struct irq_data *d, struct its_cmd_info *info) @@ -3942,6 +3936,8 @@ static void its_vpe_invall(struct its_vpe *vpe) { struct its_node *its; + guard(raw_spinlock_irqsave)(&vpe->its_vm->vmapp_lock); + list_for_each_entry(its, &its_nodes, entry) { if (!is_v4(its)) continue; @@ -4547,6 +4543,7 @@ static int its_vpe_irq_domain_alloc(struct irq_domain *domain, unsigned int virq vm->db_lpi_base = base; vm->nr_db_lpis = nr_ids; vm->vprop_page = vprop_page; + raw_spin_lock_init(&vm->vmapp_lock); if (gic_rdists->has_rvpeid) irqchip = &its_vpe_4_1_irq_chip; diff --git a/include/linux/irqchip/arm-gic-v4.h b/include/linux/irqchip/arm-gic-v4.h index 2c63375bbd43..ecabed6d3307 100644 --- a/include/linux/irqchip/arm-gic-v4.h +++ b/include/linux/irqchip/arm-gic-v4.h @@ -25,6 +25,14 @@ struct its_vm { irq_hw_number_t db_lpi_base; unsigned long *db_bitmap; int nr_db_lpis; + /* + * Ensures mutual exclusion between updates to vlpi_count[] + * and map/unmap when using the ITSList mechanism. + * + * The lock order for any sequence involving the ITSList is + * vmapp_lock -> vpe_lock ->vmovp_lock. + */ + raw_spinlock_t vmapp_lock; u32 vlpi_count[GICv4_ITS_LIST_MAX]; }; -- cgit v1.2.3 From a84a07fa3100d7ad46a3d6882af25a3df9c9e7e3 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 5 Jul 2024 10:31:55 +0100 Subject: irqchip/gic-v4: Make sure a VPE is locked when VMAPP is issued In order to make sure that vpe->col_idx is correctly sampled when a VMAPP command is issued, the vpe_lock must be held for the VPE. This is now possible since the introduction of the per-VM vmapp_lock, which can be taken before vpe_lock in the correct locking order. Signed-off-by: Marc Zyngier Signed-off-by: Thomas Gleixner Tested-by: Nianyao Tang Link: https://lore.kernel.org/r/20240705093155.871070-4-maz@kernel.org --- drivers/irqchip/irq-gic-v3-its.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 215c7ab74e3a..c23a64fcf9b3 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -1810,7 +1810,9 @@ static void its_map_vm(struct its_node *its, struct its_vm *vm) for (i = 0; i < vm->nr_vpes; i++) { struct its_vpe *vpe = vm->vpes[i]; - its_send_vmapp(its, vpe, true); + scoped_guard(raw_spinlock, &vpe->vpe_lock) + its_send_vmapp(its, vpe, true); + its_send_vinvall(its, vpe); } } @@ -1827,8 +1829,10 @@ static void its_unmap_vm(struct its_node *its, struct its_vm *vm) if (!--vm->vlpi_count[its->list_nr]) { int i; - for (i = 0; i < vm->nr_vpes; i++) + for (i = 0; i < vm->nr_vpes; i++) { + guard(raw_spinlock)(&vm->vpes[i]->vpe_lock); its_send_vmapp(its, vm->vpes[i], false); + } } } -- cgit v1.2.3 From 9a58480e5e532903f08b2a34f05076d3ec3a5c00 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Sun, 30 Jun 2024 17:36:46 +0200 Subject: irqchip/bcm2835: Enable SKIP_SET_WAKE and MASK_ON_SUSPEND The BCM2835 ARMCTRL interrupt controller doesn't provide any facility to configure the wakeup sources. That's the reason why the driver lacks the irq_set_wake() callback for the interrupt chip. But this prevent to properly enter power management states like "suspend to idle". Enable the flags IRQCHIP_SKIP_SET_WAKE and IRQCHIP_MASK_ON_SUSPEND so the interrupt suspend logic can handle the chip correctly. Signed-off-by: Stefan Wahren Signed-off-by: Thomas Gleixner Reviewed-by: Florian Fainelli --- drivers/irqchip/irq-bcm2835.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-bcm2835.c b/drivers/irqchip/irq-bcm2835.c index e94e2882286c..6c20604c2242 100644 --- a/drivers/irqchip/irq-bcm2835.c +++ b/drivers/irqchip/irq-bcm2835.c @@ -102,7 +102,9 @@ static void armctrl_unmask_irq(struct irq_data *d) static struct irq_chip armctrl_chip = { .name = "ARMCTRL-level", .irq_mask = armctrl_mask_irq, - .irq_unmask = armctrl_unmask_irq + .irq_unmask = armctrl_unmask_irq, + .flags = IRQCHIP_MASK_ON_SUSPEND | + IRQCHIP_SKIP_SET_WAKE, }; static int armctrl_xlate(struct irq_domain *d, struct device_node *ctrlr, -- cgit v1.2.3 From 10697eee6a6ff59207663f536dc8e8de7a4fd3e7 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 2 Jul 2024 11:24:14 +0200 Subject: irqchip/gic-v3: Pass #redistributor-regions to gic_of_setup_kvm_info() The caller of gic_of_setup_kvm_info() already queried DT for the value of the #redistributor-regions property. So just pass this value, instead of doing the DT look-up again in the callee. Signed-off-by: Geert Uytterhoeven Signed-off-by: Thomas Gleixner Acked-by: Marc Zyngier Link: https://lore.kernel.org/r/808286a3ac08f60585ae7e2c848e0f9b3cb79cf8.1719912215.git.geert+renesas@glider.be --- drivers/irqchip/irq-gic-v3.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index e4bc5f04226e..7c12d11513c6 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -2190,11 +2190,10 @@ out_put_node: of_node_put(parts_node); } -static void __init gic_of_setup_kvm_info(struct device_node *node) +static void __init gic_of_setup_kvm_info(struct device_node *node, u32 nr_redist_regions) { int ret; struct resource r; - u32 gicv_idx; gic_v3_kvm_info.type = GIC_V3; @@ -2202,12 +2201,8 @@ static void __init gic_of_setup_kvm_info(struct device_node *node) if (!gic_v3_kvm_info.maint_irq) return; - if (of_property_read_u32(node, "#redistributor-regions", - &gicv_idx)) - gicv_idx = 1; - - gicv_idx += 3; /* Also skip GICD, GICC, GICH */ - ret = of_address_to_resource(node, gicv_idx, &r); + /* Also skip GICD, GICC, GICH */ + ret = of_address_to_resource(node, nr_redist_regions + 3, &r); if (!ret) gic_v3_kvm_info.vcpu = r; @@ -2297,7 +2292,7 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare gic_populate_ppi_partitions(node); if (static_branch_likely(&supports_deactivate_key)) - gic_of_setup_kvm_info(node); + gic_of_setup_kvm_info(node, nr_redist_regions); return 0; out_unmap_rdist: -- cgit v1.2.3 From 33b1c47d1fc0b5f06a393bb915db85baacba18ea Mon Sep 17 00:00:00 2001 From: Shenwei Wang Date: Wed, 3 Jul 2024 11:32:50 -0500 Subject: irqchip/imx-irqsteer: Handle runtime power management correctly The power domain is automatically activated from clk_prepare(). However, on certain platforms like i.MX8QM and i.MX8QXP, the power-on handling invokes sleeping functions, which triggers the 'scheduling while atomic' bug in the context switch path during device probing: BUG: scheduling while atomic: kworker/u13:1/48/0x00000002 Call trace: __schedule_bug+0x54/0x6c __schedule+0x7f0/0xa94 schedule+0x5c/0xc4 schedule_preempt_disabled+0x24/0x40 __mutex_lock.constprop.0+0x2c0/0x540 __mutex_lock_slowpath+0x14/0x20 mutex_lock+0x48/0x54 clk_prepare_lock+0x44/0xa0 clk_prepare+0x20/0x44 imx_irqsteer_resume+0x28/0xe0 pm_generic_runtime_resume+0x2c/0x44 __genpd_runtime_resume+0x30/0x80 genpd_runtime_resume+0xc8/0x2c0 __rpm_callback+0x48/0x1d8 rpm_callback+0x6c/0x78 rpm_resume+0x490/0x6b4 __pm_runtime_resume+0x50/0x94 irq_chip_pm_get+0x2c/0xa0 __irq_do_set_handler+0x178/0x24c irq_set_chained_handler_and_data+0x60/0xa4 mxc_gpio_probe+0x160/0x4b0 Cure this by implementing the irq_bus_lock/sync_unlock() interrupt chip callbacks and handle power management in them as they are invoked from non-atomic context. [ tglx: Rewrote change log, added Fixes tag ] Fixes: 0136afa08967 ("irqchip: Add driver for imx-irqsteer controller") Signed-off-by: Shenwei Wang Signed-off-by: Thomas Gleixner Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20240703163250.47887-1-shenwei.wang@nxp.com --- drivers/irqchip/irq-imx-irqsteer.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-imx-irqsteer.c b/drivers/irqchip/irq-imx-irqsteer.c index 20cf7a9e9ece..75a0e980ff35 100644 --- a/drivers/irqchip/irq-imx-irqsteer.c +++ b/drivers/irqchip/irq-imx-irqsteer.c @@ -36,6 +36,7 @@ struct irqsteer_data { int channel; struct irq_domain *domain; u32 *saved_reg; + struct device *dev; }; static int imx_irqsteer_get_reg_index(struct irqsteer_data *data, @@ -72,10 +73,26 @@ static void imx_irqsteer_irq_mask(struct irq_data *d) raw_spin_unlock_irqrestore(&data->lock, flags); } +static void imx_irqsteer_irq_bus_lock(struct irq_data *d) +{ + struct irqsteer_data *data = d->chip_data; + + pm_runtime_get_sync(data->dev); +} + +static void imx_irqsteer_irq_bus_sync_unlock(struct irq_data *d) +{ + struct irqsteer_data *data = d->chip_data; + + pm_runtime_put_autosuspend(data->dev); +} + static const struct irq_chip imx_irqsteer_irq_chip = { - .name = "irqsteer", - .irq_mask = imx_irqsteer_irq_mask, - .irq_unmask = imx_irqsteer_irq_unmask, + .name = "irqsteer", + .irq_mask = imx_irqsteer_irq_mask, + .irq_unmask = imx_irqsteer_irq_unmask, + .irq_bus_lock = imx_irqsteer_irq_bus_lock, + .irq_bus_sync_unlock = imx_irqsteer_irq_bus_sync_unlock, }; static int imx_irqsteer_irq_map(struct irq_domain *h, unsigned int irq, @@ -150,6 +167,7 @@ static int imx_irqsteer_probe(struct platform_device *pdev) if (!data) return -ENOMEM; + data->dev = &pdev->dev; data->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(data->regs)) { dev_err(&pdev->dev, "failed to initialize reg\n"); -- cgit v1.2.3