summaryrefslogtreecommitdiffstats
path: root/drivers/irqchip/irq-armada-370-xp.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-07-03 16:50:31 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2017-07-03 16:50:31 -0700
commit03ffbcdd7898c0b5299efeb9f18de927487ec1cf (patch)
tree0569222e4dc9db22049d7d8d15920cc085a194f6 /drivers/irqchip/irq-armada-370-xp.c
parent1b044f1cfc65a7d90b209dfabd57e16d98b58c5b (diff)
parentf9632de40ee0161e864bea8c1b017d957fd7312c (diff)
downloadlinux-stable-03ffbcdd7898c0b5299efeb9f18de927487ec1cf.tar.gz
linux-stable-03ffbcdd7898c0b5299efeb9f18de927487ec1cf.tar.bz2
linux-stable-03ffbcdd7898c0b5299efeb9f18de927487ec1cf.zip
Merge branch 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull irq updates from Thomas Gleixner: "The irq department delivers: - Expand the generic infrastructure handling the irq migration on CPU hotplug and convert X86 over to it. (Thomas Gleixner) Aside of consolidating code this is a preparatory change for: - Finalizing the affinity management for multi-queue devices. The main change here is to shut down interrupts which are affine to a outgoing CPU and reenabling them when the CPU comes online again. That avoids moving interrupts pointlessly around and breaking and reestablishing affinities for no value. (Christoph Hellwig) Note: This contains also the BLOCK-MQ and NVME changes which depend on the rework of the irq core infrastructure. Jens acked them and agreed that they should go with the irq changes. - Consolidation of irq domain code (Marc Zyngier) - State tracking consolidation in the core code (Jeffy Chen) - Add debug infrastructure for hierarchical irq domains (Thomas Gleixner) - Infrastructure enhancement for managing generic interrupt chips via devmem (Bartosz Golaszewski) - Constification work all over the place (Tobias Klauser) - Two new interrupt controller drivers for MVEBU (Thomas Petazzoni) - The usual set of fixes, updates and enhancements all over the place" * 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (112 commits) irqchip/or1k-pic: Fix interrupt acknowledgement irqchip/irq-mvebu-gicp: Allocate enough memory for spi_bitmap irqchip/gic-v3: Fix out-of-bound access in gic_set_affinity nvme: Allocate queues for all possible CPUs blk-mq: Create hctx for each present CPU blk-mq: Include all present CPUs in the default queue mapping genirq: Avoid unnecessary low level irq function calls genirq: Set irq masked state when initializing irq_desc genirq/timings: Add infrastructure for estimating the next interrupt arrival time genirq/timings: Add infrastructure to track the interrupt timings genirq/debugfs: Remove pointless NULL pointer check irqchip/gic-v3-its: Don't assume GICv3 hardware supports 16bit INTID irqchip/gic-v3-its: Add ACPI NUMA node mapping irqchip/gic-v3-its-platform-msi: Make of_device_ids const irqchip/gic-v3-its: Make of_device_ids const irqchip/irq-mvebu-icu: Add new driver for Marvell ICU irqchip/irq-mvebu-gicp: Add new driver for Marvell GICP dt-bindings/interrupt-controller: Add DT binding for the Marvell ICU genirq/irqdomain: Remove auto-recursive hierarchy support irqchip/MSI: Use irq_domain_update_bus_token instead of an open coded access ...
Diffstat (limited to 'drivers/irqchip/irq-armada-370-xp.c')
-rw-r--r--drivers/irqchip/irq-armada-370-xp.c148
1 files changed, 130 insertions, 18 deletions
diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c
index eb0d4d41b156..b207b2c3aa55 100644
--- a/drivers/irqchip/irq-armada-370-xp.c
+++ b/drivers/irqchip/irq-armada-370-xp.c
@@ -34,25 +34,104 @@
#include <asm/smp_plat.h>
#include <asm/mach/irq.h>
-/* Interrupt Controller Registers Map */
-#define ARMADA_370_XP_INT_SET_MASK_OFFS (0x48)
-#define ARMADA_370_XP_INT_CLEAR_MASK_OFFS (0x4C)
-#define ARMADA_370_XP_INT_FABRIC_MASK_OFFS (0x54)
-#define ARMADA_370_XP_INT_CAUSE_PERF(cpu) (1 << cpu)
+/*
+ * Overall diagram of the Armada XP interrupt controller:
+ *
+ * To CPU 0 To CPU 1
+ *
+ * /\ /\
+ * || ||
+ * +---------------+ +---------------+
+ * | | | |
+ * | per-CPU | | per-CPU |
+ * | mask/unmask | | mask/unmask |
+ * | CPU0 | | CPU1 |
+ * | | | |
+ * +---------------+ +---------------+
+ * /\ /\
+ * || ||
+ * \\_______________________//
+ * ||
+ * +-------------------+
+ * | |
+ * | Global interrupt |
+ * | mask/unmask |
+ * | |
+ * +-------------------+
+ * /\
+ * ||
+ * interrupt from
+ * device
+ *
+ * The "global interrupt mask/unmask" is modified using the
+ * ARMADA_370_XP_INT_SET_ENABLE_OFFS and
+ * ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS registers, which are relative
+ * to "main_int_base".
+ *
+ * The "per-CPU mask/unmask" is modified using the
+ * ARMADA_370_XP_INT_SET_MASK_OFFS and
+ * ARMADA_370_XP_INT_CLEAR_MASK_OFFS registers, which are relative to
+ * "per_cpu_int_base". This base address points to a special address,
+ * which automatically accesses the registers of the current CPU.
+ *
+ * The per-CPU mask/unmask can also be adjusted using the global
+ * per-interrupt ARMADA_370_XP_INT_SOURCE_CTL register, which we use
+ * to configure interrupt affinity.
+ *
+ * Due to this model, all interrupts need to be mask/unmasked at two
+ * different levels: at the global level and at the per-CPU level.
+ *
+ * This driver takes the following approach to deal with this:
+ *
+ * - For global interrupts:
+ *
+ * At ->map() time, a global interrupt is unmasked at the per-CPU
+ * mask/unmask level. It is therefore unmasked at this level for
+ * the current CPU, running the ->map() code. This allows to have
+ * the interrupt unmasked at this level in non-SMP
+ * configurations. In SMP configurations, the ->set_affinity()
+ * callback is called, which using the
+ * ARMADA_370_XP_INT_SOURCE_CTL() readjusts the per-CPU mask/unmask
+ * for the interrupt.
+ *
+ * The ->mask() and ->unmask() operations only mask/unmask the
+ * interrupt at the "global" level.
+ *
+ * So, a global interrupt is enabled at the per-CPU level as soon
+ * as it is mapped. At run time, the masking/unmasking takes place
+ * at the global level.
+ *
+ * - For per-CPU interrupts
+ *
+ * At ->map() time, a per-CPU interrupt is unmasked at the global
+ * mask/unmask level.
+ *
+ * The ->mask() and ->unmask() operations mask/unmask the interrupt
+ * at the per-CPU level.
+ *
+ * So, a per-CPU interrupt is enabled at the global level as soon
+ * as it is mapped. At run time, the masking/unmasking takes place
+ * at the per-CPU level.
+ */
+/* Registers relative to main_int_base */
#define ARMADA_370_XP_INT_CONTROL (0x00)
+#define ARMADA_370_XP_SW_TRIG_INT_OFFS (0x04)
#define ARMADA_370_XP_INT_SET_ENABLE_OFFS (0x30)
#define ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS (0x34)
#define ARMADA_370_XP_INT_SOURCE_CTL(irq) (0x100 + irq*4)
#define ARMADA_370_XP_INT_SOURCE_CPU_MASK 0xF
#define ARMADA_370_XP_INT_IRQ_FIQ_MASK(cpuid) ((BIT(0) | BIT(8)) << cpuid)
-#define ARMADA_370_XP_CPU_INTACK_OFFS (0x44)
+/* Registers relative to per_cpu_int_base */
+#define ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS (0x08)
+#define ARMADA_370_XP_IN_DRBEL_MSK_OFFS (0x0c)
#define ARMADA_375_PPI_CAUSE (0x10)
-
-#define ARMADA_370_XP_SW_TRIG_INT_OFFS (0x4)
-#define ARMADA_370_XP_IN_DRBEL_MSK_OFFS (0xc)
-#define ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS (0x8)
+#define ARMADA_370_XP_CPU_INTACK_OFFS (0x44)
+#define ARMADA_370_XP_INT_SET_MASK_OFFS (0x48)
+#define ARMADA_370_XP_INT_CLEAR_MASK_OFFS (0x4C)
+#define ARMADA_370_XP_INT_FABRIC_MASK_OFFS (0x54)
+#define ARMADA_370_XP_INT_CAUSE_PERF(cpu) (1 << cpu)
#define ARMADA_370_XP_MAX_PER_CPU_IRQS (28)
@@ -281,13 +360,11 @@ static int armada_370_xp_mpic_irq_map(struct irq_domain *h,
irq_set_percpu_devid(virq);
irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip,
handle_percpu_devid_irq);
-
} else {
irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip,
handle_level_irq);
}
irq_set_probe(virq);
- irq_clear_status_flags(virq, IRQ_NOAUTOEN);
return 0;
}
@@ -345,16 +422,40 @@ static void armada_mpic_send_doorbell(const struct cpumask *mask,
ARMADA_370_XP_SW_TRIG_INT_OFFS);
}
+static void armada_xp_mpic_reenable_percpu(void)
+{
+ unsigned int irq;
+
+ /* Re-enable per-CPU interrupts that were enabled before suspend */
+ for (irq = 0; irq < ARMADA_370_XP_MAX_PER_CPU_IRQS; irq++) {
+ struct irq_data *data;
+ int virq;
+
+ virq = irq_linear_revmap(armada_370_xp_mpic_domain, irq);
+ if (virq == 0)
+ continue;
+
+ data = irq_get_irq_data(virq);
+
+ if (!irq_percpu_is_enabled(virq))
+ continue;
+
+ armada_370_xp_irq_unmask(data);
+ }
+}
+
static int armada_xp_mpic_starting_cpu(unsigned int cpu)
{
armada_xp_mpic_perf_init();
armada_xp_mpic_smp_cpu_init();
+ armada_xp_mpic_reenable_percpu();
return 0;
}
static int mpic_cascaded_starting_cpu(unsigned int cpu)
{
armada_xp_mpic_perf_init();
+ armada_xp_mpic_reenable_percpu();
enable_percpu_irq(parent_irq, IRQ_TYPE_NONE);
return 0;
}
@@ -502,16 +603,27 @@ static void armada_370_xp_mpic_resume(void)
if (virq == 0)
continue;
- if (!is_percpu_irq(irq))
+ data = irq_get_irq_data(virq);
+
+ if (!is_percpu_irq(irq)) {
+ /* Non per-CPU interrupts */
writel(irq, per_cpu_int_base +
ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
- else
+ if (!irqd_irq_disabled(data))
+ armada_370_xp_irq_unmask(data);
+ } else {
+ /* Per-CPU interrupts */
writel(irq, main_int_base +
ARMADA_370_XP_INT_SET_ENABLE_OFFS);
- data = irq_get_irq_data(virq);
- if (!irqd_irq_disabled(data))
- armada_370_xp_irq_unmask(data);
+ /*
+ * Re-enable on the current CPU,
+ * armada_xp_mpic_reenable_percpu() will take
+ * care of secondary CPUs when they come up.
+ */
+ if (irq_percpu_is_enabled(virq))
+ armada_370_xp_irq_unmask(data);
+ }
}
/* Reconfigure doorbells for IPIs and MSIs */
@@ -563,7 +675,7 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node,
irq_domain_add_linear(node, nr_irqs,
&armada_370_xp_mpic_irq_ops, NULL);
BUG_ON(!armada_370_xp_mpic_domain);
- armada_370_xp_mpic_domain->bus_token = DOMAIN_BUS_WIRED;
+ irq_domain_update_bus_token(armada_370_xp_mpic_domain, DOMAIN_BUS_WIRED);
/* Setup for the boot CPU */
armada_xp_mpic_perf_init();