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