summaryrefslogtreecommitdiffstats
path: root/arch/sh/boards/ec3104/irq.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/sh/boards/ec3104/irq.c')
-rw-r--r--arch/sh/boards/ec3104/irq.c196
1 files changed, 196 insertions, 0 deletions
diff --git a/arch/sh/boards/ec3104/irq.c b/arch/sh/boards/ec3104/irq.c
new file mode 100644
index 000000000000..ffa4ff1f090f
--- /dev/null
+++ b/arch/sh/boards/ec3104/irq.c
@@ -0,0 +1,196 @@
+/*
+ * linux/arch/sh/boards/ec3104/irq.c
+ * EC3104 companion chip support
+ *
+ * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
+ *
+ */
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/ec3104/ec3104.h>
+
+/* This is for debugging mostly; here's the table that I intend to keep
+ * in here:
+ *
+ * index function base addr power interrupt bit
+ * 0 power b0ec0000 --- 00000001 (unused)
+ * 1 irqs b0ec1000 --- 00000002 (unused)
+ * 2 ?? b0ec2000 b0ec0008 00000004
+ * 3 PS2 (1) b0ec3000 b0ec000c 00000008
+ * 4 PS2 (2) b0ec4000 b0ec0010 00000010
+ * 5 ?? b0ec5000 b0ec0014 00000020
+ * 6 I2C b0ec6000 b0ec0018 00000040
+ * 7 serial (1) b0ec7000 b0ec001c 00000080
+ * 8 serial (2) b0ec8000 b0ec0020 00000100
+ * 9 serial (3) b0ec9000 b0ec0024 00000200
+ * 10 serial (4) b0eca000 b0ec0028 00000400
+ * 12 GPIO (1) b0ecc000 b0ec0030
+ * 13 GPIO (2) b0ecc000 b0ec0030
+ * 16 pcmcia (1) b0ed0000 b0ec0040 00010000
+ * 17 pcmcia (2) b0ed1000 b0ec0044 00020000
+ */
+
+/* I used the register names from another interrupt controller I worked with,
+ * since it seems to be identical to the ec3104 except that all bits are
+ * inverted:
+ *
+ * IRR: Interrupt Request Register (pending and enabled interrupts)
+ * IMR: Interrupt Mask Register (which interrupts are enabled)
+ * IPR: Interrupt Pending Register (pending interrupts, even disabled ones)
+ *
+ * 0 bits mean pending or enabled, 1 bits mean not pending or disabled. all
+ * IRQs seem to be level-triggered.
+ */
+
+#define EC3104_IRR (EC3104_BASE + 0x1000)
+#define EC3104_IMR (EC3104_BASE + 0x1004)
+#define EC3104_IPR (EC3104_BASE + 0x1008)
+
+#define ctrl_readl(addr) (*(volatile u32 *)(addr))
+#define ctrl_writel(data,addr) (*(volatile u32 *)(addr) = (data))
+#define ctrl_readb(addr) (*(volatile u8 *)(addr))
+
+static char *ec3104_name(unsigned index)
+{
+ switch(index) {
+ case 0:
+ return "power management";
+ case 1:
+ return "interrupts";
+ case 3:
+ return "PS2 (1)";
+ case 4:
+ return "PS2 (2)";
+ case 5:
+ return "I2C (1)";
+ case 6:
+ return "I2C (2)";
+ case 7:
+ return "serial (1)";
+ case 8:
+ return "serial (2)";
+ case 9:
+ return "serial (3)";
+ case 10:
+ return "serial (4)";
+ case 16:
+ return "pcmcia (1)";
+ case 17:
+ return "pcmcia (2)";
+ default: {
+ static char buf[32];
+
+ sprintf(buf, "unknown (%d)", index);
+
+ return buf;
+ }
+ }
+}
+
+int get_pending_interrupts(char *buf)
+{
+ u32 ipr;
+ u32 bit;
+ char *p = buf;
+
+ p += sprintf(p, "pending: (");
+
+ ipr = ctrl_inl(EC3104_IPR);
+
+ for (bit = 1; bit < 32; bit++)
+ if (!(ipr & (1<<bit)))
+ p += sprintf(p, "%s ", ec3104_name(bit));
+
+ p += sprintf(p, ")\n");
+
+ return p - buf;
+}
+
+static inline u32 ec3104_irq2mask(unsigned int irq)
+{
+ return (1 << (irq - EC3104_IRQBASE));
+}
+
+static inline void mask_ec3104_irq(unsigned int irq)
+{
+ u32 mask;
+
+ mask = ctrl_readl(EC3104_IMR);
+
+ mask |= ec3104_irq2mask(irq);
+
+ ctrl_writel(mask, EC3104_IMR);
+}
+
+static inline void unmask_ec3104_irq(unsigned int irq)
+{
+ u32 mask;
+
+ mask = ctrl_readl(EC3104_IMR);
+
+ mask &= ~ec3104_irq2mask(irq);
+
+ ctrl_writel(mask, EC3104_IMR);
+}
+
+static void disable_ec3104_irq(unsigned int irq)
+{
+ mask_ec3104_irq(irq);
+}
+
+static void enable_ec3104_irq(unsigned int irq)
+{
+ unmask_ec3104_irq(irq);
+}
+
+static void mask_and_ack_ec3104_irq(unsigned int irq)
+{
+ mask_ec3104_irq(irq);
+}
+
+static void end_ec3104_irq(unsigned int irq)
+{
+ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+ unmask_ec3104_irq(irq);
+}
+
+static unsigned int startup_ec3104_irq(unsigned int irq)
+{
+ unmask_ec3104_irq(irq);
+
+ return 0;
+}
+
+static void shutdown_ec3104_irq(unsigned int irq)
+{
+ mask_ec3104_irq(irq);
+
+}
+
+static struct hw_interrupt_type ec3104_int = {
+ .typename = "EC3104",
+ .enable = enable_ec3104_irq,
+ .disable = disable_ec3104_irq,
+ .ack = mask_and_ack_ec3104_irq,
+ .end = end_ec3104_irq,
+ .startup = startup_ec3104_irq,
+ .shutdown = shutdown_ec3104_irq,
+};
+
+/* Yuck. the _demux API is ugly */
+int ec3104_irq_demux(int irq)
+{
+ if (irq == EC3104_IRQ) {
+ unsigned int mask;
+
+ mask = ctrl_readl(EC3104_IRR);
+
+ if (mask == 0xffffffff)
+ return EC3104_IRQ;
+ else
+ return EC3104_IRQBASE + ffz(mask);
+ }
+
+ return irq;
+}