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