162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * intc-2.c 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * General interrupt controller code for the many ColdFire cores that use 562306a36Sopenharmony_ci * interrupt controllers with 63 interrupt sources, organized as 56 fully- 662306a36Sopenharmony_ci * programmable + 7 fixed-level interrupt sources. This includes the 523x 762306a36Sopenharmony_ci * family, the 5270, 5271, 5274, 5275, and the 528x family which have two such 862306a36Sopenharmony_ci * controllers, and the 547x and 548x families which have only one of them. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * The external 7 fixed interrupts are part of the Edge Port unit of these 1162306a36Sopenharmony_ci * ColdFire parts. They can be configured as level or edge triggered. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * (C) Copyright 2009-2011, Greg Ungerer <gerg@snapgear.com> 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 1662306a36Sopenharmony_ci * License. See the file COPYING in the main directory of this archive 1762306a36Sopenharmony_ci * for more details. 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/types.h> 2162306a36Sopenharmony_ci#include <linux/init.h> 2262306a36Sopenharmony_ci#include <linux/kernel.h> 2362306a36Sopenharmony_ci#include <linux/interrupt.h> 2462306a36Sopenharmony_ci#include <linux/irq.h> 2562306a36Sopenharmony_ci#include <linux/io.h> 2662306a36Sopenharmony_ci#include <asm/coldfire.h> 2762306a36Sopenharmony_ci#include <asm/mcfsim.h> 2862306a36Sopenharmony_ci#include <asm/traps.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* 3162306a36Sopenharmony_ci * Bit definitions for the ICR family of registers. 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_ci#define MCFSIM_ICR_LEVEL(l) ((l)<<3) /* Level l intr */ 3462306a36Sopenharmony_ci#define MCFSIM_ICR_PRI(p) (p) /* Priority p intr */ 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* 3762306a36Sopenharmony_ci * The EDGE Port interrupts are the fixed 7 external interrupts. 3862306a36Sopenharmony_ci * They need some special treatment, for example they need to be acked. 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_ci#define EINT0 64 /* Is not actually used, but spot reserved for it */ 4162306a36Sopenharmony_ci#define EINT1 65 /* EDGE Port interrupt 1 */ 4262306a36Sopenharmony_ci#define EINT7 71 /* EDGE Port interrupt 7 */ 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#ifdef MCFICM_INTC1 4562306a36Sopenharmony_ci#define NR_VECS 128 4662306a36Sopenharmony_ci#else 4762306a36Sopenharmony_ci#define NR_VECS 64 4862306a36Sopenharmony_ci#endif 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic void intc_irq_mask(struct irq_data *d) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci unsigned int irq = d->irq - MCFINT_VECBASE; 5362306a36Sopenharmony_ci unsigned long imraddr; 5462306a36Sopenharmony_ci u32 val, imrbit; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#ifdef MCFICM_INTC1 5762306a36Sopenharmony_ci imraddr = (irq & 0x40) ? MCFICM_INTC1 : MCFICM_INTC0; 5862306a36Sopenharmony_ci#else 5962306a36Sopenharmony_ci imraddr = MCFICM_INTC0; 6062306a36Sopenharmony_ci#endif 6162306a36Sopenharmony_ci imraddr += (irq & 0x20) ? MCFINTC_IMRH : MCFINTC_IMRL; 6262306a36Sopenharmony_ci imrbit = 0x1 << (irq & 0x1f); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci val = __raw_readl(imraddr); 6562306a36Sopenharmony_ci __raw_writel(val | imrbit, imraddr); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic void intc_irq_unmask(struct irq_data *d) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci unsigned int irq = d->irq - MCFINT_VECBASE; 7162306a36Sopenharmony_ci unsigned long imraddr; 7262306a36Sopenharmony_ci u32 val, imrbit; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci#ifdef MCFICM_INTC1 7562306a36Sopenharmony_ci imraddr = (irq & 0x40) ? MCFICM_INTC1 : MCFICM_INTC0; 7662306a36Sopenharmony_ci#else 7762306a36Sopenharmony_ci imraddr = MCFICM_INTC0; 7862306a36Sopenharmony_ci#endif 7962306a36Sopenharmony_ci imraddr += ((irq & 0x20) ? MCFINTC_IMRH : MCFINTC_IMRL); 8062306a36Sopenharmony_ci imrbit = 0x1 << (irq & 0x1f); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci /* Don't set the "maskall" bit! */ 8362306a36Sopenharmony_ci if ((irq & 0x20) == 0) 8462306a36Sopenharmony_ci imrbit |= 0x1; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci val = __raw_readl(imraddr); 8762306a36Sopenharmony_ci __raw_writel(val & ~imrbit, imraddr); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* 9162306a36Sopenharmony_ci * Only the external (or EDGE Port) interrupts need to be acknowledged 9262306a36Sopenharmony_ci * here, as part of the IRQ handler. They only really need to be ack'ed 9362306a36Sopenharmony_ci * if they are in edge triggered mode, but there is no harm in doing it 9462306a36Sopenharmony_ci * for all types. 9562306a36Sopenharmony_ci */ 9662306a36Sopenharmony_cistatic void intc_irq_ack(struct irq_data *d) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci unsigned int irq = d->irq; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci __raw_writeb(0x1 << (irq - EINT0), MCFEPORT_EPFR); 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci/* 10462306a36Sopenharmony_ci * Each vector needs a unique priority and level associated with it. 10562306a36Sopenharmony_ci * We don't really care so much what they are, we don't rely on the 10662306a36Sopenharmony_ci * traditional priority interrupt scheme of the m68k/ColdFire. This 10762306a36Sopenharmony_ci * only needs to be set once for an interrupt, and we will never change 10862306a36Sopenharmony_ci * these values once we have set them. 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_cistatic u8 intc_intpri = MCFSIM_ICR_LEVEL(6) | MCFSIM_ICR_PRI(6); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic unsigned int intc_irq_startup(struct irq_data *d) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci unsigned int irq = d->irq - MCFINT_VECBASE; 11562306a36Sopenharmony_ci unsigned long icraddr; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci#ifdef MCFICM_INTC1 11862306a36Sopenharmony_ci icraddr = (irq & 0x40) ? MCFICM_INTC1 : MCFICM_INTC0; 11962306a36Sopenharmony_ci#else 12062306a36Sopenharmony_ci icraddr = MCFICM_INTC0; 12162306a36Sopenharmony_ci#endif 12262306a36Sopenharmony_ci icraddr += MCFINTC_ICR0 + (irq & 0x3f); 12362306a36Sopenharmony_ci if (__raw_readb(icraddr) == 0) 12462306a36Sopenharmony_ci __raw_writeb(intc_intpri--, icraddr); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci irq = d->irq; 12762306a36Sopenharmony_ci if ((irq >= EINT1) && (irq <= EINT7)) { 12862306a36Sopenharmony_ci u8 v; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci irq -= EINT0; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci /* Set EPORT line as input */ 13362306a36Sopenharmony_ci v = __raw_readb(MCFEPORT_EPDDR); 13462306a36Sopenharmony_ci __raw_writeb(v & ~(0x1 << irq), MCFEPORT_EPDDR); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* Set EPORT line as interrupt source */ 13762306a36Sopenharmony_ci v = __raw_readb(MCFEPORT_EPIER); 13862306a36Sopenharmony_ci __raw_writeb(v | (0x1 << irq), MCFEPORT_EPIER); 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci intc_irq_unmask(d); 14262306a36Sopenharmony_ci return 0; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic int intc_irq_set_type(struct irq_data *d, unsigned int type) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci unsigned int irq = d->irq; 14862306a36Sopenharmony_ci u16 pa, tb; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci switch (type) { 15162306a36Sopenharmony_ci case IRQ_TYPE_EDGE_RISING: 15262306a36Sopenharmony_ci tb = 0x1; 15362306a36Sopenharmony_ci break; 15462306a36Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 15562306a36Sopenharmony_ci tb = 0x2; 15662306a36Sopenharmony_ci break; 15762306a36Sopenharmony_ci case IRQ_TYPE_EDGE_BOTH: 15862306a36Sopenharmony_ci tb = 0x3; 15962306a36Sopenharmony_ci break; 16062306a36Sopenharmony_ci default: 16162306a36Sopenharmony_ci /* Level triggered */ 16262306a36Sopenharmony_ci tb = 0; 16362306a36Sopenharmony_ci break; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci if (tb) 16762306a36Sopenharmony_ci irq_set_handler(irq, handle_edge_irq); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci irq -= EINT0; 17062306a36Sopenharmony_ci pa = __raw_readw(MCFEPORT_EPPAR); 17162306a36Sopenharmony_ci pa = (pa & ~(0x3 << (irq * 2))) | (tb << (irq * 2)); 17262306a36Sopenharmony_ci __raw_writew(pa, MCFEPORT_EPPAR); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci return 0; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic struct irq_chip intc_irq_chip = { 17862306a36Sopenharmony_ci .name = "CF-INTC", 17962306a36Sopenharmony_ci .irq_startup = intc_irq_startup, 18062306a36Sopenharmony_ci .irq_mask = intc_irq_mask, 18162306a36Sopenharmony_ci .irq_unmask = intc_irq_unmask, 18262306a36Sopenharmony_ci}; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic struct irq_chip intc_irq_chip_edge_port = { 18562306a36Sopenharmony_ci .name = "CF-INTC-EP", 18662306a36Sopenharmony_ci .irq_startup = intc_irq_startup, 18762306a36Sopenharmony_ci .irq_mask = intc_irq_mask, 18862306a36Sopenharmony_ci .irq_unmask = intc_irq_unmask, 18962306a36Sopenharmony_ci .irq_ack = intc_irq_ack, 19062306a36Sopenharmony_ci .irq_set_type = intc_irq_set_type, 19162306a36Sopenharmony_ci}; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_civoid __init init_IRQ(void) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci int irq; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* Mask all interrupt sources */ 19862306a36Sopenharmony_ci __raw_writel(0x1, MCFICM_INTC0 + MCFINTC_IMRL); 19962306a36Sopenharmony_ci#ifdef MCFICM_INTC1 20062306a36Sopenharmony_ci __raw_writel(0x1, MCFICM_INTC1 + MCFINTC_IMRL); 20162306a36Sopenharmony_ci#endif 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci for (irq = MCFINT_VECBASE; (irq < MCFINT_VECBASE + NR_VECS); irq++) { 20462306a36Sopenharmony_ci if ((irq >= EINT1) && (irq <=EINT7)) 20562306a36Sopenharmony_ci irq_set_chip(irq, &intc_irq_chip_edge_port); 20662306a36Sopenharmony_ci else 20762306a36Sopenharmony_ci irq_set_chip(irq, &intc_irq_chip); 20862306a36Sopenharmony_ci irq_set_irq_type(irq, IRQ_TYPE_LEVEL_HIGH); 20962306a36Sopenharmony_ci irq_set_handler(irq, handle_level_irq); 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 213