xref: /linux/drivers/irqchip/irq-mips-gic.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
12299c49dSSteven J. Hill /*
22299c49dSSteven J. Hill  * This file is subject to the terms and conditions of the GNU General Public
32299c49dSSteven J. Hill  * License.  See the file "COPYING" in the main directory of this archive
42299c49dSSteven J. Hill  * for more details.
52299c49dSSteven J. Hill  *
62299c49dSSteven J. Hill  * Copyright (C) 2008 Ralf Baechle (ralf@linux-mips.org)
72299c49dSSteven J. Hill  * Copyright (C) 2012 MIPS Technologies, Inc.  All rights reserved.
82299c49dSSteven J. Hill  */
91f19aee0SMatt Redfearn 
101f19aee0SMatt Redfearn #define pr_fmt(fmt) "irq-mips-gic: " fmt
111f19aee0SMatt Redfearn 
12357a9c4bSGeert Uytterhoeven #include <linux/bitfield.h>
1339b8d525SRalf Baechle #include <linux/bitmap.h>
14fb8f7be1SAndrew Bresticker #include <linux/clocksource.h>
15da61fcf9SPaul Burton #include <linux/cpuhotplug.h>
1639b8d525SRalf Baechle #include <linux/init.h>
1718743d27SAndrew Bresticker #include <linux/interrupt.h>
18fb8f7be1SAndrew Bresticker #include <linux/irq.h>
1941a83e06SJoel Porquet #include <linux/irqchip.h>
201982752fSMarc Zyngier #include <linux/irqdomain.h>
21a7057270SAndrew Bresticker #include <linux/of_address.h>
22aa493737SPaul Burton #include <linux/percpu.h>
2318743d27SAndrew Bresticker #include <linux/sched.h>
24631330f5SRalf Baechle #include <linux/smp.h>
2539b8d525SRalf Baechle 
26e83f7e02SPaul Burton #include <asm/mips-cps.h>
2798b67c37SSteven J. Hill #include <asm/setup.h>
2898b67c37SSteven J. Hill #include <asm/traps.h>
2939b8d525SRalf Baechle 
30a7057270SAndrew Bresticker #include <dt-bindings/interrupt-controller/mips-gic.h>
31a7057270SAndrew Bresticker 
32b11d4c1fSPaul Burton #define GIC_MAX_INTRS		256
33aa493737SPaul Burton #define GIC_MAX_LONGS		BITS_TO_LONGS(GIC_MAX_INTRS)
3498b67c37SSteven J. Hill 
35b11d4c1fSPaul Burton /* Add 2 to convert GIC CPU pin to core interrupt */
36b11d4c1fSPaul Burton #define GIC_CPU_PIN_OFFSET	2
37822350bcSJeffrey Deans 
38b11d4c1fSPaul Burton /* Mapped interrupt to pin X, then GIC will generate the vector (X+1). */
39b11d4c1fSPaul Burton #define GIC_PIN_TO_VEC_OFFSET	1
402af70a96SQais Yousef 
41b11d4c1fSPaul Burton /* Convert between local/shared IRQ number and GIC HW IRQ number. */
42b11d4c1fSPaul Burton #define GIC_LOCAL_HWIRQ_BASE	0
43b11d4c1fSPaul Burton #define GIC_LOCAL_TO_HWIRQ(x)	(GIC_LOCAL_HWIRQ_BASE + (x))
44b11d4c1fSPaul Burton #define GIC_HWIRQ_TO_LOCAL(x)	((x) - GIC_LOCAL_HWIRQ_BASE)
45b11d4c1fSPaul Burton #define GIC_SHARED_HWIRQ_BASE	GIC_NUM_LOCAL_INTRS
46b11d4c1fSPaul Burton #define GIC_SHARED_TO_HWIRQ(x)	(GIC_SHARED_HWIRQ_BASE + (x))
47b11d4c1fSPaul Burton #define GIC_HWIRQ_TO_SHARED(x)	((x) - GIC_SHARED_HWIRQ_BASE)
48b11d4c1fSPaul Burton 
49582e2b4aSPaul Burton void __iomem *mips_gic_base;
500b271f56SSteven J. Hill 
51b0e453ffSWei Yongjun static DEFINE_PER_CPU_READ_MOSTLY(unsigned long[GIC_MAX_LONGS], pcpu_masks);
52822350bcSJeffrey Deans 
5395150ae8SAndrew Bresticker static DEFINE_SPINLOCK(gic_lock);
54c49581a4SAndrew Bresticker static struct irq_domain *gic_irq_domain;
552af70a96SQais Yousef static struct irq_domain *gic_ipi_domain;
56fbd55241SAndrew Bresticker static int gic_shared_intrs;
573263d085SAndrew Bresticker static unsigned int gic_cpu_pin;
581b6af71aSJames Hogan static unsigned int timer_cpu_pin;
594a6a3ea3SAndrew Bresticker static struct irq_chip gic_level_irq_controller, gic_edge_irq_controller;
6061dc367eSPaul Burton static DECLARE_BITMAP(ipi_resrv, GIC_MAX_INTRS);
6161dc367eSPaul Burton static DECLARE_BITMAP(ipi_available, GIC_MAX_INTRS);
6239b8d525SRalf Baechle 
63da61fcf9SPaul Burton static struct gic_all_vpes_chip_data {
64da61fcf9SPaul Burton 	u32	map;
65da61fcf9SPaul Burton 	bool	mask;
66da61fcf9SPaul Burton } gic_all_vpes_chip_data[GIC_NUM_LOCAL_INTRS];
67da61fcf9SPaul Burton 
687778c4b2SPaul Burton static void gic_clear_pcpu_masks(unsigned int intr)
695f68fea0SAndrew Bresticker {
707778c4b2SPaul Burton 	unsigned int i;
715f68fea0SAndrew Bresticker 
727778c4b2SPaul Burton 	/* Clear the interrupt's bit in all pcpu_masks */
737778c4b2SPaul Burton 	for_each_possible_cpu(i)
747778c4b2SPaul Burton 		clear_bit(intr, per_cpu_ptr(pcpu_masks, i));
75835d2b45SPaul Burton }
76835d2b45SPaul Burton 
77e9de688dSAndrew Bresticker static bool gic_local_irq_is_routable(int intr)
78e9de688dSAndrew Bresticker {
79e9de688dSAndrew Bresticker 	u32 vpe_ctl;
80e9de688dSAndrew Bresticker 
81e9de688dSAndrew Bresticker 	/* All local interrupts are routable in EIC mode. */
82e9de688dSAndrew Bresticker 	if (cpu_has_veic)
83e9de688dSAndrew Bresticker 		return true;
84e9de688dSAndrew Bresticker 
850d0cf58cSPaul Burton 	vpe_ctl = read_gic_vl_ctl();
86e9de688dSAndrew Bresticker 	switch (intr) {
87e9de688dSAndrew Bresticker 	case GIC_LOCAL_INT_TIMER:
880d0cf58cSPaul Burton 		return vpe_ctl & GIC_VX_CTL_TIMER_ROUTABLE;
89e9de688dSAndrew Bresticker 	case GIC_LOCAL_INT_PERFCTR:
900d0cf58cSPaul Burton 		return vpe_ctl & GIC_VX_CTL_PERFCNT_ROUTABLE;
91e9de688dSAndrew Bresticker 	case GIC_LOCAL_INT_FDC:
920d0cf58cSPaul Burton 		return vpe_ctl & GIC_VX_CTL_FDC_ROUTABLE;
93e9de688dSAndrew Bresticker 	case GIC_LOCAL_INT_SWINT0:
94e9de688dSAndrew Bresticker 	case GIC_LOCAL_INT_SWINT1:
950d0cf58cSPaul Burton 		return vpe_ctl & GIC_VX_CTL_SWINT_ROUTABLE;
96e9de688dSAndrew Bresticker 	default:
97e9de688dSAndrew Bresticker 		return true;
98e9de688dSAndrew Bresticker 	}
99e9de688dSAndrew Bresticker }
100e9de688dSAndrew Bresticker 
1013263d085SAndrew Bresticker static void gic_bind_eic_interrupt(int irq, int set)
10298b67c37SSteven J. Hill {
10398b67c37SSteven J. Hill 	/* Convert irq vector # to hw int # */
10498b67c37SSteven J. Hill 	irq -= GIC_PIN_TO_VEC_OFFSET;
10598b67c37SSteven J. Hill 
10698b67c37SSteven J. Hill 	/* Set irq to use shadow set */
1070d0cf58cSPaul Burton 	write_gic_vl_eic_shadow_set(irq, set);
10898b67c37SSteven J. Hill }
10998b67c37SSteven J. Hill 
110bb11cff3SQais Yousef static void gic_send_ipi(struct irq_data *d, unsigned int cpu)
11139b8d525SRalf Baechle {
112bb11cff3SQais Yousef 	irq_hw_number_t hwirq = GIC_HWIRQ_TO_SHARED(irqd_to_hwirq(d));
113bb11cff3SQais Yousef 
1143680746aSPaul Burton 	write_gic_wedge(GIC_WEDGE_RW | hwirq);
11539b8d525SRalf Baechle }
11639b8d525SRalf Baechle 
117e9de688dSAndrew Bresticker int gic_get_c0_compare_int(void)
118e9de688dSAndrew Bresticker {
119e9de688dSAndrew Bresticker 	if (!gic_local_irq_is_routable(GIC_LOCAL_INT_TIMER))
120e9de688dSAndrew Bresticker 		return MIPS_CPU_IRQ_BASE + cp0_compare_irq;
121e9de688dSAndrew Bresticker 	return irq_create_mapping(gic_irq_domain,
122e9de688dSAndrew Bresticker 				  GIC_LOCAL_TO_HWIRQ(GIC_LOCAL_INT_TIMER));
123e9de688dSAndrew Bresticker }
124e9de688dSAndrew Bresticker 
125e9de688dSAndrew Bresticker int gic_get_c0_perfcount_int(void)
126e9de688dSAndrew Bresticker {
127e9de688dSAndrew Bresticker 	if (!gic_local_irq_is_routable(GIC_LOCAL_INT_PERFCTR)) {
1287e3e6cb2SJames Hogan 		/* Is the performance counter shared with the timer? */
129e9de688dSAndrew Bresticker 		if (cp0_perfcount_irq < 0)
130e9de688dSAndrew Bresticker 			return -1;
131e9de688dSAndrew Bresticker 		return MIPS_CPU_IRQ_BASE + cp0_perfcount_irq;
132e9de688dSAndrew Bresticker 	}
133e9de688dSAndrew Bresticker 	return irq_create_mapping(gic_irq_domain,
134e9de688dSAndrew Bresticker 				  GIC_LOCAL_TO_HWIRQ(GIC_LOCAL_INT_PERFCTR));
135e9de688dSAndrew Bresticker }
136e9de688dSAndrew Bresticker 
1376429e2b6SJames Hogan int gic_get_c0_fdc_int(void)
1386429e2b6SJames Hogan {
1396429e2b6SJames Hogan 	if (!gic_local_irq_is_routable(GIC_LOCAL_INT_FDC)) {
1406429e2b6SJames Hogan 		/* Is the FDC IRQ even present? */
1416429e2b6SJames Hogan 		if (cp0_fdc_irq < 0)
1426429e2b6SJames Hogan 			return -1;
1436429e2b6SJames Hogan 		return MIPS_CPU_IRQ_BASE + cp0_fdc_irq;
1446429e2b6SJames Hogan 	}
1456429e2b6SJames Hogan 
1466429e2b6SJames Hogan 	return irq_create_mapping(gic_irq_domain,
1476429e2b6SJames Hogan 				  GIC_LOCAL_TO_HWIRQ(GIC_LOCAL_INT_FDC));
1486429e2b6SJames Hogan }
1496429e2b6SJames Hogan 
1501b3ed367SRabin Vincent static void gic_handle_shared_int(bool chained)
15139b8d525SRalf Baechle {
152046a6ee2SMarc Zyngier 	unsigned int intr;
1538f5ee79cSAndrew Bresticker 	unsigned long *pcpu_mask;
1548f5ee79cSAndrew Bresticker 	DECLARE_BITMAP(pending, GIC_MAX_INTRS);
15539b8d525SRalf Baechle 
15639b8d525SRalf Baechle 	/* Get per-cpu bitmaps */
157aa493737SPaul Burton 	pcpu_mask = this_cpu_ptr(pcpu_masks);
15839b8d525SRalf Baechle 
1597778c4b2SPaul Burton 	if (mips_cm_is64)
160e98fcb2aSPaul Burton 		__ioread64_copy(pending, addr_gic_pend(),
161e98fcb2aSPaul Burton 				DIV_ROUND_UP(gic_shared_intrs, 64));
1627778c4b2SPaul Burton 	else
163e98fcb2aSPaul Burton 		__ioread32_copy(pending, addr_gic_pend(),
164e98fcb2aSPaul Burton 				DIV_ROUND_UP(gic_shared_intrs, 32));
16539b8d525SRalf Baechle 
166fbd55241SAndrew Bresticker 	bitmap_and(pending, pending, pcpu_mask, gic_shared_intrs);
16739b8d525SRalf Baechle 
168cae750baSPaul Burton 	for_each_set_bit(intr, pending, gic_shared_intrs) {
1691b3ed367SRabin Vincent 		if (chained)
170046a6ee2SMarc Zyngier 			generic_handle_domain_irq(gic_irq_domain,
171046a6ee2SMarc Zyngier 						  GIC_SHARED_TO_HWIRQ(intr));
1721b3ed367SRabin Vincent 		else
1731fee9db9SMarc Zyngier 			do_domain_IRQ(gic_irq_domain,
1741fee9db9SMarc Zyngier 				      GIC_SHARED_TO_HWIRQ(intr));
175d7eb4f2eSQais Yousef 	}
17639b8d525SRalf Baechle }
17739b8d525SRalf Baechle 
178161d049eSThomas Gleixner static void gic_mask_irq(struct irq_data *d)
17939b8d525SRalf Baechle {
1807778c4b2SPaul Burton 	unsigned int intr = GIC_HWIRQ_TO_SHARED(d->hwirq);
1817778c4b2SPaul Burton 
18290019f8fSPaul Burton 	write_gic_rmask(intr);
1837778c4b2SPaul Burton 	gic_clear_pcpu_masks(intr);
18439b8d525SRalf Baechle }
18539b8d525SRalf Baechle 
186161d049eSThomas Gleixner static void gic_unmask_irq(struct irq_data *d)
18739b8d525SRalf Baechle {
1887778c4b2SPaul Burton 	unsigned int intr = GIC_HWIRQ_TO_SHARED(d->hwirq);
1897778c4b2SPaul Burton 	unsigned int cpu;
1907778c4b2SPaul Burton 
19190019f8fSPaul Burton 	write_gic_smask(intr);
1927778c4b2SPaul Burton 
1937778c4b2SPaul Burton 	gic_clear_pcpu_masks(intr);
194d9f82930SPaul Burton 	cpu = cpumask_first(irq_data_get_effective_affinity_mask(d));
1957778c4b2SPaul Burton 	set_bit(intr, per_cpu_ptr(pcpu_masks, cpu));
19639b8d525SRalf Baechle }
19739b8d525SRalf Baechle 
1985561c9e4SAndrew Bresticker static void gic_ack_irq(struct irq_data *d)
1995561c9e4SAndrew Bresticker {
200e9de688dSAndrew Bresticker 	unsigned int irq = GIC_HWIRQ_TO_SHARED(d->hwirq);
201c49581a4SAndrew Bresticker 
2023680746aSPaul Burton 	write_gic_wedge(irq);
2035561c9e4SAndrew Bresticker }
2045561c9e4SAndrew Bresticker 
20595150ae8SAndrew Bresticker static int gic_set_type(struct irq_data *d, unsigned int type)
20695150ae8SAndrew Bresticker {
2075af3e93eSPaul Burton 	unsigned int irq, pol, trig, dual;
20895150ae8SAndrew Bresticker 	unsigned long flags;
2095af3e93eSPaul Burton 
2105af3e93eSPaul Burton 	irq = GIC_HWIRQ_TO_SHARED(d->hwirq);
21139b8d525SRalf Baechle 
21295150ae8SAndrew Bresticker 	spin_lock_irqsave(&gic_lock, flags);
21395150ae8SAndrew Bresticker 	switch (type & IRQ_TYPE_SENSE_MASK) {
21495150ae8SAndrew Bresticker 	case IRQ_TYPE_EDGE_FALLING:
2155af3e93eSPaul Burton 		pol = GIC_POL_FALLING_EDGE;
2165af3e93eSPaul Burton 		trig = GIC_TRIG_EDGE;
2175af3e93eSPaul Burton 		dual = GIC_DUAL_SINGLE;
21895150ae8SAndrew Bresticker 		break;
21995150ae8SAndrew Bresticker 	case IRQ_TYPE_EDGE_RISING:
2205af3e93eSPaul Burton 		pol = GIC_POL_RISING_EDGE;
2215af3e93eSPaul Burton 		trig = GIC_TRIG_EDGE;
2225af3e93eSPaul Burton 		dual = GIC_DUAL_SINGLE;
22395150ae8SAndrew Bresticker 		break;
22495150ae8SAndrew Bresticker 	case IRQ_TYPE_EDGE_BOTH:
2255af3e93eSPaul Burton 		pol = 0; /* Doesn't matter */
2265af3e93eSPaul Burton 		trig = GIC_TRIG_EDGE;
2275af3e93eSPaul Burton 		dual = GIC_DUAL_DUAL;
22895150ae8SAndrew Bresticker 		break;
22995150ae8SAndrew Bresticker 	case IRQ_TYPE_LEVEL_LOW:
2305af3e93eSPaul Burton 		pol = GIC_POL_ACTIVE_LOW;
2315af3e93eSPaul Burton 		trig = GIC_TRIG_LEVEL;
2325af3e93eSPaul Burton 		dual = GIC_DUAL_SINGLE;
23395150ae8SAndrew Bresticker 		break;
23495150ae8SAndrew Bresticker 	case IRQ_TYPE_LEVEL_HIGH:
23595150ae8SAndrew Bresticker 	default:
2365af3e93eSPaul Burton 		pol = GIC_POL_ACTIVE_HIGH;
2375af3e93eSPaul Burton 		trig = GIC_TRIG_LEVEL;
2385af3e93eSPaul Burton 		dual = GIC_DUAL_SINGLE;
23995150ae8SAndrew Bresticker 		break;
24095150ae8SAndrew Bresticker 	}
24195150ae8SAndrew Bresticker 
2425af3e93eSPaul Burton 	change_gic_pol(irq, pol);
2435af3e93eSPaul Burton 	change_gic_trig(irq, trig);
2445af3e93eSPaul Burton 	change_gic_dual(irq, dual);
2455af3e93eSPaul Burton 
2465af3e93eSPaul Burton 	if (trig == GIC_TRIG_EDGE)
247a595fc51SThomas Gleixner 		irq_set_chip_handler_name_locked(d, &gic_edge_irq_controller,
2484a6a3ea3SAndrew Bresticker 						 handle_edge_irq, NULL);
249a595fc51SThomas Gleixner 	else
250a595fc51SThomas Gleixner 		irq_set_chip_handler_name_locked(d, &gic_level_irq_controller,
2514a6a3ea3SAndrew Bresticker 						 handle_level_irq, NULL);
25295150ae8SAndrew Bresticker 	spin_unlock_irqrestore(&gic_lock, flags);
25395150ae8SAndrew Bresticker 
25495150ae8SAndrew Bresticker 	return 0;
25595150ae8SAndrew Bresticker }
25695150ae8SAndrew Bresticker 
25795150ae8SAndrew Bresticker #ifdef CONFIG_SMP
258161d049eSThomas Gleixner static int gic_set_affinity(struct irq_data *d, const struct cpumask *cpumask,
259161d049eSThomas Gleixner 			    bool force)
26039b8d525SRalf Baechle {
261e9de688dSAndrew Bresticker 	unsigned int irq = GIC_HWIRQ_TO_SHARED(d->hwirq);
26239b8d525SRalf Baechle 	unsigned long flags;
26307df8bfeSPaul Burton 	unsigned int cpu;
26439b8d525SRalf Baechle 
26507df8bfeSPaul Burton 	cpu = cpumask_first_and(cpumask, cpu_online_mask);
26607df8bfeSPaul Burton 	if (cpu >= NR_CPUS)
26714d160abSAndrew Bresticker 		return -EINVAL;
26839b8d525SRalf Baechle 
26939b8d525SRalf Baechle 	/* Assumption : cpumask refers to a single CPU */
27039b8d525SRalf Baechle 	spin_lock_irqsave(&gic_lock, flags);
271c214c035STony Wu 
27239b8d525SRalf Baechle 	/* Re-route this IRQ */
27307df8bfeSPaul Burton 	write_gic_map_vp(irq, BIT(mips_cm_vp_id(cpu)));
27439b8d525SRalf Baechle 
27539b8d525SRalf Baechle 	/* Update the pcpu_masks */
2767778c4b2SPaul Burton 	gic_clear_pcpu_masks(irq);
2777778c4b2SPaul Burton 	if (read_gic_mask(irq))
27807df8bfeSPaul Burton 		set_bit(irq, per_cpu_ptr(pcpu_masks, cpu));
27939b8d525SRalf Baechle 
28018416e45SMarc Zyngier 	irq_data_update_effective_affinity(d, cpumask_of(cpu));
28139b8d525SRalf Baechle 	spin_unlock_irqrestore(&gic_lock, flags);
28239b8d525SRalf Baechle 
2837f15a648SPaul Burton 	return IRQ_SET_MASK_OK;
28439b8d525SRalf Baechle }
28539b8d525SRalf Baechle #endif
28639b8d525SRalf Baechle 
2874a6a3ea3SAndrew Bresticker static struct irq_chip gic_level_irq_controller = {
2884a6a3ea3SAndrew Bresticker 	.name			=	"MIPS GIC",
2894a6a3ea3SAndrew Bresticker 	.irq_mask		=	gic_mask_irq,
2904a6a3ea3SAndrew Bresticker 	.irq_unmask		=	gic_unmask_irq,
2914a6a3ea3SAndrew Bresticker 	.irq_set_type		=	gic_set_type,
2924a6a3ea3SAndrew Bresticker #ifdef CONFIG_SMP
2934a6a3ea3SAndrew Bresticker 	.irq_set_affinity	=	gic_set_affinity,
2944a6a3ea3SAndrew Bresticker #endif
2954a6a3ea3SAndrew Bresticker };
2964a6a3ea3SAndrew Bresticker 
2974a6a3ea3SAndrew Bresticker static struct irq_chip gic_edge_irq_controller = {
29839b8d525SRalf Baechle 	.name			=	"MIPS GIC",
2995561c9e4SAndrew Bresticker 	.irq_ack		=	gic_ack_irq,
300161d049eSThomas Gleixner 	.irq_mask		=	gic_mask_irq,
301161d049eSThomas Gleixner 	.irq_unmask		=	gic_unmask_irq,
30295150ae8SAndrew Bresticker 	.irq_set_type		=	gic_set_type,
30339b8d525SRalf Baechle #ifdef CONFIG_SMP
304161d049eSThomas Gleixner 	.irq_set_affinity	=	gic_set_affinity,
30539b8d525SRalf Baechle #endif
306bb11cff3SQais Yousef 	.ipi_send_single	=	gic_send_ipi,
30739b8d525SRalf Baechle };
30839b8d525SRalf Baechle 
3091b3ed367SRabin Vincent static void gic_handle_local_int(bool chained)
310e9de688dSAndrew Bresticker {
311e9de688dSAndrew Bresticker 	unsigned long pending, masked;
312046a6ee2SMarc Zyngier 	unsigned int intr;
313e9de688dSAndrew Bresticker 
3149da3c645SPaul Burton 	pending = read_gic_vl_pend();
3159da3c645SPaul Burton 	masked = read_gic_vl_mask();
316e9de688dSAndrew Bresticker 
317e9de688dSAndrew Bresticker 	bitmap_and(&pending, &pending, &masked, GIC_NUM_LOCAL_INTRS);
318e9de688dSAndrew Bresticker 
3190f4ed158SPaul Burton 	for_each_set_bit(intr, &pending, GIC_NUM_LOCAL_INTRS) {
3201b3ed367SRabin Vincent 		if (chained)
321046a6ee2SMarc Zyngier 			generic_handle_domain_irq(gic_irq_domain,
322046a6ee2SMarc Zyngier 						  GIC_LOCAL_TO_HWIRQ(intr));
3231b3ed367SRabin Vincent 		else
3241fee9db9SMarc Zyngier 			do_domain_IRQ(gic_irq_domain,
3251fee9db9SMarc Zyngier 				      GIC_LOCAL_TO_HWIRQ(intr));
326d7eb4f2eSQais Yousef 	}
327e9de688dSAndrew Bresticker }
328e9de688dSAndrew Bresticker 
329e9de688dSAndrew Bresticker static void gic_mask_local_irq(struct irq_data *d)
330e9de688dSAndrew Bresticker {
331e9de688dSAndrew Bresticker 	int intr = GIC_HWIRQ_TO_LOCAL(d->hwirq);
332e9de688dSAndrew Bresticker 
3339da3c645SPaul Burton 	write_gic_vl_rmask(BIT(intr));
334e9de688dSAndrew Bresticker }
335e9de688dSAndrew Bresticker 
336e9de688dSAndrew Bresticker static void gic_unmask_local_irq(struct irq_data *d)
337e9de688dSAndrew Bresticker {
338e9de688dSAndrew Bresticker 	int intr = GIC_HWIRQ_TO_LOCAL(d->hwirq);
339e9de688dSAndrew Bresticker 
3409da3c645SPaul Burton 	write_gic_vl_smask(BIT(intr));
341e9de688dSAndrew Bresticker }
342e9de688dSAndrew Bresticker 
343e9de688dSAndrew Bresticker static struct irq_chip gic_local_irq_controller = {
344e9de688dSAndrew Bresticker 	.name			=	"MIPS GIC Local",
345e9de688dSAndrew Bresticker 	.irq_mask		=	gic_mask_local_irq,
346e9de688dSAndrew Bresticker 	.irq_unmask		=	gic_unmask_local_irq,
347e9de688dSAndrew Bresticker };
348e9de688dSAndrew Bresticker 
349e9de688dSAndrew Bresticker static void gic_mask_local_irq_all_vpes(struct irq_data *d)
350e9de688dSAndrew Bresticker {
351da61fcf9SPaul Burton 	struct gic_all_vpes_chip_data *cd;
352e9de688dSAndrew Bresticker 	unsigned long flags;
353da61fcf9SPaul Burton 	int intr, cpu;
354da61fcf9SPaul Burton 
355da61fcf9SPaul Burton 	intr = GIC_HWIRQ_TO_LOCAL(d->hwirq);
356da61fcf9SPaul Burton 	cd = irq_data_get_irq_chip_data(d);
357da61fcf9SPaul Burton 	cd->mask = false;
358e9de688dSAndrew Bresticker 
359e9de688dSAndrew Bresticker 	spin_lock_irqsave(&gic_lock, flags);
360da61fcf9SPaul Burton 	for_each_online_cpu(cpu) {
361da61fcf9SPaul Burton 		write_gic_vl_other(mips_cm_vp_id(cpu));
3629da3c645SPaul Burton 		write_gic_vo_rmask(BIT(intr));
363e9de688dSAndrew Bresticker 	}
364e9de688dSAndrew Bresticker 	spin_unlock_irqrestore(&gic_lock, flags);
365e9de688dSAndrew Bresticker }
366e9de688dSAndrew Bresticker 
367e9de688dSAndrew Bresticker static void gic_unmask_local_irq_all_vpes(struct irq_data *d)
368e9de688dSAndrew Bresticker {
369da61fcf9SPaul Burton 	struct gic_all_vpes_chip_data *cd;
370e9de688dSAndrew Bresticker 	unsigned long flags;
371da61fcf9SPaul Burton 	int intr, cpu;
372da61fcf9SPaul Burton 
373da61fcf9SPaul Burton 	intr = GIC_HWIRQ_TO_LOCAL(d->hwirq);
374da61fcf9SPaul Burton 	cd = irq_data_get_irq_chip_data(d);
375da61fcf9SPaul Burton 	cd->mask = true;
376e9de688dSAndrew Bresticker 
377e9de688dSAndrew Bresticker 	spin_lock_irqsave(&gic_lock, flags);
378da61fcf9SPaul Burton 	for_each_online_cpu(cpu) {
379da61fcf9SPaul Burton 		write_gic_vl_other(mips_cm_vp_id(cpu));
3809da3c645SPaul Burton 		write_gic_vo_smask(BIT(intr));
381e9de688dSAndrew Bresticker 	}
382e9de688dSAndrew Bresticker 	spin_unlock_irqrestore(&gic_lock, flags);
383e9de688dSAndrew Bresticker }
384e9de688dSAndrew Bresticker 
385dd098a0eSMarc Zyngier static void gic_all_vpes_irq_cpu_online(void)
386da61fcf9SPaul Burton {
387dd098a0eSMarc Zyngier 	static const unsigned int local_intrs[] = {
388dd098a0eSMarc Zyngier 		GIC_LOCAL_INT_TIMER,
389dd098a0eSMarc Zyngier 		GIC_LOCAL_INT_PERFCTR,
390dd098a0eSMarc Zyngier 		GIC_LOCAL_INT_FDC,
391dd098a0eSMarc Zyngier 	};
392dd098a0eSMarc Zyngier 	unsigned long flags;
393dd098a0eSMarc Zyngier 	int i;
394dd098a0eSMarc Zyngier 
395dd098a0eSMarc Zyngier 	spin_lock_irqsave(&gic_lock, flags);
396dd098a0eSMarc Zyngier 
397dd098a0eSMarc Zyngier 	for (i = 0; i < ARRAY_SIZE(local_intrs); i++) {
398dd098a0eSMarc Zyngier 		unsigned int intr = local_intrs[i];
399da61fcf9SPaul Burton 		struct gic_all_vpes_chip_data *cd;
400da61fcf9SPaul Burton 
401dd098a0eSMarc Zyngier 		cd = &gic_all_vpes_chip_data[intr];
4026d4d367dSPaul Burton 		write_gic_vl_map(mips_gic_vx_map_reg(intr), cd->map);
403da61fcf9SPaul Burton 		if (cd->mask)
404da61fcf9SPaul Burton 			write_gic_vl_smask(BIT(intr));
405da61fcf9SPaul Burton 	}
406da61fcf9SPaul Burton 
407dd098a0eSMarc Zyngier 	spin_unlock_irqrestore(&gic_lock, flags);
408dd098a0eSMarc Zyngier }
409dd098a0eSMarc Zyngier 
410e9de688dSAndrew Bresticker static struct irq_chip gic_all_vpes_local_irq_controller = {
411e9de688dSAndrew Bresticker 	.name			= "MIPS GIC Local",
412e9de688dSAndrew Bresticker 	.irq_mask		= gic_mask_local_irq_all_vpes,
413e9de688dSAndrew Bresticker 	.irq_unmask		= gic_unmask_local_irq_all_vpes,
414e9de688dSAndrew Bresticker };
415e9de688dSAndrew Bresticker 
41618743d27SAndrew Bresticker static void __gic_irq_dispatch(void)
41739b8d525SRalf Baechle {
4181b3ed367SRabin Vincent 	gic_handle_local_int(false);
4191b3ed367SRabin Vincent 	gic_handle_shared_int(false);
42018743d27SAndrew Bresticker }
42118743d27SAndrew Bresticker 
422bd0b9ac4SThomas Gleixner static void gic_irq_dispatch(struct irq_desc *desc)
42318743d27SAndrew Bresticker {
4241b3ed367SRabin Vincent 	gic_handle_local_int(true);
4251b3ed367SRabin Vincent 	gic_handle_shared_int(true);
42618743d27SAndrew Bresticker }
42718743d27SAndrew Bresticker 
428e9de688dSAndrew Bresticker static int gic_shared_irq_domain_map(struct irq_domain *d, unsigned int virq,
4297778c4b2SPaul Burton 				     irq_hw_number_t hw, unsigned int cpu)
430e9de688dSAndrew Bresticker {
431e9de688dSAndrew Bresticker 	int intr = GIC_HWIRQ_TO_SHARED(hw);
432d9f82930SPaul Burton 	struct irq_data *data;
433c49581a4SAndrew Bresticker 	unsigned long flags;
434c49581a4SAndrew Bresticker 
435d9f82930SPaul Burton 	data = irq_get_irq_data(virq);
436d9f82930SPaul Burton 
437c49581a4SAndrew Bresticker 	spin_lock_irqsave(&gic_lock, flags);
438d3e8cf44SPaul Burton 	write_gic_map_pin(intr, GIC_MAP_PIN_MAP_TO_PIN | gic_cpu_pin);
4397778c4b2SPaul Burton 	write_gic_map_vp(intr, BIT(mips_cm_vp_id(cpu)));
440d9f82930SPaul Burton 	irq_data_update_effective_affinity(data, cpumask_of(cpu));
441c49581a4SAndrew Bresticker 	spin_unlock_irqrestore(&gic_lock, flags);
442c49581a4SAndrew Bresticker 
443c49581a4SAndrew Bresticker 	return 0;
444c49581a4SAndrew Bresticker }
445c49581a4SAndrew Bresticker 
446b87281e7SPaul Burton static int gic_irq_domain_xlate(struct irq_domain *d, struct device_node *ctrlr,
447c98c1822SQais Yousef 				const u32 *intspec, unsigned int intsize,
448c98c1822SQais Yousef 				irq_hw_number_t *out_hwirq,
449c98c1822SQais Yousef 				unsigned int *out_type)
450c98c1822SQais Yousef {
451c98c1822SQais Yousef 	if (intsize != 3)
452c98c1822SQais Yousef 		return -EINVAL;
453c98c1822SQais Yousef 
454c98c1822SQais Yousef 	if (intspec[0] == GIC_SHARED)
455c98c1822SQais Yousef 		*out_hwirq = GIC_SHARED_TO_HWIRQ(intspec[1]);
456c98c1822SQais Yousef 	else if (intspec[0] == GIC_LOCAL)
457c98c1822SQais Yousef 		*out_hwirq = GIC_LOCAL_TO_HWIRQ(intspec[1]);
458c98c1822SQais Yousef 	else
459c98c1822SQais Yousef 		return -EINVAL;
460c98c1822SQais Yousef 	*out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
461c98c1822SQais Yousef 
462c98c1822SQais Yousef 	return 0;
463c98c1822SQais Yousef }
464c98c1822SQais Yousef 
4658ada00a6SMatt Redfearn static int gic_irq_domain_map(struct irq_domain *d, unsigned int virq,
4668ada00a6SMatt Redfearn 			      irq_hw_number_t hwirq)
467c98c1822SQais Yousef {
468da61fcf9SPaul Burton 	struct gic_all_vpes_chip_data *cd;
46963b746b1SPaul Burton 	unsigned long flags;
47063b746b1SPaul Burton 	unsigned int intr;
471da61fcf9SPaul Burton 	int err, cpu;
47263b746b1SPaul Burton 	u32 map;
473c98c1822SQais Yousef 
4748ada00a6SMatt Redfearn 	if (hwirq >= GIC_SHARED_HWIRQ_BASE) {
475b87281e7SPaul Burton 		/* verify that shared irqs don't conflict with an IPI irq */
476b87281e7SPaul Burton 		if (test_bit(GIC_HWIRQ_TO_SHARED(hwirq), ipi_resrv))
477b87281e7SPaul Burton 			return -EBUSY;
478c98c1822SQais Yousef 
479b87281e7SPaul Burton 		err = irq_domain_set_hwirq_and_chip(d, virq, hwirq,
480b87281e7SPaul Burton 						    &gic_level_irq_controller,
481b87281e7SPaul Burton 						    NULL);
482b87281e7SPaul Burton 		if (err)
483b87281e7SPaul Burton 			return err;
484b87281e7SPaul Burton 
48518416e45SMarc Zyngier 		irqd_set_single_target(irq_desc_get_irq_data(irq_to_desc(virq)));
486b87281e7SPaul Burton 		return gic_shared_irq_domain_map(d, virq, hwirq, 0);
487c98c1822SQais Yousef 	}
488c98c1822SQais Yousef 
48963b746b1SPaul Burton 	intr = GIC_HWIRQ_TO_LOCAL(hwirq);
49063b746b1SPaul Burton 	map = GIC_MAP_PIN_MAP_TO_PIN | gic_cpu_pin;
49163b746b1SPaul Burton 
492dd098a0eSMarc Zyngier 	/*
493dd098a0eSMarc Zyngier 	 * If adding support for more per-cpu interrupts, keep the the
494dd098a0eSMarc Zyngier 	 * array in gic_all_vpes_irq_cpu_online() in sync.
495dd098a0eSMarc Zyngier 	 */
49663b746b1SPaul Burton 	switch (intr) {
497b87281e7SPaul Burton 	case GIC_LOCAL_INT_TIMER:
49863b746b1SPaul Burton 		/* CONFIG_MIPS_CMP workaround (see __gic_init) */
49963b746b1SPaul Burton 		map = GIC_MAP_PIN_MAP_TO_PIN | timer_cpu_pin;
500df561f66SGustavo A. R. Silva 		fallthrough;
501b87281e7SPaul Burton 	case GIC_LOCAL_INT_PERFCTR:
502b87281e7SPaul Burton 	case GIC_LOCAL_INT_FDC:
503b87281e7SPaul Burton 		/*
504b87281e7SPaul Burton 		 * HACK: These are all really percpu interrupts, but
505b87281e7SPaul Burton 		 * the rest of the MIPS kernel code does not use the
506b87281e7SPaul Burton 		 * percpu IRQ API for them.
507b87281e7SPaul Burton 		 */
508da61fcf9SPaul Burton 		cd = &gic_all_vpes_chip_data[intr];
509da61fcf9SPaul Burton 		cd->map = map;
510b87281e7SPaul Burton 		err = irq_domain_set_hwirq_and_chip(d, virq, hwirq,
511b87281e7SPaul Burton 						    &gic_all_vpes_local_irq_controller,
512da61fcf9SPaul Burton 						    cd);
513b87281e7SPaul Burton 		if (err)
514b87281e7SPaul Burton 			return err;
515b87281e7SPaul Burton 
516b87281e7SPaul Burton 		irq_set_handler(virq, handle_percpu_irq);
517b87281e7SPaul Burton 		break;
518b87281e7SPaul Burton 
519b87281e7SPaul Burton 	default:
520b87281e7SPaul Burton 		err = irq_domain_set_hwirq_and_chip(d, virq, hwirq,
521b87281e7SPaul Burton 						    &gic_local_irq_controller,
522b87281e7SPaul Burton 						    NULL);
523b87281e7SPaul Burton 		if (err)
524b87281e7SPaul Burton 			return err;
525b87281e7SPaul Burton 
526b87281e7SPaul Burton 		irq_set_handler(virq, handle_percpu_devid_irq);
527b87281e7SPaul Burton 		irq_set_percpu_devid(virq);
528b87281e7SPaul Burton 		break;
529c98c1822SQais Yousef 	}
530c98c1822SQais Yousef 
53163b746b1SPaul Burton 	if (!gic_local_irq_is_routable(intr))
53263b746b1SPaul Burton 		return -EPERM;
53363b746b1SPaul Burton 
53463b746b1SPaul Burton 	spin_lock_irqsave(&gic_lock, flags);
535da61fcf9SPaul Burton 	for_each_online_cpu(cpu) {
536da61fcf9SPaul Burton 		write_gic_vl_other(mips_cm_vp_id(cpu));
5376d4d367dSPaul Burton 		write_gic_vo_map(mips_gic_vx_map_reg(intr), map);
53863b746b1SPaul Burton 	}
53963b746b1SPaul Burton 	spin_unlock_irqrestore(&gic_lock, flags);
54063b746b1SPaul Burton 
54163b746b1SPaul Burton 	return 0;
542b87281e7SPaul Burton }
543b87281e7SPaul Burton 
5448ada00a6SMatt Redfearn static int gic_irq_domain_alloc(struct irq_domain *d, unsigned int virq,
5458ada00a6SMatt Redfearn 				unsigned int nr_irqs, void *arg)
5468ada00a6SMatt Redfearn {
5478ada00a6SMatt Redfearn 	struct irq_fwspec *fwspec = arg;
5488ada00a6SMatt Redfearn 	irq_hw_number_t hwirq;
5498ada00a6SMatt Redfearn 
5508ada00a6SMatt Redfearn 	if (fwspec->param[0] == GIC_SHARED)
5518ada00a6SMatt Redfearn 		hwirq = GIC_SHARED_TO_HWIRQ(fwspec->param[1]);
5528ada00a6SMatt Redfearn 	else
5538ada00a6SMatt Redfearn 		hwirq = GIC_LOCAL_TO_HWIRQ(fwspec->param[1]);
5548ada00a6SMatt Redfearn 
5558ada00a6SMatt Redfearn 	return gic_irq_domain_map(d, virq, hwirq);
5568ada00a6SMatt Redfearn }
5578ada00a6SMatt Redfearn 
558b87281e7SPaul Burton void gic_irq_domain_free(struct irq_domain *d, unsigned int virq,
559c98c1822SQais Yousef 			 unsigned int nr_irqs)
560c98c1822SQais Yousef {
561c98c1822SQais Yousef }
562c98c1822SQais Yousef 
563b87281e7SPaul Burton static const struct irq_domain_ops gic_irq_domain_ops = {
564b87281e7SPaul Burton 	.xlate = gic_irq_domain_xlate,
565b87281e7SPaul Burton 	.alloc = gic_irq_domain_alloc,
566b87281e7SPaul Burton 	.free = gic_irq_domain_free,
5678ada00a6SMatt Redfearn 	.map = gic_irq_domain_map,
5682af70a96SQais Yousef };
5692af70a96SQais Yousef 
5702af70a96SQais Yousef static int gic_ipi_domain_xlate(struct irq_domain *d, struct device_node *ctrlr,
5712af70a96SQais Yousef 				const u32 *intspec, unsigned int intsize,
5722af70a96SQais Yousef 				irq_hw_number_t *out_hwirq,
5732af70a96SQais Yousef 				unsigned int *out_type)
5742af70a96SQais Yousef {
5752af70a96SQais Yousef 	/*
5762af70a96SQais Yousef 	 * There's nothing to translate here. hwirq is dynamically allocated and
5772af70a96SQais Yousef 	 * the irq type is always edge triggered.
5782af70a96SQais Yousef 	 * */
5792af70a96SQais Yousef 	*out_hwirq = 0;
5802af70a96SQais Yousef 	*out_type = IRQ_TYPE_EDGE_RISING;
5812af70a96SQais Yousef 
5822af70a96SQais Yousef 	return 0;
5832af70a96SQais Yousef }
5842af70a96SQais Yousef 
5852af70a96SQais Yousef static int gic_ipi_domain_alloc(struct irq_domain *d, unsigned int virq,
5862af70a96SQais Yousef 				unsigned int nr_irqs, void *arg)
5872af70a96SQais Yousef {
5882af70a96SQais Yousef 	struct cpumask *ipimask = arg;
589b87281e7SPaul Burton 	irq_hw_number_t hwirq, base_hwirq;
590b87281e7SPaul Burton 	int cpu, ret, i;
5912af70a96SQais Yousef 
592b87281e7SPaul Burton 	base_hwirq = find_first_bit(ipi_available, gic_shared_intrs);
593b87281e7SPaul Burton 	if (base_hwirq == gic_shared_intrs)
594b87281e7SPaul Burton 		return -ENOMEM;
595b87281e7SPaul Burton 
596b87281e7SPaul Burton 	/* check that we have enough space */
597b87281e7SPaul Burton 	for (i = base_hwirq; i < nr_irqs; i++) {
598b87281e7SPaul Burton 		if (!test_bit(i, ipi_available))
599b87281e7SPaul Burton 			return -EBUSY;
600b87281e7SPaul Burton 	}
601b87281e7SPaul Burton 	bitmap_clear(ipi_available, base_hwirq, nr_irqs);
602b87281e7SPaul Burton 
603b87281e7SPaul Burton 	/* map the hwirq for each cpu consecutively */
604b87281e7SPaul Burton 	i = 0;
605b87281e7SPaul Burton 	for_each_cpu(cpu, ipimask) {
606b87281e7SPaul Burton 		hwirq = GIC_SHARED_TO_HWIRQ(base_hwirq + i);
607b87281e7SPaul Burton 
608b87281e7SPaul Burton 		ret = irq_domain_set_hwirq_and_chip(d, virq + i, hwirq,
609b87281e7SPaul Burton 						    &gic_edge_irq_controller,
610b87281e7SPaul Burton 						    NULL);
6112af70a96SQais Yousef 		if (ret)
612b87281e7SPaul Burton 			goto error;
6132af70a96SQais Yousef 
614b87281e7SPaul Burton 		ret = irq_domain_set_hwirq_and_chip(d->parent, virq + i, hwirq,
6152af70a96SQais Yousef 						    &gic_edge_irq_controller,
6162af70a96SQais Yousef 						    NULL);
6172af70a96SQais Yousef 		if (ret)
6182af70a96SQais Yousef 			goto error;
6192af70a96SQais Yousef 
6202af70a96SQais Yousef 		ret = irq_set_irq_type(virq + i, IRQ_TYPE_EDGE_RISING);
6212af70a96SQais Yousef 		if (ret)
6222af70a96SQais Yousef 			goto error;
623b87281e7SPaul Burton 
624b87281e7SPaul Burton 		ret = gic_shared_irq_domain_map(d, virq + i, hwirq, cpu);
625b87281e7SPaul Burton 		if (ret)
626b87281e7SPaul Burton 			goto error;
627b87281e7SPaul Burton 
628b87281e7SPaul Burton 		i++;
6292af70a96SQais Yousef 	}
6302af70a96SQais Yousef 
6312af70a96SQais Yousef 	return 0;
6322af70a96SQais Yousef error:
633b87281e7SPaul Burton 	bitmap_set(ipi_available, base_hwirq, nr_irqs);
6342af70a96SQais Yousef 	return ret;
6352af70a96SQais Yousef }
6362af70a96SQais Yousef 
637b0e453ffSWei Yongjun static void gic_ipi_domain_free(struct irq_domain *d, unsigned int virq,
6382af70a96SQais Yousef 				unsigned int nr_irqs)
6392af70a96SQais Yousef {
640b87281e7SPaul Burton 	irq_hw_number_t base_hwirq;
641b87281e7SPaul Burton 	struct irq_data *data;
642b87281e7SPaul Burton 
643b87281e7SPaul Burton 	data = irq_get_irq_data(virq);
644b87281e7SPaul Burton 	if (!data)
645b87281e7SPaul Burton 		return;
646b87281e7SPaul Burton 
647b87281e7SPaul Burton 	base_hwirq = GIC_HWIRQ_TO_SHARED(irqd_to_hwirq(data));
648b87281e7SPaul Burton 	bitmap_set(ipi_available, base_hwirq, nr_irqs);
6492af70a96SQais Yousef }
6502af70a96SQais Yousef 
651b0e453ffSWei Yongjun static int gic_ipi_domain_match(struct irq_domain *d, struct device_node *node,
6522af70a96SQais Yousef 				enum irq_domain_bus_token bus_token)
6532af70a96SQais Yousef {
6542af70a96SQais Yousef 	bool is_ipi;
6552af70a96SQais Yousef 
6562af70a96SQais Yousef 	switch (bus_token) {
6572af70a96SQais Yousef 	case DOMAIN_BUS_IPI:
6582af70a96SQais Yousef 		is_ipi = d->bus_token == bus_token;
659547aefc4SPaul Burton 		return (!node || to_of_node(d->fwnode) == node) && is_ipi;
6602af70a96SQais Yousef 		break;
6612af70a96SQais Yousef 	default:
6622af70a96SQais Yousef 		return 0;
6632af70a96SQais Yousef 	}
6642af70a96SQais Yousef }
6652af70a96SQais Yousef 
6660b7e815aSTobias Klauser static const struct irq_domain_ops gic_ipi_domain_ops = {
6672af70a96SQais Yousef 	.xlate = gic_ipi_domain_xlate,
6682af70a96SQais Yousef 	.alloc = gic_ipi_domain_alloc,
6692af70a96SQais Yousef 	.free = gic_ipi_domain_free,
6702af70a96SQais Yousef 	.match = gic_ipi_domain_match,
671c49581a4SAndrew Bresticker };
672c49581a4SAndrew Bresticker 
673da61fcf9SPaul Burton static int gic_cpu_startup(unsigned int cpu)
674da61fcf9SPaul Burton {
675890f6b55SPaul Burton 	/* Enable or disable EIC */
676890f6b55SPaul Burton 	change_gic_vl_ctl(GIC_VX_CTL_EIC,
677890f6b55SPaul Burton 			  cpu_has_veic ? GIC_VX_CTL_EIC : 0);
678890f6b55SPaul Burton 
67925ac19e1SPaul Burton 	/* Clear all local IRQ masks (ie. disable all local interrupts) */
68025ac19e1SPaul Burton 	write_gic_vl_rmask(~0);
68125ac19e1SPaul Burton 
682dd098a0eSMarc Zyngier 	/* Enable desired interrupts */
683dd098a0eSMarc Zyngier 	gic_all_vpes_irq_cpu_online();
684da61fcf9SPaul Burton 
685da61fcf9SPaul Burton 	return 0;
686da61fcf9SPaul Burton }
68739b8d525SRalf Baechle 
688fbea7541SPaul Burton static int __init gic_of_init(struct device_node *node,
689fbea7541SPaul Burton 			      struct device_node *parent)
69039b8d525SRalf Baechle {
69125c51dadSPaul Burton 	unsigned int cpu_vec, i, gicconfig, v[2], num_ipis;
692b2b2e584SPaul Burton 	unsigned long reserved;
693fbea7541SPaul Burton 	phys_addr_t gic_base;
694fbea7541SPaul Burton 	struct resource res;
695fbea7541SPaul Burton 	size_t gic_len;
69639b8d525SRalf Baechle 
697fbea7541SPaul Burton 	/* Find the first available CPU vector. */
698b2b2e584SPaul Burton 	i = 0;
699a08588eaSPaul Burton 	reserved = (C_SW0 | C_SW1) >> __ffs(C_SW0);
700fbea7541SPaul Burton 	while (!of_property_read_u32_index(node, "mti,reserved-cpu-vectors",
701fbea7541SPaul Burton 					   i++, &cpu_vec))
702fbea7541SPaul Burton 		reserved |= BIT(cpu_vec);
703c0a9f72cSAlex Smith 
704b2b2e584SPaul Burton 	cpu_vec = find_first_zero_bit(&reserved, hweight_long(ST0_IM));
705b2b2e584SPaul Burton 	if (cpu_vec == hweight_long(ST0_IM)) {
7061f19aee0SMatt Redfearn 		pr_err("No CPU vectors available\n");
707fbea7541SPaul Burton 		return -ENODEV;
708fbea7541SPaul Burton 	}
70939b8d525SRalf Baechle 
710fbea7541SPaul Burton 	if (of_address_to_resource(node, 0, &res)) {
711fbea7541SPaul Burton 		/*
712fbea7541SPaul Burton 		 * Probe the CM for the GIC base address if not specified
713fbea7541SPaul Burton 		 * in the device-tree.
714fbea7541SPaul Burton 		 */
715fbea7541SPaul Burton 		if (mips_cm_present()) {
716fbea7541SPaul Burton 			gic_base = read_gcr_gic_base() &
717fbea7541SPaul Burton 				~CM_GCR_GIC_BASE_GICEN;
718fbea7541SPaul Burton 			gic_len = 0x20000;
719666740fdSMatt Redfearn 			pr_warn("Using inherited base address %pa\n",
720666740fdSMatt Redfearn 				&gic_base);
721fbea7541SPaul Burton 		} else {
7221f19aee0SMatt Redfearn 			pr_err("Failed to get memory range\n");
723fbea7541SPaul Burton 			return -ENODEV;
724fbea7541SPaul Burton 		}
725fbea7541SPaul Burton 	} else {
726fbea7541SPaul Burton 		gic_base = res.start;
727fbea7541SPaul Burton 		gic_len = resource_size(&res);
728fbea7541SPaul Burton 	}
72939b8d525SRalf Baechle 
730fbea7541SPaul Burton 	if (mips_cm_present()) {
731fbea7541SPaul Burton 		write_gcr_gic_base(gic_base | CM_GCR_GIC_BASE_GICEN);
732fbea7541SPaul Burton 		/* Ensure GIC region is enabled before trying to access it */
733fbea7541SPaul Burton 		__sync();
734fbea7541SPaul Burton 	}
735fbea7541SPaul Burton 
7364bdc0d67SChristoph Hellwig 	mips_gic_base = ioremap(gic_base, gic_len);
73739b8d525SRalf Baechle 
7383680746aSPaul Burton 	gicconfig = read_gic_config();
739357a9c4bSGeert Uytterhoeven 	gic_shared_intrs = FIELD_GET(GIC_CONFIG_NUMINTERRUPTS, gicconfig);
7403680746aSPaul Burton 	gic_shared_intrs = (gic_shared_intrs + 1) * 8;
74139b8d525SRalf Baechle 
74239b8d525SRalf Baechle 	if (cpu_has_veic) {
74339b8d525SRalf Baechle 		/* Always use vector 1 in EIC mode */
74439b8d525SRalf Baechle 		gic_cpu_pin = 0;
7451b6af71aSJames Hogan 		timer_cpu_pin = gic_cpu_pin;
74639b8d525SRalf Baechle 		set_vi_handler(gic_cpu_pin + GIC_PIN_TO_VEC_OFFSET,
74739b8d525SRalf Baechle 			       __gic_irq_dispatch);
74839b8d525SRalf Baechle 	} else {
74939b8d525SRalf Baechle 		gic_cpu_pin = cpu_vec - GIC_CPU_PIN_OFFSET;
75039b8d525SRalf Baechle 		irq_set_chained_handler(MIPS_CPU_IRQ_BASE + cpu_vec,
75139b8d525SRalf Baechle 					gic_irq_dispatch);
7521b6af71aSJames Hogan 		/*
7531b6af71aSJames Hogan 		 * With the CMP implementation of SMP (deprecated), other CPUs
7541b6af71aSJames Hogan 		 * are started by the bootloader and put into a timer based
7551b6af71aSJames Hogan 		 * waiting poll loop. We must not re-route those CPU's local
7561b6af71aSJames Hogan 		 * timer interrupts as the wait instruction will never finish,
7571b6af71aSJames Hogan 		 * so just handle whatever CPU interrupt it is routed to by
7581b6af71aSJames Hogan 		 * default.
7591b6af71aSJames Hogan 		 *
7601b6af71aSJames Hogan 		 * This workaround should be removed when CMP support is
7611b6af71aSJames Hogan 		 * dropped.
7621b6af71aSJames Hogan 		 */
7631b6af71aSJames Hogan 		if (IS_ENABLED(CONFIG_MIPS_CMP) &&
7641b6af71aSJames Hogan 		    gic_local_irq_is_routable(GIC_LOCAL_INT_TIMER)) {
7650d0cf58cSPaul Burton 			timer_cpu_pin = read_gic_vl_timer_map() & GIC_MAP_PIN_MAP;
7661b6af71aSJames Hogan 			irq_set_chained_handler(MIPS_CPU_IRQ_BASE +
7671b6af71aSJames Hogan 						GIC_CPU_PIN_OFFSET +
7681b6af71aSJames Hogan 						timer_cpu_pin,
7691b6af71aSJames Hogan 						gic_irq_dispatch);
7701b6af71aSJames Hogan 		} else {
7711b6af71aSJames Hogan 			timer_cpu_pin = gic_cpu_pin;
7721b6af71aSJames Hogan 		}
77339b8d525SRalf Baechle 	}
77439b8d525SRalf Baechle 
775a7057270SAndrew Bresticker 	gic_irq_domain = irq_domain_add_simple(node, GIC_NUM_LOCAL_INTRS +
776fbea7541SPaul Burton 					       gic_shared_intrs, 0,
77739b8d525SRalf Baechle 					       &gic_irq_domain_ops, NULL);
778fbea7541SPaul Burton 	if (!gic_irq_domain) {
7791f19aee0SMatt Redfearn 		pr_err("Failed to add IRQ domain");
780fbea7541SPaul Burton 		return -ENXIO;
781fbea7541SPaul Burton 	}
78239b8d525SRalf Baechle 
7832af70a96SQais Yousef 	gic_ipi_domain = irq_domain_add_hierarchy(gic_irq_domain,
7842af70a96SQais Yousef 						  IRQ_DOMAIN_FLAG_IPI_PER_CPU,
7852af70a96SQais Yousef 						  GIC_NUM_LOCAL_INTRS + gic_shared_intrs,
7862af70a96SQais Yousef 						  node, &gic_ipi_domain_ops, NULL);
787fbea7541SPaul Burton 	if (!gic_ipi_domain) {
7881f19aee0SMatt Redfearn 		pr_err("Failed to add IPI domain");
789fbea7541SPaul Burton 		return -ENXIO;
790fbea7541SPaul Burton 	}
7912af70a96SQais Yousef 
79296f0d93aSMarc Zyngier 	irq_domain_update_bus_token(gic_ipi_domain, DOMAIN_BUS_IPI);
7932af70a96SQais Yousef 
79416a8083cSQais Yousef 	if (node &&
79516a8083cSQais Yousef 	    !of_property_read_u32_array(node, "mti,reserved-ipi-vectors", v, 2)) {
79616a8083cSQais Yousef 		bitmap_set(ipi_resrv, v[0], v[1]);
79716a8083cSQais Yousef 	} else {
79825c51dadSPaul Burton 		/*
79925c51dadSPaul Burton 		 * Reserve 2 interrupts per possible CPU/VP for use as IPIs,
80025c51dadSPaul Burton 		 * meeting the requirements of arch/mips SMP.
80125c51dadSPaul Burton 		 */
80225c51dadSPaul Burton 		num_ipis = 2 * num_possible_cpus();
80325c51dadSPaul Burton 		bitmap_set(ipi_resrv, gic_shared_intrs - num_ipis, num_ipis);
80416a8083cSQais Yousef 	}
8052af70a96SQais Yousef 
806f8dcd9e8SPaul Burton 	bitmap_copy(ipi_available, ipi_resrv, GIC_MAX_INTRS);
80787888bcbSPaul Burton 
80887888bcbSPaul Burton 	board_bind_eic_interrupt = &gic_bind_eic_interrupt;
80987888bcbSPaul Burton 
81087888bcbSPaul Burton 	/* Setup defaults */
81187888bcbSPaul Burton 	for (i = 0; i < gic_shared_intrs; i++) {
81287888bcbSPaul Burton 		change_gic_pol(i, GIC_POL_ACTIVE_HIGH);
81387888bcbSPaul Burton 		change_gic_trig(i, GIC_TRIG_LEVEL);
81490019f8fSPaul Burton 		write_gic_rmask(i);
81539b8d525SRalf Baechle 	}
816a7057270SAndrew Bresticker 
817da61fcf9SPaul Burton 	return cpuhp_setup_state(CPUHP_AP_IRQ_MIPS_GIC_STARTING,
818da61fcf9SPaul Burton 				 "irqchip/mips/gic:starting",
819da61fcf9SPaul Burton 				 gic_cpu_startup, NULL);
820a7057270SAndrew Bresticker }
821a7057270SAndrew Bresticker IRQCHIP_DECLARE(mips_gic, "mti,gic", gic_of_init);
822