/*
 * linux/include/asm-arm/arch-sa1100/irq.h
 *
 * Copyright (C) 1996-1999 Russell king
 * Copyright (C) 1999 Hugo Fiennes
 *
 * Changelog:
 *   22-08-1998	RMK	Restructured IRQ routines
 *   06-01-1999	HBF	SA1100 twiddles
 *   12-02-1999	NP	added ICCR
 *   17-02-1999	NP	empeg henry ugly hacks now in a separate file ;)
 *   11-08-1999	PD	SA1101 support added
 *   25-09-1999	RMK	Merged into main ARM tree, cleaned up
 */
#include <linux/config.h>

static inline unsigned int fixup_irq(unsigned int irq)
{
#ifdef CONFIG_SA1101
	if (irq == SA1101_IRQ) {
		unsigned long mask;
		mask = INTSTATCLR0 & INTENABLE0;
		irq  = 32;
		if (!mask) {
			mask = IRQSTATCLR1 & INTENABLE1;
			irq  = 64;
		}
		if (mask)
			while ((mask & 1) == 0) {
				mask >>= 1;
				irq += 1;
			}
	}
#endif
	return irq;
}

/* We don't need to ACK IRQs on the SA1100 unless they're <= 10,
 * ie, an edge-detect.
 */
static void sa1100_mask_and_ack_irq(unsigned int irq)
{
	ICMR &= ~(1 << irq);
	if (irq <= 10)
		GEDR = 1 << irq;
}

static void sa1100_mask_irq(unsigned int irq)
{
	ICMR &= ~(1 << irq);
}

static void sa1100_unmask_irq(unsigned int irq)
{
	ICMR |= 1 << irq;
}

#ifdef CONFIG_SA1101

static void sa1101_mask_and_ack_lowirq(unsigned int irq)
{
	unsigned int mask = 1 << (irq & 31);

	INTENABLE0 &= ~mask;
	GEDR = 1 << SA1101_IRQ;
	INTSTATCLR0 = mask;
}

static void sa1101_mask_and_ack_highirq(unsigned int irq)
{
	unsigned int mask = 1 << (irq & 31);

	INTENABLE1 &= ~mask;
	GEDR = 1 << SA1101_IRQ;
	INTSTATCLR1 = mask;
}

static void sa1101_mask_lowirq(unsigned int irq)
{
	unsigned int mask = 1 << (irq & 31);

	INTENABLE0 &= ~mask;
}

static void sa1101_mask_highirq(unsigned int irq)
{
	unsigned int mask = 1 << (irq & 31);

	INTENABLE1 &= ~mask;
}

/*
 * unmasking an IRq with the wrong polarity can be
 * fatal, but there is no need to check this in the
 * interrupt code - it will be spotted anyway ;-)
 */
static void sa1101_unmask_lowirq(unsigned int irq)
{
	unsigned int mask = 1 << (irq & 31);

	INTENABLE0 |= mask;
	ICMR |= 1 << SA1101_IRQ;
}

static void sa1101_unmask_highirq(unsigned int irq)
{
	unsigned int mask = 1 << (irq & 31);

	INTENABLE1 |= mask;
	ICMR |= 1 << SA1101_IRQ;
}
#endif

static __inline__ void irq_init_irq(void)
{
	int irq;

	/* disable all IRQs */
	ICMR = 0;

	/* all IRQs are IRQ, not FIQ */
	ICLR = 0;

	/* clear all GPIO edge detects */
	GEDR = -1;

#ifdef CONFIG_SA1101
	/* turn on interrupt controller */
	SKPCR |= 4;

	/* disable all IRQs */
	INTENABLE0 = 0;
	INTENABLE1 = 0;

	/* detect on rising edge */
	INTPOL0 = 0;
	INTPOL1 = 0;

	/* clear all IRQs */
	INTSTATCLR0 = -1;
	INTSTATCLR1 = -1;

	/* SA1101 generates a rising edge */
	GRER |= 1 << SA1101_IRQ;
	GPER &= ~(1 << SA1101_IRQ);
#endif

	/*
	 * 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;

#ifndef CONFIG_SA1101
	for (irq = 0; irq < NR_IRQS; irq++) {
		irq_desc[irq].valid	= 1;
		irq_desc[irq].probe_ok	= 1;
		irq_desc[irq].mask_ack	= sa1100_mask_and_ack_irq;
		irq_desc[irq].mask	= sa1100_mask_irq;
		irq_desc[irq].unmask	= sa1100_unmask_irq;
	}
#else
	for (irq = 0; irq < 31; irq++) {
		irq_desc[irq].valid	= 1;
		irq_desc[irq].probe_ok	= 1;
		irq_desc[irq].mask_ack	= sa1100_mask_and_ack_irq;
		irq_desc[irq].mask	= sa1100_mask_irq;
		irq_desc[irq].unmask	= sa1100_unmask_irq;
	}
	for (irq = 32; irq < 63; irq++) {
		irq_desc[irq].valid	= 1;
		irq_desc[irq].probe_ok	= 1;
		irq_desc[irq].mask_ack	= sa1101_mask_and_ack_lowirq;
		irq_desc[irq].mask	= sa1101_mask_lowirq;
		irq_desc[irq].unmask	= sa1101_unmask_lowirq;
	}
	for (irq = 64; irq < NR_IRQS; irq++) {
		irq_desc[irq].valid	= 1;
		irq_desc[irq].probe_ok	= 1;
		irq_desc[irq].mask_ack	= sa1101_mask_and_ack_highirq;
		irq_desc[irq].mask	= sa1101_mask_highirq;
		irq_desc[irq].unmask	= sa1101_unmask_highirq;
	}
#endif
}
