linux/arch/m68k/coldfire/intc-5272.c
<<
>>
Prefs
   1/*
   2 * intc.c  --  interrupt controller or ColdFire 5272 SoC
   3 *
   4 * (C) Copyright 2009, Greg Ungerer <gerg@snapgear.com>
   5 *
   6 * This file is subject to the terms and conditions of the GNU General Public
   7 * License.  See the file COPYING in the main directory of this archive
   8 * for more details.
   9 */
  10
  11#include <linux/types.h>
  12#include <linux/init.h>
  13#include <linux/kernel.h>
  14#include <linux/interrupt.h>
  15#include <linux/kernel_stat.h>
  16#include <linux/irq.h>
  17#include <linux/io.h>
  18#include <asm/coldfire.h>
  19#include <asm/mcfsim.h>
  20#include <asm/traps.h>
  21
  22/*
  23 * The 5272 ColdFire interrupt controller is nothing like any other
  24 * ColdFire interrupt controller - it truly is completely different.
  25 * Given its age it is unlikely to be used on any other ColdFire CPU.
  26 */
  27
  28/*
  29 * The masking and priproty setting of interrupts on the 5272 is done
  30 * via a set of 4 "Interrupt Controller Registers" (ICR). There is a
  31 * loose mapping of vector number to register and internal bits, but
  32 * a table is the easiest and quickest way to map them.
  33 *
  34 * Note that the external interrupts are edge triggered (unlike the
  35 * internal interrupt sources which are level triggered). Which means
  36 * they also need acknowledging via acknowledge bits.
  37 */
  38struct irqmap {
  39        unsigned int    icr;
  40        unsigned char   index;
  41        unsigned char   ack;
  42};
  43
  44static struct irqmap intc_irqmap[MCFINT_VECMAX - MCFINT_VECBASE] = {
  45        /*MCF_IRQ_SPURIOUS*/    { .icr = 0,           .index = 0,  .ack = 0, },
  46        /*MCF_IRQ_EINT1*/       { .icr = MCFSIM_ICR1, .index = 28, .ack = 1, },
  47        /*MCF_IRQ_EINT2*/       { .icr = MCFSIM_ICR1, .index = 24, .ack = 1, },
  48        /*MCF_IRQ_EINT3*/       { .icr = MCFSIM_ICR1, .index = 20, .ack = 1, },
  49        /*MCF_IRQ_EINT4*/       { .icr = MCFSIM_ICR1, .index = 16, .ack = 1, },
  50        /*MCF_IRQ_TIMER1*/      { .icr = MCFSIM_ICR1, .index = 12, .ack = 0, },
  51        /*MCF_IRQ_TIMER2*/      { .icr = MCFSIM_ICR1, .index = 8,  .ack = 0, },
  52        /*MCF_IRQ_TIMER3*/      { .icr = MCFSIM_ICR1, .index = 4,  .ack = 0, },
  53        /*MCF_IRQ_TIMER4*/      { .icr = MCFSIM_ICR1, .index = 0,  .ack = 0, },
  54        /*MCF_IRQ_UART1*/       { .icr = MCFSIM_ICR2, .index = 28, .ack = 0, },
  55        /*MCF_IRQ_UART2*/       { .icr = MCFSIM_ICR2, .index = 24, .ack = 0, },
  56        /*MCF_IRQ_PLIP*/        { .icr = MCFSIM_ICR2, .index = 20, .ack = 0, },
  57        /*MCF_IRQ_PLIA*/        { .icr = MCFSIM_ICR2, .index = 16, .ack = 0, },
  58        /*MCF_IRQ_USB0*/        { .icr = MCFSIM_ICR2, .index = 12, .ack = 0, },
  59        /*MCF_IRQ_USB1*/        { .icr = MCFSIM_ICR2, .index = 8,  .ack = 0, },
  60        /*MCF_IRQ_USB2*/        { .icr = MCFSIM_ICR2, .index = 4,  .ack = 0, },
  61        /*MCF_IRQ_USB3*/        { .icr = MCFSIM_ICR2, .index = 0,  .ack = 0, },
  62        /*MCF_IRQ_USB4*/        { .icr = MCFSIM_ICR3, .index = 28, .ack = 0, },
  63        /*MCF_IRQ_USB5*/        { .icr = MCFSIM_ICR3, .index = 24, .ack = 0, },
  64        /*MCF_IRQ_USB6*/        { .icr = MCFSIM_ICR3, .index = 20, .ack = 0, },
  65        /*MCF_IRQ_USB7*/        { .icr = MCFSIM_ICR3, .index = 16, .ack = 0, },
  66        /*MCF_IRQ_DMA*/         { .icr = MCFSIM_ICR3, .index = 12, .ack = 0, },
  67        /*MCF_IRQ_ERX*/         { .icr = MCFSIM_ICR3, .index = 8,  .ack = 0, },
  68        /*MCF_IRQ_ETX*/         { .icr = MCFSIM_ICR3, .index = 4,  .ack = 0, },
  69        /*MCF_IRQ_ENTC*/        { .icr = MCFSIM_ICR3, .index = 0,  .ack = 0, },
  70        /*MCF_IRQ_QSPI*/        { .icr = MCFSIM_ICR4, .index = 28, .ack = 0, },
  71        /*MCF_IRQ_EINT5*/       { .icr = MCFSIM_ICR4, .index = 24, .ack = 1, },
  72        /*MCF_IRQ_EINT6*/       { .icr = MCFSIM_ICR4, .index = 20, .ack = 1, },
  73        /*MCF_IRQ_SWTO*/        { .icr = MCFSIM_ICR4, .index = 16, .ack = 0, },
  74};
  75
  76/*
  77 * The act of masking the interrupt also has a side effect of 'ack'ing
  78 * an interrupt on this irq (for the external irqs). So this mask function
  79 * is also an ack_mask function.
  80 */
  81static void intc_irq_mask(struct irq_data *d)
  82{
  83        unsigned int irq = d->irq;
  84
  85        if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX)) {
  86                u32 v;
  87                irq -= MCFINT_VECBASE;
  88                v = 0x8 << intc_irqmap[irq].index;
  89                writel(v, intc_irqmap[irq].icr);
  90        }
  91}
  92
  93static void intc_irq_unmask(struct irq_data *d)
  94{
  95        unsigned int irq = d->irq;
  96
  97        if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX)) {
  98                u32 v;
  99                irq -= MCFINT_VECBASE;
 100                v = 0xd << intc_irqmap[irq].index;
 101                writel(v, intc_irqmap[irq].icr);
 102        }
 103}
 104
 105static void intc_irq_ack(struct irq_data *d)
 106{
 107        unsigned int irq = d->irq;
 108
 109        /* Only external interrupts are acked */
 110        if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX)) {
 111                irq -= MCFINT_VECBASE;
 112                if (intc_irqmap[irq].ack) {
 113                        u32 v;
 114                        v = readl(intc_irqmap[irq].icr);
 115                        v &= (0x7 << intc_irqmap[irq].index);
 116                        v |= (0x8 << intc_irqmap[irq].index);
 117                        writel(v, intc_irqmap[irq].icr);
 118                }
 119        }
 120}
 121
 122static int intc_irq_set_type(struct irq_data *d, unsigned int type)
 123{
 124        unsigned int irq = d->irq;
 125
 126        if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX)) {
 127                irq -= MCFINT_VECBASE;
 128                if (intc_irqmap[irq].ack) {
 129                        u32 v;
 130                        v = readl(MCFSIM_PITR);
 131                        if (type == IRQ_TYPE_EDGE_FALLING)
 132                                v &= ~(0x1 << (32 - irq));
 133                        else
 134                                v |= (0x1 << (32 - irq));
 135                        writel(v, MCFSIM_PITR);
 136                }
 137        }
 138        return 0;
 139}
 140
 141/*
 142 * Simple flow handler to deal with the external edge triggered interrupts.
 143 * We need to be careful with the masking/acking due to the side effects
 144 * of masking an interrupt.
 145 */
 146static void intc_external_irq(struct irq_desc *desc)
 147{
 148        irq_desc_get_chip(desc)->irq_ack(&desc->irq_data);
 149        handle_simple_irq(desc);
 150}
 151
 152static struct irq_chip intc_irq_chip = {
 153        .name           = "CF-INTC",
 154        .irq_mask       = intc_irq_mask,
 155        .irq_unmask     = intc_irq_unmask,
 156        .irq_mask_ack   = intc_irq_mask,
 157        .irq_ack        = intc_irq_ack,
 158        .irq_set_type   = intc_irq_set_type,
 159};
 160
 161void __init init_IRQ(void)
 162{
 163        int irq, edge;
 164
 165        /* Mask all interrupt sources */
 166        writel(0x88888888, MCFSIM_ICR1);
 167        writel(0x88888888, MCFSIM_ICR2);
 168        writel(0x88888888, MCFSIM_ICR3);
 169        writel(0x88888888, MCFSIM_ICR4);
 170
 171        for (irq = 0; (irq < NR_IRQS); irq++) {
 172                irq_set_chip(irq, &intc_irq_chip);
 173                edge = 0;
 174                if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX))
 175                        edge = intc_irqmap[irq - MCFINT_VECBASE].ack;
 176                if (edge) {
 177                        irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING);
 178                        irq_set_handler(irq, intc_external_irq);
 179                } else {
 180                        irq_set_irq_type(irq, IRQ_TYPE_LEVEL_HIGH);
 181                        irq_set_handler(irq, handle_level_irq);
 182                }
 183        }
 184}
 185
 186