diff mbox series

[082/120] MIPS: PS2: GS: IRQ support

Message ID 09484c957252ee213b72c6173f692f7b28a29109.1567326213.git.noring@nocrew.org (mailing list archive)
State RFC
Headers show
Series Linux for the PlayStation 2 | expand

Commit Message

Fredrik Noring Sept. 1, 2019, 4:20 p.m. UTC
Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/include/asm/mach-ps2/irq.h |  1 +
 drivers/ps2/Makefile                 |  1 +
 drivers/ps2/gs-irq.c                 | 93 ++++++++++++++++++++++++++++
 3 files changed, 95 insertions(+)
 create mode 100644 drivers/ps2/gs-irq.c
diff mbox series

Patch

diff --git a/arch/mips/include/asm/mach-ps2/irq.h b/arch/mips/include/asm/mach-ps2/irq.h
index 57b3e539ad92..132ad329de6b 100644
--- a/arch/mips/include/asm/mach-ps2/irq.h
+++ b/arch/mips/include/asm/mach-ps2/irq.h
@@ -120,6 +120,7 @@ 
 
 int __init intc_irq_init(void);
 int __init dmac_irq_init(void);
+int gs_irq_init(void);
 
 /*
  * IRQs asserted by the I/O processor (IOP) via the sub-system interface (SIF).
diff --git a/drivers/ps2/Makefile b/drivers/ps2/Makefile
index 533ab86d6a58..7f108d4ee451 100644
--- a/drivers/ps2/Makefile
+++ b/drivers/ps2/Makefile
@@ -1,3 +1,4 @@ 
+obj-$(CONFIG_PS2_GS)		+= gs-irq.o
 obj-$(CONFIG_PS2_GS)		+= gs-registers.o
 
 obj-m				+= iop-heap.o
diff --git a/drivers/ps2/gs-irq.c b/drivers/ps2/gs-irq.c
new file mode 100644
index 000000000000..55116a8b5582
--- /dev/null
+++ b/drivers/ps2/gs-irq.c
@@ -0,0 +1,93 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 Graphics Synthesizer (GS) IRQs
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/types.h>
+
+#include <asm/irq_cpu.h>
+
+#include <asm/mach-ps2/irq.h>
+#include <asm/mach-ps2/gs.h>
+#include <asm/mach-ps2/gs-registers.h>
+
+static void gs_reverse_mask(struct irq_data *data)
+{
+	gs_xorq_imr(BIT(8 + data->irq - IRQ_GS));
+}
+
+static void gs_ack(struct irq_data *data)
+{
+	gs_writeq_csr(BIT(data->irq - IRQ_GS));
+}
+
+#define GS_IRQ_TYPE(irq_, name_)			\
+	{						\
+		.irq = irq_,				\
+		.irq_chip = {				\
+			.name = name_,			\
+			.irq_unmask = gs_reverse_mask,	\
+			.irq_mask = gs_reverse_mask,	\
+			.irq_ack = gs_ack		\
+		}					\
+	}
+
+static struct {
+	unsigned int irq;
+	struct irq_chip irq_chip;
+} gs_irqs[] = {
+	GS_IRQ_TYPE(IRQ_GS_SIGNAL,  "GS SIGNAL"),
+	GS_IRQ_TYPE(IRQ_GS_FINISH,  "GS FINISH"),
+	GS_IRQ_TYPE(IRQ_GS_HSYNC,   "GS HSYNC"),
+	GS_IRQ_TYPE(IRQ_GS_VSYNC,   "GS VSYNC"),
+	GS_IRQ_TYPE(IRQ_GS_EDW,     "GS EDW"),
+	GS_IRQ_TYPE(IRQ_GS_EXHSYNC, "GS EXHSYNC"),
+	GS_IRQ_TYPE(IRQ_GS_EXVSYNC, "GS EXVSYNC"),
+};
+
+static irqreturn_t gs_cascade(int irq, void *data)
+{
+	unsigned int pending = gs_readq_csr() & 0x1f;
+
+	if (!pending)
+		return IRQ_NONE;
+
+	while (pending) {
+		const unsigned int irq_gs = __fls(pending);
+
+		if (generic_handle_irq(irq_gs + IRQ_GS) < 0)
+			spurious_interrupt();
+		pending &= ~BIT(irq_gs);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static struct irqaction cascade_gs_irqaction = {
+	.name = "GS cascade",
+	.handler = gs_cascade,
+};
+
+int gs_irq_init(void)
+{
+	size_t i;
+	int err;
+
+	gs_writeq_imr(0x7f00);	/* Disable GS interrupts */
+	gs_writeq_csr(0x00ff);	/* Clear GS events */
+
+	for (i = 0; i < ARRAY_SIZE(gs_irqs); i++)
+		irq_set_chip_and_handler(gs_irqs[i].irq,
+			&gs_irqs[i].irq_chip, handle_level_irq);
+
+	err = setup_irq(IRQ_INTC_GS, &cascade_gs_irqaction);
+	if (err)
+		pr_err("gs-irq: Failed to setup GS IRQs (err = %d)\n", err);
+
+	return err;
+}