18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * intc-2.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * General interrupt controller code for the many ColdFire cores that use 58c2ecf20Sopenharmony_ci * interrupt controllers with 63 interrupt sources, organized as 56 fully- 68c2ecf20Sopenharmony_ci * programmable + 7 fixed-level interrupt sources. This includes the 523x 78c2ecf20Sopenharmony_ci * family, the 5270, 5271, 5274, 5275, and the 528x family which have two such 88c2ecf20Sopenharmony_ci * controllers, and the 547x and 548x families which have only one of them. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * The external 7 fixed interrupts are part the the Edge Port unit of these 118c2ecf20Sopenharmony_ci * ColdFire parts. They can be configured as level or edge triggered. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * (C) Copyright 2009-2011, Greg Ungerer <gerg@snapgear.com> 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 168c2ecf20Sopenharmony_ci * License. See the file COPYING in the main directory of this archive 178c2ecf20Sopenharmony_ci * for more details. 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/types.h> 218c2ecf20Sopenharmony_ci#include <linux/init.h> 228c2ecf20Sopenharmony_ci#include <linux/kernel.h> 238c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 248c2ecf20Sopenharmony_ci#include <linux/irq.h> 258c2ecf20Sopenharmony_ci#include <linux/io.h> 268c2ecf20Sopenharmony_ci#include <asm/coldfire.h> 278c2ecf20Sopenharmony_ci#include <asm/mcfsim.h> 288c2ecf20Sopenharmony_ci#include <asm/traps.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* 318c2ecf20Sopenharmony_ci * Bit definitions for the ICR family of registers. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci#define MCFSIM_ICR_LEVEL(l) ((l)<<3) /* Level l intr */ 348c2ecf20Sopenharmony_ci#define MCFSIM_ICR_PRI(p) (p) /* Priority p intr */ 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* 378c2ecf20Sopenharmony_ci * The EDGE Port interrupts are the fixed 7 external interrupts. 388c2ecf20Sopenharmony_ci * They need some special treatment, for example they need to be acked. 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_ci#define EINT0 64 /* Is not actually used, but spot reserved for it */ 418c2ecf20Sopenharmony_ci#define EINT1 65 /* EDGE Port interrupt 1 */ 428c2ecf20Sopenharmony_ci#define EINT7 71 /* EDGE Port interrupt 7 */ 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#ifdef MCFICM_INTC1 458c2ecf20Sopenharmony_ci#define NR_VECS 128 468c2ecf20Sopenharmony_ci#else 478c2ecf20Sopenharmony_ci#define NR_VECS 64 488c2ecf20Sopenharmony_ci#endif 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic void intc_irq_mask(struct irq_data *d) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci unsigned int irq = d->irq - MCFINT_VECBASE; 538c2ecf20Sopenharmony_ci unsigned long imraddr; 548c2ecf20Sopenharmony_ci u32 val, imrbit; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#ifdef MCFICM_INTC1 578c2ecf20Sopenharmony_ci imraddr = (irq & 0x40) ? MCFICM_INTC1 : MCFICM_INTC0; 588c2ecf20Sopenharmony_ci#else 598c2ecf20Sopenharmony_ci imraddr = MCFICM_INTC0; 608c2ecf20Sopenharmony_ci#endif 618c2ecf20Sopenharmony_ci imraddr += (irq & 0x20) ? MCFINTC_IMRH : MCFINTC_IMRL; 628c2ecf20Sopenharmony_ci imrbit = 0x1 << (irq & 0x1f); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci val = __raw_readl(imraddr); 658c2ecf20Sopenharmony_ci __raw_writel(val | imrbit, imraddr); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic void intc_irq_unmask(struct irq_data *d) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci unsigned int irq = d->irq - MCFINT_VECBASE; 718c2ecf20Sopenharmony_ci unsigned long imraddr; 728c2ecf20Sopenharmony_ci u32 val, imrbit; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci#ifdef MCFICM_INTC1 758c2ecf20Sopenharmony_ci imraddr = (irq & 0x40) ? MCFICM_INTC1 : MCFICM_INTC0; 768c2ecf20Sopenharmony_ci#else 778c2ecf20Sopenharmony_ci imraddr = MCFICM_INTC0; 788c2ecf20Sopenharmony_ci#endif 798c2ecf20Sopenharmony_ci imraddr += ((irq & 0x20) ? MCFINTC_IMRH : MCFINTC_IMRL); 808c2ecf20Sopenharmony_ci imrbit = 0x1 << (irq & 0x1f); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci /* Don't set the "maskall" bit! */ 838c2ecf20Sopenharmony_ci if ((irq & 0x20) == 0) 848c2ecf20Sopenharmony_ci imrbit |= 0x1; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci val = __raw_readl(imraddr); 878c2ecf20Sopenharmony_ci __raw_writel(val & ~imrbit, imraddr); 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/* 918c2ecf20Sopenharmony_ci * Only the external (or EDGE Port) interrupts need to be acknowledged 928c2ecf20Sopenharmony_ci * here, as part of the IRQ handler. They only really need to be ack'ed 938c2ecf20Sopenharmony_ci * if they are in edge triggered mode, but there is no harm in doing it 948c2ecf20Sopenharmony_ci * for all types. 958c2ecf20Sopenharmony_ci */ 968c2ecf20Sopenharmony_cistatic void intc_irq_ack(struct irq_data *d) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci unsigned int irq = d->irq; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci __raw_writeb(0x1 << (irq - EINT0), MCFEPORT_EPFR); 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/* 1048c2ecf20Sopenharmony_ci * Each vector needs a unique priority and level associated with it. 1058c2ecf20Sopenharmony_ci * We don't really care so much what they are, we don't rely on the 1068c2ecf20Sopenharmony_ci * traditional priority interrupt scheme of the m68k/ColdFire. This 1078c2ecf20Sopenharmony_ci * only needs to be set once for an interrupt, and we will never change 1088c2ecf20Sopenharmony_ci * these values once we have set them. 1098c2ecf20Sopenharmony_ci */ 1108c2ecf20Sopenharmony_cistatic u8 intc_intpri = MCFSIM_ICR_LEVEL(6) | MCFSIM_ICR_PRI(6); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic unsigned int intc_irq_startup(struct irq_data *d) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci unsigned int irq = d->irq - MCFINT_VECBASE; 1158c2ecf20Sopenharmony_ci unsigned long icraddr; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci#ifdef MCFICM_INTC1 1188c2ecf20Sopenharmony_ci icraddr = (irq & 0x40) ? MCFICM_INTC1 : MCFICM_INTC0; 1198c2ecf20Sopenharmony_ci#else 1208c2ecf20Sopenharmony_ci icraddr = MCFICM_INTC0; 1218c2ecf20Sopenharmony_ci#endif 1228c2ecf20Sopenharmony_ci icraddr += MCFINTC_ICR0 + (irq & 0x3f); 1238c2ecf20Sopenharmony_ci if (__raw_readb(icraddr) == 0) 1248c2ecf20Sopenharmony_ci __raw_writeb(intc_intpri--, icraddr); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci irq = d->irq; 1278c2ecf20Sopenharmony_ci if ((irq >= EINT1) && (irq <= EINT7)) { 1288c2ecf20Sopenharmony_ci u8 v; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci irq -= EINT0; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* Set EPORT line as input */ 1338c2ecf20Sopenharmony_ci v = __raw_readb(MCFEPORT_EPDDR); 1348c2ecf20Sopenharmony_ci __raw_writeb(v & ~(0x1 << irq), MCFEPORT_EPDDR); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci /* Set EPORT line as interrupt source */ 1378c2ecf20Sopenharmony_ci v = __raw_readb(MCFEPORT_EPIER); 1388c2ecf20Sopenharmony_ci __raw_writeb(v | (0x1 << irq), MCFEPORT_EPIER); 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci intc_irq_unmask(d); 1428c2ecf20Sopenharmony_ci return 0; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic int intc_irq_set_type(struct irq_data *d, unsigned int type) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci unsigned int irq = d->irq; 1488c2ecf20Sopenharmony_ci u16 pa, tb; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci switch (type) { 1518c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_RISING: 1528c2ecf20Sopenharmony_ci tb = 0x1; 1538c2ecf20Sopenharmony_ci break; 1548c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 1558c2ecf20Sopenharmony_ci tb = 0x2; 1568c2ecf20Sopenharmony_ci break; 1578c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_BOTH: 1588c2ecf20Sopenharmony_ci tb = 0x3; 1598c2ecf20Sopenharmony_ci break; 1608c2ecf20Sopenharmony_ci default: 1618c2ecf20Sopenharmony_ci /* Level triggered */ 1628c2ecf20Sopenharmony_ci tb = 0; 1638c2ecf20Sopenharmony_ci break; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (tb) 1678c2ecf20Sopenharmony_ci irq_set_handler(irq, handle_edge_irq); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci irq -= EINT0; 1708c2ecf20Sopenharmony_ci pa = __raw_readw(MCFEPORT_EPPAR); 1718c2ecf20Sopenharmony_ci pa = (pa & ~(0x3 << (irq * 2))) | (tb << (irq * 2)); 1728c2ecf20Sopenharmony_ci __raw_writew(pa, MCFEPORT_EPPAR); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci return 0; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic struct irq_chip intc_irq_chip = { 1788c2ecf20Sopenharmony_ci .name = "CF-INTC", 1798c2ecf20Sopenharmony_ci .irq_startup = intc_irq_startup, 1808c2ecf20Sopenharmony_ci .irq_mask = intc_irq_mask, 1818c2ecf20Sopenharmony_ci .irq_unmask = intc_irq_unmask, 1828c2ecf20Sopenharmony_ci}; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic struct irq_chip intc_irq_chip_edge_port = { 1858c2ecf20Sopenharmony_ci .name = "CF-INTC-EP", 1868c2ecf20Sopenharmony_ci .irq_startup = intc_irq_startup, 1878c2ecf20Sopenharmony_ci .irq_mask = intc_irq_mask, 1888c2ecf20Sopenharmony_ci .irq_unmask = intc_irq_unmask, 1898c2ecf20Sopenharmony_ci .irq_ack = intc_irq_ack, 1908c2ecf20Sopenharmony_ci .irq_set_type = intc_irq_set_type, 1918c2ecf20Sopenharmony_ci}; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_civoid __init init_IRQ(void) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci int irq; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci /* Mask all interrupt sources */ 1988c2ecf20Sopenharmony_ci __raw_writel(0x1, MCFICM_INTC0 + MCFINTC_IMRL); 1998c2ecf20Sopenharmony_ci#ifdef MCFICM_INTC1 2008c2ecf20Sopenharmony_ci __raw_writel(0x1, MCFICM_INTC1 + MCFINTC_IMRL); 2018c2ecf20Sopenharmony_ci#endif 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci for (irq = MCFINT_VECBASE; (irq < MCFINT_VECBASE + NR_VECS); irq++) { 2048c2ecf20Sopenharmony_ci if ((irq >= EINT1) && (irq <=EINT7)) 2058c2ecf20Sopenharmony_ci irq_set_chip(irq, &intc_irq_chip_edge_port); 2068c2ecf20Sopenharmony_ci else 2078c2ecf20Sopenharmony_ci irq_set_chip(irq, &intc_irq_chip); 2088c2ecf20Sopenharmony_ci irq_set_irq_type(irq, IRQ_TYPE_LEVEL_HIGH); 2098c2ecf20Sopenharmony_ci irq_set_handler(irq, handle_level_irq); 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 213