patch-2.4.4 linux/arch/sh/kernel/setup_ec3104.c

Next file: linux/arch/sh/kernel/setup_hd64461.c
Previous file: linux/arch/sh/kernel/setup_dc.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.3/linux/arch/sh/kernel/setup_ec3104.c linux/arch/sh/kernel/setup_ec3104.c
@@ -0,0 +1,242 @@
+/*
+ * linux/arch/sh/kernel/setup_ec3104.c
+ *  EC3104 companion chip support
+ *
+ * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
+ *
+ */
+/* EC3104 note:
+ * This code was written without any documentation about the EC3104 chip.  While
+ * I hope I got most of the basic functionality right, the register names I use
+ * are most likely completely different from those in the chip documentation.
+ *
+ * If you have any further information about the EC3104, please tell me
+ * (prumpf@tux.org).
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/types.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/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)
+{
+	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;
+}
+
+int __init setup_ec3104(void)
+{
+	char str[8];
+	int i;
+	
+	if (!MACH_EC3104)
+		printk("!MACH_EC3104\n");
+
+	if (0)
+		return 0;
+
+	for (i=0; i<8; i++)
+		str[i] = ctrl_readb(EC3104_BASE + i);
+
+	for (i = EC3104_IRQBASE; i < EC3104_IRQBASE + 32; i++)
+		irq_desc[i].handler = &ec3104_int;
+
+	printk("initializing EC3104 \"%.8s\" at %08x, IRQ %d, IRQ base %d\n",
+	       str, EC3104_BASE, EC3104_IRQ, EC3104_IRQBASE);
+
+
+	/* mask all interrupts.  this should have been done by the boot
+	 * loader for us but we want to be sure ... */
+	ctrl_writel(0xffffffff, EC3104_IMR);
+	
+	return 0;
+}
+
+module_init(setup_ec3104);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)