Path: blob/master/arch/m68k/platform/coldfire/intc-2.c
10819 views
/*1* intc-2.c2*3* General interrupt controller code for the many ColdFire cores that use4* interrupt controllers with 63 interrupt sources, organized as 56 fully-5* programmable + 7 fixed-level interrupt sources. This includes the 523x6* family, the 5270, 5271, 5274, 5275, and the 528x family which have two such7* controllers, and the 547x and 548x families which have only one of them.8*9* The external 7 fixed interrupts are part the the Edge Port unit of these10* ColdFire parts. They can be configured as level or edge triggered.11*12* (C) Copyright 2009-2011, Greg Ungerer <[email protected]>13*14* This file is subject to the terms and conditions of the GNU General Public15* License. See the file COPYING in the main directory of this archive16* for more details.17*/1819#include <linux/types.h>20#include <linux/init.h>21#include <linux/kernel.h>22#include <linux/interrupt.h>23#include <linux/irq.h>24#include <linux/io.h>25#include <asm/coldfire.h>26#include <asm/mcfsim.h>27#include <asm/traps.h>2829/*30* Bit definitions for the ICR family of registers.31*/32#define MCFSIM_ICR_LEVEL(l) ((l)<<3) /* Level l intr */33#define MCFSIM_ICR_PRI(p) (p) /* Priority p intr */3435/*36* The EDGE Port interrupts are the fixed 7 external interrupts.37* They need some special treatment, for example they need to be acked.38*/39#define EINT0 64 /* Is not actually used, but spot reserved for it */40#define EINT1 65 /* EDGE Port interrupt 1 */41#define EINT7 71 /* EDGE Port interrupt 7 */4243#ifdef MCFICM_INTC144#define NR_VECS 12845#else46#define NR_VECS 6447#endif4849static void intc_irq_mask(struct irq_data *d)50{51unsigned int irq = d->irq - MCFINT_VECBASE;52unsigned long imraddr;53u32 val, imrbit;5455#ifdef MCFICM_INTC156imraddr = (irq & 0x40) ? MCFICM_INTC1 : MCFICM_INTC0;57#else58imraddr = MCFICM_INTC0;59#endif60imraddr += (irq & 0x20) ? MCFINTC_IMRH : MCFINTC_IMRL;61imrbit = 0x1 << (irq & 0x1f);6263val = __raw_readl(imraddr);64__raw_writel(val | imrbit, imraddr);65}6667static void intc_irq_unmask(struct irq_data *d)68{69unsigned int irq = d->irq - MCFINT_VECBASE;70unsigned long imraddr;71u32 val, imrbit;7273#ifdef MCFICM_INTC174imraddr = (irq & 0x40) ? MCFICM_INTC1 : MCFICM_INTC0;75#else76imraddr = MCFICM_INTC0;77#endif78imraddr += ((irq & 0x20) ? MCFINTC_IMRH : MCFINTC_IMRL);79imrbit = 0x1 << (irq & 0x1f);8081/* Don't set the "maskall" bit! */82if ((irq & 0x20) == 0)83imrbit |= 0x1;8485val = __raw_readl(imraddr);86__raw_writel(val & ~imrbit, imraddr);87}8889/*90* Only the external (or EDGE Port) interrupts need to be acknowledged91* here, as part of the IRQ handler. They only really need to be ack'ed92* if they are in edge triggered mode, but there is no harm in doing it93* for all types.94*/95static void intc_irq_ack(struct irq_data *d)96{97unsigned int irq = d->irq;9899__raw_writeb(0x1 << (irq - EINT0), MCFEPORT_EPFR);100}101102/*103* Each vector needs a unique priority and level associated with it.104* We don't really care so much what they are, we don't rely on the105* traditional priority interrupt scheme of the m68k/ColdFire. This106* only needs to be set once for an interrupt, and we will never change107* these values once we have set them.108*/109static u8 intc_intpri = MCFSIM_ICR_LEVEL(6) | MCFSIM_ICR_PRI(6);110111static unsigned int intc_irq_startup(struct irq_data *d)112{113unsigned int irq = d->irq - MCFINT_VECBASE;114unsigned long icraddr;115116#ifdef MCFICM_INTC1117icraddr = (irq & 0x40) ? MCFICM_INTC1 : MCFICM_INTC0;118#else119icraddr = MCFICM_INTC0;120#endif121icraddr += MCFINTC_ICR0 + (irq & 0x3f);122if (__raw_readb(icraddr) == 0)123__raw_writeb(intc_intpri--, icraddr);124125irq = d->irq;126if ((irq >= EINT1) && (irq <= EINT7)) {127u8 v;128129irq -= EINT0;130131/* Set EPORT line as input */132v = __raw_readb(MCFEPORT_EPDDR);133__raw_writeb(v & ~(0x1 << irq), MCFEPORT_EPDDR);134135/* Set EPORT line as interrupt source */136v = __raw_readb(MCFEPORT_EPIER);137__raw_writeb(v | (0x1 << irq), MCFEPORT_EPIER);138}139140intc_irq_unmask(d);141return 0;142}143144static int intc_irq_set_type(struct irq_data *d, unsigned int type)145{146unsigned int irq = d->irq;147u16 pa, tb;148149switch (type) {150case IRQ_TYPE_EDGE_RISING:151tb = 0x1;152break;153case IRQ_TYPE_EDGE_FALLING:154tb = 0x2;155break;156case IRQ_TYPE_EDGE_BOTH:157tb = 0x3;158break;159default:160/* Level triggered */161tb = 0;162break;163}164165if (tb)166irq_set_handler(irq, handle_edge_irq);167168irq -= EINT0;169pa = __raw_readw(MCFEPORT_EPPAR);170pa = (pa & ~(0x3 << (irq * 2))) | (tb << (irq * 2));171__raw_writew(pa, MCFEPORT_EPPAR);172173return 0;174}175176static struct irq_chip intc_irq_chip = {177.name = "CF-INTC",178.irq_startup = intc_irq_startup,179.irq_mask = intc_irq_mask,180.irq_unmask = intc_irq_unmask,181};182183static struct irq_chip intc_irq_chip_edge_port = {184.name = "CF-INTC-EP",185.irq_startup = intc_irq_startup,186.irq_mask = intc_irq_mask,187.irq_unmask = intc_irq_unmask,188.irq_ack = intc_irq_ack,189.irq_set_type = intc_irq_set_type,190};191192void __init init_IRQ(void)193{194int irq;195196init_vectors();197198/* Mask all interrupt sources */199__raw_writel(0x1, MCFICM_INTC0 + MCFINTC_IMRL);200#ifdef MCFICM_INTC1201__raw_writel(0x1, MCFICM_INTC1 + MCFINTC_IMRL);202#endif203204for (irq = MCFINT_VECBASE; (irq < MCFINT_VECBASE + NR_VECS); irq++) {205if ((irq >= EINT1) && (irq <=EINT7))206irq_set_chip(irq, &intc_irq_chip_edge_port);207else208irq_set_chip(irq, &intc_irq_chip);209irq_set_irq_type(irq, IRQ_TYPE_LEVEL_HIGH);210irq_set_handler(irq, handle_level_irq);211}212}213214215216