linux/arch/mips/pmcs-msp71xx/msp_irq_per.c
<<
>>
Prefs
   1/*
   2 * Copyright 2010 PMC-Sierra, Inc, derived from irq_cpu.c
   3 *
   4 * This file define the irq handler for MSP PER subsystem interrupts.
   5 *
   6 * This program is free software; you can redistribute  it and/or modify it
   7 * under  the terms of  the GNU General  Public License as published by the
   8 * Free Software Foundation;  either version 2 of the  License, or (at your
   9 * option) any later version.
  10 */
  11
  12#include <linux/init.h>
  13#include <linux/interrupt.h>
  14#include <linux/kernel.h>
  15#include <linux/spinlock.h>
  16#include <linux/bitops.h>
  17
  18#include <asm/mipsregs.h>
  19
  20#include <msp_cic_int.h>
  21#include <msp_regs.h>
  22
  23
  24/*
  25 * Convenience Macro.  Should be somewhere generic.
  26 */
  27#define get_current_vpe()       \
  28        ((read_c0_tcbind() >> TCBIND_CURVPE_SHIFT) & TCBIND_CURVPE)
  29
  30#ifdef CONFIG_SMP
  31/*
  32 * The PER registers must be protected from concurrent access.
  33 */
  34
  35static DEFINE_SPINLOCK(per_lock);
  36#endif
  37
  38/* ensure writes to per are completed */
  39
  40static inline void per_wmb(void)
  41{
  42        const volatile void __iomem *per_mem = PER_INT_MSK_REG;
  43        volatile u32 dummy_read;
  44
  45        wmb();
  46        dummy_read = __raw_readl(per_mem);
  47        dummy_read++;
  48}
  49
  50static inline void unmask_per_irq(struct irq_data *d)
  51{
  52#ifdef CONFIG_SMP
  53        unsigned long flags;
  54        spin_lock_irqsave(&per_lock, flags);
  55        *PER_INT_MSK_REG |= (1 << (d->irq - MSP_PER_INTBASE));
  56        spin_unlock_irqrestore(&per_lock, flags);
  57#else
  58        *PER_INT_MSK_REG |= (1 << (d->irq - MSP_PER_INTBASE));
  59#endif
  60        per_wmb();
  61}
  62
  63static inline void mask_per_irq(struct irq_data *d)
  64{
  65#ifdef CONFIG_SMP
  66        unsigned long flags;
  67        spin_lock_irqsave(&per_lock, flags);
  68        *PER_INT_MSK_REG &= ~(1 << (d->irq - MSP_PER_INTBASE));
  69        spin_unlock_irqrestore(&per_lock, flags);
  70#else
  71        *PER_INT_MSK_REG &= ~(1 << (d->irq - MSP_PER_INTBASE));
  72#endif
  73        per_wmb();
  74}
  75
  76static inline void msp_per_irq_ack(struct irq_data *d)
  77{
  78        mask_per_irq(d);
  79        /*
  80         * In the PER interrupt controller, only bits 11 and 10
  81         * are write-to-clear, (SPI TX complete, SPI RX complete).
  82         * It does nothing for any others.
  83         */
  84        *PER_INT_STS_REG = (1 << (d->irq - MSP_PER_INTBASE));
  85}
  86
  87#ifdef CONFIG_SMP
  88static int msp_per_irq_set_affinity(struct irq_data *d,
  89                                    const struct cpumask *affinity, bool force)
  90{
  91        /* WTF is this doing ????? */
  92        unmask_per_irq(d);
  93        return 0;
  94}
  95#endif
  96
  97static struct irq_chip msp_per_irq_controller = {
  98        .name = "MSP_PER",
  99        .irq_enable = unmask_per_irq,
 100        .irq_disable = mask_per_irq,
 101        .irq_ack = msp_per_irq_ack,
 102#ifdef CONFIG_SMP
 103        .irq_set_affinity = msp_per_irq_set_affinity,
 104#endif
 105};
 106
 107void __init msp_per_irq_init(void)
 108{
 109        int i;
 110        /* Mask/clear interrupts. */
 111        *PER_INT_MSK_REG  = 0x00000000;
 112        *PER_INT_STS_REG  = 0xFFFFFFFF;
 113        /* initialize all the IRQ descriptors */
 114        for (i = MSP_PER_INTBASE; i < MSP_PER_INTBASE + 32; i++) {
 115                irq_set_chip(i, &msp_per_irq_controller);
 116        }
 117}
 118
 119void msp_per_irq_dispatch(void)
 120{
 121        u32     per_mask = *PER_INT_MSK_REG;
 122        u32     per_status = *PER_INT_STS_REG;
 123        u32     pending;
 124
 125        pending = per_status & per_mask;
 126        if (pending) {
 127                do_IRQ(ffs(pending) + MSP_PER_INTBASE - 1);
 128        } else {
 129                spurious_interrupt();
 130        }
 131}
 132