From 6da4b3ab9a6e9b1b5f90322ab3fa3a7dd18edb19 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Fri, 2 Nov 2018 22:59:51 +0800 Subject: genirq/affinity: Add support for allocating interrupt sets A driver may have a need to allocate multiple sets of MSI/MSI-X interrupts, and have them appropriately affinitized. Add support for defining a number of sets in the irq_affinity structure, of varying sizes, and get each set affinitized correctly across the machine. [ tglx: Minor changelog tweaks ] Signed-off-by: Jens Axboe Signed-off-by: Ming Lei Signed-off-by: Thomas Gleixner Reviewed-by: Hannes Reinecke Reviewed-by: Ming Lei Reviewed-by: Keith Busch Reviewed-by: Sagi Grimberg Cc: linux-block@vger.kernel.org Link: https://lkml.kernel.org/r/20181102145951.31979-5-ming.lei@redhat.com --- include/linux/interrupt.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 1d6711c28271..ca397ff40836 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -247,10 +247,14 @@ struct irq_affinity_notify { * the MSI(-X) vector space * @post_vectors: Don't apply affinity to @post_vectors at end of * the MSI(-X) vector space + * @nr_sets: Length of passed in *sets array + * @sets: Number of affinitized sets */ struct irq_affinity { int pre_vectors; int post_vectors; + int nr_sets; + int *sets; }; #if defined(CONFIG_SMP) -- cgit v1.2.3 From 81b1e6e6a8590a19257e37a1633bec098d499c57 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 11 Oct 2018 11:12:34 +0200 Subject: platform-msi: Free descriptors in platform_msi_domain_free() Since the addition of platform MSI support, there were two helpers supposed to allocate/free IRQs for a device: platform_msi_domain_alloc_irqs() platform_msi_domain_free_irqs() In these helpers, IRQ descriptors are allocated in the "alloc" routine while they are freed in the "free" one. Later, two other helpers have been added to handle IRQ domains on top of MSI domains: platform_msi_domain_alloc() platform_msi_domain_free() Seen from the outside, the logic is pretty close with the former helpers and people used it with the same logic as before: a platform_msi_domain_alloc() call should be balanced with a platform_msi_domain_free() call. While this is probably what was intended to do, the platform_msi_domain_free() does not remove/free the IRQ descriptor(s) created/inserted in platform_msi_domain_alloc(). One effect of such situation is that removing a module that requested an IRQ will let one orphaned IRQ descriptor (with an allocated MSI entry) in the device descriptors list. Next time the module will be inserted back, one will observe that the allocation will happen twice in the MSI domain, one time for the remaining descriptor, one time for the new one. It also has the side effect to quickly overshoot the maximum number of allocated MSI and then prevent any module requesting an interrupt in the same domain to be inserted anymore. This situation has been met with loops of insertion/removal of the mvpp2.ko module (requesting 15 MSIs each time). Fixes: 552c494a7666 ("platform-msi: Allow creation of a MSI-based stacked irq domain") Cc: stable@vger.kernel.org Signed-off-by: Miquel Raynal Signed-off-by: Marc Zyngier --- drivers/base/platform-msi.c | 6 ++++-- include/linux/msi.h | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/base/platform-msi.c b/drivers/base/platform-msi.c index f39a920496fb..8da314b81eab 100644 --- a/drivers/base/platform-msi.c +++ b/drivers/base/platform-msi.c @@ -368,14 +368,16 @@ void platform_msi_domain_free(struct irq_domain *domain, unsigned int virq, unsigned int nvec) { struct platform_msi_priv_data *data = domain->host_data; - struct msi_desc *desc; - for_each_msi_entry(desc, data->dev) { + struct msi_desc *desc, *tmp; + for_each_msi_entry_safe(desc, tmp, data->dev) { if (WARN_ON(!desc->irq || desc->nvec_used != 1)) return; if (!(desc->irq >= virq && desc->irq < (virq + nvec))) continue; irq_domain_free_irqs_common(domain, desc->irq, 1); + list_del(&desc->list); + free_msi_entry(desc); } } diff --git a/include/linux/msi.h b/include/linux/msi.h index 0e9c50052ff3..eb213b87617c 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -116,6 +116,8 @@ struct msi_desc { list_first_entry(dev_to_msi_list((dev)), struct msi_desc, list) #define for_each_msi_entry(desc, dev) \ list_for_each_entry((desc), dev_to_msi_list((dev)), list) +#define for_each_msi_entry_safe(desc, tmp, dev) \ + list_for_each_entry_safe((desc), (tmp), dev_to_msi_list((dev)), list) #ifdef CONFIG_PCI_MSI #define first_pci_msi_entry(pdev) first_msi_entry(&(pdev)->dev) -- cgit v1.2.3 From 06459901d55ee2f690b8e1fe084fb03061d617cf Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 9 Nov 2018 18:21:32 +0100 Subject: irq/irq_sim: Store multiple interrupt offsets in a bitmap Two threads can try to fire the irq_sim with different offsets and will end up fighting for the irq_work asignment. Thomas Gleixner suggested a solution based on a bitfield where we set a bit for every offset associated with an interrupt that should be fired and then iterate over all set bits in the interrupt handler. This is a slightly modified solution using a bitmap so that we don't impose a limit on the number of interrupts one can allocate with irq_sim. Suggested-by: Thomas Gleixner Signed-off-by: Bartosz Golaszewski Signed-off-by: Marc Zyngier --- include/linux/irq_sim.h | 2 +- kernel/irq/irq_sim.c | 23 +++++++++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/irq_sim.h b/include/linux/irq_sim.h index 630a57e55db6..4500d453a63e 100644 --- a/include/linux/irq_sim.h +++ b/include/linux/irq_sim.h @@ -16,7 +16,7 @@ struct irq_sim_work_ctx { struct irq_work work; - int irq; + unsigned long *pending; }; struct irq_sim_irq_ctx { diff --git a/kernel/irq/irq_sim.c b/kernel/irq/irq_sim.c index dd20d0d528d4..98a20e1594ce 100644 --- a/kernel/irq/irq_sim.c +++ b/kernel/irq/irq_sim.c @@ -34,9 +34,20 @@ static struct irq_chip irq_sim_irqchip = { static void irq_sim_handle_irq(struct irq_work *work) { struct irq_sim_work_ctx *work_ctx; + unsigned int offset = 0; + struct irq_sim *sim; + int irqnum; work_ctx = container_of(work, struct irq_sim_work_ctx, work); - handle_simple_irq(irq_to_desc(work_ctx->irq)); + sim = container_of(work_ctx, struct irq_sim, work_ctx); + + while (!bitmap_empty(work_ctx->pending, sim->irq_count)) { + offset = find_next_bit(work_ctx->pending, + sim->irq_count, offset); + clear_bit(offset, work_ctx->pending); + irqnum = irq_sim_irqnum(sim, offset); + handle_simple_irq(irq_to_desc(irqnum)); + } } /** @@ -63,6 +74,13 @@ int irq_sim_init(struct irq_sim *sim, unsigned int num_irqs) return sim->irq_base; } + sim->work_ctx.pending = bitmap_zalloc(num_irqs, GFP_KERNEL); + if (!sim->work_ctx.pending) { + kfree(sim->irqs); + irq_free_descs(sim->irq_base, num_irqs); + return -ENOMEM; + } + for (i = 0; i < num_irqs; i++) { sim->irqs[i].irqnum = sim->irq_base + i; sim->irqs[i].enabled = false; @@ -89,6 +107,7 @@ EXPORT_SYMBOL_GPL(irq_sim_init); void irq_sim_fini(struct irq_sim *sim) { irq_work_sync(&sim->work_ctx.work); + bitmap_free(sim->work_ctx.pending); irq_free_descs(sim->irq_base, sim->irq_count); kfree(sim->irqs); } @@ -143,7 +162,7 @@ EXPORT_SYMBOL_GPL(devm_irq_sim_init); void irq_sim_fire(struct irq_sim *sim, unsigned int offset) { if (sim->irqs[offset].enabled) { - sim->work_ctx.irq = irq_sim_irqnum(sim, offset); + set_bit(offset, sim->work_ctx.pending); irq_work_queue(&sim->work_ctx.work); } } -- cgit v1.2.3 From c5f48c0a7aa1a8c82d81cdf27e63aa0a5544c6e6 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 3 Dec 2018 11:44:51 +0100 Subject: genirq: Fix various typos in comments Go over the IRQ subsystem source code (including irqchip drivers) and fix common typos in comments. No change in functionality intended. Signed-off-by: Ingo Molnar Cc: Thomas Gleixner Cc: Jason Cooper Cc: Marc Zyngier Cc: Peter Zijlstra Cc: Linus Torvalds Cc: linux-kernel@vger.kernel.org --- drivers/irqchip/irq-dw-apb-ictl.c | 2 +- drivers/irqchip/irq-gic.c | 6 +++--- drivers/irqchip/irq-renesas-h8s.c | 2 +- drivers/irqchip/irq-s3c24xx.c | 2 +- include/linux/irqchip.h | 4 ++-- kernel/irq/chip.c | 2 +- kernel/irq/ipi.c | 4 ++-- kernel/irq/manage.c | 2 +- kernel/irq/spurious.c | 6 +++--- 9 files changed, 15 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/irqchip/irq-dw-apb-ictl.c b/drivers/irqchip/irq-dw-apb-ictl.c index 0a19618ce2c8..e4550e9c810b 100644 --- a/drivers/irqchip/irq-dw-apb-ictl.c +++ b/drivers/irqchip/irq-dw-apb-ictl.c @@ -105,7 +105,7 @@ static int __init dw_apb_ictl_init(struct device_node *np, * DW IP can be configured to allow 2-64 irqs. We can determine * the number of irqs supported by writing into enable register * and look for bits not set, as corresponding flip-flops will - * have been removed by sythesis tool. + * have been removed by synthesis tool. */ /* mask and enable all interrupts */ diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index ced10c44b68a..ba2a37a27a54 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -604,8 +604,8 @@ void gic_dist_save(struct gic_chip_data *gic) /* * Restores the GIC distributor registers during resume or when coming out of * idle. Must be called before enabling interrupts. If a level interrupt - * that occured while the GIC was suspended is still present, it will be - * handled normally, but any edge interrupts that occured will not be seen by + * that occurred while the GIC was suspended is still present, it will be + * handled normally, but any edge interrupts that occurred will not be seen by * the GIC and need to be handled by the platform-specific wakeup source. */ void gic_dist_restore(struct gic_chip_data *gic) @@ -899,7 +899,7 @@ void gic_migrate_target(unsigned int new_cpu_id) gic_cpu_map[cpu] = 1 << new_cpu_id; /* - * Find all the peripheral interrupts targetting the current + * Find all the peripheral interrupts targeting the current * CPU interface and migrate them to the new CPU interface. * We skip DIST_TARGET 0 to 7 as they are read-only. */ diff --git a/drivers/irqchip/irq-renesas-h8s.c b/drivers/irqchip/irq-renesas-h8s.c index 85234d456638..4e2461bae944 100644 --- a/drivers/irqchip/irq-renesas-h8s.c +++ b/drivers/irqchip/irq-renesas-h8s.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * H8S interrupt contoller driver + * H8S interrupt controller driver * * Copyright 2015 Yoshinori Sato */ diff --git a/drivers/irqchip/irq-s3c24xx.c b/drivers/irqchip/irq-s3c24xx.c index c19766fe8a1a..b623f300f1b1 100644 --- a/drivers/irqchip/irq-s3c24xx.c +++ b/drivers/irqchip/irq-s3c24xx.c @@ -58,7 +58,7 @@ struct s3c_irq_data { }; /* - * Sructure holding the controller data + * Structure holding the controller data * @reg_pending register holding pending irqs * @reg_intpnd special register intpnd in main intc * @reg_mask mask register diff --git a/include/linux/irqchip.h b/include/linux/irqchip.h index 89c34b200671..950e4b2458f0 100644 --- a/include/linux/irqchip.h +++ b/include/linux/irqchip.h @@ -19,7 +19,7 @@ * the association between their DT compatible string and their * initialization function. * - * @name: name that must be unique accross all IRQCHIP_DECLARE of the + * @name: name that must be unique across all IRQCHIP_DECLARE of the * same file. * @compstr: compatible string of the irqchip driver * @fn: initialization function @@ -30,7 +30,7 @@ * This macro must be used by the different irqchip drivers to declare * the association between their version and their initialization function. * - * @name: name that must be unique accross all IRQCHIP_ACPI_DECLARE of the + * @name: name that must be unique across all IRQCHIP_ACPI_DECLARE of the * same file. * @subtable: Subtable to be identified in MADT * @validate: Function to be called on that subtable to check its validity. diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index a2b3d9de999c..34e969069488 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -929,7 +929,7 @@ __irq_do_set_handler(struct irq_desc *desc, irq_flow_handler_t handle, break; /* * Bail out if the outer chip is not set up - * and the interrrupt supposed to be started + * and the interrupt supposed to be started * right away. */ if (WARN_ON(is_chained)) diff --git a/kernel/irq/ipi.c b/kernel/irq/ipi.c index 8b778e37dc6d..43e3d1be622c 100644 --- a/kernel/irq/ipi.c +++ b/kernel/irq/ipi.c @@ -56,7 +56,7 @@ int irq_reserve_ipi(struct irq_domain *domain, unsigned int next; /* - * The IPI requires a seperate HW irq on each CPU. We require + * The IPI requires a separate HW irq on each CPU. We require * that the destination mask is consecutive. If an * implementation needs to support holes, it can reserve * several IPI ranges. @@ -172,7 +172,7 @@ irq_hw_number_t ipi_get_hwirq(unsigned int irq, unsigned int cpu) /* * Get the real hardware irq number if the underlying implementation - * uses a seperate irq per cpu. If the underlying implementation uses + * uses a separate irq per cpu. If the underlying implementation uses * a single hardware irq for all cpus then the IPI send mechanism * needs to take care of the cpu destinations. */ diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 9dbdccab3b6a..a4888ce4667a 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -915,7 +915,7 @@ irq_thread_check_affinity(struct irq_desc *desc, struct irqaction *action) { } #endif /* - * Interrupts which are not explicitely requested as threaded + * Interrupts which are not explicitly requested as threaded * interrupts rely on the implicit bh/preempt disable of the hard irq * context. So we need to disable bh here to avoid deadlocks and other * side effects. diff --git a/kernel/irq/spurious.c b/kernel/irq/spurious.c index d867d6ddafdd..6d2fa6914b30 100644 --- a/kernel/irq/spurious.c +++ b/kernel/irq/spurious.c @@ -66,7 +66,7 @@ static int try_one_irq(struct irq_desc *desc, bool force) raw_spin_lock(&desc->lock); /* - * PER_CPU, nested thread interrupts and interrupts explicitely + * PER_CPU, nested thread interrupts and interrupts explicitly * marked polled are excluded from polling. */ if (irq_settings_is_per_cpu(desc) || @@ -76,7 +76,7 @@ static int try_one_irq(struct irq_desc *desc, bool force) /* * Do not poll disabled interrupts unless the spurious - * disabled poller asks explicitely. + * disabled poller asks explicitly. */ if (irqd_irq_disabled(&desc->irq_data) && !force) goto out; @@ -292,7 +292,7 @@ void note_interrupt(struct irq_desc *desc, irqreturn_t action_ret) * So in case a thread is woken, we just note the fact and * defer the analysis to the next hardware interrupt. * - * The threaded handlers store whether they sucessfully + * The threaded handlers store whether they successfully * handled an interrupt and we check whether that number * changed versus the last invocation. * -- cgit v1.2.3 From da0abe1a04110491697ca9ff146e1107f40b4808 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Fri, 14 Dec 2018 14:44:16 +0000 Subject: irqchip: Add driver for Cirrus Logic Madera codecs The Cirrus Logic Madera codecs (Cirrus Logic CS47L35/85/90/91 and WM1840) are highly complex devices containing up to 7 programmable DSPs and many other internal sources of interrupts plus a number of GPIOs that can be used as interrupt inputs. The large number (>150) of internal interrupt sources are managed by an on-board interrupt controller. This driver provides the handling for the interrupt controller. As the codec is accessed via regmap, we can make use of the generic IRQ functionality from regmap to do most of the work. Only around half of the possible interrupt source are currently of interest from the driver so only this subset is defined. Others can be added in future if needed. The KConfig options are not user-configurable because this driver is mandatory so is automatically included when the parent MFD driver is selected. Signed-off-by: Richard Fitzgerald Signed-off-by: Charles Keepax Signed-off-by: Marc Zyngier --- MAINTAINERS | 2 + drivers/irqchip/Kconfig | 3 + drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-madera.c | 256 +++++++++++++++++++++++++++++++++++++ include/linux/irqchip/irq-madera.h | 132 +++++++++++++++++++ 5 files changed, 394 insertions(+) create mode 100644 drivers/irqchip/irq-madera.c create mode 100644 include/linux/irqchip/irq-madera.h (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index 380e43f585d3..9bc599d96400 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3611,8 +3611,10 @@ W: https://github.com/CirrusLogic/linux-drivers/wiki S: Supported F: Documentation/devicetree/bindings/mfd/madera.txt F: Documentation/devicetree/bindings/pinctrl/cirrus,madera-pinctrl.txt +F: include/linux/irqchip/irq-madera* F: include/linux/mfd/madera/* F: drivers/gpio/gpio-madera* +F: drivers/irqchip/irq-madera* F: drivers/mfd/madera* F: drivers/mfd/cs47l* F: drivers/pinctrl/cirrus/* diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 9d54645870ad..bab0b97b5b1f 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -150,6 +150,9 @@ config IMGPDC_IRQ select GENERIC_IRQ_CHIP select IRQ_DOMAIN +config MADERA_IRQ + tristate + config IRQ_MIPS_CPU bool select GENERIC_IRQ_CHIP diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 417108027e40..bc53a58bd403 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -92,3 +92,4 @@ obj-$(CONFIG_QCOM_PDC) += qcom-pdc.o obj-$(CONFIG_CSKY_MPINTC) += irq-csky-mpintc.o obj-$(CONFIG_CSKY_APB_INTC) += irq-csky-apb-intc.o obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o +obj-$(CONFIG_MADERA_IRQ) += irq-madera.o diff --git a/drivers/irqchip/irq-madera.c b/drivers/irqchip/irq-madera.c new file mode 100644 index 000000000000..e9256dee1a45 --- /dev/null +++ b/drivers/irqchip/irq-madera.c @@ -0,0 +1,256 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Interrupt support for Cirrus Logic Madera codecs + * + * Copyright (C) 2015-2018 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MADERA_IRQ(_irq, _reg) \ + [MADERA_IRQ_ ## _irq] = { \ + .reg_offset = (_reg) - MADERA_IRQ1_STATUS_2, \ + .mask = MADERA_ ## _irq ## _EINT1 \ + } + +/* Mappings are the same for all Madera codecs */ +static const struct regmap_irq madera_irqs[MADERA_NUM_IRQ] = { + MADERA_IRQ(FLL1_LOCK, MADERA_IRQ1_STATUS_2), + MADERA_IRQ(FLL2_LOCK, MADERA_IRQ1_STATUS_2), + MADERA_IRQ(FLL3_LOCK, MADERA_IRQ1_STATUS_2), + MADERA_IRQ(FLLAO_LOCK, MADERA_IRQ1_STATUS_2), + + MADERA_IRQ(MICDET1, MADERA_IRQ1_STATUS_6), + MADERA_IRQ(MICDET2, MADERA_IRQ1_STATUS_6), + MADERA_IRQ(HPDET, MADERA_IRQ1_STATUS_6), + + MADERA_IRQ(MICD_CLAMP_RISE, MADERA_IRQ1_STATUS_7), + MADERA_IRQ(MICD_CLAMP_FALL, MADERA_IRQ1_STATUS_7), + MADERA_IRQ(JD1_RISE, MADERA_IRQ1_STATUS_7), + MADERA_IRQ(JD1_FALL, MADERA_IRQ1_STATUS_7), + + MADERA_IRQ(ASRC2_IN1_LOCK, MADERA_IRQ1_STATUS_9), + MADERA_IRQ(ASRC2_IN2_LOCK, MADERA_IRQ1_STATUS_9), + MADERA_IRQ(ASRC1_IN1_LOCK, MADERA_IRQ1_STATUS_9), + MADERA_IRQ(ASRC1_IN2_LOCK, MADERA_IRQ1_STATUS_9), + MADERA_IRQ(DRC2_SIG_DET, MADERA_IRQ1_STATUS_9), + MADERA_IRQ(DRC1_SIG_DET, MADERA_IRQ1_STATUS_9), + + MADERA_IRQ(DSP_IRQ1, MADERA_IRQ1_STATUS_11), + MADERA_IRQ(DSP_IRQ2, MADERA_IRQ1_STATUS_11), + MADERA_IRQ(DSP_IRQ3, MADERA_IRQ1_STATUS_11), + MADERA_IRQ(DSP_IRQ4, MADERA_IRQ1_STATUS_11), + MADERA_IRQ(DSP_IRQ5, MADERA_IRQ1_STATUS_11), + MADERA_IRQ(DSP_IRQ6, MADERA_IRQ1_STATUS_11), + MADERA_IRQ(DSP_IRQ7, MADERA_IRQ1_STATUS_11), + MADERA_IRQ(DSP_IRQ8, MADERA_IRQ1_STATUS_11), + MADERA_IRQ(DSP_IRQ9, MADERA_IRQ1_STATUS_11), + MADERA_IRQ(DSP_IRQ10, MADERA_IRQ1_STATUS_11), + MADERA_IRQ(DSP_IRQ11, MADERA_IRQ1_STATUS_11), + MADERA_IRQ(DSP_IRQ12, MADERA_IRQ1_STATUS_11), + MADERA_IRQ(DSP_IRQ13, MADERA_IRQ1_STATUS_11), + MADERA_IRQ(DSP_IRQ14, MADERA_IRQ1_STATUS_11), + MADERA_IRQ(DSP_IRQ15, MADERA_IRQ1_STATUS_11), + MADERA_IRQ(DSP_IRQ16, MADERA_IRQ1_STATUS_11), + + MADERA_IRQ(HP3R_SC, MADERA_IRQ1_STATUS_12), + MADERA_IRQ(HP3L_SC, MADERA_IRQ1_STATUS_12), + MADERA_IRQ(HP2R_SC, MADERA_IRQ1_STATUS_12), + MADERA_IRQ(HP2L_SC, MADERA_IRQ1_STATUS_12), + MADERA_IRQ(HP1R_SC, MADERA_IRQ1_STATUS_12), + MADERA_IRQ(HP1L_SC, MADERA_IRQ1_STATUS_12), + + MADERA_IRQ(SPK_OVERHEAT_WARN, MADERA_IRQ1_STATUS_15), + MADERA_IRQ(SPK_OVERHEAT, MADERA_IRQ1_STATUS_15), + + MADERA_IRQ(DSP1_BUS_ERR, MADERA_IRQ1_STATUS_33), + MADERA_IRQ(DSP2_BUS_ERR, MADERA_IRQ1_STATUS_33), + MADERA_IRQ(DSP3_BUS_ERR, MADERA_IRQ1_STATUS_33), + MADERA_IRQ(DSP4_BUS_ERR, MADERA_IRQ1_STATUS_33), + MADERA_IRQ(DSP5_BUS_ERR, MADERA_IRQ1_STATUS_33), + MADERA_IRQ(DSP6_BUS_ERR, MADERA_IRQ1_STATUS_33), + MADERA_IRQ(DSP7_BUS_ERR, MADERA_IRQ1_STATUS_33), +}; + +static const struct regmap_irq_chip madera_irq_chip = { + .name = "madera IRQ", + .status_base = MADERA_IRQ1_STATUS_2, + .mask_base = MADERA_IRQ1_MASK_2, + .ack_base = MADERA_IRQ1_STATUS_2, + .runtime_pm = true, + .num_regs = 32, + .irqs = madera_irqs, + .num_irqs = ARRAY_SIZE(madera_irqs), +}; + +#ifdef CONFIG_PM_SLEEP +static int madera_suspend(struct device *dev) +{ + struct madera *madera = dev_get_drvdata(dev->parent); + + dev_dbg(madera->irq_dev, "Suspend, disabling IRQ\n"); + + /* + * A runtime resume would be needed to access the chip interrupt + * controller but runtime pm doesn't function during suspend. + * Temporarily disable interrupts until we reach suspend_noirq state. + */ + disable_irq(madera->irq); + + return 0; +} + +static int madera_suspend_noirq(struct device *dev) +{ + struct madera *madera = dev_get_drvdata(dev->parent); + + dev_dbg(madera->irq_dev, "No IRQ suspend, reenabling IRQ\n"); + + /* Re-enable interrupts to service wakeup interrupts from the chip */ + enable_irq(madera->irq); + + return 0; +} + +static int madera_resume_noirq(struct device *dev) +{ + struct madera *madera = dev_get_drvdata(dev->parent); + + dev_dbg(madera->irq_dev, "No IRQ resume, disabling IRQ\n"); + + /* + * We can't handle interrupts until runtime pm is available again. + * Disable them temporarily. + */ + disable_irq(madera->irq); + + return 0; +} + +static int madera_resume(struct device *dev) +{ + struct madera *madera = dev_get_drvdata(dev->parent); + + dev_dbg(madera->irq_dev, "Resume, reenabling IRQ\n"); + + /* Interrupts can now be handled */ + enable_irq(madera->irq); + + return 0; +} +#endif + +static const struct dev_pm_ops madera_irq_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(madera_suspend, madera_resume) + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(madera_suspend_noirq, + madera_resume_noirq) +}; + +static int madera_irq_probe(struct platform_device *pdev) +{ + struct madera *madera = dev_get_drvdata(pdev->dev.parent); + struct irq_data *irq_data; + unsigned int irq_flags = 0; + int ret; + + dev_dbg(&pdev->dev, "probe\n"); + + /* + * Read the flags from the interrupt controller if not specified + * by pdata + */ + irq_flags = madera->pdata.irq_flags; + if (!irq_flags) { + irq_data = irq_get_irq_data(madera->irq); + if (!irq_data) { + dev_err(&pdev->dev, "Invalid IRQ: %d\n", madera->irq); + return -EINVAL; + } + + irq_flags = irqd_get_trigger_type(irq_data); + + /* Codec defaults to trigger low, use this if no flags given */ + if (irq_flags == IRQ_TYPE_NONE) + irq_flags = IRQF_TRIGGER_LOW; + } + + if (irq_flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { + dev_err(&pdev->dev, "Host interrupt not level-triggered\n"); + return -EINVAL; + } + + /* + * The silicon always starts at active-low, check if we need to + * switch to active-high. + */ + if (irq_flags & IRQF_TRIGGER_HIGH) { + ret = regmap_update_bits(madera->regmap, MADERA_IRQ1_CTRL, + MADERA_IRQ_POL_MASK, 0); + if (ret) { + dev_err(&pdev->dev, + "Failed to set IRQ polarity: %d\n", ret); + return ret; + } + } + + /* + * NOTE: regmap registers this against the OF node of the parent of + * the regmap - that is, against the mfd driver + */ + ret = regmap_add_irq_chip(madera->regmap, madera->irq, IRQF_ONESHOT, 0, + &madera_irq_chip, &madera->irq_data); + if (ret) { + dev_err(&pdev->dev, "add_irq_chip failed: %d\n", ret); + return ret; + } + + /* Save dev in parent MFD struct so it is accessible to siblings */ + madera->irq_dev = &pdev->dev; + + return 0; +} + +static int madera_irq_remove(struct platform_device *pdev) +{ + struct madera *madera = dev_get_drvdata(pdev->dev.parent); + + /* + * The IRQ is disabled by the parent MFD driver before + * it starts cleaning up all child drivers + */ + madera->irq_dev = NULL; + regmap_del_irq_chip(madera->irq, madera->irq_data); + + return 0; +} + +static struct platform_driver madera_irq_driver = { + .probe = &madera_irq_probe, + .remove = &madera_irq_remove, + .driver = { + .name = "madera-irq", + .pm = &madera_irq_pm_ops, + } +}; +module_platform_driver(madera_irq_driver); + +MODULE_SOFTDEP("pre: madera"); +MODULE_DESCRIPTION("Madera IRQ driver"); +MODULE_AUTHOR("Richard Fitzgerald "); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/irqchip/irq-madera.h b/include/linux/irqchip/irq-madera.h new file mode 100644 index 000000000000..1160fa3769ae --- /dev/null +++ b/include/linux/irqchip/irq-madera.h @@ -0,0 +1,132 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Interrupt support for Cirrus Logic Madera codecs + * + * Copyright (C) 2016-2018 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#ifndef IRQCHIP_MADERA_H +#define IRQCHIP_MADERA_H + +#include +#include + +#define MADERA_IRQ_FLL1_LOCK 0 +#define MADERA_IRQ_FLL2_LOCK 1 +#define MADERA_IRQ_FLL3_LOCK 2 +#define MADERA_IRQ_FLLAO_LOCK 3 +#define MADERA_IRQ_CLK_SYS_ERR 4 +#define MADERA_IRQ_CLK_ASYNC_ERR 5 +#define MADERA_IRQ_CLK_DSP_ERR 6 +#define MADERA_IRQ_HPDET 7 +#define MADERA_IRQ_MICDET1 8 +#define MADERA_IRQ_MICDET2 9 +#define MADERA_IRQ_JD1_RISE 10 +#define MADERA_IRQ_JD1_FALL 11 +#define MADERA_IRQ_JD2_RISE 12 +#define MADERA_IRQ_JD2_FALL 13 +#define MADERA_IRQ_MICD_CLAMP_RISE 14 +#define MADERA_IRQ_MICD_CLAMP_FALL 15 +#define MADERA_IRQ_DRC2_SIG_DET 16 +#define MADERA_IRQ_DRC1_SIG_DET 17 +#define MADERA_IRQ_ASRC1_IN1_LOCK 18 +#define MADERA_IRQ_ASRC1_IN2_LOCK 19 +#define MADERA_IRQ_ASRC2_IN1_LOCK 20 +#define MADERA_IRQ_ASRC2_IN2_LOCK 21 +#define MADERA_IRQ_DSP_IRQ1 22 +#define MADERA_IRQ_DSP_IRQ2 23 +#define MADERA_IRQ_DSP_IRQ3 24 +#define MADERA_IRQ_DSP_IRQ4 25 +#define MADERA_IRQ_DSP_IRQ5 26 +#define MADERA_IRQ_DSP_IRQ6 27 +#define MADERA_IRQ_DSP_IRQ7 28 +#define MADERA_IRQ_DSP_IRQ8 29 +#define MADERA_IRQ_DSP_IRQ9 30 +#define MADERA_IRQ_DSP_IRQ10 31 +#define MADERA_IRQ_DSP_IRQ11 32 +#define MADERA_IRQ_DSP_IRQ12 33 +#define MADERA_IRQ_DSP_IRQ13 34 +#define MADERA_IRQ_DSP_IRQ14 35 +#define MADERA_IRQ_DSP_IRQ15 36 +#define MADERA_IRQ_DSP_IRQ16 37 +#define MADERA_IRQ_HP1L_SC 38 +#define MADERA_IRQ_HP1R_SC 39 +#define MADERA_IRQ_HP2L_SC 40 +#define MADERA_IRQ_HP2R_SC 41 +#define MADERA_IRQ_HP3L_SC 42 +#define MADERA_IRQ_HP3R_SC 43 +#define MADERA_IRQ_SPKOUTL_SC 44 +#define MADERA_IRQ_SPKOUTR_SC 45 +#define MADERA_IRQ_HP1L_ENABLE_DONE 46 +#define MADERA_IRQ_HP1R_ENABLE_DONE 47 +#define MADERA_IRQ_HP2L_ENABLE_DONE 48 +#define MADERA_IRQ_HP2R_ENABLE_DONE 49 +#define MADERA_IRQ_HP3L_ENABLE_DONE 50 +#define MADERA_IRQ_HP3R_ENABLE_DONE 51 +#define MADERA_IRQ_SPKOUTL_ENABLE_DONE 52 +#define MADERA_IRQ_SPKOUTR_ENABLE_DONE 53 +#define MADERA_IRQ_SPK_SHUTDOWN 54 +#define MADERA_IRQ_SPK_OVERHEAT 55 +#define MADERA_IRQ_SPK_OVERHEAT_WARN 56 +#define MADERA_IRQ_GPIO1 57 +#define MADERA_IRQ_GPIO2 58 +#define MADERA_IRQ_GPIO3 59 +#define MADERA_IRQ_GPIO4 60 +#define MADERA_IRQ_GPIO5 61 +#define MADERA_IRQ_GPIO6 62 +#define MADERA_IRQ_GPIO7 63 +#define MADERA_IRQ_GPIO8 64 +#define MADERA_IRQ_DSP1_BUS_ERR 65 +#define MADERA_IRQ_DSP2_BUS_ERR 66 +#define MADERA_IRQ_DSP3_BUS_ERR 67 +#define MADERA_IRQ_DSP4_BUS_ERR 68 +#define MADERA_IRQ_DSP5_BUS_ERR 69 +#define MADERA_IRQ_DSP6_BUS_ERR 70 +#define MADERA_IRQ_DSP7_BUS_ERR 71 + +#define MADERA_NUM_IRQ 72 + +/* + * These wrapper functions are for use by other child drivers of the + * same parent MFD. + */ +static inline int madera_get_irq_mapping(struct madera *madera, int irq) +{ + if (!madera->irq_dev) + return -ENODEV; + + return regmap_irq_get_virq(madera->irq_data, irq); +} + +static inline int madera_request_irq(struct madera *madera, int irq, + const char *name, + irq_handler_t handler, void *data) +{ + irq = madera_get_irq_mapping(madera, irq); + if (irq < 0) + return irq; + + return request_threaded_irq(irq, NULL, handler, IRQF_ONESHOT, name, + data); +} + +static inline void madera_free_irq(struct madera *madera, int irq, void *data) +{ + irq = madera_get_irq_mapping(madera, irq); + if (irq < 0) + return; + + free_irq(irq, data); +} + +static inline int madera_set_irq_wake(struct madera *madera, int irq, int on) +{ + irq = madera_get_irq_mapping(madera, irq); + if (irq < 0) + return irq; + + return irq_set_irq_wake(irq, on); +} + +#endif -- cgit v1.2.3 From bec04037e4e484f41ee4d9409e40616874169d20 Mon Sep 17 00:00:00 2001 From: Dou Liyang Date: Tue, 4 Dec 2018 23:51:20 +0800 Subject: genirq/core: Introduce struct irq_affinity_desc The interrupt affinity management uses straight cpumask pointers to convey the automatically assigned affinity masks for managed interrupts. The core interrupt descriptor allocation also decides based on the pointer being non NULL whether an interrupt is managed or not. Devices which use managed interrupts usually have two classes of interrupts: - Interrupts for multiple device queues - Interrupts for general device management Currently both classes are treated the same way, i.e. as managed interrupts. The general interrupts get the default affinity mask assigned while the device queue interrupts are spread out over the possible CPUs. Treating the general interrupts as managed is both a limitation and under certain circumstances a bug. Assume the following situation: default_irq_affinity = 4..7 So if CPUs 4-7 are offlined, then the core code will shut down the device management interrupts because the last CPU in their affinity mask went offline. It's also a limitation because it's desired to allow manual placement of the general device interrupts for various reasons. If they are marked managed then the interrupt affinity setting from both user and kernel space is disabled. To remedy that situation it's required to convey more information than the cpumasks through various interfaces related to interrupt descriptor allocation. Instead of adding yet another argument, create a new data structure 'irq_affinity_desc' which for now just contains the cpumask. This struct can be expanded to convey auxilliary information in the next step. No functional change, just preparatory work. [ tglx: Simplified logic and clarified changelog ] Suggested-by: Thomas Gleixner Suggested-by: Bjorn Helgaas Signed-off-by: Dou Liyang Signed-off-by: Thomas Gleixner Cc: linux-pci@vger.kernel.org Cc: kashyap.desai@broadcom.com Cc: shivasharan.srikanteshwara@broadcom.com Cc: sumit.saxena@broadcom.com Cc: ming.lei@redhat.com Cc: hch@lst.de Cc: douliyang1@huawei.com Link: https://lkml.kernel.org/r/20181204155122.6327-2-douliyangs@gmail.com --- drivers/pci/msi.c | 9 ++++----- include/linux/interrupt.h | 14 ++++++++++++-- include/linux/irq.h | 6 ++++-- include/linux/irqdomain.h | 6 ++++-- include/linux/msi.h | 4 ++-- kernel/irq/affinity.c | 22 ++++++++++++---------- kernel/irq/devres.c | 4 ++-- kernel/irq/irqdesc.c | 17 +++++++++-------- kernel/irq/irqdomain.c | 4 ++-- kernel/irq/msi.c | 8 ++++---- 10 files changed, 55 insertions(+), 39 deletions(-) (limited to 'include') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 265ed3e4c920..7a1c8a09efa5 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -534,14 +534,13 @@ error_attrs: static struct msi_desc * msi_setup_entry(struct pci_dev *dev, int nvec, const struct irq_affinity *affd) { - struct cpumask *masks = NULL; + struct irq_affinity_desc *masks = NULL; struct msi_desc *entry; u16 control; if (affd) masks = irq_create_affinity_masks(nvec, affd); - /* MSI Entry Initialization */ entry = alloc_msi_entry(&dev->dev, nvec, masks); if (!entry) @@ -672,7 +671,7 @@ static int msix_setup_entries(struct pci_dev *dev, void __iomem *base, struct msix_entry *entries, int nvec, const struct irq_affinity *affd) { - struct cpumask *curmsk, *masks = NULL; + struct irq_affinity_desc *curmsk, *masks = NULL; struct msi_desc *entry; int ret, i; @@ -1264,7 +1263,7 @@ const struct cpumask *pci_irq_get_affinity(struct pci_dev *dev, int nr) for_each_pci_msi_entry(entry, dev) { if (i == nr) - return entry->affinity; + return &entry->affinity->mask; i++; } WARN_ON_ONCE(1); @@ -1276,7 +1275,7 @@ const struct cpumask *pci_irq_get_affinity(struct pci_dev *dev, int nr) nr >= entry->nvec_used)) return NULL; - return &entry->affinity[nr]; + return &entry->affinity[nr].mask; } else { return cpu_possible_mask; } diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index ca397ff40836..c44b7844dc83 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -257,6 +257,14 @@ struct irq_affinity { int *sets; }; +/** + * struct irq_affinity_desc - Interrupt affinity descriptor + * @mask: cpumask to hold the affinity assignment + */ +struct irq_affinity_desc { + struct cpumask mask; +}; + #if defined(CONFIG_SMP) extern cpumask_var_t irq_default_affinity; @@ -303,7 +311,9 @@ extern int irq_set_affinity_hint(unsigned int irq, const struct cpumask *m); extern int irq_set_affinity_notifier(unsigned int irq, struct irq_affinity_notify *notify); -struct cpumask *irq_create_affinity_masks(int nvec, const struct irq_affinity *affd); +struct irq_affinity_desc * +irq_create_affinity_masks(int nvec, const struct irq_affinity *affd); + int irq_calc_affinity_vectors(int minvec, int maxvec, const struct irq_affinity *affd); #else /* CONFIG_SMP */ @@ -337,7 +347,7 @@ irq_set_affinity_notifier(unsigned int irq, struct irq_affinity_notify *notify) return 0; } -static inline struct cpumask * +static inline struct irq_affinity_desc * irq_create_affinity_masks(int nvec, const struct irq_affinity *affd) { return NULL; diff --git a/include/linux/irq.h b/include/linux/irq.h index c9bffda04a45..def2b2aac8b1 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -27,6 +27,7 @@ struct seq_file; struct module; struct msi_msg; +struct irq_affinity_desc; enum irqchip_irq_state; /* @@ -834,11 +835,12 @@ struct cpumask *irq_data_get_effective_affinity_mask(struct irq_data *d) unsigned int arch_dynirq_lower_bound(unsigned int from); int __irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node, - struct module *owner, const struct cpumask *affinity); + struct module *owner, + const struct irq_affinity_desc *affinity); int __devm_irq_alloc_descs(struct device *dev, int irq, unsigned int from, unsigned int cnt, int node, struct module *owner, - const struct cpumask *affinity); + const struct irq_affinity_desc *affinity); /* use macros to avoid needing export.h for THIS_MODULE */ #define irq_alloc_descs(irq, from, cnt, node) \ diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index 068aa46f0d55..35965f41d7be 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -43,6 +43,7 @@ struct irq_chip; struct irq_data; struct cpumask; struct seq_file; +struct irq_affinity_desc; /* Number of irqs reserved for a legacy isa controller */ #define NUM_ISA_INTERRUPTS 16 @@ -266,7 +267,7 @@ extern bool irq_domain_check_msi_remap(void); extern void irq_set_default_host(struct irq_domain *host); extern int irq_domain_alloc_descs(int virq, unsigned int nr_irqs, irq_hw_number_t hwirq, int node, - const struct cpumask *affinity); + const struct irq_affinity_desc *affinity); static inline struct fwnode_handle *of_node_to_fwnode(struct device_node *node) { @@ -449,7 +450,8 @@ static inline struct irq_domain *irq_domain_add_hierarchy(struct irq_domain *par extern int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base, unsigned int nr_irqs, int node, void *arg, - bool realloc, const struct cpumask *affinity); + bool realloc, + const struct irq_affinity_desc *affinity); extern void irq_domain_free_irqs(unsigned int virq, unsigned int nr_irqs); extern int irq_domain_activate_irq(struct irq_data *irq_data, bool early); extern void irq_domain_deactivate_irq(struct irq_data *irq_data); diff --git a/include/linux/msi.h b/include/linux/msi.h index eb213b87617c..784fb52b9900 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -76,7 +76,7 @@ struct msi_desc { unsigned int nvec_used; struct device *dev; struct msi_msg msg; - struct cpumask *affinity; + struct irq_affinity_desc *affinity; union { /* PCI MSI/X specific data */ @@ -138,7 +138,7 @@ static inline void pci_write_msi_msg(unsigned int irq, struct msi_msg *msg) #endif /* CONFIG_PCI_MSI */ struct msi_desc *alloc_msi_entry(struct device *dev, int nvec, - const struct cpumask *affinity); + const struct irq_affinity_desc *affinity); void free_msi_entry(struct msi_desc *entry); void __pci_read_msi_msg(struct msi_desc *entry, struct msi_msg *msg); void __pci_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg); diff --git a/kernel/irq/affinity.c b/kernel/irq/affinity.c index e423bff1928c..c0fe591b0dc9 100644 --- a/kernel/irq/affinity.c +++ b/kernel/irq/affinity.c @@ -99,7 +99,7 @@ static int __irq_build_affinity_masks(const struct irq_affinity *affd, cpumask_var_t *node_to_cpumask, const struct cpumask *cpu_mask, struct cpumask *nmsk, - struct cpumask *masks) + struct irq_affinity_desc *masks) { int n, nodes, cpus_per_vec, extra_vecs, done = 0; int last_affv = firstvec + numvecs; @@ -117,7 +117,9 @@ static int __irq_build_affinity_masks(const struct irq_affinity *affd, */ if (numvecs <= nodes) { for_each_node_mask(n, nodemsk) { - cpumask_or(masks + curvec, masks + curvec, node_to_cpumask[n]); + cpumask_or(&masks[curvec].mask, + &masks[curvec].mask, + node_to_cpumask[n]); if (++curvec == last_affv) curvec = firstvec; } @@ -150,7 +152,8 @@ static int __irq_build_affinity_masks(const struct irq_affinity *affd, cpus_per_vec++; --extra_vecs; } - irq_spread_init_one(masks + curvec, nmsk, cpus_per_vec); + irq_spread_init_one(&masks[curvec].mask, nmsk, + cpus_per_vec); } done += v; @@ -173,7 +176,7 @@ out: static int irq_build_affinity_masks(const struct irq_affinity *affd, int startvec, int numvecs, int firstvec, cpumask_var_t *node_to_cpumask, - struct cpumask *masks) + struct irq_affinity_desc *masks) { int curvec = startvec, nr_present, nr_others; int ret = -ENOMEM; @@ -226,15 +229,15 @@ static int irq_build_affinity_masks(const struct irq_affinity *affd, * @nvecs: The total number of vectors * @affd: Description of the affinity requirements * - * Returns the masks pointer or NULL if allocation failed. + * Returns the irq_affinity_desc pointer or NULL if allocation failed. */ -struct cpumask * +struct irq_affinity_desc * irq_create_affinity_masks(int nvecs, const struct irq_affinity *affd) { int affvecs = nvecs - affd->pre_vectors - affd->post_vectors; int curvec, usedvecs; cpumask_var_t *node_to_cpumask; - struct cpumask *masks = NULL; + struct irq_affinity_desc *masks = NULL; int i, nr_sets; /* @@ -254,8 +257,7 @@ irq_create_affinity_masks(int nvecs, const struct irq_affinity *affd) /* Fill out vectors at the beginning that don't need affinity */ for (curvec = 0; curvec < affd->pre_vectors; curvec++) - cpumask_copy(masks + curvec, irq_default_affinity); - + cpumask_copy(&masks[curvec].mask, irq_default_affinity); /* * Spread on present CPUs starting from affd->pre_vectors. If we * have multiple sets, build each sets affinity mask separately. @@ -285,7 +287,7 @@ irq_create_affinity_masks(int nvecs, const struct irq_affinity *affd) else curvec = affd->pre_vectors + usedvecs; for (; curvec < nvecs; curvec++) - cpumask_copy(masks + curvec, irq_default_affinity); + cpumask_copy(&masks[curvec].mask, irq_default_affinity); outnodemsk: free_node_to_cpumask(node_to_cpumask); diff --git a/kernel/irq/devres.c b/kernel/irq/devres.c index 6a682c229e10..5d5378ea0afe 100644 --- a/kernel/irq/devres.c +++ b/kernel/irq/devres.c @@ -169,7 +169,7 @@ static void devm_irq_desc_release(struct device *dev, void *res) * @cnt: Number of consecutive irqs to allocate * @node: Preferred node on which the irq descriptor should be allocated * @owner: Owning module (can be NULL) - * @affinity: Optional pointer to an affinity mask array of size @cnt + * @affinity: Optional pointer to an irq_affinity_desc array of size @cnt * which hints where the irq descriptors should be allocated * and which default affinities to use * @@ -179,7 +179,7 @@ static void devm_irq_desc_release(struct device *dev, void *res) */ int __devm_irq_alloc_descs(struct device *dev, int irq, unsigned int from, unsigned int cnt, int node, struct module *owner, - const struct cpumask *affinity) + const struct irq_affinity_desc *affinity) { struct irq_desc_devres *dr; int base; diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c index 578d0e5f1b5b..cb401d6c5040 100644 --- a/kernel/irq/irqdesc.c +++ b/kernel/irq/irqdesc.c @@ -449,28 +449,29 @@ static void free_desc(unsigned int irq) } static int alloc_descs(unsigned int start, unsigned int cnt, int node, - const struct cpumask *affinity, struct module *owner) + const struct irq_affinity_desc *affinity, + struct module *owner) { - const struct cpumask *mask = NULL; struct irq_desc *desc; unsigned int flags; int i; /* Validate affinity mask(s) */ if (affinity) { - for (i = 0, mask = affinity; i < cnt; i++, mask++) { - if (cpumask_empty(mask)) + for (i = 0; i < cnt; i++) { + if (cpumask_empty(&affinity[i].mask)) return -EINVAL; } } flags = affinity ? IRQD_AFFINITY_MANAGED | IRQD_MANAGED_SHUTDOWN : 0; - mask = NULL; for (i = 0; i < cnt; i++) { + const struct cpumask *mask = NULL; + if (affinity) { node = cpu_to_node(cpumask_first(affinity)); - mask = affinity; + mask = &affinity->mask; affinity++; } desc = alloc_desc(start + i, node, flags, mask, owner); @@ -575,7 +576,7 @@ static void free_desc(unsigned int irq) } static inline int alloc_descs(unsigned int start, unsigned int cnt, int node, - const struct cpumask *affinity, + const struct irq_affinity_desc *affinity, struct module *owner) { u32 i; @@ -705,7 +706,7 @@ EXPORT_SYMBOL_GPL(irq_free_descs); */ int __ref __irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node, - struct module *owner, const struct cpumask *affinity) + struct module *owner, const struct irq_affinity_desc *affinity) { int start, ret; diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 3366d11c3e02..8b0be4bd6565 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -969,7 +969,7 @@ const struct irq_domain_ops irq_domain_simple_ops = { EXPORT_SYMBOL_GPL(irq_domain_simple_ops); int irq_domain_alloc_descs(int virq, unsigned int cnt, irq_hw_number_t hwirq, - int node, const struct cpumask *affinity) + int node, const struct irq_affinity_desc *affinity) { unsigned int hint; @@ -1281,7 +1281,7 @@ int irq_domain_alloc_irqs_hierarchy(struct irq_domain *domain, */ int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base, unsigned int nr_irqs, int node, void *arg, - bool realloc, const struct cpumask *affinity) + bool realloc, const struct irq_affinity_desc *affinity) { int i, ret, virq; diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 4ca2fd46645d..ad26fbcfbfc8 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -23,11 +23,11 @@ * @nvec: The number of vectors used in this entry * @affinity: Optional pointer to an affinity mask array size of @nvec * - * If @affinity is not NULL then a an affinity array[@nvec] is allocated - * and the affinity masks from @affinity are copied. + * If @affinity is not NULL then an affinity array[@nvec] is allocated + * and the affinity masks and flags from @affinity are copied. */ -struct msi_desc * -alloc_msi_entry(struct device *dev, int nvec, const struct cpumask *affinity) +struct msi_desc *alloc_msi_entry(struct device *dev, int nvec, + const struct irq_affinity_desc *affinity) { struct msi_desc *desc; -- cgit v1.2.3 From c410abbbacb9b378365ba17a30df08b4b9eec64f Mon Sep 17 00:00:00 2001 From: Dou Liyang Date: Tue, 4 Dec 2018 23:51:21 +0800 Subject: genirq/affinity: Add is_managed to struct irq_affinity_desc Devices which use managed interrupts usually have two classes of interrupts: - Interrupts for multiple device queues - Interrupts for general device management Currently both classes are treated the same way, i.e. as managed interrupts. The general interrupts get the default affinity mask assigned while the device queue interrupts are spread out over the possible CPUs. Treating the general interrupts as managed is both a limitation and under certain circumstances a bug. Assume the following situation: default_irq_affinity = 4..7 So if CPUs 4-7 are offlined, then the core code will shut down the device management interrupts because the last CPU in their affinity mask went offline. It's also a limitation because it's desired to allow manual placement of the general device interrupts for various reasons. If they are marked managed then the interrupt affinity setting from both user and kernel space is disabled. That limitation was reported by Kashyap and Sumit. Expand struct irq_affinity_desc with a new bit 'is_managed' which is set for truly managed interrupts (queue interrupts) and cleared for the general device interrupts. [ tglx: Simplify code and massage changelog ] Reported-by: Kashyap Desai Reported-by: Sumit Saxena Signed-off-by: Dou Liyang Signed-off-by: Thomas Gleixner Cc: linux-pci@vger.kernel.org Cc: shivasharan.srikanteshwara@broadcom.com Cc: ming.lei@redhat.com Cc: hch@lst.de Cc: bhelgaas@google.com Cc: douliyang1@huawei.com Link: https://lkml.kernel.org/r/20181204155122.6327-3-douliyangs@gmail.com --- include/linux/interrupt.h | 1 + kernel/irq/affinity.c | 4 ++++ kernel/irq/irqdesc.c | 13 ++++++++----- 3 files changed, 13 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index c44b7844dc83..c672f34235e7 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -263,6 +263,7 @@ struct irq_affinity { */ struct irq_affinity_desc { struct cpumask mask; + unsigned int is_managed : 1; }; #if defined(CONFIG_SMP) diff --git a/kernel/irq/affinity.c b/kernel/irq/affinity.c index c0fe591b0dc9..45b68b4ea48b 100644 --- a/kernel/irq/affinity.c +++ b/kernel/irq/affinity.c @@ -289,6 +289,10 @@ irq_create_affinity_masks(int nvecs, const struct irq_affinity *affd) for (; curvec < nvecs; curvec++) cpumask_copy(&masks[curvec].mask, irq_default_affinity); + /* Mark the managed interrupts */ + for (i = affd->pre_vectors; i < nvecs - affd->post_vectors; i++) + masks[i].is_managed = 1; + outnodemsk: free_node_to_cpumask(node_to_cpumask); return masks; diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c index cb401d6c5040..ee062b7939d3 100644 --- a/kernel/irq/irqdesc.c +++ b/kernel/irq/irqdesc.c @@ -453,27 +453,30 @@ static int alloc_descs(unsigned int start, unsigned int cnt, int node, struct module *owner) { struct irq_desc *desc; - unsigned int flags; int i; /* Validate affinity mask(s) */ if (affinity) { - for (i = 0; i < cnt; i++) { + for (i = 0; i < cnt; i++, i++) { if (cpumask_empty(&affinity[i].mask)) return -EINVAL; } } - flags = affinity ? IRQD_AFFINITY_MANAGED | IRQD_MANAGED_SHUTDOWN : 0; - for (i = 0; i < cnt; i++) { const struct cpumask *mask = NULL; + unsigned int flags = 0; if (affinity) { - node = cpu_to_node(cpumask_first(affinity)); + if (affinity->is_managed) { + flags = IRQD_AFFINITY_MANAGED | + IRQD_MANAGED_SHUTDOWN; + } mask = &affinity->mask; + node = cpu_to_node(cpumask_first(mask)); affinity++; } + desc = alloc_desc(start + i, node, flags, mask, owner); if (!desc) goto err; -- cgit v1.2.3