summaryrefslogtreecommitdiff
path: root/drivers/irqchip
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-05-12 09:56:30 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2017-05-12 09:56:30 -0700
commitac3c4aa248c5b5390c40fad1ceb0a15a53f57a36 (patch)
treea7b09cc6d8a9ec28bfb7d2ab7d280dd8e61bfb5e /drivers/irqchip
parent0ba1c1950c320fcfdfe3dbc7470884e4a390083c (diff)
parent3e441845caf1c9591b5b961f34ff1a37d023c9e2 (diff)
Merge branch 'upstream' of git://git.linux-mips.org/pub/scm/ralf/upstream-linus
Pull MIPS updates from James Hogan: "math-emu: - Add missing clearing of BLTZALL and BGEZALL emulation counters - Fix BC1EQZ and BC1NEZ condition handling - Fix BLEZL and BGTZL identification BPF: - Add JIT support for SKF_AD_HATYPE - Use unsigned access for unsigned SKB fields - Quit clobbering callee saved registers in JIT code - Fix multiple problems in JIT skb access helpers Loongson 3: - Select MIPS_L1_CACHE_SHIFT_6 Octeon: - Remove vestiges of CONFIG_CAVIUM_OCTEON_2ND_KERNEL - Remove unused L2C types and macros. - Remove unused SLI types and macros. - Fix compile error when USB is not enabled. - Octeon: Remove unused PCIERCX types and macros. - Octeon: Clean up platform code. SNI: - Remove recursive include of cpu-feature-overrides.h Sibyte: - Export symbol periph_rev to sb1250-mac network driver. - Fix Kconfig warning. Generic platform: - Enable Root FS on NFS in generic_defconfig SMP-MT: - Use CPU interrupt controller IPI IRQ domain support UASM: - Add support for LHU for uasm. - Remove needless ISA abstraction mm: - Add 48-bit VA space and 4-level page tables for 4K pages. PCI: - Add controllers before the specified head irqchip driver for MIPS CPU: - Replace magic 0x100 with IE_SW0 - Prepare for non-legacy IRQ domains - Introduce IPI IRQ domain support MAINTAINERS: - Update email-id of Rahul Bedarkar NET: - sb1250-mac: Add missing MODULE_LICENSE() CPUFREQ: - Loongson2: drop set_cpus_allowed_ptr() Misc: - Disable Werror when W= is set - Opt into HAVE_COPY_THREAD_TLS - Enable GENERIC_CPU_AUTOPROBE - Use common outgoing-CPU-notification code - Remove dead define of ST_OFF - Remove CONFIG_ARCH_HAS_ILOG2_U{32,64} - Stengthen IPI IRQ domain sanity check - Remove confusing else statement in __do_page_fault() - Don't unnecessarily include kmalloc.h into <asm/cache.h>. - Delete unused definition of SMP_CACHE_SHIFT. - Delete redundant definition of SMP_CACHE_BYTES" * 'upstream' of git://git.linux-mips.org/pub/scm/ralf/upstream-linus: (39 commits) MIPS: Sibyte: Fix Kconfig warning. MIPS: Sibyte: Export symbol periph_rev to sb1250-mac network driver. NET: sb1250-mac: Add missing MODULE_LICENSE() MAINTAINERS: Update email-id of Rahul Bedarkar MIPS: Remove confusing else statement in __do_page_fault() MIPS: Stengthen IPI IRQ domain sanity check MIPS: smp-mt: Use CPU interrupt controller IPI IRQ domain support irqchip: mips-cpu: Introduce IPI IRQ domain support irqchip: mips-cpu: Prepare for non-legacy IRQ domains irqchip: mips-cpu: Replace magic 0x100 with IE_SW0 MIPS: Remove CONFIG_ARCH_HAS_ILOG2_U{32,64} MIPS: generic: Enable Root FS on NFS in generic_defconfig MIPS: mach-rm: Remove recursive include of cpu-feature-overrides.h MIPS: Opt into HAVE_COPY_THREAD_TLS CPUFREQ: Loongson2: drop set_cpus_allowed_ptr() MIPS: uasm: Remove needless ISA abstraction MIPS: Remove dead define of ST_OFF MIPS: Use common outgoing-CPU-notification code MIPS: math-emu: Fix BC1EQZ and BC1NEZ condition handling MIPS: r2-on-r6-emu: Clear BLTZALL and BGEZALL debugfs counters ...
Diffstat (limited to 'drivers/irqchip')
-rw-r--r--drivers/irqchip/Kconfig2
-rw-r--r--drivers/irqchip/irq-mips-cpu.c146
2 files changed, 131 insertions, 17 deletions
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 595d0c95563b..478f8ace2664 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -134,7 +134,9 @@ config IMGPDC_IRQ
config IRQ_MIPS_CPU
bool
select GENERIC_IRQ_CHIP
+ select GENERIC_IRQ_IPI if SYS_SUPPORTS_MULTITHREADING
select IRQ_DOMAIN
+ select IRQ_DOMAIN_HIERARCHY if GENERIC_IRQ_IPI
config CLPS711X_IRQCHIP
bool
diff --git a/drivers/irqchip/irq-mips-cpu.c b/drivers/irqchip/irq-mips-cpu.c
index 8c504f562e9d..b247f3c743ac 100644
--- a/drivers/irqchip/irq-mips-cpu.c
+++ b/drivers/irqchip/irq-mips-cpu.c
@@ -17,15 +17,14 @@
/*
* Almost all MIPS CPUs define 8 interrupt sources. They are typically
* level triggered (i.e., cannot be cleared from CPU; must be cleared from
- * device). The first two are software interrupts which we don't really
- * use or support. The last one is usually the CPU timer interrupt if
- * counter register is present or, for CPUs with an external FPU, by
- * convention it's the FPU exception interrupt.
+ * device).
*
- * Don't even think about using this on SMP. You have been warned.
+ * The first two are software interrupts (i.e. not exposed as pins) which
+ * may be used for IPIs in multi-threaded single-core systems.
*
- * This file exports one global function:
- * void mips_cpu_irq_init(void);
+ * The last one is usually the CPU timer interrupt if the counter register
+ * is present, or for old CPUs with an external FPU by convention it's the
+ * FPU exception interrupt.
*/
#include <linux/init.h>
#include <linux/interrupt.h>
@@ -39,15 +38,18 @@
#include <asm/mipsmtregs.h>
#include <asm/setup.h>
+static struct irq_domain *irq_domain;
+static struct irq_domain *ipi_domain;
+
static inline void unmask_mips_irq(struct irq_data *d)
{
- set_c0_status(0x100 << (d->irq - MIPS_CPU_IRQ_BASE));
+ set_c0_status(IE_SW0 << d->hwirq);
irq_enable_hazard();
}
static inline void mask_mips_irq(struct irq_data *d)
{
- clear_c0_status(0x100 << (d->irq - MIPS_CPU_IRQ_BASE));
+ clear_c0_status(IE_SW0 << d->hwirq);
irq_disable_hazard();
}
@@ -70,7 +72,7 @@ static unsigned int mips_mt_cpu_irq_startup(struct irq_data *d)
{
unsigned int vpflags = dvpe();
- clear_c0_cause(0x100 << (d->irq - MIPS_CPU_IRQ_BASE));
+ clear_c0_cause(C_SW0 << d->hwirq);
evpe(vpflags);
unmask_mips_irq(d);
return 0;
@@ -83,11 +85,34 @@ static unsigned int mips_mt_cpu_irq_startup(struct irq_data *d)
static void mips_mt_cpu_irq_ack(struct irq_data *d)
{
unsigned int vpflags = dvpe();
- clear_c0_cause(0x100 << (d->irq - MIPS_CPU_IRQ_BASE));
+ clear_c0_cause(C_SW0 << d->hwirq);
evpe(vpflags);
mask_mips_irq(d);
}
+#ifdef CONFIG_GENERIC_IRQ_IPI
+
+static void mips_mt_send_ipi(struct irq_data *d, unsigned int cpu)
+{
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+ unsigned long flags;
+ int vpflags;
+
+ local_irq_save(flags);
+
+ /* We can only send IPIs to VPEs within the local core */
+ WARN_ON(cpu_data[cpu].core != current_cpu_data.core);
+
+ vpflags = dvpe();
+ settc(cpu_vpe_id(&cpu_data[cpu]));
+ write_vpe_c0_cause(read_vpe_c0_cause() | (C_SW0 << hwirq));
+ evpe(vpflags);
+
+ local_irq_restore(flags);
+}
+
+#endif /* CONFIG_GENERIC_IRQ_IPI */
+
static struct irq_chip mips_mt_cpu_irq_controller = {
.name = "MIPS",
.irq_startup = mips_mt_cpu_irq_startup,
@@ -98,11 +123,15 @@ static struct irq_chip mips_mt_cpu_irq_controller = {
.irq_eoi = unmask_mips_irq,
.irq_disable = mask_mips_irq,
.irq_enable = unmask_mips_irq,
+#ifdef CONFIG_GENERIC_IRQ_IPI
+ .ipi_send_single = mips_mt_send_ipi,
+#endif
};
asmlinkage void __weak plat_irq_dispatch(void)
{
unsigned long pending = read_c0_cause() & read_c0_status() & ST0_IM;
+ unsigned int virq;
int irq;
if (!pending) {
@@ -113,7 +142,11 @@ asmlinkage void __weak plat_irq_dispatch(void)
pending >>= CAUSEB_IP;
while (pending) {
irq = fls(pending) - 1;
- do_IRQ(MIPS_CPU_IRQ_BASE + irq);
+ if (IS_ENABLED(CONFIG_GENERIC_IRQ_IPI) && irq < 2)
+ virq = irq_linear_revmap(ipi_domain, irq);
+ else
+ virq = irq_linear_revmap(irq_domain, irq);
+ do_IRQ(virq);
pending &= ~BIT(irq);
}
}
@@ -143,18 +176,97 @@ static const struct irq_domain_ops mips_cpu_intc_irq_domain_ops = {
.xlate = irq_domain_xlate_onecell,
};
-static void __init __mips_cpu_irq_init(struct device_node *of_node)
+#ifdef CONFIG_GENERIC_IRQ_IPI
+
+struct cpu_ipi_domain_state {
+ DECLARE_BITMAP(allocated, 2);
+};
+
+static int mips_cpu_ipi_alloc(struct irq_domain *domain, unsigned int virq,
+ unsigned int nr_irqs, void *arg)
{
- struct irq_domain *domain;
+ struct cpu_ipi_domain_state *state = domain->host_data;
+ unsigned int i, hwirq;
+ int ret;
+ for (i = 0; i < nr_irqs; i++) {
+ hwirq = find_first_zero_bit(state->allocated, 2);
+ if (hwirq == 2)
+ return -EBUSY;
+ bitmap_set(state->allocated, hwirq, 1);
+
+ ret = irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq,
+ &mips_mt_cpu_irq_controller,
+ NULL);
+ if (ret)
+ return ret;
+
+ ret = irq_set_irq_type(virq + i, IRQ_TYPE_LEVEL_HIGH);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mips_cpu_ipi_match(struct irq_domain *d, struct device_node *node,
+ enum irq_domain_bus_token bus_token)
+{
+ bool is_ipi;
+
+ switch (bus_token) {
+ case DOMAIN_BUS_IPI:
+ is_ipi = d->bus_token == bus_token;
+ return (!node || (to_of_node(d->fwnode) == node)) && is_ipi;
+ default:
+ return 0;
+ }
+}
+
+static const struct irq_domain_ops mips_cpu_ipi_chip_ops = {
+ .alloc = mips_cpu_ipi_alloc,
+ .match = mips_cpu_ipi_match,
+};
+
+static void mips_cpu_register_ipi_domain(struct device_node *of_node)
+{
+ struct cpu_ipi_domain_state *ipi_domain_state;
+
+ ipi_domain_state = kzalloc(sizeof(*ipi_domain_state), GFP_KERNEL);
+ ipi_domain = irq_domain_add_hierarchy(irq_domain,
+ IRQ_DOMAIN_FLAG_IPI_SINGLE,
+ 2, of_node,
+ &mips_cpu_ipi_chip_ops,
+ ipi_domain_state);
+ if (!ipi_domain)
+ panic("Failed to add MIPS CPU IPI domain");
+ ipi_domain->bus_token = DOMAIN_BUS_IPI;
+}
+
+#else /* !CONFIG_GENERIC_IRQ_IPI */
+
+static inline void mips_cpu_register_ipi_domain(struct device_node *of_node) {}
+
+#endif /* !CONFIG_GENERIC_IRQ_IPI */
+
+static void __init __mips_cpu_irq_init(struct device_node *of_node)
+{
/* Mask interrupts. */
clear_c0_status(ST0_IM);
clear_c0_cause(CAUSEF_IP);
- domain = irq_domain_add_legacy(of_node, 8, MIPS_CPU_IRQ_BASE, 0,
- &mips_cpu_intc_irq_domain_ops, NULL);
- if (!domain)
+ irq_domain = irq_domain_add_legacy(of_node, 8, MIPS_CPU_IRQ_BASE, 0,
+ &mips_cpu_intc_irq_domain_ops,
+ NULL);
+ if (!irq_domain)
panic("Failed to add irqdomain for MIPS CPU");
+
+ /*
+ * Only proceed to register the software interrupt IPI implementation
+ * for CPUs which implement the MIPS MT (multi-threading) ASE.
+ */
+ if (cpu_has_mipsmt)
+ mips_cpu_register_ipi_domain(of_node);
}
void __init mips_cpu_irq_init(void)