18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * intc-simr.c
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Interrupt controller code for the ColdFire 5208, 5207 & 532x parts.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * (C) Copyright 2009-2011, Greg Ungerer <gerg@snapgear.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
98c2ecf20Sopenharmony_ci * License.  See the file COPYING in the main directory of this archive
108c2ecf20Sopenharmony_ci * for more details.
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/types.h>
148c2ecf20Sopenharmony_ci#include <linux/init.h>
158c2ecf20Sopenharmony_ci#include <linux/kernel.h>
168c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
178c2ecf20Sopenharmony_ci#include <linux/irq.h>
188c2ecf20Sopenharmony_ci#include <linux/io.h>
198c2ecf20Sopenharmony_ci#include <asm/coldfire.h>
208c2ecf20Sopenharmony_ci#include <asm/mcfsim.h>
218c2ecf20Sopenharmony_ci#include <asm/traps.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/*
248c2ecf20Sopenharmony_ci *	The EDGE Port interrupts are the fixed 7 external interrupts.
258c2ecf20Sopenharmony_ci *	They need some special treatment, for example they need to be acked.
268c2ecf20Sopenharmony_ci */
278c2ecf20Sopenharmony_ci#ifdef CONFIG_M520x
288c2ecf20Sopenharmony_ci/*
298c2ecf20Sopenharmony_ci *	The 520x parts only support a limited range of these external
308c2ecf20Sopenharmony_ci *	interrupts, only 1, 4 and 7 (as interrupts 65, 66 and 67).
318c2ecf20Sopenharmony_ci */
328c2ecf20Sopenharmony_ci#define	EINT0	64	/* Is not actually used, but spot reserved for it */
338c2ecf20Sopenharmony_ci#define	EINT1	65	/* EDGE Port interrupt 1 */
348c2ecf20Sopenharmony_ci#define	EINT4	66	/* EDGE Port interrupt 4 */
358c2ecf20Sopenharmony_ci#define	EINT7	67	/* EDGE Port interrupt 7 */
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic unsigned int irqebitmap[] = { 0, 1, 4, 7 };
388c2ecf20Sopenharmony_cistatic inline unsigned int irq2ebit(unsigned int irq)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	return irqebitmap[irq - EINT0];
418c2ecf20Sopenharmony_ci}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#else
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci/*
468c2ecf20Sopenharmony_ci *	Most of the ColdFire parts with the EDGE Port module just have
478c2ecf20Sopenharmony_ci *	a strait direct mapping of the 7 external interrupts. Although
488c2ecf20Sopenharmony_ci *	there is a bit reserved for 0, it is not used.
498c2ecf20Sopenharmony_ci */
508c2ecf20Sopenharmony_ci#define	EINT0	64	/* Is not actually used, but spot reserved for it */
518c2ecf20Sopenharmony_ci#define	EINT1	65	/* EDGE Port interrupt 1 */
528c2ecf20Sopenharmony_ci#define	EINT7	71	/* EDGE Port interrupt 7 */
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic inline unsigned int irq2ebit(unsigned int irq)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	return irq - EINT0;
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci#endif
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci/*
628c2ecf20Sopenharmony_ci *	There maybe one, two or three interrupt control units, each has 64
638c2ecf20Sopenharmony_ci *	interrupts. If there is no second or third unit then MCFINTC1_* or
648c2ecf20Sopenharmony_ci *	MCFINTC2_* defines will be 0 (and code for them optimized away).
658c2ecf20Sopenharmony_ci */
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic void intc_irq_mask(struct irq_data *d)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	unsigned int irq = d->irq - MCFINT_VECBASE;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	if (MCFINTC2_SIMR && (irq > 128))
728c2ecf20Sopenharmony_ci		__raw_writeb(irq - 128, MCFINTC2_SIMR);
738c2ecf20Sopenharmony_ci	else if (MCFINTC1_SIMR && (irq > 64))
748c2ecf20Sopenharmony_ci		__raw_writeb(irq - 64, MCFINTC1_SIMR);
758c2ecf20Sopenharmony_ci	else
768c2ecf20Sopenharmony_ci		__raw_writeb(irq, MCFINTC0_SIMR);
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic void intc_irq_unmask(struct irq_data *d)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	unsigned int irq = d->irq - MCFINT_VECBASE;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	if (MCFINTC2_CIMR && (irq > 128))
848c2ecf20Sopenharmony_ci		__raw_writeb(irq - 128, MCFINTC2_CIMR);
858c2ecf20Sopenharmony_ci	else if (MCFINTC1_CIMR && (irq > 64))
868c2ecf20Sopenharmony_ci		__raw_writeb(irq - 64, MCFINTC1_CIMR);
878c2ecf20Sopenharmony_ci	else
888c2ecf20Sopenharmony_ci		__raw_writeb(irq, MCFINTC0_CIMR);
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic void intc_irq_ack(struct irq_data *d)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	unsigned int ebit = irq2ebit(d->irq);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	__raw_writeb(0x1 << ebit, MCFEPORT_EPFR);
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic unsigned int intc_irq_startup(struct irq_data *d)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	unsigned int irq = d->irq;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	if ((irq >= EINT1) && (irq <= EINT7)) {
1038c2ecf20Sopenharmony_ci		unsigned int ebit = irq2ebit(irq);
1048c2ecf20Sopenharmony_ci		u8 v;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci#if defined(MCFEPORT_EPDDR)
1078c2ecf20Sopenharmony_ci		/* Set EPORT line as input */
1088c2ecf20Sopenharmony_ci		v = __raw_readb(MCFEPORT_EPDDR);
1098c2ecf20Sopenharmony_ci		__raw_writeb(v & ~(0x1 << ebit), MCFEPORT_EPDDR);
1108c2ecf20Sopenharmony_ci#endif
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci		/* Set EPORT line as interrupt source */
1138c2ecf20Sopenharmony_ci		v = __raw_readb(MCFEPORT_EPIER);
1148c2ecf20Sopenharmony_ci		__raw_writeb(v | (0x1 << ebit), MCFEPORT_EPIER);
1158c2ecf20Sopenharmony_ci	}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	irq -= MCFINT_VECBASE;
1188c2ecf20Sopenharmony_ci	if (MCFINTC2_ICR0 && (irq > 128))
1198c2ecf20Sopenharmony_ci		__raw_writeb(5, MCFINTC2_ICR0 + irq - 128);
1208c2ecf20Sopenharmony_ci	else if (MCFINTC1_ICR0 && (irq > 64))
1218c2ecf20Sopenharmony_ci		__raw_writeb(5, MCFINTC1_ICR0 + irq - 64);
1228c2ecf20Sopenharmony_ci	else
1238c2ecf20Sopenharmony_ci		__raw_writeb(5, MCFINTC0_ICR0 + irq);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	intc_irq_unmask(d);
1268c2ecf20Sopenharmony_ci	return 0;
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic int intc_irq_set_type(struct irq_data *d, unsigned int type)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	unsigned int ebit, irq = d->irq;
1328c2ecf20Sopenharmony_ci	u16 pa, tb;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	switch (type) {
1358c2ecf20Sopenharmony_ci	case IRQ_TYPE_EDGE_RISING:
1368c2ecf20Sopenharmony_ci		tb = 0x1;
1378c2ecf20Sopenharmony_ci		break;
1388c2ecf20Sopenharmony_ci	case IRQ_TYPE_EDGE_FALLING:
1398c2ecf20Sopenharmony_ci		tb = 0x2;
1408c2ecf20Sopenharmony_ci		break;
1418c2ecf20Sopenharmony_ci	case IRQ_TYPE_EDGE_BOTH:
1428c2ecf20Sopenharmony_ci		tb = 0x3;
1438c2ecf20Sopenharmony_ci		break;
1448c2ecf20Sopenharmony_ci	default:
1458c2ecf20Sopenharmony_ci		/* Level triggered */
1468c2ecf20Sopenharmony_ci		tb = 0;
1478c2ecf20Sopenharmony_ci		break;
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	if (tb)
1518c2ecf20Sopenharmony_ci		irq_set_handler(irq, handle_edge_irq);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	ebit = irq2ebit(irq) * 2;
1548c2ecf20Sopenharmony_ci	pa = __raw_readw(MCFEPORT_EPPAR);
1558c2ecf20Sopenharmony_ci	pa = (pa & ~(0x3 << ebit)) | (tb << ebit);
1568c2ecf20Sopenharmony_ci	__raw_writew(pa, MCFEPORT_EPPAR);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	return 0;
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistatic struct irq_chip intc_irq_chip = {
1628c2ecf20Sopenharmony_ci	.name		= "CF-INTC",
1638c2ecf20Sopenharmony_ci	.irq_startup	= intc_irq_startup,
1648c2ecf20Sopenharmony_ci	.irq_mask	= intc_irq_mask,
1658c2ecf20Sopenharmony_ci	.irq_unmask	= intc_irq_unmask,
1668c2ecf20Sopenharmony_ci};
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_cistatic struct irq_chip intc_irq_chip_edge_port = {
1698c2ecf20Sopenharmony_ci	.name		= "CF-INTC-EP",
1708c2ecf20Sopenharmony_ci	.irq_startup	= intc_irq_startup,
1718c2ecf20Sopenharmony_ci	.irq_mask	= intc_irq_mask,
1728c2ecf20Sopenharmony_ci	.irq_unmask	= intc_irq_unmask,
1738c2ecf20Sopenharmony_ci	.irq_ack	= intc_irq_ack,
1748c2ecf20Sopenharmony_ci	.irq_set_type	= intc_irq_set_type,
1758c2ecf20Sopenharmony_ci};
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_civoid __init init_IRQ(void)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	int irq, eirq;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	/* Mask all interrupt sources */
1828c2ecf20Sopenharmony_ci	__raw_writeb(0xff, MCFINTC0_SIMR);
1838c2ecf20Sopenharmony_ci	if (MCFINTC1_SIMR)
1848c2ecf20Sopenharmony_ci		__raw_writeb(0xff, MCFINTC1_SIMR);
1858c2ecf20Sopenharmony_ci	if (MCFINTC2_SIMR)
1868c2ecf20Sopenharmony_ci		__raw_writeb(0xff, MCFINTC2_SIMR);
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	eirq = MCFINT_VECBASE + 64 + (MCFINTC1_ICR0 ? 64 : 0) +
1898c2ecf20Sopenharmony_ci						(MCFINTC2_ICR0 ? 64 : 0);
1908c2ecf20Sopenharmony_ci	for (irq = MCFINT_VECBASE; (irq < eirq); irq++) {
1918c2ecf20Sopenharmony_ci		if ((irq >= EINT1) && (irq <= EINT7))
1928c2ecf20Sopenharmony_ci			irq_set_chip(irq, &intc_irq_chip_edge_port);
1938c2ecf20Sopenharmony_ci		else
1948c2ecf20Sopenharmony_ci			irq_set_chip(irq, &intc_irq_chip);
1958c2ecf20Sopenharmony_ci		irq_set_irq_type(irq, IRQ_TYPE_LEVEL_HIGH);
1968c2ecf20Sopenharmony_ci		irq_set_handler(irq, handle_level_irq);
1978c2ecf20Sopenharmony_ci	}
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
200