162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * intc-simr.c
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Interrupt controller code for the ColdFire 5208, 5207 & 532x parts.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * (C) Copyright 2009-2011, Greg Ungerer <gerg@snapgear.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
962306a36Sopenharmony_ci * License.  See the file COPYING in the main directory of this archive
1062306a36Sopenharmony_ci * for more details.
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/types.h>
1462306a36Sopenharmony_ci#include <linux/init.h>
1562306a36Sopenharmony_ci#include <linux/kernel.h>
1662306a36Sopenharmony_ci#include <linux/interrupt.h>
1762306a36Sopenharmony_ci#include <linux/irq.h>
1862306a36Sopenharmony_ci#include <linux/io.h>
1962306a36Sopenharmony_ci#include <asm/coldfire.h>
2062306a36Sopenharmony_ci#include <asm/mcfsim.h>
2162306a36Sopenharmony_ci#include <asm/traps.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/*
2462306a36Sopenharmony_ci *	The EDGE Port interrupts are the fixed 7 external interrupts.
2562306a36Sopenharmony_ci *	They need some special treatment, for example they need to be acked.
2662306a36Sopenharmony_ci */
2762306a36Sopenharmony_ci#ifdef CONFIG_M520x
2862306a36Sopenharmony_ci/*
2962306a36Sopenharmony_ci *	The 520x parts only support a limited range of these external
3062306a36Sopenharmony_ci *	interrupts, only 1, 4 and 7 (as interrupts 65, 66 and 67).
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_ci#define	EINT0	64	/* Is not actually used, but spot reserved for it */
3362306a36Sopenharmony_ci#define	EINT1	65	/* EDGE Port interrupt 1 */
3462306a36Sopenharmony_ci#define	EINT4	66	/* EDGE Port interrupt 4 */
3562306a36Sopenharmony_ci#define	EINT7	67	/* EDGE Port interrupt 7 */
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic unsigned int irqebitmap[] = { 0, 1, 4, 7 };
3862306a36Sopenharmony_cistatic inline unsigned int irq2ebit(unsigned int irq)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	return irqebitmap[irq - EINT0];
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#else
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/*
4662306a36Sopenharmony_ci *	Most of the ColdFire parts with the EDGE Port module just have
4762306a36Sopenharmony_ci *	a strait direct mapping of the 7 external interrupts. Although
4862306a36Sopenharmony_ci *	there is a bit reserved for 0, it is not used.
4962306a36Sopenharmony_ci */
5062306a36Sopenharmony_ci#define	EINT0	64	/* Is not actually used, but spot reserved for it */
5162306a36Sopenharmony_ci#define	EINT1	65	/* EDGE Port interrupt 1 */
5262306a36Sopenharmony_ci#define	EINT7	71	/* EDGE Port interrupt 7 */
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic inline unsigned int irq2ebit(unsigned int irq)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	return irq - EINT0;
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci#endif
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/*
6262306a36Sopenharmony_ci *	There maybe one, two or three interrupt control units, each has 64
6362306a36Sopenharmony_ci *	interrupts. If there is no second or third unit then MCFINTC1_* or
6462306a36Sopenharmony_ci *	MCFINTC2_* defines will be 0 (and code for them optimized away).
6562306a36Sopenharmony_ci */
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic void intc_irq_mask(struct irq_data *d)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	unsigned int irq = d->irq - MCFINT_VECBASE;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	if (MCFINTC2_SIMR && (irq > 127))
7262306a36Sopenharmony_ci		__raw_writeb(irq - 128, MCFINTC2_SIMR);
7362306a36Sopenharmony_ci	else if (MCFINTC1_SIMR && (irq > 63))
7462306a36Sopenharmony_ci		__raw_writeb(irq - 64, MCFINTC1_SIMR);
7562306a36Sopenharmony_ci	else
7662306a36Sopenharmony_ci		__raw_writeb(irq, MCFINTC0_SIMR);
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic void intc_irq_unmask(struct irq_data *d)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	unsigned int irq = d->irq - MCFINT_VECBASE;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	if (MCFINTC2_CIMR && (irq > 127))
8462306a36Sopenharmony_ci		__raw_writeb(irq - 128, MCFINTC2_CIMR);
8562306a36Sopenharmony_ci	else if (MCFINTC1_CIMR && (irq > 63))
8662306a36Sopenharmony_ci		__raw_writeb(irq - 64, MCFINTC1_CIMR);
8762306a36Sopenharmony_ci	else
8862306a36Sopenharmony_ci		__raw_writeb(irq, MCFINTC0_CIMR);
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic void intc_irq_ack(struct irq_data *d)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	unsigned int ebit = irq2ebit(d->irq);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	__raw_writeb(0x1 << ebit, MCFEPORT_EPFR);
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic unsigned int intc_irq_startup(struct irq_data *d)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	unsigned int irq = d->irq;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	if ((irq >= EINT1) && (irq <= EINT7)) {
10362306a36Sopenharmony_ci		unsigned int ebit = irq2ebit(irq);
10462306a36Sopenharmony_ci		u8 v;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci#if defined(MCFEPORT_EPDDR)
10762306a36Sopenharmony_ci		/* Set EPORT line as input */
10862306a36Sopenharmony_ci		v = __raw_readb(MCFEPORT_EPDDR);
10962306a36Sopenharmony_ci		__raw_writeb(v & ~(0x1 << ebit), MCFEPORT_EPDDR);
11062306a36Sopenharmony_ci#endif
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci		/* Set EPORT line as interrupt source */
11362306a36Sopenharmony_ci		v = __raw_readb(MCFEPORT_EPIER);
11462306a36Sopenharmony_ci		__raw_writeb(v | (0x1 << ebit), MCFEPORT_EPIER);
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	irq -= MCFINT_VECBASE;
11862306a36Sopenharmony_ci	if (MCFINTC2_ICR0 && (irq > 127))
11962306a36Sopenharmony_ci		__raw_writeb(5, MCFINTC2_ICR0 + irq - 128);
12062306a36Sopenharmony_ci	else if (MCFINTC1_ICR0 && (irq > 63))
12162306a36Sopenharmony_ci		__raw_writeb(5, MCFINTC1_ICR0 + irq - 64);
12262306a36Sopenharmony_ci	else
12362306a36Sopenharmony_ci		__raw_writeb(5, MCFINTC0_ICR0 + irq);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	intc_irq_unmask(d);
12662306a36Sopenharmony_ci	return 0;
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic int intc_irq_set_type(struct irq_data *d, unsigned int type)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	unsigned int ebit, irq = d->irq;
13262306a36Sopenharmony_ci	u16 pa, tb;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	switch (type) {
13562306a36Sopenharmony_ci	case IRQ_TYPE_EDGE_RISING:
13662306a36Sopenharmony_ci		tb = 0x1;
13762306a36Sopenharmony_ci		break;
13862306a36Sopenharmony_ci	case IRQ_TYPE_EDGE_FALLING:
13962306a36Sopenharmony_ci		tb = 0x2;
14062306a36Sopenharmony_ci		break;
14162306a36Sopenharmony_ci	case IRQ_TYPE_EDGE_BOTH:
14262306a36Sopenharmony_ci		tb = 0x3;
14362306a36Sopenharmony_ci		break;
14462306a36Sopenharmony_ci	default:
14562306a36Sopenharmony_ci		/* Level triggered */
14662306a36Sopenharmony_ci		tb = 0;
14762306a36Sopenharmony_ci		break;
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	if (tb)
15162306a36Sopenharmony_ci		irq_set_handler(irq, handle_edge_irq);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	ebit = irq2ebit(irq) * 2;
15462306a36Sopenharmony_ci	pa = __raw_readw(MCFEPORT_EPPAR);
15562306a36Sopenharmony_ci	pa = (pa & ~(0x3 << ebit)) | (tb << ebit);
15662306a36Sopenharmony_ci	__raw_writew(pa, MCFEPORT_EPPAR);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	return 0;
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic struct irq_chip intc_irq_chip = {
16262306a36Sopenharmony_ci	.name		= "CF-INTC",
16362306a36Sopenharmony_ci	.irq_startup	= intc_irq_startup,
16462306a36Sopenharmony_ci	.irq_mask	= intc_irq_mask,
16562306a36Sopenharmony_ci	.irq_unmask	= intc_irq_unmask,
16662306a36Sopenharmony_ci};
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic struct irq_chip intc_irq_chip_edge_port = {
16962306a36Sopenharmony_ci	.name		= "CF-INTC-EP",
17062306a36Sopenharmony_ci	.irq_startup	= intc_irq_startup,
17162306a36Sopenharmony_ci	.irq_mask	= intc_irq_mask,
17262306a36Sopenharmony_ci	.irq_unmask	= intc_irq_unmask,
17362306a36Sopenharmony_ci	.irq_ack	= intc_irq_ack,
17462306a36Sopenharmony_ci	.irq_set_type	= intc_irq_set_type,
17562306a36Sopenharmony_ci};
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_civoid __init init_IRQ(void)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	int irq, eirq;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	/* Mask all interrupt sources */
18262306a36Sopenharmony_ci	__raw_writeb(0xff, MCFINTC0_SIMR);
18362306a36Sopenharmony_ci	if (MCFINTC1_SIMR)
18462306a36Sopenharmony_ci		__raw_writeb(0xff, MCFINTC1_SIMR);
18562306a36Sopenharmony_ci	if (MCFINTC2_SIMR)
18662306a36Sopenharmony_ci		__raw_writeb(0xff, MCFINTC2_SIMR);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	eirq = MCFINT_VECBASE + 64 + (MCFINTC1_ICR0 ? 64 : 0) +
18962306a36Sopenharmony_ci						(MCFINTC2_ICR0 ? 64 : 0);
19062306a36Sopenharmony_ci	for (irq = MCFINT_VECBASE; (irq < eirq); irq++) {
19162306a36Sopenharmony_ci		if ((irq >= EINT1) && (irq <= EINT7))
19262306a36Sopenharmony_ci			irq_set_chip(irq, &intc_irq_chip_edge_port);
19362306a36Sopenharmony_ci		else
19462306a36Sopenharmony_ci			irq_set_chip(irq, &intc_irq_chip);
19562306a36Sopenharmony_ci		irq_set_irq_type(irq, IRQ_TYPE_LEVEL_HIGH);
19662306a36Sopenharmony_ci		irq_set_handler(irq, handle_level_irq);
19762306a36Sopenharmony_ci	}
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
200