xref: /linux/arch/mips/cavium-octeon/octeon-irq.c (revision d5dedd4507d307eb3f35f21b6e16f336fdc0d82a)
15b3b1688SDavid Daney /*
25b3b1688SDavid Daney  * This file is subject to the terms and conditions of the GNU General Public
35b3b1688SDavid Daney  * License.  See the file "COPYING" in the main directory of this archive
45b3b1688SDavid Daney  * for more details.
55b3b1688SDavid Daney  *
65b3b1688SDavid Daney  * Copyright (C) 2004-2008 Cavium Networks
75b3b1688SDavid Daney  */
85b3b1688SDavid Daney #include <linux/irq.h>
95b3b1688SDavid Daney #include <linux/interrupt.h>
105b3b1688SDavid Daney #include <linux/hardirq.h>
115b3b1688SDavid Daney 
125b3b1688SDavid Daney #include <asm/octeon/octeon.h>
135b3b1688SDavid Daney 
145b3b1688SDavid Daney DEFINE_RWLOCK(octeon_irq_ciu0_rwlock);
155b3b1688SDavid Daney DEFINE_RWLOCK(octeon_irq_ciu1_rwlock);
165b3b1688SDavid Daney DEFINE_SPINLOCK(octeon_irq_msi_lock);
175b3b1688SDavid Daney 
185b3b1688SDavid Daney static void octeon_irq_core_ack(unsigned int irq)
195b3b1688SDavid Daney {
205b3b1688SDavid Daney 	unsigned int bit = irq - OCTEON_IRQ_SW0;
215b3b1688SDavid Daney 	/*
225b3b1688SDavid Daney 	 * We don't need to disable IRQs to make these atomic since
235b3b1688SDavid Daney 	 * they are already disabled earlier in the low level
245b3b1688SDavid Daney 	 * interrupt code.
255b3b1688SDavid Daney 	 */
265b3b1688SDavid Daney 	clear_c0_status(0x100 << bit);
275b3b1688SDavid Daney 	/* The two user interrupts must be cleared manually. */
285b3b1688SDavid Daney 	if (bit < 2)
295b3b1688SDavid Daney 		clear_c0_cause(0x100 << bit);
305b3b1688SDavid Daney }
315b3b1688SDavid Daney 
325b3b1688SDavid Daney static void octeon_irq_core_eoi(unsigned int irq)
335b3b1688SDavid Daney {
34ae035505SThomas Gleixner 	struct irq_desc *desc = irq_desc + irq;
355b3b1688SDavid Daney 	unsigned int bit = irq - OCTEON_IRQ_SW0;
365b3b1688SDavid Daney 	/*
375b3b1688SDavid Daney 	 * If an IRQ is being processed while we are disabling it the
385b3b1688SDavid Daney 	 * handler will attempt to unmask the interrupt after it has
395b3b1688SDavid Daney 	 * been disabled.
405b3b1688SDavid Daney 	 */
415b3b1688SDavid Daney 	if (desc->status & IRQ_DISABLED)
425b3b1688SDavid Daney 		return;
435b3b1688SDavid Daney 
445b3b1688SDavid Daney 	/* There is a race here.  We should fix it.  */
455b3b1688SDavid Daney 
465b3b1688SDavid Daney 	/*
475b3b1688SDavid Daney 	 * We don't need to disable IRQs to make these atomic since
485b3b1688SDavid Daney 	 * they are already disabled earlier in the low level
495b3b1688SDavid Daney 	 * interrupt code.
505b3b1688SDavid Daney 	 */
515b3b1688SDavid Daney 	set_c0_status(0x100 << bit);
525b3b1688SDavid Daney }
535b3b1688SDavid Daney 
545b3b1688SDavid Daney static void octeon_irq_core_enable(unsigned int irq)
555b3b1688SDavid Daney {
565b3b1688SDavid Daney 	unsigned long flags;
575b3b1688SDavid Daney 	unsigned int bit = irq - OCTEON_IRQ_SW0;
585b3b1688SDavid Daney 
595b3b1688SDavid Daney 	/*
605b3b1688SDavid Daney 	 * We need to disable interrupts to make sure our updates are
615b3b1688SDavid Daney 	 * atomic.
625b3b1688SDavid Daney 	 */
635b3b1688SDavid Daney 	local_irq_save(flags);
645b3b1688SDavid Daney 	set_c0_status(0x100 << bit);
655b3b1688SDavid Daney 	local_irq_restore(flags);
665b3b1688SDavid Daney }
675b3b1688SDavid Daney 
685b3b1688SDavid Daney static void octeon_irq_core_disable_local(unsigned int irq)
695b3b1688SDavid Daney {
705b3b1688SDavid Daney 	unsigned long flags;
715b3b1688SDavid Daney 	unsigned int bit = irq - OCTEON_IRQ_SW0;
725b3b1688SDavid Daney 	/*
735b3b1688SDavid Daney 	 * We need to disable interrupts to make sure our updates are
745b3b1688SDavid Daney 	 * atomic.
755b3b1688SDavid Daney 	 */
765b3b1688SDavid Daney 	local_irq_save(flags);
775b3b1688SDavid Daney 	clear_c0_status(0x100 << bit);
785b3b1688SDavid Daney 	local_irq_restore(flags);
795b3b1688SDavid Daney }
805b3b1688SDavid Daney 
815b3b1688SDavid Daney static void octeon_irq_core_disable(unsigned int irq)
825b3b1688SDavid Daney {
835b3b1688SDavid Daney #ifdef CONFIG_SMP
845b3b1688SDavid Daney 	on_each_cpu((void (*)(void *)) octeon_irq_core_disable_local,
855b3b1688SDavid Daney 		    (void *) (long) irq, 1);
865b3b1688SDavid Daney #else
875b3b1688SDavid Daney 	octeon_irq_core_disable_local(irq);
885b3b1688SDavid Daney #endif
895b3b1688SDavid Daney }
905b3b1688SDavid Daney 
915b3b1688SDavid Daney static struct irq_chip octeon_irq_chip_core = {
925b3b1688SDavid Daney 	.name = "Core",
935b3b1688SDavid Daney 	.enable = octeon_irq_core_enable,
945b3b1688SDavid Daney 	.disable = octeon_irq_core_disable,
955b3b1688SDavid Daney 	.ack = octeon_irq_core_ack,
965b3b1688SDavid Daney 	.eoi = octeon_irq_core_eoi,
975b3b1688SDavid Daney };
985b3b1688SDavid Daney 
995b3b1688SDavid Daney 
1005b3b1688SDavid Daney static void octeon_irq_ciu0_ack(unsigned int irq)
1015b3b1688SDavid Daney {
1025b3b1688SDavid Daney 	/*
1035b3b1688SDavid Daney 	 * In order to avoid any locking accessing the CIU, we
1045b3b1688SDavid Daney 	 * acknowledge CIU interrupts by disabling all of them.  This
1055b3b1688SDavid Daney 	 * way we can use a per core register and avoid any out of
1065b3b1688SDavid Daney 	 * core locking requirements.  This has the side affect that
1075b3b1688SDavid Daney 	 * CIU interrupts can't be processed recursively.
1085b3b1688SDavid Daney 	 *
1095b3b1688SDavid Daney 	 * We don't need to disable IRQs to make these atomic since
1105b3b1688SDavid Daney 	 * they are already disabled earlier in the low level
1115b3b1688SDavid Daney 	 * interrupt code.
1125b3b1688SDavid Daney 	 */
1135b3b1688SDavid Daney 	clear_c0_status(0x100 << 2);
1145b3b1688SDavid Daney }
1155b3b1688SDavid Daney 
1165b3b1688SDavid Daney static void octeon_irq_ciu0_eoi(unsigned int irq)
1175b3b1688SDavid Daney {
1185b3b1688SDavid Daney 	/*
1195b3b1688SDavid Daney 	 * Enable all CIU interrupts again.  We don't need to disable
1205b3b1688SDavid Daney 	 * IRQs to make these atomic since they are already disabled
1215b3b1688SDavid Daney 	 * earlier in the low level interrupt code.
1225b3b1688SDavid Daney 	 */
1235b3b1688SDavid Daney 	set_c0_status(0x100 << 2);
1245b3b1688SDavid Daney }
1255b3b1688SDavid Daney 
1265b3b1688SDavid Daney static void octeon_irq_ciu0_enable(unsigned int irq)
1275b3b1688SDavid Daney {
1285b3b1688SDavid Daney 	int coreid = cvmx_get_core_num();
1295b3b1688SDavid Daney 	unsigned long flags;
1305b3b1688SDavid Daney 	uint64_t en0;
1315b3b1688SDavid Daney 	int bit = irq - OCTEON_IRQ_WORKQ0;	/* Bit 0-63 of EN0 */
1325b3b1688SDavid Daney 
1335b3b1688SDavid Daney 	/*
1345b3b1688SDavid Daney 	 * A read lock is used here to make sure only one core is ever
1355b3b1688SDavid Daney 	 * updating the CIU enable bits at a time. During an enable
1365b3b1688SDavid Daney 	 * the cores don't interfere with each other. During a disable
1375b3b1688SDavid Daney 	 * the write lock stops any enables that might cause a
1385b3b1688SDavid Daney 	 * problem.
1395b3b1688SDavid Daney 	 */
1405b3b1688SDavid Daney 	read_lock_irqsave(&octeon_irq_ciu0_rwlock, flags);
1415b3b1688SDavid Daney 	en0 = cvmx_read_csr(CVMX_CIU_INTX_EN0(coreid * 2));
1425b3b1688SDavid Daney 	en0 |= 1ull << bit;
1435b3b1688SDavid Daney 	cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), en0);
1445b3b1688SDavid Daney 	cvmx_read_csr(CVMX_CIU_INTX_EN0(coreid * 2));
1455b3b1688SDavid Daney 	read_unlock_irqrestore(&octeon_irq_ciu0_rwlock, flags);
1465b3b1688SDavid Daney }
1475b3b1688SDavid Daney 
1485b3b1688SDavid Daney static void octeon_irq_ciu0_disable(unsigned int irq)
1495b3b1688SDavid Daney {
1505b3b1688SDavid Daney 	int bit = irq - OCTEON_IRQ_WORKQ0;	/* Bit 0-63 of EN0 */
1515b3b1688SDavid Daney 	unsigned long flags;
1525b3b1688SDavid Daney 	uint64_t en0;
1535b3b1688SDavid Daney #ifdef CONFIG_SMP
1545b3b1688SDavid Daney 	int cpu;
1555b3b1688SDavid Daney 	write_lock_irqsave(&octeon_irq_ciu0_rwlock, flags);
1565b3b1688SDavid Daney 	for_each_online_cpu(cpu) {
1575b3b1688SDavid Daney 		int coreid = cpu_logical_map(cpu);
1585b3b1688SDavid Daney 		en0 = cvmx_read_csr(CVMX_CIU_INTX_EN0(coreid * 2));
1595b3b1688SDavid Daney 		en0 &= ~(1ull << bit);
1605b3b1688SDavid Daney 		cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), en0);
1615b3b1688SDavid Daney 	}
1625b3b1688SDavid Daney 	/*
1635b3b1688SDavid Daney 	 * We need to do a read after the last update to make sure all
1645b3b1688SDavid Daney 	 * of them are done.
1655b3b1688SDavid Daney 	 */
1665b3b1688SDavid Daney 	cvmx_read_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num() * 2));
1675b3b1688SDavid Daney 	write_unlock_irqrestore(&octeon_irq_ciu0_rwlock, flags);
1685b3b1688SDavid Daney #else
1695b3b1688SDavid Daney 	int coreid = cvmx_get_core_num();
1705b3b1688SDavid Daney 	local_irq_save(flags);
1715b3b1688SDavid Daney 	en0 = cvmx_read_csr(CVMX_CIU_INTX_EN0(coreid * 2));
1725b3b1688SDavid Daney 	en0 &= ~(1ull << bit);
1735b3b1688SDavid Daney 	cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), en0);
1745b3b1688SDavid Daney 	cvmx_read_csr(CVMX_CIU_INTX_EN0(coreid * 2));
1755b3b1688SDavid Daney 	local_irq_restore(flags);
1765b3b1688SDavid Daney #endif
1775b3b1688SDavid Daney }
1785b3b1688SDavid Daney 
1795b3b1688SDavid Daney #ifdef CONFIG_SMP
180*d5dedd45SYinghai Lu static int octeon_irq_ciu0_set_affinity(unsigned int irq, const struct cpumask *dest)
1815b3b1688SDavid Daney {
1825b3b1688SDavid Daney 	int cpu;
1835b3b1688SDavid Daney 	int bit = irq - OCTEON_IRQ_WORKQ0;	/* Bit 0-63 of EN0 */
1845b3b1688SDavid Daney 
1855b3b1688SDavid Daney 	write_lock(&octeon_irq_ciu0_rwlock);
1865b3b1688SDavid Daney 	for_each_online_cpu(cpu) {
1875b3b1688SDavid Daney 		int coreid = cpu_logical_map(cpu);
1885b3b1688SDavid Daney 		uint64_t en0 =
1895b3b1688SDavid Daney 			cvmx_read_csr(CVMX_CIU_INTX_EN0(coreid * 2));
1905b3b1688SDavid Daney 		if (cpumask_test_cpu(cpu, dest))
1915b3b1688SDavid Daney 			en0 |= 1ull << bit;
1925b3b1688SDavid Daney 		else
1935b3b1688SDavid Daney 			en0 &= ~(1ull << bit);
1945b3b1688SDavid Daney 		cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), en0);
1955b3b1688SDavid Daney 	}
1965b3b1688SDavid Daney 	/*
1975b3b1688SDavid Daney 	 * We need to do a read after the last update to make sure all
1985b3b1688SDavid Daney 	 * of them are done.
1995b3b1688SDavid Daney 	 */
2005b3b1688SDavid Daney 	cvmx_read_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num() * 2));
2015b3b1688SDavid Daney 	write_unlock(&octeon_irq_ciu0_rwlock);
202*d5dedd45SYinghai Lu 
203*d5dedd45SYinghai Lu 	return 0;
2045b3b1688SDavid Daney }
2055b3b1688SDavid Daney #endif
2065b3b1688SDavid Daney 
2075b3b1688SDavid Daney static struct irq_chip octeon_irq_chip_ciu0 = {
2085b3b1688SDavid Daney 	.name = "CIU0",
2095b3b1688SDavid Daney 	.enable = octeon_irq_ciu0_enable,
2105b3b1688SDavid Daney 	.disable = octeon_irq_ciu0_disable,
2115b3b1688SDavid Daney 	.ack = octeon_irq_ciu0_ack,
2125b3b1688SDavid Daney 	.eoi = octeon_irq_ciu0_eoi,
2135b3b1688SDavid Daney #ifdef CONFIG_SMP
2145b3b1688SDavid Daney 	.set_affinity = octeon_irq_ciu0_set_affinity,
2155b3b1688SDavid Daney #endif
2165b3b1688SDavid Daney };
2175b3b1688SDavid Daney 
2185b3b1688SDavid Daney 
2195b3b1688SDavid Daney static void octeon_irq_ciu1_ack(unsigned int irq)
2205b3b1688SDavid Daney {
2215b3b1688SDavid Daney 	/*
2225b3b1688SDavid Daney 	 * In order to avoid any locking accessing the CIU, we
2235b3b1688SDavid Daney 	 * acknowledge CIU interrupts by disabling all of them.  This
2245b3b1688SDavid Daney 	 * way we can use a per core register and avoid any out of
2255b3b1688SDavid Daney 	 * core locking requirements.  This has the side affect that
2265b3b1688SDavid Daney 	 * CIU interrupts can't be processed recursively.  We don't
2275b3b1688SDavid Daney 	 * need to disable IRQs to make these atomic since they are
2285b3b1688SDavid Daney 	 * already disabled earlier in the low level interrupt code.
2295b3b1688SDavid Daney 	 */
2305b3b1688SDavid Daney 	clear_c0_status(0x100 << 3);
2315b3b1688SDavid Daney }
2325b3b1688SDavid Daney 
2335b3b1688SDavid Daney static void octeon_irq_ciu1_eoi(unsigned int irq)
2345b3b1688SDavid Daney {
2355b3b1688SDavid Daney 	/*
2365b3b1688SDavid Daney 	 * Enable all CIU interrupts again.  We don't need to disable
2375b3b1688SDavid Daney 	 * IRQs to make these atomic since they are already disabled
2385b3b1688SDavid Daney 	 * earlier in the low level interrupt code.
2395b3b1688SDavid Daney 	 */
2405b3b1688SDavid Daney 	set_c0_status(0x100 << 3);
2415b3b1688SDavid Daney }
2425b3b1688SDavid Daney 
2435b3b1688SDavid Daney static void octeon_irq_ciu1_enable(unsigned int irq)
2445b3b1688SDavid Daney {
2455b3b1688SDavid Daney 	int coreid = cvmx_get_core_num();
2465b3b1688SDavid Daney 	unsigned long flags;
2475b3b1688SDavid Daney 	uint64_t en1;
2485b3b1688SDavid Daney 	int bit = irq - OCTEON_IRQ_WDOG0;	/* Bit 0-63 of EN1 */
2495b3b1688SDavid Daney 
2505b3b1688SDavid Daney 	/*
2515b3b1688SDavid Daney 	 * A read lock is used here to make sure only one core is ever
2525b3b1688SDavid Daney 	 * updating the CIU enable bits at a time.  During an enable
2535b3b1688SDavid Daney 	 * the cores don't interfere with each other.  During a disable
2545b3b1688SDavid Daney 	 * the write lock stops any enables that might cause a
2555b3b1688SDavid Daney 	 * problem.
2565b3b1688SDavid Daney 	 */
2575b3b1688SDavid Daney 	read_lock_irqsave(&octeon_irq_ciu1_rwlock, flags);
2585b3b1688SDavid Daney 	en1 = cvmx_read_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1));
2595b3b1688SDavid Daney 	en1 |= 1ull << bit;
2605b3b1688SDavid Daney 	cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), en1);
2615b3b1688SDavid Daney 	cvmx_read_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1));
2625b3b1688SDavid Daney 	read_unlock_irqrestore(&octeon_irq_ciu1_rwlock, flags);
2635b3b1688SDavid Daney }
2645b3b1688SDavid Daney 
2655b3b1688SDavid Daney static void octeon_irq_ciu1_disable(unsigned int irq)
2665b3b1688SDavid Daney {
2675b3b1688SDavid Daney 	int bit = irq - OCTEON_IRQ_WDOG0;	/* Bit 0-63 of EN1 */
2685b3b1688SDavid Daney 	unsigned long flags;
2695b3b1688SDavid Daney 	uint64_t en1;
2705b3b1688SDavid Daney #ifdef CONFIG_SMP
2715b3b1688SDavid Daney 	int cpu;
2725b3b1688SDavid Daney 	write_lock_irqsave(&octeon_irq_ciu1_rwlock, flags);
2735b3b1688SDavid Daney 	for_each_online_cpu(cpu) {
2745b3b1688SDavid Daney 		int coreid = cpu_logical_map(cpu);
2755b3b1688SDavid Daney 		en1 = cvmx_read_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1));
2765b3b1688SDavid Daney 		en1 &= ~(1ull << bit);
2775b3b1688SDavid Daney 		cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), en1);
2785b3b1688SDavid Daney 	}
2795b3b1688SDavid Daney 	/*
2805b3b1688SDavid Daney 	 * We need to do a read after the last update to make sure all
2815b3b1688SDavid Daney 	 * of them are done.
2825b3b1688SDavid Daney 	 */
2835b3b1688SDavid Daney 	cvmx_read_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num() * 2 + 1));
2845b3b1688SDavid Daney 	write_unlock_irqrestore(&octeon_irq_ciu1_rwlock, flags);
2855b3b1688SDavid Daney #else
2865b3b1688SDavid Daney 	int coreid = cvmx_get_core_num();
2875b3b1688SDavid Daney 	local_irq_save(flags);
2885b3b1688SDavid Daney 	en1 = cvmx_read_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1));
2895b3b1688SDavid Daney 	en1 &= ~(1ull << bit);
2905b3b1688SDavid Daney 	cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), en1);
2915b3b1688SDavid Daney 	cvmx_read_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1));
2925b3b1688SDavid Daney 	local_irq_restore(flags);
2935b3b1688SDavid Daney #endif
2945b3b1688SDavid Daney }
2955b3b1688SDavid Daney 
2965b3b1688SDavid Daney #ifdef CONFIG_SMP
297*d5dedd45SYinghai Lu static int octeon_irq_ciu1_set_affinity(unsigned int irq, const struct cpumask *dest)
2985b3b1688SDavid Daney {
2995b3b1688SDavid Daney 	int cpu;
3005b3b1688SDavid Daney 	int bit = irq - OCTEON_IRQ_WDOG0;	/* Bit 0-63 of EN1 */
3015b3b1688SDavid Daney 
3025b3b1688SDavid Daney 	write_lock(&octeon_irq_ciu1_rwlock);
3035b3b1688SDavid Daney 	for_each_online_cpu(cpu) {
3045b3b1688SDavid Daney 		int coreid = cpu_logical_map(cpu);
3055b3b1688SDavid Daney 		uint64_t en1 =
3065b3b1688SDavid Daney 			cvmx_read_csr(CVMX_CIU_INTX_EN1
3075b3b1688SDavid Daney 				(coreid * 2 + 1));
3085b3b1688SDavid Daney 		if (cpumask_test_cpu(cpu, dest))
3095b3b1688SDavid Daney 			en1 |= 1ull << bit;
3105b3b1688SDavid Daney 		else
3115b3b1688SDavid Daney 			en1 &= ~(1ull << bit);
3125b3b1688SDavid Daney 		cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), en1);
3135b3b1688SDavid Daney 	}
3145b3b1688SDavid Daney 	/*
3155b3b1688SDavid Daney 	 * We need to do a read after the last update to make sure all
3165b3b1688SDavid Daney 	 * of them are done.
3175b3b1688SDavid Daney 	 */
3185b3b1688SDavid Daney 	cvmx_read_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num() * 2 + 1));
3195b3b1688SDavid Daney 	write_unlock(&octeon_irq_ciu1_rwlock);
320*d5dedd45SYinghai Lu 
321*d5dedd45SYinghai Lu 	return 0;
3225b3b1688SDavid Daney }
3235b3b1688SDavid Daney #endif
3245b3b1688SDavid Daney 
3255b3b1688SDavid Daney static struct irq_chip octeon_irq_chip_ciu1 = {
3265b3b1688SDavid Daney 	.name = "CIU1",
3275b3b1688SDavid Daney 	.enable = octeon_irq_ciu1_enable,
3285b3b1688SDavid Daney 	.disable = octeon_irq_ciu1_disable,
3295b3b1688SDavid Daney 	.ack = octeon_irq_ciu1_ack,
3305b3b1688SDavid Daney 	.eoi = octeon_irq_ciu1_eoi,
3315b3b1688SDavid Daney #ifdef CONFIG_SMP
3325b3b1688SDavid Daney 	.set_affinity = octeon_irq_ciu1_set_affinity,
3335b3b1688SDavid Daney #endif
3345b3b1688SDavid Daney };
3355b3b1688SDavid Daney 
3365b3b1688SDavid Daney #ifdef CONFIG_PCI_MSI
3375b3b1688SDavid Daney 
3385b3b1688SDavid Daney static void octeon_irq_msi_ack(unsigned int irq)
3395b3b1688SDavid Daney {
3405b3b1688SDavid Daney 	if (!octeon_has_feature(OCTEON_FEATURE_PCIE)) {
3415b3b1688SDavid Daney 		/* These chips have PCI */
3425b3b1688SDavid Daney 		cvmx_write_csr(CVMX_NPI_NPI_MSI_RCV,
3435b3b1688SDavid Daney 			       1ull << (irq - OCTEON_IRQ_MSI_BIT0));
3445b3b1688SDavid Daney 	} else {
3455b3b1688SDavid Daney 		/*
3465b3b1688SDavid Daney 		 * These chips have PCIe. Thankfully the ACK doesn't
3475b3b1688SDavid Daney 		 * need any locking.
3485b3b1688SDavid Daney 		 */
3495b3b1688SDavid Daney 		cvmx_write_csr(CVMX_PEXP_NPEI_MSI_RCV0,
3505b3b1688SDavid Daney 			       1ull << (irq - OCTEON_IRQ_MSI_BIT0));
3515b3b1688SDavid Daney 	}
3525b3b1688SDavid Daney }
3535b3b1688SDavid Daney 
3545b3b1688SDavid Daney static void octeon_irq_msi_eoi(unsigned int irq)
3555b3b1688SDavid Daney {
3565b3b1688SDavid Daney 	/* Nothing needed */
3575b3b1688SDavid Daney }
3585b3b1688SDavid Daney 
3595b3b1688SDavid Daney static void octeon_irq_msi_enable(unsigned int irq)
3605b3b1688SDavid Daney {
3615b3b1688SDavid Daney 	if (!octeon_has_feature(OCTEON_FEATURE_PCIE)) {
3625b3b1688SDavid Daney 		/*
3635b3b1688SDavid Daney 		 * Octeon PCI doesn't have the ability to mask/unmask
3645b3b1688SDavid Daney 		 * MSI interrupts individually.  Instead of
3655b3b1688SDavid Daney 		 * masking/unmasking them in groups of 16, we simple
3665b3b1688SDavid Daney 		 * assume MSI devices are well behaved.  MSI
3675b3b1688SDavid Daney 		 * interrupts are always enable and the ACK is assumed
3685b3b1688SDavid Daney 		 * to be enough.
3695b3b1688SDavid Daney 		 */
3705b3b1688SDavid Daney 	} else {
3715b3b1688SDavid Daney 		/* These chips have PCIe.  Note that we only support
3725b3b1688SDavid Daney 		 * the first 64 MSI interrupts.  Unfortunately all the
3735b3b1688SDavid Daney 		 * MSI enables are in the same register.  We use
3745b3b1688SDavid Daney 		 * MSI0's lock to control access to them all.
3755b3b1688SDavid Daney 		 */
3765b3b1688SDavid Daney 		uint64_t en;
3775b3b1688SDavid Daney 		unsigned long flags;
3785b3b1688SDavid Daney 		spin_lock_irqsave(&octeon_irq_msi_lock, flags);
3795b3b1688SDavid Daney 		en = cvmx_read_csr(CVMX_PEXP_NPEI_MSI_ENB0);
3805b3b1688SDavid Daney 		en |= 1ull << (irq - OCTEON_IRQ_MSI_BIT0);
3815b3b1688SDavid Daney 		cvmx_write_csr(CVMX_PEXP_NPEI_MSI_ENB0, en);
3825b3b1688SDavid Daney 		cvmx_read_csr(CVMX_PEXP_NPEI_MSI_ENB0);
3835b3b1688SDavid Daney 		spin_unlock_irqrestore(&octeon_irq_msi_lock, flags);
3845b3b1688SDavid Daney 	}
3855b3b1688SDavid Daney }
3865b3b1688SDavid Daney 
3875b3b1688SDavid Daney static void octeon_irq_msi_disable(unsigned int irq)
3885b3b1688SDavid Daney {
3895b3b1688SDavid Daney 	if (!octeon_has_feature(OCTEON_FEATURE_PCIE)) {
3905b3b1688SDavid Daney 		/* See comment in enable */
3915b3b1688SDavid Daney 	} else {
3925b3b1688SDavid Daney 		/*
3935b3b1688SDavid Daney 		 * These chips have PCIe.  Note that we only support
3945b3b1688SDavid Daney 		 * the first 64 MSI interrupts.  Unfortunately all the
3955b3b1688SDavid Daney 		 * MSI enables are in the same register.  We use
3965b3b1688SDavid Daney 		 * MSI0's lock to control access to them all.
3975b3b1688SDavid Daney 		 */
3985b3b1688SDavid Daney 		uint64_t en;
3995b3b1688SDavid Daney 		unsigned long flags;
4005b3b1688SDavid Daney 		spin_lock_irqsave(&octeon_irq_msi_lock, flags);
4015b3b1688SDavid Daney 		en = cvmx_read_csr(CVMX_PEXP_NPEI_MSI_ENB0);
4025b3b1688SDavid Daney 		en &= ~(1ull << (irq - OCTEON_IRQ_MSI_BIT0));
4035b3b1688SDavid Daney 		cvmx_write_csr(CVMX_PEXP_NPEI_MSI_ENB0, en);
4045b3b1688SDavid Daney 		cvmx_read_csr(CVMX_PEXP_NPEI_MSI_ENB0);
4055b3b1688SDavid Daney 		spin_unlock_irqrestore(&octeon_irq_msi_lock, flags);
4065b3b1688SDavid Daney 	}
4075b3b1688SDavid Daney }
4085b3b1688SDavid Daney 
4095b3b1688SDavid Daney static struct irq_chip octeon_irq_chip_msi = {
4105b3b1688SDavid Daney 	.name = "MSI",
4115b3b1688SDavid Daney 	.enable = octeon_irq_msi_enable,
4125b3b1688SDavid Daney 	.disable = octeon_irq_msi_disable,
4135b3b1688SDavid Daney 	.ack = octeon_irq_msi_ack,
4145b3b1688SDavid Daney 	.eoi = octeon_irq_msi_eoi,
4155b3b1688SDavid Daney };
4165b3b1688SDavid Daney #endif
4175b3b1688SDavid Daney 
4185b3b1688SDavid Daney void __init arch_init_irq(void)
4195b3b1688SDavid Daney {
4205b3b1688SDavid Daney 	int irq;
4215b3b1688SDavid Daney 
4225b3b1688SDavid Daney #ifdef CONFIG_SMP
4235b3b1688SDavid Daney 	/* Set the default affinity to the boot cpu. */
4245b3b1688SDavid Daney 	cpumask_clear(irq_default_affinity);
4255b3b1688SDavid Daney 	cpumask_set_cpu(smp_processor_id(), irq_default_affinity);
4265b3b1688SDavid Daney #endif
4275b3b1688SDavid Daney 
4285b3b1688SDavid Daney 	if (NR_IRQS < OCTEON_IRQ_LAST)
4295b3b1688SDavid Daney 		pr_err("octeon_irq_init: NR_IRQS is set too low\n");
4305b3b1688SDavid Daney 
4315b3b1688SDavid Daney 	/* 0 - 15 reserved for i8259 master and slave controller. */
4325b3b1688SDavid Daney 
4335b3b1688SDavid Daney 	/* 17 - 23 Mips internal */
4345b3b1688SDavid Daney 	for (irq = OCTEON_IRQ_SW0; irq <= OCTEON_IRQ_TIMER; irq++) {
4355b3b1688SDavid Daney 		set_irq_chip_and_handler(irq, &octeon_irq_chip_core,
4365b3b1688SDavid Daney 					 handle_percpu_irq);
4375b3b1688SDavid Daney 	}
4385b3b1688SDavid Daney 
4395b3b1688SDavid Daney 	/* 24 - 87 CIU_INT_SUM0 */
4405b3b1688SDavid Daney 	for (irq = OCTEON_IRQ_WORKQ0; irq <= OCTEON_IRQ_BOOTDMA; irq++) {
4415b3b1688SDavid Daney 		set_irq_chip_and_handler(irq, &octeon_irq_chip_ciu0,
4425b3b1688SDavid Daney 					 handle_percpu_irq);
4435b3b1688SDavid Daney 	}
4445b3b1688SDavid Daney 
4455b3b1688SDavid Daney 	/* 88 - 151 CIU_INT_SUM1 */
4465b3b1688SDavid Daney 	for (irq = OCTEON_IRQ_WDOG0; irq <= OCTEON_IRQ_RESERVED151; irq++) {
4475b3b1688SDavid Daney 		set_irq_chip_and_handler(irq, &octeon_irq_chip_ciu1,
4485b3b1688SDavid Daney 					 handle_percpu_irq);
4495b3b1688SDavid Daney 	}
4505b3b1688SDavid Daney 
4515b3b1688SDavid Daney #ifdef CONFIG_PCI_MSI
4525b3b1688SDavid Daney 	/* 152 - 215 PCI/PCIe MSI interrupts */
4535b3b1688SDavid Daney 	for (irq = OCTEON_IRQ_MSI_BIT0; irq <= OCTEON_IRQ_MSI_BIT63; irq++) {
4545b3b1688SDavid Daney 		set_irq_chip_and_handler(irq, &octeon_irq_chip_msi,
4555b3b1688SDavid Daney 					 handle_percpu_irq);
4565b3b1688SDavid Daney 	}
4575b3b1688SDavid Daney #endif
4585b3b1688SDavid Daney 	set_c0_status(0x300 << 2);
4595b3b1688SDavid Daney }
4605b3b1688SDavid Daney 
4615b3b1688SDavid Daney asmlinkage void plat_irq_dispatch(void)
4625b3b1688SDavid Daney {
4635b3b1688SDavid Daney 	const unsigned long core_id = cvmx_get_core_num();
4645b3b1688SDavid Daney 	const uint64_t ciu_sum0_address = CVMX_CIU_INTX_SUM0(core_id * 2);
4655b3b1688SDavid Daney 	const uint64_t ciu_en0_address = CVMX_CIU_INTX_EN0(core_id * 2);
4665b3b1688SDavid Daney 	const uint64_t ciu_sum1_address = CVMX_CIU_INT_SUM1;
4675b3b1688SDavid Daney 	const uint64_t ciu_en1_address = CVMX_CIU_INTX_EN1(core_id * 2 + 1);
4685b3b1688SDavid Daney 	unsigned long cop0_cause;
4695b3b1688SDavid Daney 	unsigned long cop0_status;
4705b3b1688SDavid Daney 	uint64_t ciu_en;
4715b3b1688SDavid Daney 	uint64_t ciu_sum;
4725b3b1688SDavid Daney 
4735b3b1688SDavid Daney 	while (1) {
4745b3b1688SDavid Daney 		cop0_cause = read_c0_cause();
4755b3b1688SDavid Daney 		cop0_status = read_c0_status();
4765b3b1688SDavid Daney 		cop0_cause &= cop0_status;
4775b3b1688SDavid Daney 		cop0_cause &= ST0_IM;
4785b3b1688SDavid Daney 
4795b3b1688SDavid Daney 		if (unlikely(cop0_cause & STATUSF_IP2)) {
4805b3b1688SDavid Daney 			ciu_sum = cvmx_read_csr(ciu_sum0_address);
4815b3b1688SDavid Daney 			ciu_en = cvmx_read_csr(ciu_en0_address);
4825b3b1688SDavid Daney 			ciu_sum &= ciu_en;
4835b3b1688SDavid Daney 			if (likely(ciu_sum))
4845b3b1688SDavid Daney 				do_IRQ(fls64(ciu_sum) + OCTEON_IRQ_WORKQ0 - 1);
4855b3b1688SDavid Daney 			else
4865b3b1688SDavid Daney 				spurious_interrupt();
4875b3b1688SDavid Daney 		} else if (unlikely(cop0_cause & STATUSF_IP3)) {
4885b3b1688SDavid Daney 			ciu_sum = cvmx_read_csr(ciu_sum1_address);
4895b3b1688SDavid Daney 			ciu_en = cvmx_read_csr(ciu_en1_address);
4905b3b1688SDavid Daney 			ciu_sum &= ciu_en;
4915b3b1688SDavid Daney 			if (likely(ciu_sum))
4925b3b1688SDavid Daney 				do_IRQ(fls64(ciu_sum) + OCTEON_IRQ_WDOG0 - 1);
4935b3b1688SDavid Daney 			else
4945b3b1688SDavid Daney 				spurious_interrupt();
4955b3b1688SDavid Daney 		} else if (likely(cop0_cause)) {
4965b3b1688SDavid Daney 			do_IRQ(fls(cop0_cause) - 9 + MIPS_CPU_IRQ_BASE);
4975b3b1688SDavid Daney 		} else {
4985b3b1688SDavid Daney 			break;
4995b3b1688SDavid Daney 		}
5005b3b1688SDavid Daney 	}
5015b3b1688SDavid Daney }
502