18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * intc.c -- interrupt controller or ColdFire 5272 SoC 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * (C) Copyright 2009, Greg Ungerer <gerg@snapgear.com> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 78c2ecf20Sopenharmony_ci * License. See the file COPYING in the main directory of this archive 88c2ecf20Sopenharmony_ci * for more details. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/types.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/kernel_stat.h> 168c2ecf20Sopenharmony_ci#include <linux/irq.h> 178c2ecf20Sopenharmony_ci#include <linux/io.h> 188c2ecf20Sopenharmony_ci#include <asm/coldfire.h> 198c2ecf20Sopenharmony_ci#include <asm/mcfsim.h> 208c2ecf20Sopenharmony_ci#include <asm/traps.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* 238c2ecf20Sopenharmony_ci * The 5272 ColdFire interrupt controller is nothing like any other 248c2ecf20Sopenharmony_ci * ColdFire interrupt controller - it truly is completely different. 258c2ecf20Sopenharmony_ci * Given its age it is unlikely to be used on any other ColdFire CPU. 268c2ecf20Sopenharmony_ci */ 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* 298c2ecf20Sopenharmony_ci * The masking and priproty setting of interrupts on the 5272 is done 308c2ecf20Sopenharmony_ci * via a set of 4 "Interrupt Controller Registers" (ICR). There is a 318c2ecf20Sopenharmony_ci * loose mapping of vector number to register and internal bits, but 328c2ecf20Sopenharmony_ci * a table is the easiest and quickest way to map them. 338c2ecf20Sopenharmony_ci * 348c2ecf20Sopenharmony_ci * Note that the external interrupts are edge triggered (unlike the 358c2ecf20Sopenharmony_ci * internal interrupt sources which are level triggered). Which means 368c2ecf20Sopenharmony_ci * they also need acknowledging via acknowledge bits. 378c2ecf20Sopenharmony_ci */ 388c2ecf20Sopenharmony_cistruct irqmap { 398c2ecf20Sopenharmony_ci unsigned int icr; 408c2ecf20Sopenharmony_ci unsigned char index; 418c2ecf20Sopenharmony_ci unsigned char ack; 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic struct irqmap intc_irqmap[MCFINT_VECMAX - MCFINT_VECBASE] = { 458c2ecf20Sopenharmony_ci /*MCF_IRQ_SPURIOUS*/ { .icr = 0, .index = 0, .ack = 0, }, 468c2ecf20Sopenharmony_ci /*MCF_IRQ_EINT1*/ { .icr = MCFSIM_ICR1, .index = 28, .ack = 1, }, 478c2ecf20Sopenharmony_ci /*MCF_IRQ_EINT2*/ { .icr = MCFSIM_ICR1, .index = 24, .ack = 1, }, 488c2ecf20Sopenharmony_ci /*MCF_IRQ_EINT3*/ { .icr = MCFSIM_ICR1, .index = 20, .ack = 1, }, 498c2ecf20Sopenharmony_ci /*MCF_IRQ_EINT4*/ { .icr = MCFSIM_ICR1, .index = 16, .ack = 1, }, 508c2ecf20Sopenharmony_ci /*MCF_IRQ_TIMER1*/ { .icr = MCFSIM_ICR1, .index = 12, .ack = 0, }, 518c2ecf20Sopenharmony_ci /*MCF_IRQ_TIMER2*/ { .icr = MCFSIM_ICR1, .index = 8, .ack = 0, }, 528c2ecf20Sopenharmony_ci /*MCF_IRQ_TIMER3*/ { .icr = MCFSIM_ICR1, .index = 4, .ack = 0, }, 538c2ecf20Sopenharmony_ci /*MCF_IRQ_TIMER4*/ { .icr = MCFSIM_ICR1, .index = 0, .ack = 0, }, 548c2ecf20Sopenharmony_ci /*MCF_IRQ_UART1*/ { .icr = MCFSIM_ICR2, .index = 28, .ack = 0, }, 558c2ecf20Sopenharmony_ci /*MCF_IRQ_UART2*/ { .icr = MCFSIM_ICR2, .index = 24, .ack = 0, }, 568c2ecf20Sopenharmony_ci /*MCF_IRQ_PLIP*/ { .icr = MCFSIM_ICR2, .index = 20, .ack = 0, }, 578c2ecf20Sopenharmony_ci /*MCF_IRQ_PLIA*/ { .icr = MCFSIM_ICR2, .index = 16, .ack = 0, }, 588c2ecf20Sopenharmony_ci /*MCF_IRQ_USB0*/ { .icr = MCFSIM_ICR2, .index = 12, .ack = 0, }, 598c2ecf20Sopenharmony_ci /*MCF_IRQ_USB1*/ { .icr = MCFSIM_ICR2, .index = 8, .ack = 0, }, 608c2ecf20Sopenharmony_ci /*MCF_IRQ_USB2*/ { .icr = MCFSIM_ICR2, .index = 4, .ack = 0, }, 618c2ecf20Sopenharmony_ci /*MCF_IRQ_USB3*/ { .icr = MCFSIM_ICR2, .index = 0, .ack = 0, }, 628c2ecf20Sopenharmony_ci /*MCF_IRQ_USB4*/ { .icr = MCFSIM_ICR3, .index = 28, .ack = 0, }, 638c2ecf20Sopenharmony_ci /*MCF_IRQ_USB5*/ { .icr = MCFSIM_ICR3, .index = 24, .ack = 0, }, 648c2ecf20Sopenharmony_ci /*MCF_IRQ_USB6*/ { .icr = MCFSIM_ICR3, .index = 20, .ack = 0, }, 658c2ecf20Sopenharmony_ci /*MCF_IRQ_USB7*/ { .icr = MCFSIM_ICR3, .index = 16, .ack = 0, }, 668c2ecf20Sopenharmony_ci /*MCF_IRQ_DMA*/ { .icr = MCFSIM_ICR3, .index = 12, .ack = 0, }, 678c2ecf20Sopenharmony_ci /*MCF_IRQ_ERX*/ { .icr = MCFSIM_ICR3, .index = 8, .ack = 0, }, 688c2ecf20Sopenharmony_ci /*MCF_IRQ_ETX*/ { .icr = MCFSIM_ICR3, .index = 4, .ack = 0, }, 698c2ecf20Sopenharmony_ci /*MCF_IRQ_ENTC*/ { .icr = MCFSIM_ICR3, .index = 0, .ack = 0, }, 708c2ecf20Sopenharmony_ci /*MCF_IRQ_QSPI*/ { .icr = MCFSIM_ICR4, .index = 28, .ack = 0, }, 718c2ecf20Sopenharmony_ci /*MCF_IRQ_EINT5*/ { .icr = MCFSIM_ICR4, .index = 24, .ack = 1, }, 728c2ecf20Sopenharmony_ci /*MCF_IRQ_EINT6*/ { .icr = MCFSIM_ICR4, .index = 20, .ack = 1, }, 738c2ecf20Sopenharmony_ci /*MCF_IRQ_SWTO*/ { .icr = MCFSIM_ICR4, .index = 16, .ack = 0, }, 748c2ecf20Sopenharmony_ci}; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* 778c2ecf20Sopenharmony_ci * The act of masking the interrupt also has a side effect of 'ack'ing 788c2ecf20Sopenharmony_ci * an interrupt on this irq (for the external irqs). So this mask function 798c2ecf20Sopenharmony_ci * is also an ack_mask function. 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_cistatic void intc_irq_mask(struct irq_data *d) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci unsigned int irq = d->irq; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX)) { 868c2ecf20Sopenharmony_ci u32 v; 878c2ecf20Sopenharmony_ci irq -= MCFINT_VECBASE; 888c2ecf20Sopenharmony_ci v = 0x8 << intc_irqmap[irq].index; 898c2ecf20Sopenharmony_ci writel(v, intc_irqmap[irq].icr); 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic void intc_irq_unmask(struct irq_data *d) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci unsigned int irq = d->irq; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX)) { 988c2ecf20Sopenharmony_ci u32 v; 998c2ecf20Sopenharmony_ci irq -= MCFINT_VECBASE; 1008c2ecf20Sopenharmony_ci v = 0xd << intc_irqmap[irq].index; 1018c2ecf20Sopenharmony_ci writel(v, intc_irqmap[irq].icr); 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic void intc_irq_ack(struct irq_data *d) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci unsigned int irq = d->irq; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* Only external interrupts are acked */ 1108c2ecf20Sopenharmony_ci if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX)) { 1118c2ecf20Sopenharmony_ci irq -= MCFINT_VECBASE; 1128c2ecf20Sopenharmony_ci if (intc_irqmap[irq].ack) { 1138c2ecf20Sopenharmony_ci u32 v; 1148c2ecf20Sopenharmony_ci v = readl(intc_irqmap[irq].icr); 1158c2ecf20Sopenharmony_ci v &= (0x7 << intc_irqmap[irq].index); 1168c2ecf20Sopenharmony_ci v |= (0x8 << intc_irqmap[irq].index); 1178c2ecf20Sopenharmony_ci writel(v, intc_irqmap[irq].icr); 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic int intc_irq_set_type(struct irq_data *d, unsigned int type) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci unsigned int irq = d->irq; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX)) { 1278c2ecf20Sopenharmony_ci irq -= MCFINT_VECBASE; 1288c2ecf20Sopenharmony_ci if (intc_irqmap[irq].ack) { 1298c2ecf20Sopenharmony_ci u32 v; 1308c2ecf20Sopenharmony_ci v = readl(MCFSIM_PITR); 1318c2ecf20Sopenharmony_ci if (type == IRQ_TYPE_EDGE_FALLING) 1328c2ecf20Sopenharmony_ci v &= ~(0x1 << (32 - irq)); 1338c2ecf20Sopenharmony_ci else 1348c2ecf20Sopenharmony_ci v |= (0x1 << (32 - irq)); 1358c2ecf20Sopenharmony_ci writel(v, MCFSIM_PITR); 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci return 0; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci/* 1428c2ecf20Sopenharmony_ci * Simple flow handler to deal with the external edge triggered interrupts. 1438c2ecf20Sopenharmony_ci * We need to be careful with the masking/acking due to the side effects 1448c2ecf20Sopenharmony_ci * of masking an interrupt. 1458c2ecf20Sopenharmony_ci */ 1468c2ecf20Sopenharmony_cistatic void intc_external_irq(struct irq_desc *desc) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci irq_desc_get_chip(desc)->irq_ack(&desc->irq_data); 1498c2ecf20Sopenharmony_ci handle_simple_irq(desc); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic struct irq_chip intc_irq_chip = { 1538c2ecf20Sopenharmony_ci .name = "CF-INTC", 1548c2ecf20Sopenharmony_ci .irq_mask = intc_irq_mask, 1558c2ecf20Sopenharmony_ci .irq_unmask = intc_irq_unmask, 1568c2ecf20Sopenharmony_ci .irq_mask_ack = intc_irq_mask, 1578c2ecf20Sopenharmony_ci .irq_ack = intc_irq_ack, 1588c2ecf20Sopenharmony_ci .irq_set_type = intc_irq_set_type, 1598c2ecf20Sopenharmony_ci}; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_civoid __init init_IRQ(void) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci int irq, edge; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci /* Mask all interrupt sources */ 1668c2ecf20Sopenharmony_ci writel(0x88888888, MCFSIM_ICR1); 1678c2ecf20Sopenharmony_ci writel(0x88888888, MCFSIM_ICR2); 1688c2ecf20Sopenharmony_ci writel(0x88888888, MCFSIM_ICR3); 1698c2ecf20Sopenharmony_ci writel(0x88888888, MCFSIM_ICR4); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci for (irq = 0; (irq < NR_IRQS); irq++) { 1728c2ecf20Sopenharmony_ci irq_set_chip(irq, &intc_irq_chip); 1738c2ecf20Sopenharmony_ci edge = 0; 1748c2ecf20Sopenharmony_ci if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX)) 1758c2ecf20Sopenharmony_ci edge = intc_irqmap[irq - MCFINT_VECBASE].ack; 1768c2ecf20Sopenharmony_ci if (edge) { 1778c2ecf20Sopenharmony_ci irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING); 1788c2ecf20Sopenharmony_ci irq_set_handler(irq, intc_external_irq); 1798c2ecf20Sopenharmony_ci } else { 1808c2ecf20Sopenharmony_ci irq_set_irq_type(irq, IRQ_TYPE_LEVEL_HIGH); 1818c2ecf20Sopenharmony_ci irq_set_handler(irq, handle_level_irq); 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 186