162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * intc.c  --  interrupt controller or ColdFire 5272 SoC
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * (C) Copyright 2009, Greg Ungerer <gerg@snapgear.com>
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
762306a36Sopenharmony_ci * License.  See the file COPYING in the main directory of this archive
862306a36Sopenharmony_ci * for more details.
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/types.h>
1262306a36Sopenharmony_ci#include <linux/init.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/interrupt.h>
1562306a36Sopenharmony_ci#include <linux/kernel_stat.h>
1662306a36Sopenharmony_ci#include <linux/irq.h>
1762306a36Sopenharmony_ci#include <linux/io.h>
1862306a36Sopenharmony_ci#include <asm/coldfire.h>
1962306a36Sopenharmony_ci#include <asm/mcfsim.h>
2062306a36Sopenharmony_ci#include <asm/traps.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/*
2362306a36Sopenharmony_ci * The 5272 ColdFire interrupt controller is nothing like any other
2462306a36Sopenharmony_ci * ColdFire interrupt controller - it truly is completely different.
2562306a36Sopenharmony_ci * Given its age it is unlikely to be used on any other ColdFire CPU.
2662306a36Sopenharmony_ci */
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/*
2962306a36Sopenharmony_ci * The masking and priproty setting of interrupts on the 5272 is done
3062306a36Sopenharmony_ci * via a set of 4 "Interrupt Controller Registers" (ICR). There is a
3162306a36Sopenharmony_ci * loose mapping of vector number to register and internal bits, but
3262306a36Sopenharmony_ci * a table is the easiest and quickest way to map them.
3362306a36Sopenharmony_ci *
3462306a36Sopenharmony_ci * Note that the external interrupts are edge triggered (unlike the
3562306a36Sopenharmony_ci * internal interrupt sources which are level triggered). Which means
3662306a36Sopenharmony_ci * they also need acknowledging via acknowledge bits.
3762306a36Sopenharmony_ci */
3862306a36Sopenharmony_cistruct irqmap {
3962306a36Sopenharmony_ci	unsigned int	icr;
4062306a36Sopenharmony_ci	unsigned char	index;
4162306a36Sopenharmony_ci	unsigned char	ack;
4262306a36Sopenharmony_ci};
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic struct irqmap intc_irqmap[MCFINT_VECMAX - MCFINT_VECBASE] = {
4562306a36Sopenharmony_ci	/*MCF_IRQ_SPURIOUS*/	{ .icr = 0,           .index = 0,  .ack = 0, },
4662306a36Sopenharmony_ci	/*MCF_IRQ_EINT1*/	{ .icr = MCFSIM_ICR1, .index = 28, .ack = 1, },
4762306a36Sopenharmony_ci	/*MCF_IRQ_EINT2*/	{ .icr = MCFSIM_ICR1, .index = 24, .ack = 1, },
4862306a36Sopenharmony_ci	/*MCF_IRQ_EINT3*/	{ .icr = MCFSIM_ICR1, .index = 20, .ack = 1, },
4962306a36Sopenharmony_ci	/*MCF_IRQ_EINT4*/	{ .icr = MCFSIM_ICR1, .index = 16, .ack = 1, },
5062306a36Sopenharmony_ci	/*MCF_IRQ_TIMER1*/	{ .icr = MCFSIM_ICR1, .index = 12, .ack = 0, },
5162306a36Sopenharmony_ci	/*MCF_IRQ_TIMER2*/	{ .icr = MCFSIM_ICR1, .index = 8,  .ack = 0, },
5262306a36Sopenharmony_ci	/*MCF_IRQ_TIMER3*/	{ .icr = MCFSIM_ICR1, .index = 4,  .ack = 0, },
5362306a36Sopenharmony_ci	/*MCF_IRQ_TIMER4*/	{ .icr = MCFSIM_ICR1, .index = 0,  .ack = 0, },
5462306a36Sopenharmony_ci	/*MCF_IRQ_UART1*/	{ .icr = MCFSIM_ICR2, .index = 28, .ack = 0, },
5562306a36Sopenharmony_ci	/*MCF_IRQ_UART2*/	{ .icr = MCFSIM_ICR2, .index = 24, .ack = 0, },
5662306a36Sopenharmony_ci	/*MCF_IRQ_PLIP*/	{ .icr = MCFSIM_ICR2, .index = 20, .ack = 0, },
5762306a36Sopenharmony_ci	/*MCF_IRQ_PLIA*/	{ .icr = MCFSIM_ICR2, .index = 16, .ack = 0, },
5862306a36Sopenharmony_ci	/*MCF_IRQ_USB0*/	{ .icr = MCFSIM_ICR2, .index = 12, .ack = 0, },
5962306a36Sopenharmony_ci	/*MCF_IRQ_USB1*/	{ .icr = MCFSIM_ICR2, .index = 8,  .ack = 0, },
6062306a36Sopenharmony_ci	/*MCF_IRQ_USB2*/	{ .icr = MCFSIM_ICR2, .index = 4,  .ack = 0, },
6162306a36Sopenharmony_ci	/*MCF_IRQ_USB3*/	{ .icr = MCFSIM_ICR2, .index = 0,  .ack = 0, },
6262306a36Sopenharmony_ci	/*MCF_IRQ_USB4*/	{ .icr = MCFSIM_ICR3, .index = 28, .ack = 0, },
6362306a36Sopenharmony_ci	/*MCF_IRQ_USB5*/	{ .icr = MCFSIM_ICR3, .index = 24, .ack = 0, },
6462306a36Sopenharmony_ci	/*MCF_IRQ_USB6*/	{ .icr = MCFSIM_ICR3, .index = 20, .ack = 0, },
6562306a36Sopenharmony_ci	/*MCF_IRQ_USB7*/	{ .icr = MCFSIM_ICR3, .index = 16, .ack = 0, },
6662306a36Sopenharmony_ci	/*MCF_IRQ_DMA*/		{ .icr = MCFSIM_ICR3, .index = 12, .ack = 0, },
6762306a36Sopenharmony_ci	/*MCF_IRQ_ERX*/		{ .icr = MCFSIM_ICR3, .index = 8,  .ack = 0, },
6862306a36Sopenharmony_ci	/*MCF_IRQ_ETX*/		{ .icr = MCFSIM_ICR3, .index = 4,  .ack = 0, },
6962306a36Sopenharmony_ci	/*MCF_IRQ_ENTC*/	{ .icr = MCFSIM_ICR3, .index = 0,  .ack = 0, },
7062306a36Sopenharmony_ci	/*MCF_IRQ_QSPI*/	{ .icr = MCFSIM_ICR4, .index = 28, .ack = 0, },
7162306a36Sopenharmony_ci	/*MCF_IRQ_EINT5*/	{ .icr = MCFSIM_ICR4, .index = 24, .ack = 1, },
7262306a36Sopenharmony_ci	/*MCF_IRQ_EINT6*/	{ .icr = MCFSIM_ICR4, .index = 20, .ack = 1, },
7362306a36Sopenharmony_ci	/*MCF_IRQ_SWTO*/	{ .icr = MCFSIM_ICR4, .index = 16, .ack = 0, },
7462306a36Sopenharmony_ci};
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci/*
7762306a36Sopenharmony_ci * The act of masking the interrupt also has a side effect of 'ack'ing
7862306a36Sopenharmony_ci * an interrupt on this irq (for the external irqs). So this mask function
7962306a36Sopenharmony_ci * is also an ack_mask function.
8062306a36Sopenharmony_ci */
8162306a36Sopenharmony_cistatic void intc_irq_mask(struct irq_data *d)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	unsigned int irq = d->irq;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX)) {
8662306a36Sopenharmony_ci		u32 v;
8762306a36Sopenharmony_ci		irq -= MCFINT_VECBASE;
8862306a36Sopenharmony_ci		v = 0x8 << intc_irqmap[irq].index;
8962306a36Sopenharmony_ci		writel(v, intc_irqmap[irq].icr);
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic void intc_irq_unmask(struct irq_data *d)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	unsigned int irq = d->irq;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX)) {
9862306a36Sopenharmony_ci		u32 v;
9962306a36Sopenharmony_ci		irq -= MCFINT_VECBASE;
10062306a36Sopenharmony_ci		v = 0xd << intc_irqmap[irq].index;
10162306a36Sopenharmony_ci		writel(v, intc_irqmap[irq].icr);
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic void intc_irq_ack(struct irq_data *d)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	unsigned int irq = d->irq;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	/* Only external interrupts are acked */
11062306a36Sopenharmony_ci	if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX)) {
11162306a36Sopenharmony_ci		irq -= MCFINT_VECBASE;
11262306a36Sopenharmony_ci		if (intc_irqmap[irq].ack) {
11362306a36Sopenharmony_ci			u32 v;
11462306a36Sopenharmony_ci			v = readl(intc_irqmap[irq].icr);
11562306a36Sopenharmony_ci			v &= (0x7 << intc_irqmap[irq].index);
11662306a36Sopenharmony_ci			v |= (0x8 << intc_irqmap[irq].index);
11762306a36Sopenharmony_ci			writel(v, intc_irqmap[irq].icr);
11862306a36Sopenharmony_ci		}
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic int intc_irq_set_type(struct irq_data *d, unsigned int type)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	unsigned int irq = d->irq;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX)) {
12762306a36Sopenharmony_ci		irq -= MCFINT_VECBASE;
12862306a36Sopenharmony_ci		if (intc_irqmap[irq].ack) {
12962306a36Sopenharmony_ci			u32 v;
13062306a36Sopenharmony_ci			v = readl(MCFSIM_PITR);
13162306a36Sopenharmony_ci			if (type == IRQ_TYPE_EDGE_FALLING)
13262306a36Sopenharmony_ci				v &= ~(0x1 << (32 - irq));
13362306a36Sopenharmony_ci			else
13462306a36Sopenharmony_ci				v |= (0x1 << (32 - irq));
13562306a36Sopenharmony_ci			writel(v, MCFSIM_PITR);
13662306a36Sopenharmony_ci		}
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci	return 0;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci/*
14262306a36Sopenharmony_ci * Simple flow handler to deal with the external edge triggered interrupts.
14362306a36Sopenharmony_ci * We need to be careful with the masking/acking due to the side effects
14462306a36Sopenharmony_ci * of masking an interrupt.
14562306a36Sopenharmony_ci */
14662306a36Sopenharmony_cistatic void intc_external_irq(struct irq_desc *desc)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	irq_desc_get_chip(desc)->irq_ack(&desc->irq_data);
14962306a36Sopenharmony_ci	handle_simple_irq(desc);
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic struct irq_chip intc_irq_chip = {
15362306a36Sopenharmony_ci	.name		= "CF-INTC",
15462306a36Sopenharmony_ci	.irq_mask	= intc_irq_mask,
15562306a36Sopenharmony_ci	.irq_unmask	= intc_irq_unmask,
15662306a36Sopenharmony_ci	.irq_mask_ack	= intc_irq_mask,
15762306a36Sopenharmony_ci	.irq_ack	= intc_irq_ack,
15862306a36Sopenharmony_ci	.irq_set_type	= intc_irq_set_type,
15962306a36Sopenharmony_ci};
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_civoid __init init_IRQ(void)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	int irq, edge;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	/* Mask all interrupt sources */
16662306a36Sopenharmony_ci	writel(0x88888888, MCFSIM_ICR1);
16762306a36Sopenharmony_ci	writel(0x88888888, MCFSIM_ICR2);
16862306a36Sopenharmony_ci	writel(0x88888888, MCFSIM_ICR3);
16962306a36Sopenharmony_ci	writel(0x88888888, MCFSIM_ICR4);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	for (irq = 0; (irq < NR_IRQS); irq++) {
17262306a36Sopenharmony_ci		irq_set_chip(irq, &intc_irq_chip);
17362306a36Sopenharmony_ci		edge = 0;
17462306a36Sopenharmony_ci		if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX))
17562306a36Sopenharmony_ci			edge = intc_irqmap[irq - MCFINT_VECBASE].ack;
17662306a36Sopenharmony_ci		if (edge) {
17762306a36Sopenharmony_ci			irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING);
17862306a36Sopenharmony_ci			irq_set_handler(irq, intc_external_irq);
17962306a36Sopenharmony_ci		} else {
18062306a36Sopenharmony_ci			irq_set_irq_type(irq, IRQ_TYPE_LEVEL_HIGH);
18162306a36Sopenharmony_ci			irq_set_handler(irq, handle_level_irq);
18262306a36Sopenharmony_ci		}
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
186