patch-2.4.9 linux/arch/arm/mach-sa1100/irq.c

Next file: linux/arch/arm/mach-sa1100/itsy.c
Previous file: linux/arch/arm/mach-sa1100/hw.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.8/linux/arch/arm/mach-sa1100/irq.c linux/arch/arm/mach-sa1100/irq.c
@@ -0,0 +1,230 @@
+/*
+ * linux/arch/arm/mach-sa1100/irq.c
+ *
+ * Copyright (C) 1999-2001 Nicolas Pitre
+ *
+ * Generic IRQ handling for the SA11x0, GPIO 11-27 IRQ demultiplexing.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+
+#include <asm/hardware.h>
+#include <asm/irq.h>
+#include <asm/mach/irq.h>
+#include <asm/arch/irq.h>
+
+#include "generic.h"
+
+
+/*
+ * SA1100 GPIO edge detection for IRQs:
+ * IRQs are generated on Falling-Edge, Rising-Edge, or both.
+ * This must be called *before* the appropriate IRQ is registered.
+ * Use this instead of directly setting GRER/GFER.
+ */
+
+static int GPIO_IRQ_rising_edge;
+static int GPIO_IRQ_falling_edge;
+
+void set_GPIO_IRQ_edge( int gpio_mask, int edge )
+{
+	if (edge & GPIO_FALLING_EDGE)
+		GPIO_IRQ_falling_edge |= gpio_mask;
+	else
+		GPIO_IRQ_falling_edge &= ~gpio_mask;
+	if (edge & GPIO_RISING_EDGE)
+		GPIO_IRQ_rising_edge |= gpio_mask;
+	else
+		GPIO_IRQ_rising_edge &= ~gpio_mask;
+}
+
+EXPORT_SYMBOL(set_GPIO_IRQ_edge);
+
+
+/*
+ * We don't need to ACK IRQs on the SA1100 unless they're GPIOs
+ * this is for internal IRQs i.e. from 11 to 31.
+ */
+
+static void sa1100_mask_irq(unsigned int irq)
+{
+	ICMR &= ~(1 << irq);
+}
+
+static void sa1100_unmask_irq(unsigned int irq)
+{
+	ICMR |= (1 << irq);
+}
+
+/*
+ * GPIO IRQs must be acknoledged.  This is for IRQs from 0 to 10.
+ */
+
+static void sa1100_mask_and_ack_GPIO0_10_irq(unsigned int irq)
+{
+	ICMR &= ~(1 << irq);
+	GEDR = (1 << irq);
+}
+
+static void sa1100_mask_GPIO0_10_irq(unsigned int irq)
+{
+	ICMR &= ~(1 << irq);
+}
+
+static void sa1100_unmask_GPIO0_10_irq(unsigned int irq)
+{
+	GRER = (GRER & ~(1 << irq)) | (GPIO_IRQ_rising_edge & (1 << irq));
+	GFER = (GFER & ~(1 << irq)) | (GPIO_IRQ_falling_edge & (1 << irq));
+	ICMR |= (1 << irq);
+}
+
+/*
+ * Install handler for GPIO 11-27 edge detect interrupts
+ */
+
+static int GPIO_11_27_enabled;		/* enabled i.e. unmasked GPIO IRQs */
+static int GPIO_11_27_spurious;		/* GPIOs that triggered when masked */
+
+static void sa1100_GPIO11_27_demux(int irq, void *dev_id,
+				   struct pt_regs *regs)
+{
+	int i, spurious;
+
+	while ((irq = (GEDR & 0xfffff800))) {
+		/*
+		 * We don't want to clear GRER/GFER when the corresponding
+		 * IRQ is masked because we could miss a level transition
+		 * i.e. an IRQ which need servicing as soon as it is
+		 * unmasked.  However, such situation should happen only
+		 * during the loop below.  Thus all IRQs which aren't
+		 * enabled at this point are considered spurious.  Those
+		 * are cleared but only de-activated if they happen twice.
+		 */
+		spurious = irq & ~GPIO_11_27_enabled;
+		if (spurious) {
+			GEDR = spurious;
+			GRER &= ~(spurious & GPIO_11_27_spurious);
+			GFER &= ~(spurious & GPIO_11_27_spurious);
+			GPIO_11_27_spurious |= spurious;
+			irq ^= spurious;
+			if (!irq) continue;
+		}
+
+		for (i = 11; i <= 27; ++i) {
+			if (irq & (1<<i)) {
+				do_IRQ (IRQ_GPIO_11_27(i), regs);
+			}
+		}
+	}
+}
+
+static struct irqaction GPIO11_27_irq = {
+	name:		"GPIO 11-27",
+	handler:	sa1100_GPIO11_27_demux,
+	flags:		SA_INTERRUPT
+};
+
+static void sa1100_mask_and_ack_GPIO11_27_irq(unsigned int irq)
+{
+	int mask = (1 << GPIO_11_27_IRQ(irq));
+	GPIO_11_27_spurious &= ~mask;
+	GPIO_11_27_enabled &= ~mask;
+	GEDR = mask;
+}
+
+static void sa1100_mask_GPIO11_27_irq(unsigned int irq)
+{
+	int mask = (1 << GPIO_11_27_IRQ(irq));
+	GPIO_11_27_spurious &= ~mask;
+	GPIO_11_27_enabled &= ~mask;
+}
+
+static void sa1100_unmask_GPIO11_27_irq(unsigned int irq)
+{
+	int mask = (1 << GPIO_11_27_IRQ(irq));
+	if (GPIO_11_27_spurious & mask) {
+		/*
+		 * We don't want to miss an interrupt that would have occurred
+		 * while it was masked.  Simulate it if it is the case.
+		 */
+		int state = GPLR;
+		if (((state & GPIO_IRQ_rising_edge) |
+		     (~state & GPIO_IRQ_falling_edge)) & mask)
+		{
+			/* just in case it gets referenced: */
+			struct pt_regs dummy;
+
+			memzero(&dummy, sizeof(dummy));
+			do_IRQ(irq, &dummy);
+
+			/* we are being called recursively from do_IRQ() */
+			return;
+		}
+	}
+	GPIO_11_27_enabled |= mask;
+	GRER = (GRER & ~mask) | (GPIO_IRQ_rising_edge & mask);
+	GFER = (GFER & ~mask) | (GPIO_IRQ_falling_edge & mask);
+}
+
+
+void __init sa1100_init_irq(void)
+{
+	int irq;
+
+	/* disable all IRQs */
+	ICMR = 0;
+
+	/* all IRQs are IRQ, not FIQ */
+	ICLR = 0;
+
+	/* clear all GPIO edge detects */
+	GFER = 0;
+	GRER = 0;
+	GEDR = -1;
+
+	/*
+	 * Whatever the doc says, this has to be set for the wait-on-irq
+	 * instruction to work... on a SA1100 rev 9 at least.
+	 */
+	ICCR = 1;
+
+	for (irq = 0; irq <= 10; irq++) {
+		irq_desc[irq].valid	= 1;
+		irq_desc[irq].probe_ok	= 1;
+		irq_desc[irq].mask_ack	= sa1100_mask_and_ack_GPIO0_10_irq;
+		irq_desc[irq].mask	= sa1100_mask_GPIO0_10_irq;
+		irq_desc[irq].unmask	= sa1100_unmask_GPIO0_10_irq;
+	}
+
+	for (irq = 11; irq <= 31; irq++) {
+		irq_desc[irq].valid	= 1;
+		irq_desc[irq].probe_ok	= 0;
+		irq_desc[irq].mask_ack	= sa1100_mask_irq;
+		irq_desc[irq].mask	= sa1100_mask_irq;
+		irq_desc[irq].unmask	= sa1100_unmask_irq;
+	}
+
+	for (irq = 32; irq <= 48; irq++) {
+		irq_desc[irq].valid	= 1;
+		irq_desc[irq].probe_ok	= 1;
+		irq_desc[irq].mask_ack	= sa1100_mask_and_ack_GPIO11_27_irq;
+		irq_desc[irq].mask	= sa1100_mask_GPIO11_27_irq;
+		irq_desc[irq].unmask	= sa1100_unmask_GPIO11_27_irq;
+	}
+	setup_arm_irq( IRQ_GPIO11_27, &GPIO11_27_irq );
+
+	/*
+	 * We generally don't want the LCD IRQ being
+	 * enabled as soon as we request it.
+	 */
+	irq_desc[IRQ_LCD].noautoenable = 1;
+}

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