1/* 2 * Copyright 2010 PMC-Sierra, Inc, derived from irq_cpu.c 3 * 4 * This file define the irq handler for MSP CIC 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/bitops.h> 16#include <linux/irq.h> 17 18#include <asm/mipsregs.h> 19 20#include <msp_cic_int.h> 21#include <msp_regs.h> 22 23/* 24 * External API 25 */ 26extern void msp_per_irq_init(void); 27extern void msp_per_irq_dispatch(void); 28 29 30/* 31 * Convenience Macro. Should be somewhere generic. 32 */ 33#define get_current_vpe() \ 34 ((read_c0_tcbind() >> TCBIND_CURVPE_SHIFT) & TCBIND_CURVPE) 35 36#ifdef CONFIG_SMP 37 38#define LOCK_VPE(flags, mtflags) \ 39do { \ 40 local_irq_save(flags); \ 41 mtflags = dmt(); \ 42} while (0) 43 44#define UNLOCK_VPE(flags, mtflags) \ 45do { \ 46 emt(mtflags); \ 47 local_irq_restore(flags);\ 48} while (0) 49 50#define LOCK_CORE(flags, mtflags) \ 51do { \ 52 local_irq_save(flags); \ 53 mtflags = dvpe(); \ 54} while (0) 55 56#define UNLOCK_CORE(flags, mtflags) \ 57do { \ 58 evpe(mtflags); \ 59 local_irq_restore(flags);\ 60} while (0) 61 62#else 63 64#define LOCK_VPE(flags, mtflags) 65#define UNLOCK_VPE(flags, mtflags) 66#endif 67 68/* ensure writes to cic are completed */ 69static inline void cic_wmb(void) 70{ 71 const volatile void __iomem *cic_mem = CIC_VPE0_MSK_REG; 72 volatile u32 dummy_read; 73 74 wmb(); 75 dummy_read = __raw_readl(cic_mem); 76 dummy_read++; 77} 78 79static void unmask_cic_irq(struct irq_data *d) 80{ 81 volatile u32 *cic_msk_reg = CIC_VPE0_MSK_REG; 82 int vpe; 83#ifdef CONFIG_SMP 84 unsigned int mtflags; 85 unsigned long flags; 86 87 /* 88 * Make sure we have IRQ affinity. It may have changed while 89 * we were processing the IRQ. 90 */ 91 if (!cpumask_test_cpu(smp_processor_id(), d->affinity)) 92 return; 93#endif 94 95 vpe = get_current_vpe(); 96 LOCK_VPE(flags, mtflags); 97 cic_msk_reg[vpe] |= (1 << (d->irq - MSP_CIC_INTBASE)); 98 UNLOCK_VPE(flags, mtflags); 99 cic_wmb(); 100} 101 102static void mask_cic_irq(struct irq_data *d) 103{ 104 volatile u32 *cic_msk_reg = CIC_VPE0_MSK_REG; 105 int vpe = get_current_vpe(); 106#ifdef CONFIG_SMP 107 unsigned long flags, mtflags; 108#endif 109 LOCK_VPE(flags, mtflags); 110 cic_msk_reg[vpe] &= ~(1 << (d->irq - MSP_CIC_INTBASE)); 111 UNLOCK_VPE(flags, mtflags); 112 cic_wmb(); 113} 114static void msp_cic_irq_ack(struct irq_data *d) 115{ 116 mask_cic_irq(d); 117 /* 118 * Only really necessary for 18, 16-14 and sometimes 3:0 119 * (since these can be edge sensitive) but it doesn't 120 * hurt for the others 121 */ 122 *CIC_STS_REG = (1 << (d->irq - MSP_CIC_INTBASE)); 123} 124 125/* Note: Limiting to VSMP. */ 126 127#ifdef CONFIG_MIPS_MT_SMP 128static int msp_cic_irq_set_affinity(struct irq_data *d, 129 const struct cpumask *cpumask, bool force) 130{ 131 int cpu; 132 unsigned long flags; 133 unsigned int mtflags; 134 unsigned long imask = (1 << (d->irq - MSP_CIC_INTBASE)); 135 volatile u32 *cic_mask = (volatile u32 *)CIC_VPE0_MSK_REG; 136 137 /* timer balancing should be disabled in kernel code */ 138 BUG_ON(d->irq == MSP_INT_VPE0_TIMER || d->irq == MSP_INT_VPE1_TIMER); 139 140 LOCK_CORE(flags, mtflags); 141 /* enable if any of each VPE's TCs require this IRQ */ 142 for_each_online_cpu(cpu) { 143 if (cpumask_test_cpu(cpu, cpumask)) 144 cic_mask[cpu] |= imask; 145 else 146 cic_mask[cpu] &= ~imask; 147 148 } 149 150 UNLOCK_CORE(flags, mtflags); 151 return 0; 152 153} 154#endif 155 156static struct irq_chip msp_cic_irq_controller = { 157 .name = "MSP_CIC", 158 .irq_mask = mask_cic_irq, 159 .irq_mask_ack = msp_cic_irq_ack, 160 .irq_unmask = unmask_cic_irq, 161 .irq_ack = msp_cic_irq_ack, 162#ifdef CONFIG_MIPS_MT_SMP 163 .irq_set_affinity = msp_cic_irq_set_affinity, 164#endif 165}; 166 167void __init msp_cic_irq_init(void) 168{ 169 int i; 170 /* Mask/clear interrupts. */ 171 *CIC_VPE0_MSK_REG = 0x00000000; 172 *CIC_VPE1_MSK_REG = 0x00000000; 173 *CIC_STS_REG = 0xFFFFFFFF; 174 /* 175 * The MSP7120 RG and EVBD boards use IRQ[6:4] for PCI. 176 * These inputs map to EXT_INT_POL[6:4] inside the CIC. 177 * They are to be active low, level sensitive. 178 */ 179 *CIC_EXT_CFG_REG &= 0xFFFF8F8F; 180 181 /* initialize all the IRQ descriptors */ 182 for (i = MSP_CIC_INTBASE ; i < MSP_CIC_INTBASE + 32 ; i++) { 183 irq_set_chip_and_handler(i, &msp_cic_irq_controller, 184 handle_level_irq); 185 } 186 187 /* Initialize the PER interrupt sub-system */ 188 msp_per_irq_init(); 189} 190 191/* CIC masked by CIC vector processing before dispatch called */ 192void msp_cic_irq_dispatch(void) 193{ 194 volatile u32 *cic_msk_reg = (volatile u32 *)CIC_VPE0_MSK_REG; 195 u32 cic_mask; 196 u32 pending; 197 int cic_status = *CIC_STS_REG; 198 cic_mask = cic_msk_reg[get_current_vpe()]; 199 pending = cic_status & cic_mask; 200 if (pending & (1 << (MSP_INT_VPE0_TIMER - MSP_CIC_INTBASE))) { 201 do_IRQ(MSP_INT_VPE0_TIMER); 202 } else if (pending & (1 << (MSP_INT_VPE1_TIMER - MSP_CIC_INTBASE))) { 203 do_IRQ(MSP_INT_VPE1_TIMER); 204 } else if (pending & (1 << (MSP_INT_PER - MSP_CIC_INTBASE))) { 205 msp_per_irq_dispatch(); 206 } else if (pending) { 207 do_IRQ(ffs(pending) + MSP_CIC_INTBASE - 1); 208 } else{ 209 spurious_interrupt(); 210 } 211} 212