162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * intc.c -- interrupt controller or ColdFire 5272 SoC 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * (C) Copyright 2009, Greg Ungerer <gerg@snapgear.com> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 762306a36Sopenharmony_ci * License. See the file COPYING in the main directory of this archive 862306a36Sopenharmony_ci * for more details. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/types.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/kernel_stat.h> 1662306a36Sopenharmony_ci#include <linux/irq.h> 1762306a36Sopenharmony_ci#include <linux/io.h> 1862306a36Sopenharmony_ci#include <asm/coldfire.h> 1962306a36Sopenharmony_ci#include <asm/mcfsim.h> 2062306a36Sopenharmony_ci#include <asm/traps.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* 2362306a36Sopenharmony_ci * The 5272 ColdFire interrupt controller is nothing like any other 2462306a36Sopenharmony_ci * ColdFire interrupt controller - it truly is completely different. 2562306a36Sopenharmony_ci * Given its age it is unlikely to be used on any other ColdFire CPU. 2662306a36Sopenharmony_ci */ 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* 2962306a36Sopenharmony_ci * The masking and priproty setting of interrupts on the 5272 is done 3062306a36Sopenharmony_ci * via a set of 4 "Interrupt Controller Registers" (ICR). There is a 3162306a36Sopenharmony_ci * loose mapping of vector number to register and internal bits, but 3262306a36Sopenharmony_ci * a table is the easiest and quickest way to map them. 3362306a36Sopenharmony_ci * 3462306a36Sopenharmony_ci * Note that the external interrupts are edge triggered (unlike the 3562306a36Sopenharmony_ci * internal interrupt sources which are level triggered). Which means 3662306a36Sopenharmony_ci * they also need acknowledging via acknowledge bits. 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_cistruct irqmap { 3962306a36Sopenharmony_ci unsigned int icr; 4062306a36Sopenharmony_ci unsigned char index; 4162306a36Sopenharmony_ci unsigned char ack; 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic struct irqmap intc_irqmap[MCFINT_VECMAX - MCFINT_VECBASE] = { 4562306a36Sopenharmony_ci /*MCF_IRQ_SPURIOUS*/ { .icr = 0, .index = 0, .ack = 0, }, 4662306a36Sopenharmony_ci /*MCF_IRQ_EINT1*/ { .icr = MCFSIM_ICR1, .index = 28, .ack = 1, }, 4762306a36Sopenharmony_ci /*MCF_IRQ_EINT2*/ { .icr = MCFSIM_ICR1, .index = 24, .ack = 1, }, 4862306a36Sopenharmony_ci /*MCF_IRQ_EINT3*/ { .icr = MCFSIM_ICR1, .index = 20, .ack = 1, }, 4962306a36Sopenharmony_ci /*MCF_IRQ_EINT4*/ { .icr = MCFSIM_ICR1, .index = 16, .ack = 1, }, 5062306a36Sopenharmony_ci /*MCF_IRQ_TIMER1*/ { .icr = MCFSIM_ICR1, .index = 12, .ack = 0, }, 5162306a36Sopenharmony_ci /*MCF_IRQ_TIMER2*/ { .icr = MCFSIM_ICR1, .index = 8, .ack = 0, }, 5262306a36Sopenharmony_ci /*MCF_IRQ_TIMER3*/ { .icr = MCFSIM_ICR1, .index = 4, .ack = 0, }, 5362306a36Sopenharmony_ci /*MCF_IRQ_TIMER4*/ { .icr = MCFSIM_ICR1, .index = 0, .ack = 0, }, 5462306a36Sopenharmony_ci /*MCF_IRQ_UART1*/ { .icr = MCFSIM_ICR2, .index = 28, .ack = 0, }, 5562306a36Sopenharmony_ci /*MCF_IRQ_UART2*/ { .icr = MCFSIM_ICR2, .index = 24, .ack = 0, }, 5662306a36Sopenharmony_ci /*MCF_IRQ_PLIP*/ { .icr = MCFSIM_ICR2, .index = 20, .ack = 0, }, 5762306a36Sopenharmony_ci /*MCF_IRQ_PLIA*/ { .icr = MCFSIM_ICR2, .index = 16, .ack = 0, }, 5862306a36Sopenharmony_ci /*MCF_IRQ_USB0*/ { .icr = MCFSIM_ICR2, .index = 12, .ack = 0, }, 5962306a36Sopenharmony_ci /*MCF_IRQ_USB1*/ { .icr = MCFSIM_ICR2, .index = 8, .ack = 0, }, 6062306a36Sopenharmony_ci /*MCF_IRQ_USB2*/ { .icr = MCFSIM_ICR2, .index = 4, .ack = 0, }, 6162306a36Sopenharmony_ci /*MCF_IRQ_USB3*/ { .icr = MCFSIM_ICR2, .index = 0, .ack = 0, }, 6262306a36Sopenharmony_ci /*MCF_IRQ_USB4*/ { .icr = MCFSIM_ICR3, .index = 28, .ack = 0, }, 6362306a36Sopenharmony_ci /*MCF_IRQ_USB5*/ { .icr = MCFSIM_ICR3, .index = 24, .ack = 0, }, 6462306a36Sopenharmony_ci /*MCF_IRQ_USB6*/ { .icr = MCFSIM_ICR3, .index = 20, .ack = 0, }, 6562306a36Sopenharmony_ci /*MCF_IRQ_USB7*/ { .icr = MCFSIM_ICR3, .index = 16, .ack = 0, }, 6662306a36Sopenharmony_ci /*MCF_IRQ_DMA*/ { .icr = MCFSIM_ICR3, .index = 12, .ack = 0, }, 6762306a36Sopenharmony_ci /*MCF_IRQ_ERX*/ { .icr = MCFSIM_ICR3, .index = 8, .ack = 0, }, 6862306a36Sopenharmony_ci /*MCF_IRQ_ETX*/ { .icr = MCFSIM_ICR3, .index = 4, .ack = 0, }, 6962306a36Sopenharmony_ci /*MCF_IRQ_ENTC*/ { .icr = MCFSIM_ICR3, .index = 0, .ack = 0, }, 7062306a36Sopenharmony_ci /*MCF_IRQ_QSPI*/ { .icr = MCFSIM_ICR4, .index = 28, .ack = 0, }, 7162306a36Sopenharmony_ci /*MCF_IRQ_EINT5*/ { .icr = MCFSIM_ICR4, .index = 24, .ack = 1, }, 7262306a36Sopenharmony_ci /*MCF_IRQ_EINT6*/ { .icr = MCFSIM_ICR4, .index = 20, .ack = 1, }, 7362306a36Sopenharmony_ci /*MCF_IRQ_SWTO*/ { .icr = MCFSIM_ICR4, .index = 16, .ack = 0, }, 7462306a36Sopenharmony_ci}; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* 7762306a36Sopenharmony_ci * The act of masking the interrupt also has a side effect of 'ack'ing 7862306a36Sopenharmony_ci * an interrupt on this irq (for the external irqs). So this mask function 7962306a36Sopenharmony_ci * is also an ack_mask function. 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_cistatic void intc_irq_mask(struct irq_data *d) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci unsigned int irq = d->irq; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX)) { 8662306a36Sopenharmony_ci u32 v; 8762306a36Sopenharmony_ci irq -= MCFINT_VECBASE; 8862306a36Sopenharmony_ci v = 0x8 << intc_irqmap[irq].index; 8962306a36Sopenharmony_ci writel(v, intc_irqmap[irq].icr); 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic void intc_irq_unmask(struct irq_data *d) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci unsigned int irq = d->irq; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX)) { 9862306a36Sopenharmony_ci u32 v; 9962306a36Sopenharmony_ci irq -= MCFINT_VECBASE; 10062306a36Sopenharmony_ci v = 0xd << intc_irqmap[irq].index; 10162306a36Sopenharmony_ci writel(v, intc_irqmap[irq].icr); 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic void intc_irq_ack(struct irq_data *d) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci unsigned int irq = d->irq; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* Only external interrupts are acked */ 11062306a36Sopenharmony_ci if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX)) { 11162306a36Sopenharmony_ci irq -= MCFINT_VECBASE; 11262306a36Sopenharmony_ci if (intc_irqmap[irq].ack) { 11362306a36Sopenharmony_ci u32 v; 11462306a36Sopenharmony_ci v = readl(intc_irqmap[irq].icr); 11562306a36Sopenharmony_ci v &= (0x7 << intc_irqmap[irq].index); 11662306a36Sopenharmony_ci v |= (0x8 << intc_irqmap[irq].index); 11762306a36Sopenharmony_ci writel(v, intc_irqmap[irq].icr); 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic int intc_irq_set_type(struct irq_data *d, unsigned int type) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci unsigned int irq = d->irq; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX)) { 12762306a36Sopenharmony_ci irq -= MCFINT_VECBASE; 12862306a36Sopenharmony_ci if (intc_irqmap[irq].ack) { 12962306a36Sopenharmony_ci u32 v; 13062306a36Sopenharmony_ci v = readl(MCFSIM_PITR); 13162306a36Sopenharmony_ci if (type == IRQ_TYPE_EDGE_FALLING) 13262306a36Sopenharmony_ci v &= ~(0x1 << (32 - irq)); 13362306a36Sopenharmony_ci else 13462306a36Sopenharmony_ci v |= (0x1 << (32 - irq)); 13562306a36Sopenharmony_ci writel(v, MCFSIM_PITR); 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci return 0; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci/* 14262306a36Sopenharmony_ci * Simple flow handler to deal with the external edge triggered interrupts. 14362306a36Sopenharmony_ci * We need to be careful with the masking/acking due to the side effects 14462306a36Sopenharmony_ci * of masking an interrupt. 14562306a36Sopenharmony_ci */ 14662306a36Sopenharmony_cistatic void intc_external_irq(struct irq_desc *desc) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci irq_desc_get_chip(desc)->irq_ack(&desc->irq_data); 14962306a36Sopenharmony_ci handle_simple_irq(desc); 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic struct irq_chip intc_irq_chip = { 15362306a36Sopenharmony_ci .name = "CF-INTC", 15462306a36Sopenharmony_ci .irq_mask = intc_irq_mask, 15562306a36Sopenharmony_ci .irq_unmask = intc_irq_unmask, 15662306a36Sopenharmony_ci .irq_mask_ack = intc_irq_mask, 15762306a36Sopenharmony_ci .irq_ack = intc_irq_ack, 15862306a36Sopenharmony_ci .irq_set_type = intc_irq_set_type, 15962306a36Sopenharmony_ci}; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_civoid __init init_IRQ(void) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci int irq, edge; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* Mask all interrupt sources */ 16662306a36Sopenharmony_ci writel(0x88888888, MCFSIM_ICR1); 16762306a36Sopenharmony_ci writel(0x88888888, MCFSIM_ICR2); 16862306a36Sopenharmony_ci writel(0x88888888, MCFSIM_ICR3); 16962306a36Sopenharmony_ci writel(0x88888888, MCFSIM_ICR4); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci for (irq = 0; (irq < NR_IRQS); irq++) { 17262306a36Sopenharmony_ci irq_set_chip(irq, &intc_irq_chip); 17362306a36Sopenharmony_ci edge = 0; 17462306a36Sopenharmony_ci if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX)) 17562306a36Sopenharmony_ci edge = intc_irqmap[irq - MCFINT_VECBASE].ack; 17662306a36Sopenharmony_ci if (edge) { 17762306a36Sopenharmony_ci irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING); 17862306a36Sopenharmony_ci irq_set_handler(irq, intc_external_irq); 17962306a36Sopenharmony_ci } else { 18062306a36Sopenharmony_ci irq_set_irq_type(irq, IRQ_TYPE_LEVEL_HIGH); 18162306a36Sopenharmony_ci irq_set_handler(irq, handle_level_irq); 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 186