linux/arch/mips/pmcs-msp71xx/msp_irq_cic.c
Thomas Gleixner 2874c5fd28 treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 152
Based on 1 normalized pattern(s):

  this program is free software you can redistribute it and or modify
  it under the terms of the gnu general public license as published by
  the free software foundation either version 2 of the license or at
  your option any later version

extracted by the scancode license scanner the SPDX license identifier

  GPL-2.0-or-later

has been chosen to replace the boilerplate/reference in 3029 file(s).

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Allison Randal <allison@lohutok.net>
Cc: linux-spdx@vger.kernel.org
Link: https://lkml.kernel.org/r/20190527070032.746973796@linutronix.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2019-05-30 11:26:32 -07:00

209 lines
4.7 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2010 PMC-Sierra, Inc, derived from irq_cpu.c
*
* This file define the irq handler for MSP CIC subsystem interrupts.
*/
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/bitops.h>
#include <linux/irq.h>
#include <asm/mipsregs.h>
#include <msp_cic_int.h>
#include <msp_regs.h>
/*
* External API
*/
extern void msp_per_irq_init(void);
extern void msp_per_irq_dispatch(void);
/*
* Convenience Macro. Should be somewhere generic.
*/
#define get_current_vpe() \
((read_c0_tcbind() >> TCBIND_CURVPE_SHIFT) & TCBIND_CURVPE)
#ifdef CONFIG_SMP
#define LOCK_VPE(flags, mtflags) \
do { \
local_irq_save(flags); \
mtflags = dmt(); \
} while (0)
#define UNLOCK_VPE(flags, mtflags) \
do { \
emt(mtflags); \
local_irq_restore(flags);\
} while (0)
#define LOCK_CORE(flags, mtflags) \
do { \
local_irq_save(flags); \
mtflags = dvpe(); \
} while (0)
#define UNLOCK_CORE(flags, mtflags) \
do { \
evpe(mtflags); \
local_irq_restore(flags);\
} while (0)
#else
#define LOCK_VPE(flags, mtflags)
#define UNLOCK_VPE(flags, mtflags)
#endif
/* ensure writes to cic are completed */
static inline void cic_wmb(void)
{
const volatile void __iomem *cic_mem = CIC_VPE0_MSK_REG;
volatile u32 dummy_read;
wmb();
dummy_read = __raw_readl(cic_mem);
dummy_read++;
}
static void unmask_cic_irq(struct irq_data *d)
{
volatile u32 *cic_msk_reg = CIC_VPE0_MSK_REG;
int vpe;
#ifdef CONFIG_SMP
unsigned int mtflags;
unsigned long flags;
/*
* Make sure we have IRQ affinity. It may have changed while
* we were processing the IRQ.
*/
if (!cpumask_test_cpu(smp_processor_id(),
irq_data_get_affinity_mask(d)))
return;
#endif
vpe = get_current_vpe();
LOCK_VPE(flags, mtflags);
cic_msk_reg[vpe] |= (1 << (d->irq - MSP_CIC_INTBASE));
UNLOCK_VPE(flags, mtflags);
cic_wmb();
}
static void mask_cic_irq(struct irq_data *d)
{
volatile u32 *cic_msk_reg = CIC_VPE0_MSK_REG;
int vpe = get_current_vpe();
#ifdef CONFIG_SMP
unsigned long flags, mtflags;
#endif
LOCK_VPE(flags, mtflags);
cic_msk_reg[vpe] &= ~(1 << (d->irq - MSP_CIC_INTBASE));
UNLOCK_VPE(flags, mtflags);
cic_wmb();
}
static void msp_cic_irq_ack(struct irq_data *d)
{
mask_cic_irq(d);
/*
* Only really necessary for 18, 16-14 and sometimes 3:0
* (since these can be edge sensitive) but it doesn't
* hurt for the others
*/
*CIC_STS_REG = (1 << (d->irq - MSP_CIC_INTBASE));
}
/* Note: Limiting to VSMP. */
#ifdef CONFIG_MIPS_MT_SMP
static int msp_cic_irq_set_affinity(struct irq_data *d,
const struct cpumask *cpumask, bool force)
{
int cpu;
unsigned long flags;
unsigned int mtflags;
unsigned long imask = (1 << (d->irq - MSP_CIC_INTBASE));
volatile u32 *cic_mask = (volatile u32 *)CIC_VPE0_MSK_REG;
/* timer balancing should be disabled in kernel code */
BUG_ON(d->irq == MSP_INT_VPE0_TIMER || d->irq == MSP_INT_VPE1_TIMER);
LOCK_CORE(flags, mtflags);
/* enable if any of each VPE's TCs require this IRQ */
for_each_online_cpu(cpu) {
if (cpumask_test_cpu(cpu, cpumask))
cic_mask[cpu] |= imask;
else
cic_mask[cpu] &= ~imask;
}
UNLOCK_CORE(flags, mtflags);
return 0;
}
#endif
static struct irq_chip msp_cic_irq_controller = {
.name = "MSP_CIC",
.irq_mask = mask_cic_irq,
.irq_mask_ack = msp_cic_irq_ack,
.irq_unmask = unmask_cic_irq,
.irq_ack = msp_cic_irq_ack,
#ifdef CONFIG_MIPS_MT_SMP
.irq_set_affinity = msp_cic_irq_set_affinity,
#endif
};
void __init msp_cic_irq_init(void)
{
int i;
/* Mask/clear interrupts. */
*CIC_VPE0_MSK_REG = 0x00000000;
*CIC_VPE1_MSK_REG = 0x00000000;
*CIC_STS_REG = 0xFFFFFFFF;
/*
* The MSP7120 RG and EVBD boards use IRQ[6:4] for PCI.
* These inputs map to EXT_INT_POL[6:4] inside the CIC.
* They are to be active low, level sensitive.
*/
*CIC_EXT_CFG_REG &= 0xFFFF8F8F;
/* initialize all the IRQ descriptors */
for (i = MSP_CIC_INTBASE ; i < MSP_CIC_INTBASE + 32 ; i++) {
irq_set_chip_and_handler(i, &msp_cic_irq_controller,
handle_level_irq);
}
/* Initialize the PER interrupt sub-system */
msp_per_irq_init();
}
/* CIC masked by CIC vector processing before dispatch called */
void msp_cic_irq_dispatch(void)
{
volatile u32 *cic_msk_reg = (volatile u32 *)CIC_VPE0_MSK_REG;
u32 cic_mask;
u32 pending;
int cic_status = *CIC_STS_REG;
cic_mask = cic_msk_reg[get_current_vpe()];
pending = cic_status & cic_mask;
if (pending & (1 << (MSP_INT_VPE0_TIMER - MSP_CIC_INTBASE))) {
do_IRQ(MSP_INT_VPE0_TIMER);
} else if (pending & (1 << (MSP_INT_VPE1_TIMER - MSP_CIC_INTBASE))) {
do_IRQ(MSP_INT_VPE1_TIMER);
} else if (pending & (1 << (MSP_INT_PER - MSP_CIC_INTBASE))) {
msp_per_irq_dispatch();
} else if (pending) {
do_IRQ(ffs(pending) + MSP_CIC_INTBASE - 1);
} else{
spurious_interrupt();
}
}