xref: /linux/arch/mips/cavium-octeon/octeon-irq.c (revision 87161ccdc61862c8b49e75c21209d7f79dc758e9)
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  *
6a0c16582SDavid Daney  * Copyright (C) 2004-2012 Cavium, Inc.
75b3b1688SDavid Daney  */
80c326387SDavid Daney 
95b3b1688SDavid Daney #include <linux/interrupt.h>
10a0c16582SDavid Daney #include <linux/irqdomain.h>
110c326387SDavid Daney #include <linux/bitops.h>
120c326387SDavid Daney #include <linux/percpu.h>
13a0c16582SDavid Daney #include <linux/slab.h>
140c326387SDavid Daney #include <linux/irq.h>
15631330f5SRalf Baechle #include <linux/smp.h>
16a0c16582SDavid Daney #include <linux/of.h>
175b3b1688SDavid Daney 
185b3b1688SDavid Daney #include <asm/octeon/octeon.h>
195b3b1688SDavid Daney 
2039961422SDavid Daney static DEFINE_RAW_SPINLOCK(octeon_irq_ciu0_lock);
2139961422SDavid Daney static DEFINE_RAW_SPINLOCK(octeon_irq_ciu1_lock);
225b3b1688SDavid Daney 
230c326387SDavid Daney static DEFINE_PER_CPU(unsigned long, octeon_irq_ciu0_en_mirror);
240c326387SDavid Daney static DEFINE_PER_CPU(unsigned long, octeon_irq_ciu1_en_mirror);
250c326387SDavid Daney 
260c326387SDavid Daney static __read_mostly u8 octeon_irq_ciu_to_irq[8][64];
270c326387SDavid Daney 
280c326387SDavid Daney union octeon_ciu_chip_data {
290c326387SDavid Daney 	void *p;
300c326387SDavid Daney 	unsigned long l;
310c326387SDavid Daney 	struct {
320c326387SDavid Daney 		unsigned int line:6;
330c326387SDavid Daney 		unsigned int bit:6;
340c326387SDavid Daney 	} s;
350c326387SDavid Daney };
360c326387SDavid Daney 
370c326387SDavid Daney struct octeon_core_chip_data {
380c326387SDavid Daney 	struct mutex core_irq_mutex;
390c326387SDavid Daney 	bool current_en;
400c326387SDavid Daney 	bool desired_en;
410c326387SDavid Daney 	u8 bit;
420c326387SDavid Daney };
430c326387SDavid Daney 
440c326387SDavid Daney #define MIPS_CORE_IRQ_LINES 8
450c326387SDavid Daney 
460c326387SDavid Daney static struct octeon_core_chip_data octeon_irq_core_chip_data[MIPS_CORE_IRQ_LINES];
470c326387SDavid Daney 
48a0c16582SDavid Daney static void octeon_irq_set_ciu_mapping(int irq, int line, int bit,
490c326387SDavid Daney 				       struct irq_chip *chip,
500c326387SDavid Daney 				       irq_flow_handler_t handler)
510c326387SDavid Daney {
520c326387SDavid Daney 	union octeon_ciu_chip_data cd;
530c326387SDavid Daney 
540c326387SDavid Daney 	irq_set_chip_and_handler(irq, chip, handler);
550c326387SDavid Daney 
560c326387SDavid Daney 	cd.l = 0;
570c326387SDavid Daney 	cd.s.line = line;
580c326387SDavid Daney 	cd.s.bit = bit;
590c326387SDavid Daney 
600c326387SDavid Daney 	irq_set_chip_data(irq, cd.p);
610c326387SDavid Daney 	octeon_irq_ciu_to_irq[line][bit] = irq;
620c326387SDavid Daney }
630c326387SDavid Daney 
64*87161ccdSDavid Daney static void octeon_irq_force_ciu_mapping(struct irq_domain *domain,
65*87161ccdSDavid Daney 					 int irq, int line, int bit)
66*87161ccdSDavid Daney {
67*87161ccdSDavid Daney 	irq_domain_associate(domain, irq, line << 6 | bit);
68*87161ccdSDavid Daney }
69*87161ccdSDavid Daney 
70cd847b78SDavid Daney static int octeon_coreid_for_cpu(int cpu)
71cd847b78SDavid Daney {
72cd847b78SDavid Daney #ifdef CONFIG_SMP
73cd847b78SDavid Daney 	return cpu_logical_map(cpu);
74cd847b78SDavid Daney #else
75cd847b78SDavid Daney 	return cvmx_get_core_num();
76cd847b78SDavid Daney #endif
77cd847b78SDavid Daney }
78cd847b78SDavid Daney 
790c326387SDavid Daney static int octeon_cpu_for_coreid(int coreid)
805b3b1688SDavid Daney {
810c326387SDavid Daney #ifdef CONFIG_SMP
820c326387SDavid Daney 	return cpu_number_map(coreid);
830c326387SDavid Daney #else
840c326387SDavid Daney 	return smp_processor_id();
850c326387SDavid Daney #endif
860c326387SDavid Daney }
870c326387SDavid Daney 
880c326387SDavid Daney static void octeon_irq_core_ack(struct irq_data *data)
890c326387SDavid Daney {
900c326387SDavid Daney 	struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data);
910c326387SDavid Daney 	unsigned int bit = cd->bit;
920c326387SDavid Daney 
935b3b1688SDavid Daney 	/*
945b3b1688SDavid Daney 	 * We don't need to disable IRQs to make these atomic since
955b3b1688SDavid Daney 	 * they are already disabled earlier in the low level
965b3b1688SDavid Daney 	 * interrupt code.
975b3b1688SDavid Daney 	 */
985b3b1688SDavid Daney 	clear_c0_status(0x100 << bit);
995b3b1688SDavid Daney 	/* The two user interrupts must be cleared manually. */
1005b3b1688SDavid Daney 	if (bit < 2)
1015b3b1688SDavid Daney 		clear_c0_cause(0x100 << bit);
1025b3b1688SDavid Daney }
1035b3b1688SDavid Daney 
1040c326387SDavid Daney static void octeon_irq_core_eoi(struct irq_data *data)
1055b3b1688SDavid Daney {
1060c326387SDavid Daney 	struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data);
1070c326387SDavid Daney 
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 	 */
1130c326387SDavid Daney 	set_c0_status(0x100 << cd->bit);
1145b3b1688SDavid Daney }
1155b3b1688SDavid Daney 
1160c326387SDavid Daney static void octeon_irq_core_set_enable_local(void *arg)
1175b3b1688SDavid Daney {
1180c326387SDavid Daney 	struct irq_data *data = arg;
1190c326387SDavid Daney 	struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data);
1200c326387SDavid Daney 	unsigned int mask = 0x100 << cd->bit;
1215b3b1688SDavid Daney 
1225b3b1688SDavid Daney 	/*
1230c326387SDavid Daney 	 * Interrupts are already disabled, so these are atomic.
1245b3b1688SDavid Daney 	 */
1250c326387SDavid Daney 	if (cd->desired_en)
1260c326387SDavid Daney 		set_c0_status(mask);
1270c326387SDavid Daney 	else
1280c326387SDavid Daney 		clear_c0_status(mask);
1290c326387SDavid Daney 
1305b3b1688SDavid Daney }
1315b3b1688SDavid Daney 
1320c326387SDavid Daney static void octeon_irq_core_disable(struct irq_data *data)
1335b3b1688SDavid Daney {
1340c326387SDavid Daney 	struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data);
1350c326387SDavid Daney 	cd->desired_en = false;
1365b3b1688SDavid Daney }
1375b3b1688SDavid Daney 
1380c326387SDavid Daney static void octeon_irq_core_enable(struct irq_data *data)
1395b3b1688SDavid Daney {
1400c326387SDavid Daney 	struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data);
1410c326387SDavid Daney 	cd->desired_en = true;
1420c326387SDavid Daney }
1430c326387SDavid Daney 
1440c326387SDavid Daney static void octeon_irq_core_bus_lock(struct irq_data *data)
1450c326387SDavid Daney {
1460c326387SDavid Daney 	struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data);
1470c326387SDavid Daney 
1480c326387SDavid Daney 	mutex_lock(&cd->core_irq_mutex);
1490c326387SDavid Daney }
1500c326387SDavid Daney 
1510c326387SDavid Daney static void octeon_irq_core_bus_sync_unlock(struct irq_data *data)
1520c326387SDavid Daney {
1530c326387SDavid Daney 	struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data);
1540c326387SDavid Daney 
1550c326387SDavid Daney 	if (cd->desired_en != cd->current_en) {
1560c326387SDavid Daney 		on_each_cpu(octeon_irq_core_set_enable_local, data, 1);
1570c326387SDavid Daney 
1580c326387SDavid Daney 		cd->current_en = cd->desired_en;
1590c326387SDavid Daney 	}
1600c326387SDavid Daney 
1610c326387SDavid Daney 	mutex_unlock(&cd->core_irq_mutex);
1620c326387SDavid Daney }
1630c326387SDavid Daney 
1645b3b1688SDavid Daney static struct irq_chip octeon_irq_chip_core = {
1655b3b1688SDavid Daney 	.name = "Core",
1660c326387SDavid Daney 	.irq_enable = octeon_irq_core_enable,
1670c326387SDavid Daney 	.irq_disable = octeon_irq_core_disable,
1680c326387SDavid Daney 	.irq_ack = octeon_irq_core_ack,
1690c326387SDavid Daney 	.irq_eoi = octeon_irq_core_eoi,
1700c326387SDavid Daney 	.irq_bus_lock = octeon_irq_core_bus_lock,
1710c326387SDavid Daney 	.irq_bus_sync_unlock = octeon_irq_core_bus_sync_unlock,
1720c326387SDavid Daney 
1735b7cd6fdSThomas Gleixner 	.irq_cpu_online = octeon_irq_core_eoi,
1745b7cd6fdSThomas Gleixner 	.irq_cpu_offline = octeon_irq_core_ack,
1755b7cd6fdSThomas Gleixner 	.flags = IRQCHIP_ONOFFLINE_ENABLED,
1765b3b1688SDavid Daney };
1775b3b1688SDavid Daney 
1780c326387SDavid Daney static void __init octeon_irq_init_core(void)
1790c326387SDavid Daney {
1800c326387SDavid Daney 	int i;
1810c326387SDavid Daney 	int irq;
1820c326387SDavid Daney 	struct octeon_core_chip_data *cd;
1835b3b1688SDavid Daney 
1840c326387SDavid Daney 	for (i = 0; i < MIPS_CORE_IRQ_LINES; i++) {
1850c326387SDavid Daney 		cd = &octeon_irq_core_chip_data[i];
1860c326387SDavid Daney 		cd->current_en = false;
1870c326387SDavid Daney 		cd->desired_en = false;
1880c326387SDavid Daney 		cd->bit = i;
1890c326387SDavid Daney 		mutex_init(&cd->core_irq_mutex);
1900c326387SDavid Daney 
1910c326387SDavid Daney 		irq = OCTEON_IRQ_SW0 + i;
1920c326387SDavid Daney 		irq_set_chip_data(irq, cd);
1930c326387SDavid Daney 		irq_set_chip_and_handler(irq, &octeon_irq_chip_core,
1940c326387SDavid Daney 					 handle_percpu_irq);
1950c326387SDavid Daney 	}
1965b3b1688SDavid Daney }
1975b3b1688SDavid Daney 
1980c326387SDavid Daney static int next_cpu_for_irq(struct irq_data *data)
1995aae1fd4SDavid Daney {
2005aae1fd4SDavid Daney 
2015aae1fd4SDavid Daney #ifdef CONFIG_SMP
2020c326387SDavid Daney 	int cpu;
2030c326387SDavid Daney 	int weight = cpumask_weight(data->affinity);
2045aae1fd4SDavid Daney 
2055aae1fd4SDavid Daney 	if (weight > 1) {
2060c326387SDavid Daney 		cpu = smp_processor_id();
2075aae1fd4SDavid Daney 		for (;;) {
2080c326387SDavid Daney 			cpu = cpumask_next(cpu, data->affinity);
2095aae1fd4SDavid Daney 			if (cpu >= nr_cpu_ids) {
2105aae1fd4SDavid Daney 				cpu = -1;
2115aae1fd4SDavid Daney 				continue;
2125aae1fd4SDavid Daney 			} else if (cpumask_test_cpu(cpu, cpu_online_mask)) {
2135aae1fd4SDavid Daney 				break;
2145aae1fd4SDavid Daney 			}
2155aae1fd4SDavid Daney 		}
2165aae1fd4SDavid Daney 	} else if (weight == 1) {
2170c326387SDavid Daney 		cpu = cpumask_first(data->affinity);
2185aae1fd4SDavid Daney 	} else {
2190c326387SDavid Daney 		cpu = smp_processor_id();
2205aae1fd4SDavid Daney 	}
2210c326387SDavid Daney 	return cpu;
2225aae1fd4SDavid Daney #else
2230c326387SDavid Daney 	return smp_processor_id();
2245aae1fd4SDavid Daney #endif
2255aae1fd4SDavid Daney }
2265aae1fd4SDavid Daney 
2270c326387SDavid Daney static void octeon_irq_ciu_enable(struct irq_data *data)
2285b3b1688SDavid Daney {
2290c326387SDavid Daney 	int cpu = next_cpu_for_irq(data);
2300c326387SDavid Daney 	int coreid = octeon_coreid_for_cpu(cpu);
2310c326387SDavid Daney 	unsigned long *pen;
2325aae1fd4SDavid Daney 	unsigned long flags;
2330c326387SDavid Daney 	union octeon_ciu_chip_data cd;
2345aae1fd4SDavid Daney 
2350c326387SDavid Daney 	cd.p = irq_data_get_irq_chip_data(data);
2360c326387SDavid Daney 
2370c326387SDavid Daney 	if (cd.s.line == 0) {
2385aae1fd4SDavid Daney 		raw_spin_lock_irqsave(&octeon_irq_ciu0_lock, flags);
2390c326387SDavid Daney 		pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu);
2400c326387SDavid Daney 		set_bit(cd.s.bit, pen);
2410c326387SDavid Daney 		cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), *pen);
2425aae1fd4SDavid Daney 		raw_spin_unlock_irqrestore(&octeon_irq_ciu0_lock, flags);
2430c326387SDavid Daney 	} else {
2440c326387SDavid Daney 		raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags);
2450c326387SDavid Daney 		pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu);
2460c326387SDavid Daney 		set_bit(cd.s.bit, pen);
2470c326387SDavid Daney 		cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen);
2480c326387SDavid Daney 		raw_spin_unlock_irqrestore(&octeon_irq_ciu1_lock, flags);
2490c326387SDavid Daney 	}
2505aae1fd4SDavid Daney }
2515aae1fd4SDavid Daney 
2520c326387SDavid Daney static void octeon_irq_ciu_enable_local(struct irq_data *data)
2535aae1fd4SDavid Daney {
2540c326387SDavid Daney 	unsigned long *pen;
2555b3b1688SDavid Daney 	unsigned long flags;
2560c326387SDavid Daney 	union octeon_ciu_chip_data cd;
2575b3b1688SDavid Daney 
2580c326387SDavid Daney 	cd.p = irq_data_get_irq_chip_data(data);
2590c326387SDavid Daney 
2600c326387SDavid Daney 	if (cd.s.line == 0) {
26139961422SDavid Daney 		raw_spin_lock_irqsave(&octeon_irq_ciu0_lock, flags);
2620c326387SDavid Daney 		pen = &__get_cpu_var(octeon_irq_ciu0_en_mirror);
2630c326387SDavid Daney 		set_bit(cd.s.bit, pen);
2640c326387SDavid Daney 		cvmx_write_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num() * 2), *pen);
26539961422SDavid Daney 		raw_spin_unlock_irqrestore(&octeon_irq_ciu0_lock, flags);
2660c326387SDavid Daney 	} else {
2670c326387SDavid Daney 		raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags);
2680c326387SDavid Daney 		pen = &__get_cpu_var(octeon_irq_ciu1_en_mirror);
2690c326387SDavid Daney 		set_bit(cd.s.bit, pen);
2700c326387SDavid Daney 		cvmx_write_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num() * 2 + 1), *pen);
2710c326387SDavid Daney 		raw_spin_unlock_irqrestore(&octeon_irq_ciu1_lock, flags);
2720c326387SDavid Daney 	}
2735b3b1688SDavid Daney }
2745b3b1688SDavid Daney 
2750c326387SDavid Daney static void octeon_irq_ciu_disable_local(struct irq_data *data)
2765b3b1688SDavid Daney {
2770c326387SDavid Daney 	unsigned long *pen;
2785b3b1688SDavid Daney 	unsigned long flags;
2790c326387SDavid Daney 	union octeon_ciu_chip_data cd;
2800c326387SDavid Daney 
2810c326387SDavid Daney 	cd.p = irq_data_get_irq_chip_data(data);
2820c326387SDavid Daney 
2830c326387SDavid Daney 	if (cd.s.line == 0) {
2840c326387SDavid Daney 		raw_spin_lock_irqsave(&octeon_irq_ciu0_lock, flags);
2850c326387SDavid Daney 		pen = &__get_cpu_var(octeon_irq_ciu0_en_mirror);
2860c326387SDavid Daney 		clear_bit(cd.s.bit, pen);
2870c326387SDavid Daney 		cvmx_write_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num() * 2), *pen);
2880c326387SDavid Daney 		raw_spin_unlock_irqrestore(&octeon_irq_ciu0_lock, flags);
2890c326387SDavid Daney 	} else {
2900c326387SDavid Daney 		raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags);
2910c326387SDavid Daney 		pen = &__get_cpu_var(octeon_irq_ciu1_en_mirror);
2920c326387SDavid Daney 		clear_bit(cd.s.bit, pen);
2930c326387SDavid Daney 		cvmx_write_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num() * 2 + 1), *pen);
2940c326387SDavid Daney 		raw_spin_unlock_irqrestore(&octeon_irq_ciu1_lock, flags);
2950c326387SDavid Daney 	}
2960c326387SDavid Daney }
2970c326387SDavid Daney 
2980c326387SDavid Daney static void octeon_irq_ciu_disable_all(struct irq_data *data)
2990c326387SDavid Daney {
3000c326387SDavid Daney 	unsigned long flags;
3010c326387SDavid Daney 	unsigned long *pen;
3025b3b1688SDavid Daney 	int cpu;
3030c326387SDavid Daney 	union octeon_ciu_chip_data cd;
3040c326387SDavid Daney 
3050c326387SDavid Daney 	wmb(); /* Make sure flag changes arrive before register updates. */
3060c326387SDavid Daney 
3070c326387SDavid Daney 	cd.p = irq_data_get_irq_chip_data(data);
3080c326387SDavid Daney 
3090c326387SDavid Daney 	if (cd.s.line == 0) {
31039961422SDavid Daney 		raw_spin_lock_irqsave(&octeon_irq_ciu0_lock, flags);
3115b3b1688SDavid Daney 		for_each_online_cpu(cpu) {
312cd847b78SDavid Daney 			int coreid = octeon_coreid_for_cpu(cpu);
3130c326387SDavid Daney 			pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu);
3140c326387SDavid Daney 			clear_bit(cd.s.bit, pen);
3150c326387SDavid Daney 			cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), *pen);
3165b3b1688SDavid Daney 		}
31739961422SDavid Daney 		raw_spin_unlock_irqrestore(&octeon_irq_ciu0_lock, flags);
3180c326387SDavid Daney 	} else {
3190c326387SDavid Daney 		raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags);
3200c326387SDavid Daney 		for_each_online_cpu(cpu) {
3210c326387SDavid Daney 			int coreid = octeon_coreid_for_cpu(cpu);
3220c326387SDavid Daney 			pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu);
3230c326387SDavid Daney 			clear_bit(cd.s.bit, pen);
3240c326387SDavid Daney 			cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen);
3250c326387SDavid Daney 		}
3260c326387SDavid Daney 		raw_spin_unlock_irqrestore(&octeon_irq_ciu1_lock, flags);
3270c326387SDavid Daney 	}
3280c326387SDavid Daney }
3290c326387SDavid Daney 
3300c326387SDavid Daney static void octeon_irq_ciu_enable_all(struct irq_data *data)
3310c326387SDavid Daney {
3320c326387SDavid Daney 	unsigned long flags;
3330c326387SDavid Daney 	unsigned long *pen;
3340c326387SDavid Daney 	int cpu;
3350c326387SDavid Daney 	union octeon_ciu_chip_data cd;
3360c326387SDavid Daney 
3370c326387SDavid Daney 	cd.p = irq_data_get_irq_chip_data(data);
3380c326387SDavid Daney 
3390c326387SDavid Daney 	if (cd.s.line == 0) {
3400c326387SDavid Daney 		raw_spin_lock_irqsave(&octeon_irq_ciu0_lock, flags);
3410c326387SDavid Daney 		for_each_online_cpu(cpu) {
3420c326387SDavid Daney 			int coreid = octeon_coreid_for_cpu(cpu);
3430c326387SDavid Daney 			pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu);
3440c326387SDavid Daney 			set_bit(cd.s.bit, pen);
3450c326387SDavid Daney 			cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), *pen);
3460c326387SDavid Daney 		}
3470c326387SDavid Daney 		raw_spin_unlock_irqrestore(&octeon_irq_ciu0_lock, flags);
3480c326387SDavid Daney 	} else {
3490c326387SDavid Daney 		raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags);
3500c326387SDavid Daney 		for_each_online_cpu(cpu) {
3510c326387SDavid Daney 			int coreid = octeon_coreid_for_cpu(cpu);
3520c326387SDavid Daney 			pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu);
3530c326387SDavid Daney 			set_bit(cd.s.bit, pen);
3540c326387SDavid Daney 			cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen);
3550c326387SDavid Daney 		}
3560c326387SDavid Daney 		raw_spin_unlock_irqrestore(&octeon_irq_ciu1_lock, flags);
3570c326387SDavid Daney 	}
358cd847b78SDavid Daney }
359cd847b78SDavid Daney 
360cd847b78SDavid Daney /*
3615aae1fd4SDavid Daney  * Enable the irq on the next core in the affinity set for chips that
3625aae1fd4SDavid Daney  * have the EN*_W1{S,C} registers.
363cd847b78SDavid Daney  */
3640c326387SDavid Daney static void octeon_irq_ciu_enable_v2(struct irq_data *data)
365cd847b78SDavid Daney {
3660c326387SDavid Daney 	u64 mask;
3670c326387SDavid Daney 	int cpu = next_cpu_for_irq(data);
3680c326387SDavid Daney 	union octeon_ciu_chip_data cd;
3695aae1fd4SDavid Daney 
3700c326387SDavid Daney 	cd.p = irq_data_get_irq_chip_data(data);
3710c326387SDavid Daney 	mask = 1ull << (cd.s.bit);
3720c326387SDavid Daney 
3730c326387SDavid Daney 	/*
3740c326387SDavid Daney 	 * Called under the desc lock, so these should never get out
3750c326387SDavid Daney 	 * of sync.
3760c326387SDavid Daney 	 */
3770c326387SDavid Daney 	if (cd.s.line == 0) {
3780c326387SDavid Daney 		int index = octeon_coreid_for_cpu(cpu) * 2;
3790c326387SDavid Daney 		set_bit(cd.s.bit, &per_cpu(octeon_irq_ciu0_en_mirror, cpu));
3805aae1fd4SDavid Daney 		cvmx_write_csr(CVMX_CIU_INTX_EN0_W1S(index), mask);
3810c326387SDavid Daney 	} else {
3820c326387SDavid Daney 		int index = octeon_coreid_for_cpu(cpu) * 2 + 1;
3830c326387SDavid Daney 		set_bit(cd.s.bit, &per_cpu(octeon_irq_ciu1_en_mirror, cpu));
3840c326387SDavid Daney 		cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(index), mask);
3855aae1fd4SDavid Daney 	}
3865aae1fd4SDavid Daney }
3875aae1fd4SDavid Daney 
3885aae1fd4SDavid Daney /*
3895aae1fd4SDavid Daney  * Enable the irq on the current CPU for chips that
3905aae1fd4SDavid Daney  * have the EN*_W1{S,C} registers.
3915aae1fd4SDavid Daney  */
3920c326387SDavid Daney static void octeon_irq_ciu_enable_local_v2(struct irq_data *data)
3935aae1fd4SDavid Daney {
3940c326387SDavid Daney 	u64 mask;
3950c326387SDavid Daney 	union octeon_ciu_chip_data cd;
396cd847b78SDavid Daney 
3970c326387SDavid Daney 	cd.p = irq_data_get_irq_chip_data(data);
3980c326387SDavid Daney 	mask = 1ull << (cd.s.bit);
399cd847b78SDavid Daney 
4000c326387SDavid Daney 	if (cd.s.line == 0) {
401cd847b78SDavid Daney 		int index = cvmx_get_core_num() * 2;
4020c326387SDavid Daney 		set_bit(cd.s.bit, &__get_cpu_var(octeon_irq_ciu0_en_mirror));
4030c326387SDavid Daney 		cvmx_write_csr(CVMX_CIU_INTX_EN0_W1S(index), mask);
4040c326387SDavid Daney 	} else {
4050c326387SDavid Daney 		int index = cvmx_get_core_num() * 2 + 1;
4060c326387SDavid Daney 		set_bit(cd.s.bit, &__get_cpu_var(octeon_irq_ciu1_en_mirror));
4070c326387SDavid Daney 		cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(index), mask);
4080c326387SDavid Daney 	}
40986568dc4SDavid Daney }
41086568dc4SDavid Daney 
4110c326387SDavid Daney static void octeon_irq_ciu_disable_local_v2(struct irq_data *data)
4120c326387SDavid Daney {
4130c326387SDavid Daney 	u64 mask;
4140c326387SDavid Daney 	union octeon_ciu_chip_data cd;
4150c326387SDavid Daney 
4160c326387SDavid Daney 	cd.p = irq_data_get_irq_chip_data(data);
4170c326387SDavid Daney 	mask = 1ull << (cd.s.bit);
4180c326387SDavid Daney 
4190c326387SDavid Daney 	if (cd.s.line == 0) {
4200c326387SDavid Daney 		int index = cvmx_get_core_num() * 2;
4210c326387SDavid Daney 		clear_bit(cd.s.bit, &__get_cpu_var(octeon_irq_ciu0_en_mirror));
4225aae1fd4SDavid Daney 		cvmx_write_csr(CVMX_CIU_INTX_EN0_W1C(index), mask);
4230c326387SDavid Daney 	} else {
4240c326387SDavid Daney 		int index = cvmx_get_core_num() * 2 + 1;
4250c326387SDavid Daney 		clear_bit(cd.s.bit, &__get_cpu_var(octeon_irq_ciu1_en_mirror));
4260c326387SDavid Daney 		cvmx_write_csr(CVMX_CIU_INTX_EN1_W1C(index), mask);
4270c326387SDavid Daney 	}
42886568dc4SDavid Daney }
42986568dc4SDavid Daney 
43086568dc4SDavid Daney /*
4310c326387SDavid Daney  * Write to the W1C bit in CVMX_CIU_INTX_SUM0 to clear the irq.
432dbb103b2SDavid Daney  */
4330c326387SDavid Daney static void octeon_irq_ciu_ack(struct irq_data *data)
434dbb103b2SDavid Daney {
4350c326387SDavid Daney 	u64 mask;
4360c326387SDavid Daney 	union octeon_ciu_chip_data cd;
437dbb103b2SDavid Daney 
4380c326387SDavid Daney 	cd.p = data->chip_data;
4390c326387SDavid Daney 	mask = 1ull << (cd.s.bit);
4400c326387SDavid Daney 
4410c326387SDavid Daney 	if (cd.s.line == 0) {
4420c326387SDavid Daney 		int index = cvmx_get_core_num() * 2;
4430c326387SDavid Daney 		cvmx_write_csr(CVMX_CIU_INTX_SUM0(index), mask);
4440c326387SDavid Daney 	} else {
4450c326387SDavid Daney 		cvmx_write_csr(CVMX_CIU_INT_SUM1, mask);
4460c326387SDavid Daney 	}
447dbb103b2SDavid Daney }
448dbb103b2SDavid Daney 
449dbb103b2SDavid Daney /*
450cd847b78SDavid Daney  * Disable the irq on the all cores for chips that have the EN*_W1{S,C}
451cd847b78SDavid Daney  * registers.
452cd847b78SDavid Daney  */
4530c326387SDavid Daney static void octeon_irq_ciu_disable_all_v2(struct irq_data *data)
454cd847b78SDavid Daney {
455cd847b78SDavid Daney 	int cpu;
4560c326387SDavid Daney 	u64 mask;
4570c326387SDavid Daney 	union octeon_ciu_chip_data cd;
4580c326387SDavid Daney 
4590c326387SDavid Daney 	wmb(); /* Make sure flag changes arrive before register updates. */
4600c326387SDavid Daney 
4610c326387SDavid Daney 	cd.p = data->chip_data;
4620c326387SDavid Daney 	mask = 1ull << (cd.s.bit);
4630c326387SDavid Daney 
4640c326387SDavid Daney 	if (cd.s.line == 0) {
465cd847b78SDavid Daney 		for_each_online_cpu(cpu) {
4660c326387SDavid Daney 			int index = octeon_coreid_for_cpu(cpu) * 2;
4670c326387SDavid Daney 			clear_bit(cd.s.bit, &per_cpu(octeon_irq_ciu0_en_mirror, cpu));
468cd847b78SDavid Daney 			cvmx_write_csr(CVMX_CIU_INTX_EN0_W1C(index), mask);
469cd847b78SDavid Daney 		}
4700c326387SDavid Daney 	} else {
4710c326387SDavid Daney 		for_each_online_cpu(cpu) {
4720c326387SDavid Daney 			int index = octeon_coreid_for_cpu(cpu) * 2 + 1;
4730c326387SDavid Daney 			clear_bit(cd.s.bit, &per_cpu(octeon_irq_ciu1_en_mirror, cpu));
4740c326387SDavid Daney 			cvmx_write_csr(CVMX_CIU_INTX_EN1_W1C(index), mask);
4750c326387SDavid Daney 		}
4760c326387SDavid Daney 	}
4775b3b1688SDavid Daney }
4785b3b1688SDavid Daney 
4790c326387SDavid Daney /*
4800c326387SDavid Daney  * Enable the irq on the all cores for chips that have the EN*_W1{S,C}
4810c326387SDavid Daney  * registers.
4820c326387SDavid Daney  */
4830c326387SDavid Daney static void octeon_irq_ciu_enable_all_v2(struct irq_data *data)
4845b3b1688SDavid Daney {
4855b3b1688SDavid Daney 	int cpu;
4860c326387SDavid Daney 	u64 mask;
4870c326387SDavid Daney 	union octeon_ciu_chip_data cd;
4880c326387SDavid Daney 
4890c326387SDavid Daney 	cd.p = data->chip_data;
4900c326387SDavid Daney 	mask = 1ull << (cd.s.bit);
4910c326387SDavid Daney 
4920c326387SDavid Daney 	if (cd.s.line == 0) {
4930c326387SDavid Daney 		for_each_online_cpu(cpu) {
4940c326387SDavid Daney 			int index = octeon_coreid_for_cpu(cpu) * 2;
4950c326387SDavid Daney 			set_bit(cd.s.bit, &per_cpu(octeon_irq_ciu0_en_mirror, cpu));
4960c326387SDavid Daney 			cvmx_write_csr(CVMX_CIU_INTX_EN0_W1S(index), mask);
4970c326387SDavid Daney 		}
4980c326387SDavid Daney 	} else {
4990c326387SDavid Daney 		for_each_online_cpu(cpu) {
5000c326387SDavid Daney 			int index = octeon_coreid_for_cpu(cpu) * 2 + 1;
5010c326387SDavid Daney 			set_bit(cd.s.bit, &per_cpu(octeon_irq_ciu1_en_mirror, cpu));
5020c326387SDavid Daney 			cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(index), mask);
5030c326387SDavid Daney 		}
5040c326387SDavid Daney 	}
5050c326387SDavid Daney }
5060c326387SDavid Daney 
5076d1ab4c2SDavid Daney static void octeon_irq_gpio_setup(struct irq_data *data)
5086d1ab4c2SDavid Daney {
5096d1ab4c2SDavid Daney 	union cvmx_gpio_bit_cfgx cfg;
5106d1ab4c2SDavid Daney 	union octeon_ciu_chip_data cd;
5116d1ab4c2SDavid Daney 	u32 t = irqd_get_trigger_type(data);
5126d1ab4c2SDavid Daney 
5136d1ab4c2SDavid Daney 	cd.p = irq_data_get_irq_chip_data(data);
5146d1ab4c2SDavid Daney 
5156d1ab4c2SDavid Daney 	cfg.u64 = 0;
5166d1ab4c2SDavid Daney 	cfg.s.int_en = 1;
5176d1ab4c2SDavid Daney 	cfg.s.int_type = (t & IRQ_TYPE_EDGE_BOTH) != 0;
5186d1ab4c2SDavid Daney 	cfg.s.rx_xor = (t & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_EDGE_FALLING)) != 0;
5196d1ab4c2SDavid Daney 
5206d1ab4c2SDavid Daney 	/* 140 nS glitch filter*/
5216d1ab4c2SDavid Daney 	cfg.s.fil_cnt = 7;
5226d1ab4c2SDavid Daney 	cfg.s.fil_sel = 3;
5236d1ab4c2SDavid Daney 
5246d1ab4c2SDavid Daney 	cvmx_write_csr(CVMX_GPIO_BIT_CFGX(cd.s.bit - 16), cfg.u64);
5256d1ab4c2SDavid Daney }
5266d1ab4c2SDavid Daney 
5276d1ab4c2SDavid Daney static void octeon_irq_ciu_enable_gpio_v2(struct irq_data *data)
5286d1ab4c2SDavid Daney {
5296d1ab4c2SDavid Daney 	octeon_irq_gpio_setup(data);
5306d1ab4c2SDavid Daney 	octeon_irq_ciu_enable_v2(data);
5316d1ab4c2SDavid Daney }
5326d1ab4c2SDavid Daney 
5336d1ab4c2SDavid Daney static void octeon_irq_ciu_enable_gpio(struct irq_data *data)
5346d1ab4c2SDavid Daney {
5356d1ab4c2SDavid Daney 	octeon_irq_gpio_setup(data);
5366d1ab4c2SDavid Daney 	octeon_irq_ciu_enable(data);
5376d1ab4c2SDavid Daney }
5386d1ab4c2SDavid Daney 
5396d1ab4c2SDavid Daney static int octeon_irq_ciu_gpio_set_type(struct irq_data *data, unsigned int t)
5406d1ab4c2SDavid Daney {
5416d1ab4c2SDavid Daney 	irqd_set_trigger_type(data, t);
5426d1ab4c2SDavid Daney 	octeon_irq_gpio_setup(data);
5436d1ab4c2SDavid Daney 
5446d1ab4c2SDavid Daney 	return IRQ_SET_MASK_OK;
5456d1ab4c2SDavid Daney }
5466d1ab4c2SDavid Daney 
5476d1ab4c2SDavid Daney static void octeon_irq_ciu_disable_gpio_v2(struct irq_data *data)
5486d1ab4c2SDavid Daney {
5496d1ab4c2SDavid Daney 	union octeon_ciu_chip_data cd;
5506d1ab4c2SDavid Daney 
5516d1ab4c2SDavid Daney 	cd.p = irq_data_get_irq_chip_data(data);
5526d1ab4c2SDavid Daney 	cvmx_write_csr(CVMX_GPIO_BIT_CFGX(cd.s.bit - 16), 0);
5536d1ab4c2SDavid Daney 
5546d1ab4c2SDavid Daney 	octeon_irq_ciu_disable_all_v2(data);
5556d1ab4c2SDavid Daney }
5566d1ab4c2SDavid Daney 
5576d1ab4c2SDavid Daney static void octeon_irq_ciu_disable_gpio(struct irq_data *data)
5586d1ab4c2SDavid Daney {
5596d1ab4c2SDavid Daney 	union octeon_ciu_chip_data cd;
5606d1ab4c2SDavid Daney 
5616d1ab4c2SDavid Daney 	cd.p = irq_data_get_irq_chip_data(data);
5626d1ab4c2SDavid Daney 	cvmx_write_csr(CVMX_GPIO_BIT_CFGX(cd.s.bit - 16), 0);
5636d1ab4c2SDavid Daney 
5646d1ab4c2SDavid Daney 	octeon_irq_ciu_disable_all(data);
5656d1ab4c2SDavid Daney }
5666d1ab4c2SDavid Daney 
5676d1ab4c2SDavid Daney static void octeon_irq_ciu_gpio_ack(struct irq_data *data)
5686d1ab4c2SDavid Daney {
5696d1ab4c2SDavid Daney 	union octeon_ciu_chip_data cd;
5706d1ab4c2SDavid Daney 	u64 mask;
5716d1ab4c2SDavid Daney 
5726d1ab4c2SDavid Daney 	cd.p = irq_data_get_irq_chip_data(data);
5736d1ab4c2SDavid Daney 	mask = 1ull << (cd.s.bit - 16);
5746d1ab4c2SDavid Daney 
5756d1ab4c2SDavid Daney 	cvmx_write_csr(CVMX_GPIO_INT_CLR, mask);
5766d1ab4c2SDavid Daney }
5776d1ab4c2SDavid Daney 
5786d1ab4c2SDavid Daney static void octeon_irq_handle_gpio(unsigned int irq, struct irq_desc *desc)
5796d1ab4c2SDavid Daney {
5806d1ab4c2SDavid Daney 	if (irqd_get_trigger_type(irq_desc_get_irq_data(desc)) & IRQ_TYPE_EDGE_BOTH)
5816d1ab4c2SDavid Daney 		handle_edge_irq(irq, desc);
5826d1ab4c2SDavid Daney 	else
5836d1ab4c2SDavid Daney 		handle_level_irq(irq, desc);
5846d1ab4c2SDavid Daney }
5856d1ab4c2SDavid Daney 
5860c326387SDavid Daney #ifdef CONFIG_SMP
5870c326387SDavid Daney 
5880c326387SDavid Daney static void octeon_irq_cpu_offline_ciu(struct irq_data *data)
5890c326387SDavid Daney {
5900c326387SDavid Daney 	int cpu = smp_processor_id();
5910c326387SDavid Daney 	cpumask_t new_affinity;
5920c326387SDavid Daney 
5930c326387SDavid Daney 	if (!cpumask_test_cpu(cpu, data->affinity))
5940c326387SDavid Daney 		return;
5950c326387SDavid Daney 
5960c326387SDavid Daney 	if (cpumask_weight(data->affinity) > 1) {
5970c326387SDavid Daney 		/*
5980c326387SDavid Daney 		 * It has multi CPU affinity, just remove this CPU
5990c326387SDavid Daney 		 * from the affinity set.
6000c326387SDavid Daney 		 */
6010c326387SDavid Daney 		cpumask_copy(&new_affinity, data->affinity);
6020c326387SDavid Daney 		cpumask_clear_cpu(cpu, &new_affinity);
6030c326387SDavid Daney 	} else {
6040c326387SDavid Daney 		/* Otherwise, put it on lowest numbered online CPU. */
6050c326387SDavid Daney 		cpumask_clear(&new_affinity);
6060c326387SDavid Daney 		cpumask_set_cpu(cpumask_first(cpu_online_mask), &new_affinity);
6070c326387SDavid Daney 	}
6080c326387SDavid Daney 	__irq_set_affinity_locked(data, &new_affinity);
6090c326387SDavid Daney }
6100c326387SDavid Daney 
6110c326387SDavid Daney static int octeon_irq_ciu_set_affinity(struct irq_data *data,
6120c326387SDavid Daney 				       const struct cpumask *dest, bool force)
6130c326387SDavid Daney {
6140c326387SDavid Daney 	int cpu;
6155b7cd6fdSThomas Gleixner 	bool enable_one = !irqd_irq_disabled(data) && !irqd_irq_masked(data);
616b6b74d54SDavid Daney 	unsigned long flags;
6170c326387SDavid Daney 	union octeon_ciu_chip_data cd;
6180c326387SDavid Daney 
6190c326387SDavid Daney 	cd.p = data->chip_data;
6205b3b1688SDavid Daney 
6215aae1fd4SDavid Daney 	/*
6225aae1fd4SDavid Daney 	 * For non-v2 CIU, we will allow only single CPU affinity.
6235aae1fd4SDavid Daney 	 * This removes the need to do locking in the .ack/.eoi
6245aae1fd4SDavid Daney 	 * functions.
6255aae1fd4SDavid Daney 	 */
6265aae1fd4SDavid Daney 	if (cpumask_weight(dest) != 1)
6275aae1fd4SDavid Daney 		return -EINVAL;
6285aae1fd4SDavid Daney 
6295b7cd6fdSThomas Gleixner 	if (!enable_one)
6300c326387SDavid Daney 		return 0;
6310c326387SDavid Daney 
6320c326387SDavid Daney 	if (cd.s.line == 0) {
63339961422SDavid Daney 		raw_spin_lock_irqsave(&octeon_irq_ciu0_lock, flags);
6345b3b1688SDavid Daney 		for_each_online_cpu(cpu) {
635cd847b78SDavid Daney 			int coreid = octeon_coreid_for_cpu(cpu);
6360c326387SDavid Daney 			unsigned long *pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu);
6370c326387SDavid Daney 
6385aae1fd4SDavid Daney 			if (cpumask_test_cpu(cpu, dest) && enable_one) {
6395b7cd6fdSThomas Gleixner 				enable_one = false;
6400c326387SDavid Daney 				set_bit(cd.s.bit, pen);
6415aae1fd4SDavid Daney 			} else {
6420c326387SDavid Daney 				clear_bit(cd.s.bit, pen);
6435aae1fd4SDavid Daney 			}
6440c326387SDavid Daney 			cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), *pen);
6455b3b1688SDavid Daney 		}
64639961422SDavid Daney 		raw_spin_unlock_irqrestore(&octeon_irq_ciu0_lock, flags);
6470c326387SDavid Daney 	} else {
6480c326387SDavid Daney 		raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags);
6490c326387SDavid Daney 		for_each_online_cpu(cpu) {
6500c326387SDavid Daney 			int coreid = octeon_coreid_for_cpu(cpu);
6510c326387SDavid Daney 			unsigned long *pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu);
652d5dedd45SYinghai Lu 
6530c326387SDavid Daney 			if (cpumask_test_cpu(cpu, dest) && enable_one) {
6545b7cd6fdSThomas Gleixner 				enable_one = false;
6550c326387SDavid Daney 				set_bit(cd.s.bit, pen);
6560c326387SDavid Daney 			} else {
6570c326387SDavid Daney 				clear_bit(cd.s.bit, pen);
6580c326387SDavid Daney 			}
6590c326387SDavid Daney 			cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen);
6600c326387SDavid Daney 		}
6610c326387SDavid Daney 		raw_spin_unlock_irqrestore(&octeon_irq_ciu1_lock, flags);
6620c326387SDavid Daney 	}
663d5dedd45SYinghai Lu 	return 0;
6645b3b1688SDavid Daney }
665cd847b78SDavid Daney 
666cd847b78SDavid Daney /*
667cd847b78SDavid Daney  * Set affinity for the irq for chips that have the EN*_W1{S,C}
668cd847b78SDavid Daney  * registers.
669cd847b78SDavid Daney  */
6700c326387SDavid Daney static int octeon_irq_ciu_set_affinity_v2(struct irq_data *data,
6710c326387SDavid Daney 					  const struct cpumask *dest,
6720c326387SDavid Daney 					  bool force)
673cd847b78SDavid Daney {
674cd847b78SDavid Daney 	int cpu;
6755b7cd6fdSThomas Gleixner 	bool enable_one = !irqd_irq_disabled(data) && !irqd_irq_masked(data);
6760c326387SDavid Daney 	u64 mask;
6770c326387SDavid Daney 	union octeon_ciu_chip_data cd;
6785aae1fd4SDavid Daney 
6795b7cd6fdSThomas Gleixner 	if (!enable_one)
6800c326387SDavid Daney 		return 0;
6810c326387SDavid Daney 
6820c326387SDavid Daney 	cd.p = data->chip_data;
6830c326387SDavid Daney 	mask = 1ull << cd.s.bit;
6840c326387SDavid Daney 
6850c326387SDavid Daney 	if (cd.s.line == 0) {
686cd847b78SDavid Daney 		for_each_online_cpu(cpu) {
6870c326387SDavid Daney 			unsigned long *pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu);
6880c326387SDavid Daney 			int index = octeon_coreid_for_cpu(cpu) * 2;
6895aae1fd4SDavid Daney 			if (cpumask_test_cpu(cpu, dest) && enable_one) {
6905b7cd6fdSThomas Gleixner 				enable_one = false;
6910c326387SDavid Daney 				set_bit(cd.s.bit, pen);
692cd847b78SDavid Daney 				cvmx_write_csr(CVMX_CIU_INTX_EN0_W1S(index), mask);
6935aae1fd4SDavid Daney 			} else {
6940c326387SDavid Daney 				clear_bit(cd.s.bit, pen);
695cd847b78SDavid Daney 				cvmx_write_csr(CVMX_CIU_INTX_EN0_W1C(index), mask);
696cd847b78SDavid Daney 			}
6975aae1fd4SDavid Daney 		}
6980c326387SDavid Daney 	} else {
6990c326387SDavid Daney 		for_each_online_cpu(cpu) {
7000c326387SDavid Daney 			unsigned long *pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu);
7010c326387SDavid Daney 			int index = octeon_coreid_for_cpu(cpu) * 2 + 1;
7020c326387SDavid Daney 			if (cpumask_test_cpu(cpu, dest) && enable_one) {
7035b7cd6fdSThomas Gleixner 				enable_one = false;
7040c326387SDavid Daney 				set_bit(cd.s.bit, pen);
7050c326387SDavid Daney 				cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(index), mask);
7060c326387SDavid Daney 			} else {
7070c326387SDavid Daney 				clear_bit(cd.s.bit, pen);
7080c326387SDavid Daney 				cvmx_write_csr(CVMX_CIU_INTX_EN1_W1C(index), mask);
7090c326387SDavid Daney 			}
7100c326387SDavid Daney 		}
7110c326387SDavid Daney 	}
712cd847b78SDavid Daney 	return 0;
713cd847b78SDavid Daney }
7145b3b1688SDavid Daney #endif
7155b3b1688SDavid Daney 
716cd847b78SDavid Daney /*
7170c326387SDavid Daney  * The v1 CIU code already masks things, so supply a dummy version to
7180c326387SDavid Daney  * the core chip code.
7190c326387SDavid Daney  */
7200c326387SDavid Daney static void octeon_irq_dummy_mask(struct irq_data *data)
7210c326387SDavid Daney {
7220c326387SDavid Daney }
7230c326387SDavid Daney 
7240c326387SDavid Daney /*
725cd847b78SDavid Daney  * Newer octeon chips have support for lockless CIU operation.
726cd847b78SDavid Daney  */
7270c326387SDavid Daney static struct irq_chip octeon_irq_chip_ciu_v2 = {
7280c326387SDavid Daney 	.name = "CIU",
7290c326387SDavid Daney 	.irq_enable = octeon_irq_ciu_enable_v2,
7300c326387SDavid Daney 	.irq_disable = octeon_irq_ciu_disable_all_v2,
7310c326387SDavid Daney 	.irq_ack = octeon_irq_ciu_ack,
7320c326387SDavid Daney 	.irq_mask = octeon_irq_ciu_disable_local_v2,
7330c326387SDavid Daney 	.irq_unmask = octeon_irq_ciu_enable_v2,
7345b3b1688SDavid Daney #ifdef CONFIG_SMP
7350c326387SDavid Daney 	.irq_set_affinity = octeon_irq_ciu_set_affinity_v2,
7360c326387SDavid Daney 	.irq_cpu_offline = octeon_irq_cpu_offline_ciu,
7370c326387SDavid Daney #endif
7380c326387SDavid Daney };
7390c326387SDavid Daney 
7400c326387SDavid Daney static struct irq_chip octeon_irq_chip_ciu = {
7410c326387SDavid Daney 	.name = "CIU",
7420c326387SDavid Daney 	.irq_enable = octeon_irq_ciu_enable,
7430c326387SDavid Daney 	.irq_disable = octeon_irq_ciu_disable_all,
7440c326387SDavid Daney 	.irq_ack = octeon_irq_ciu_ack,
745a339aef9SDavid Daney 	.irq_mask = octeon_irq_dummy_mask,
7460c326387SDavid Daney #ifdef CONFIG_SMP
7470c326387SDavid Daney 	.irq_set_affinity = octeon_irq_ciu_set_affinity,
7480c326387SDavid Daney 	.irq_cpu_offline = octeon_irq_cpu_offline_ciu,
7495b3b1688SDavid Daney #endif
7505b3b1688SDavid Daney };
7515b3b1688SDavid Daney 
7525aae1fd4SDavid Daney /* The mbox versions don't do any affinity or round-robin. */
7530c326387SDavid Daney static struct irq_chip octeon_irq_chip_ciu_mbox_v2 = {
7540c326387SDavid Daney 	.name = "CIU-M",
7550c326387SDavid Daney 	.irq_enable = octeon_irq_ciu_enable_all_v2,
7560c326387SDavid Daney 	.irq_disable = octeon_irq_ciu_disable_all_v2,
7570c326387SDavid Daney 	.irq_ack = octeon_irq_ciu_disable_local_v2,
7580c326387SDavid Daney 	.irq_eoi = octeon_irq_ciu_enable_local_v2,
7590c326387SDavid Daney 
7605b7cd6fdSThomas Gleixner 	.irq_cpu_online = octeon_irq_ciu_enable_local_v2,
7615b7cd6fdSThomas Gleixner 	.irq_cpu_offline = octeon_irq_ciu_disable_local_v2,
7625b7cd6fdSThomas Gleixner 	.flags = IRQCHIP_ONOFFLINE_ENABLED,
76386568dc4SDavid Daney };
76486568dc4SDavid Daney 
7650c326387SDavid Daney static struct irq_chip octeon_irq_chip_ciu_mbox = {
7660c326387SDavid Daney 	.name = "CIU-M",
7670c326387SDavid Daney 	.irq_enable = octeon_irq_ciu_enable_all,
7680c326387SDavid Daney 	.irq_disable = octeon_irq_ciu_disable_all,
7690c326387SDavid Daney 
7705b7cd6fdSThomas Gleixner 	.irq_cpu_online = octeon_irq_ciu_enable_local,
7715b7cd6fdSThomas Gleixner 	.irq_cpu_offline = octeon_irq_ciu_disable_local,
7725b7cd6fdSThomas Gleixner 	.flags = IRQCHIP_ONOFFLINE_ENABLED,
7735aae1fd4SDavid Daney };
7745b3b1688SDavid Daney 
7756d1ab4c2SDavid Daney static struct irq_chip octeon_irq_chip_ciu_gpio_v2 = {
7766d1ab4c2SDavid Daney 	.name = "CIU-GPIO",
7776d1ab4c2SDavid Daney 	.irq_enable = octeon_irq_ciu_enable_gpio_v2,
7786d1ab4c2SDavid Daney 	.irq_disable = octeon_irq_ciu_disable_gpio_v2,
7796d1ab4c2SDavid Daney 	.irq_ack = octeon_irq_ciu_gpio_ack,
7806d1ab4c2SDavid Daney 	.irq_mask = octeon_irq_ciu_disable_local_v2,
7816d1ab4c2SDavid Daney 	.irq_unmask = octeon_irq_ciu_enable_v2,
7826d1ab4c2SDavid Daney 	.irq_set_type = octeon_irq_ciu_gpio_set_type,
7836d1ab4c2SDavid Daney #ifdef CONFIG_SMP
7846d1ab4c2SDavid Daney 	.irq_set_affinity = octeon_irq_ciu_set_affinity_v2,
7856d1ab4c2SDavid Daney #endif
7866d1ab4c2SDavid Daney 	.flags = IRQCHIP_SET_TYPE_MASKED,
7876d1ab4c2SDavid Daney };
7886d1ab4c2SDavid Daney 
7896d1ab4c2SDavid Daney static struct irq_chip octeon_irq_chip_ciu_gpio = {
7906d1ab4c2SDavid Daney 	.name = "CIU-GPIO",
7916d1ab4c2SDavid Daney 	.irq_enable = octeon_irq_ciu_enable_gpio,
7926d1ab4c2SDavid Daney 	.irq_disable = octeon_irq_ciu_disable_gpio,
7936d1ab4c2SDavid Daney 	.irq_mask = octeon_irq_dummy_mask,
7946d1ab4c2SDavid Daney 	.irq_ack = octeon_irq_ciu_gpio_ack,
7956d1ab4c2SDavid Daney 	.irq_set_type = octeon_irq_ciu_gpio_set_type,
7966d1ab4c2SDavid Daney #ifdef CONFIG_SMP
7976d1ab4c2SDavid Daney 	.irq_set_affinity = octeon_irq_ciu_set_affinity,
7986d1ab4c2SDavid Daney #endif
7996d1ab4c2SDavid Daney 	.flags = IRQCHIP_SET_TYPE_MASKED,
8006d1ab4c2SDavid Daney };
8016d1ab4c2SDavid Daney 
8025b3b1688SDavid Daney /*
8030c326387SDavid Daney  * Watchdog interrupts are special.  They are associated with a single
8040c326387SDavid Daney  * core, so we hardwire the affinity to that core.
8055b3b1688SDavid Daney  */
8060c326387SDavid Daney static void octeon_irq_ciu_wd_enable(struct irq_data *data)
8075b3b1688SDavid Daney {
8085b3b1688SDavid Daney 	unsigned long flags;
8090c326387SDavid Daney 	unsigned long *pen;
8100c326387SDavid Daney 	int coreid = data->irq - OCTEON_IRQ_WDOG0;	/* Bit 0-63 of EN1 */
8110c326387SDavid Daney 	int cpu = octeon_cpu_for_coreid(coreid);
8125b3b1688SDavid Daney 
81339961422SDavid Daney 	raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags);
8140c326387SDavid Daney 	pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu);
8150c326387SDavid Daney 	set_bit(coreid, pen);
8160c326387SDavid Daney 	cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen);
81739961422SDavid Daney 	raw_spin_unlock_irqrestore(&octeon_irq_ciu1_lock, flags);
8185b3b1688SDavid Daney }
8195b3b1688SDavid Daney 
8205aae1fd4SDavid Daney /*
8215aae1fd4SDavid Daney  * Watchdog interrupts are special.  They are associated with a single
8225aae1fd4SDavid Daney  * core, so we hardwire the affinity to that core.
8235aae1fd4SDavid Daney  */
8240c326387SDavid Daney static void octeon_irq_ciu1_wd_enable_v2(struct irq_data *data)
8255aae1fd4SDavid Daney {
8260c326387SDavid Daney 	int coreid = data->irq - OCTEON_IRQ_WDOG0;
8270c326387SDavid Daney 	int cpu = octeon_cpu_for_coreid(coreid);
8285aae1fd4SDavid Daney 
8290c326387SDavid Daney 	set_bit(coreid, &per_cpu(octeon_irq_ciu1_en_mirror, cpu));
8300c326387SDavid Daney 	cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(coreid * 2 + 1), 1ull << coreid);
8315aae1fd4SDavid Daney }
8325aae1fd4SDavid Daney 
8330c326387SDavid Daney 
8340c326387SDavid Daney static struct irq_chip octeon_irq_chip_ciu_wd_v2 = {
8350c326387SDavid Daney 	.name = "CIU-W",
8360c326387SDavid Daney 	.irq_enable = octeon_irq_ciu1_wd_enable_v2,
8370c326387SDavid Daney 	.irq_disable = octeon_irq_ciu_disable_all_v2,
8380c326387SDavid Daney 	.irq_mask = octeon_irq_ciu_disable_local_v2,
8390c326387SDavid Daney 	.irq_unmask = octeon_irq_ciu_enable_local_v2,
8400c326387SDavid Daney };
8410c326387SDavid Daney 
8420c326387SDavid Daney static struct irq_chip octeon_irq_chip_ciu_wd = {
8430c326387SDavid Daney 	.name = "CIU-W",
8440c326387SDavid Daney 	.irq_enable = octeon_irq_ciu_wd_enable,
8450c326387SDavid Daney 	.irq_disable = octeon_irq_ciu_disable_all,
8460c326387SDavid Daney 	.irq_mask = octeon_irq_dummy_mask,
8470c326387SDavid Daney };
8480c326387SDavid Daney 
849a0c16582SDavid Daney static bool octeon_irq_ciu_is_edge(unsigned int line, unsigned int bit)
850a0c16582SDavid Daney {
851a0c16582SDavid Daney 	bool edge = false;
852a0c16582SDavid Daney 
853a0c16582SDavid Daney 	if (line == 0)
854a0c16582SDavid Daney 		switch (bit) {
855a0c16582SDavid Daney 		case 48 ... 49: /* GMX DRP */
856a0c16582SDavid Daney 		case 50: /* IPD_DRP */
857a0c16582SDavid Daney 		case 52 ... 55: /* Timers */
858a0c16582SDavid Daney 		case 58: /* MPI */
859a0c16582SDavid Daney 			edge = true;
860a0c16582SDavid Daney 			break;
861a0c16582SDavid Daney 		default:
862a0c16582SDavid Daney 			break;
863a0c16582SDavid Daney 		}
864a0c16582SDavid Daney 	else /* line == 1 */
865a0c16582SDavid Daney 		switch (bit) {
866a0c16582SDavid Daney 		case 47: /* PTP */
867a0c16582SDavid Daney 			edge = true;
868a0c16582SDavid Daney 			break;
869a0c16582SDavid Daney 		default:
870a0c16582SDavid Daney 			break;
871a0c16582SDavid Daney 		}
872a0c16582SDavid Daney 	return edge;
873a0c16582SDavid Daney }
874a0c16582SDavid Daney 
875a0c16582SDavid Daney struct octeon_irq_gpio_domain_data {
876a0c16582SDavid Daney 	unsigned int base_hwirq;
877a0c16582SDavid Daney };
878a0c16582SDavid Daney 
879a0c16582SDavid Daney static int octeon_irq_gpio_xlat(struct irq_domain *d,
880a0c16582SDavid Daney 				struct device_node *node,
881a0c16582SDavid Daney 				const u32 *intspec,
882a0c16582SDavid Daney 				unsigned int intsize,
883a0c16582SDavid Daney 				unsigned long *out_hwirq,
884a0c16582SDavid Daney 				unsigned int *out_type)
885a0c16582SDavid Daney {
886a0c16582SDavid Daney 	unsigned int type;
887a0c16582SDavid Daney 	unsigned int pin;
888a0c16582SDavid Daney 	unsigned int trigger;
889a0c16582SDavid Daney 
890a0c16582SDavid Daney 	if (d->of_node != node)
891a0c16582SDavid Daney 		return -EINVAL;
892a0c16582SDavid Daney 
893a0c16582SDavid Daney 	if (intsize < 2)
894a0c16582SDavid Daney 		return -EINVAL;
895a0c16582SDavid Daney 
896a0c16582SDavid Daney 	pin = intspec[0];
897a0c16582SDavid Daney 	if (pin >= 16)
898a0c16582SDavid Daney 		return -EINVAL;
899a0c16582SDavid Daney 
900a0c16582SDavid Daney 	trigger = intspec[1];
901a0c16582SDavid Daney 
902a0c16582SDavid Daney 	switch (trigger) {
903a0c16582SDavid Daney 	case 1:
904a0c16582SDavid Daney 		type = IRQ_TYPE_EDGE_RISING;
905a0c16582SDavid Daney 		break;
906a0c16582SDavid Daney 	case 2:
907a0c16582SDavid Daney 		type = IRQ_TYPE_EDGE_FALLING;
908a0c16582SDavid Daney 		break;
909a0c16582SDavid Daney 	case 4:
910a0c16582SDavid Daney 		type = IRQ_TYPE_LEVEL_HIGH;
911a0c16582SDavid Daney 		break;
912a0c16582SDavid Daney 	case 8:
913a0c16582SDavid Daney 		type = IRQ_TYPE_LEVEL_LOW;
914a0c16582SDavid Daney 		break;
915a0c16582SDavid Daney 	default:
916a0c16582SDavid Daney 		pr_err("Error: (%s) Invalid irq trigger specification: %x\n",
917a0c16582SDavid Daney 		       node->name,
918a0c16582SDavid Daney 		       trigger);
919a0c16582SDavid Daney 		type = IRQ_TYPE_LEVEL_LOW;
920a0c16582SDavid Daney 		break;
921a0c16582SDavid Daney 	}
922a0c16582SDavid Daney 	*out_type = type;
923*87161ccdSDavid Daney 	*out_hwirq = pin;
924a0c16582SDavid Daney 
925a0c16582SDavid Daney 	return 0;
926a0c16582SDavid Daney }
927a0c16582SDavid Daney 
928a0c16582SDavid Daney static int octeon_irq_ciu_xlat(struct irq_domain *d,
929a0c16582SDavid Daney 			       struct device_node *node,
930a0c16582SDavid Daney 			       const u32 *intspec,
931a0c16582SDavid Daney 			       unsigned int intsize,
932a0c16582SDavid Daney 			       unsigned long *out_hwirq,
933a0c16582SDavid Daney 			       unsigned int *out_type)
934a0c16582SDavid Daney {
935a0c16582SDavid Daney 	unsigned int ciu, bit;
936a0c16582SDavid Daney 
937a0c16582SDavid Daney 	ciu = intspec[0];
938a0c16582SDavid Daney 	bit = intspec[1];
939a0c16582SDavid Daney 
940a0c16582SDavid Daney 	if (ciu > 1 || bit > 63)
941a0c16582SDavid Daney 		return -EINVAL;
942a0c16582SDavid Daney 
943a0c16582SDavid Daney 	/* These are the GPIO lines */
944a0c16582SDavid Daney 	if (ciu == 0 && bit >= 16 && bit < 32)
945a0c16582SDavid Daney 		return -EINVAL;
946a0c16582SDavid Daney 
947a0c16582SDavid Daney 	*out_hwirq = (ciu << 6) | bit;
948a0c16582SDavid Daney 	*out_type = 0;
949a0c16582SDavid Daney 
950a0c16582SDavid Daney 	return 0;
951a0c16582SDavid Daney }
952a0c16582SDavid Daney 
953a0c16582SDavid Daney static struct irq_chip *octeon_irq_ciu_chip;
954a0c16582SDavid Daney static struct irq_chip *octeon_irq_gpio_chip;
955a0c16582SDavid Daney 
956a0c16582SDavid Daney static bool octeon_irq_virq_in_range(unsigned int virq)
957a0c16582SDavid Daney {
958a0c16582SDavid Daney 	/* We cannot let it overflow the mapping array. */
959a0c16582SDavid Daney 	if (virq < (1ul << 8 * sizeof(octeon_irq_ciu_to_irq[0][0])))
960a0c16582SDavid Daney 		return true;
961a0c16582SDavid Daney 
962a0c16582SDavid Daney 	WARN_ONCE(true, "virq out of range %u.\n", virq);
963a0c16582SDavid Daney 	return false;
964a0c16582SDavid Daney }
965a0c16582SDavid Daney 
966a0c16582SDavid Daney static int octeon_irq_ciu_map(struct irq_domain *d,
967a0c16582SDavid Daney 			      unsigned int virq, irq_hw_number_t hw)
968a0c16582SDavid Daney {
969a0c16582SDavid Daney 	unsigned int line = hw >> 6;
970a0c16582SDavid Daney 	unsigned int bit = hw & 63;
971a0c16582SDavid Daney 
972a0c16582SDavid Daney 	if (!octeon_irq_virq_in_range(virq))
973a0c16582SDavid Daney 		return -EINVAL;
974a0c16582SDavid Daney 
975a0c16582SDavid Daney 	if (line > 1 || octeon_irq_ciu_to_irq[line][bit] != 0)
976a0c16582SDavid Daney 		return -EINVAL;
977a0c16582SDavid Daney 
978a0c16582SDavid Daney 	if (octeon_irq_ciu_is_edge(line, bit))
979a0c16582SDavid Daney 		octeon_irq_set_ciu_mapping(virq, line, bit,
980a0c16582SDavid Daney 					   octeon_irq_ciu_chip,
981a0c16582SDavid Daney 					   handle_edge_irq);
982a0c16582SDavid Daney 	else
983a0c16582SDavid Daney 		octeon_irq_set_ciu_mapping(virq, line, bit,
984a0c16582SDavid Daney 					   octeon_irq_ciu_chip,
985a0c16582SDavid Daney 					   handle_level_irq);
986a0c16582SDavid Daney 
987a0c16582SDavid Daney 	return 0;
988a0c16582SDavid Daney }
989a0c16582SDavid Daney 
990a0c16582SDavid Daney static int octeon_irq_gpio_map(struct irq_domain *d,
991a0c16582SDavid Daney 			       unsigned int virq, irq_hw_number_t hw)
992a0c16582SDavid Daney {
993*87161ccdSDavid Daney 	struct octeon_irq_gpio_domain_data *gpiod = d->host_data;
994*87161ccdSDavid Daney 	unsigned int line, bit;
995a0c16582SDavid Daney 
996a0c16582SDavid Daney 	if (!octeon_irq_virq_in_range(virq))
997a0c16582SDavid Daney 		return -EINVAL;
998a0c16582SDavid Daney 
999*87161ccdSDavid Daney 	hw += gpiod->base_hwirq;
1000*87161ccdSDavid Daney 	line = hw >> 6;
1001*87161ccdSDavid Daney 	bit = hw & 63;
1002a0c16582SDavid Daney 	if (line > 1 || octeon_irq_ciu_to_irq[line][bit] != 0)
1003a0c16582SDavid Daney 		return -EINVAL;
1004a0c16582SDavid Daney 
1005a0c16582SDavid Daney 	octeon_irq_set_ciu_mapping(virq, line, bit,
1006a0c16582SDavid Daney 				   octeon_irq_gpio_chip,
1007a0c16582SDavid Daney 				   octeon_irq_handle_gpio);
1008a0c16582SDavid Daney 	return 0;
1009a0c16582SDavid Daney }
1010a0c16582SDavid Daney 
1011a0c16582SDavid Daney static struct irq_domain_ops octeon_irq_domain_ciu_ops = {
1012a0c16582SDavid Daney 	.map = octeon_irq_ciu_map,
1013a0c16582SDavid Daney 	.xlate = octeon_irq_ciu_xlat,
1014a0c16582SDavid Daney };
1015a0c16582SDavid Daney 
1016a0c16582SDavid Daney static struct irq_domain_ops octeon_irq_domain_gpio_ops = {
1017a0c16582SDavid Daney 	.map = octeon_irq_gpio_map,
1018a0c16582SDavid Daney 	.xlate = octeon_irq_gpio_xlat,
1019a0c16582SDavid Daney };
1020a0c16582SDavid Daney 
10210c326387SDavid Daney static void octeon_irq_ip2_v1(void)
10225b3b1688SDavid Daney {
10230c326387SDavid Daney 	const unsigned long core_id = cvmx_get_core_num();
10240c326387SDavid Daney 	u64 ciu_sum = cvmx_read_csr(CVMX_CIU_INTX_SUM0(core_id * 2));
1025cd847b78SDavid Daney 
10260c326387SDavid Daney 	ciu_sum &= __get_cpu_var(octeon_irq_ciu0_en_mirror);
10270c326387SDavid Daney 	clear_c0_status(STATUSF_IP2);
10280c326387SDavid Daney 	if (likely(ciu_sum)) {
10290c326387SDavid Daney 		int bit = fls64(ciu_sum) - 1;
10300c326387SDavid Daney 		int irq = octeon_irq_ciu_to_irq[0][bit];
10310c326387SDavid Daney 		if (likely(irq))
10320c326387SDavid Daney 			do_IRQ(irq);
10330c326387SDavid Daney 		else
10340c326387SDavid Daney 			spurious_interrupt();
10355aae1fd4SDavid Daney 	} else {
10360c326387SDavid Daney 		spurious_interrupt();
10375aae1fd4SDavid Daney 	}
10380c326387SDavid Daney 	set_c0_status(STATUSF_IP2);
10395b3b1688SDavid Daney }
1040cd847b78SDavid Daney 
10410c326387SDavid Daney static void octeon_irq_ip2_v2(void)
1042cd847b78SDavid Daney {
10430c326387SDavid Daney 	const unsigned long core_id = cvmx_get_core_num();
10440c326387SDavid Daney 	u64 ciu_sum = cvmx_read_csr(CVMX_CIU_INTX_SUM0(core_id * 2));
10450c326387SDavid Daney 
10460c326387SDavid Daney 	ciu_sum &= __get_cpu_var(octeon_irq_ciu0_en_mirror);
10470c326387SDavid Daney 	if (likely(ciu_sum)) {
10480c326387SDavid Daney 		int bit = fls64(ciu_sum) - 1;
10490c326387SDavid Daney 		int irq = octeon_irq_ciu_to_irq[0][bit];
10500c326387SDavid Daney 		if (likely(irq))
10510c326387SDavid Daney 			do_IRQ(irq);
10520c326387SDavid Daney 		else
10530c326387SDavid Daney 			spurious_interrupt();
10545aae1fd4SDavid Daney 	} else {
10550c326387SDavid Daney 		spurious_interrupt();
1056cd847b78SDavid Daney 	}
10575aae1fd4SDavid Daney }
10580c326387SDavid Daney static void octeon_irq_ip3_v1(void)
10590c326387SDavid Daney {
10600c326387SDavid Daney 	u64 ciu_sum = cvmx_read_csr(CVMX_CIU_INT_SUM1);
10615b3b1688SDavid Daney 
10620c326387SDavid Daney 	ciu_sum &= __get_cpu_var(octeon_irq_ciu1_en_mirror);
10630c326387SDavid Daney 	clear_c0_status(STATUSF_IP3);
10640c326387SDavid Daney 	if (likely(ciu_sum)) {
10650c326387SDavid Daney 		int bit = fls64(ciu_sum) - 1;
10660c326387SDavid Daney 		int irq = octeon_irq_ciu_to_irq[1][bit];
10670c326387SDavid Daney 		if (likely(irq))
10680c326387SDavid Daney 			do_IRQ(irq);
10690c326387SDavid Daney 		else
10700c326387SDavid Daney 			spurious_interrupt();
10710c326387SDavid Daney 	} else {
10720c326387SDavid Daney 		spurious_interrupt();
10730c326387SDavid Daney 	}
10740c326387SDavid Daney 	set_c0_status(STATUSF_IP3);
10750c326387SDavid Daney }
10760c326387SDavid Daney 
10770c326387SDavid Daney static void octeon_irq_ip3_v2(void)
10780c326387SDavid Daney {
10790c326387SDavid Daney 	u64 ciu_sum = cvmx_read_csr(CVMX_CIU_INT_SUM1);
10800c326387SDavid Daney 
10810c326387SDavid Daney 	ciu_sum &= __get_cpu_var(octeon_irq_ciu1_en_mirror);
10820c326387SDavid Daney 	if (likely(ciu_sum)) {
10830c326387SDavid Daney 		int bit = fls64(ciu_sum) - 1;
10840c326387SDavid Daney 		int irq = octeon_irq_ciu_to_irq[1][bit];
10850c326387SDavid Daney 		if (likely(irq))
10860c326387SDavid Daney 			do_IRQ(irq);
10870c326387SDavid Daney 		else
10880c326387SDavid Daney 			spurious_interrupt();
10890c326387SDavid Daney 	} else {
10900c326387SDavid Daney 		spurious_interrupt();
10910c326387SDavid Daney 	}
10920c326387SDavid Daney }
10930c326387SDavid Daney 
10940c326387SDavid Daney static void octeon_irq_ip4_mask(void)
10950c326387SDavid Daney {
10960c326387SDavid Daney 	clear_c0_status(STATUSF_IP4);
10970c326387SDavid Daney 	spurious_interrupt();
10980c326387SDavid Daney }
10990c326387SDavid Daney 
11000c326387SDavid Daney static void (*octeon_irq_ip2)(void);
11010c326387SDavid Daney static void (*octeon_irq_ip3)(void);
11020c326387SDavid Daney static void (*octeon_irq_ip4)(void);
11030c326387SDavid Daney 
11040c326387SDavid Daney void __cpuinitdata (*octeon_irq_setup_secondary)(void);
11050c326387SDavid Daney 
11060c326387SDavid Daney static void __cpuinit octeon_irq_percpu_enable(void)
11070c326387SDavid Daney {
11080c326387SDavid Daney 	irq_cpu_online();
11090c326387SDavid Daney }
11100c326387SDavid Daney 
11110c326387SDavid Daney static void __cpuinit octeon_irq_init_ciu_percpu(void)
11120c326387SDavid Daney {
11130c326387SDavid Daney 	int coreid = cvmx_get_core_num();
1114cd847b78SDavid Daney 	/*
11150c326387SDavid Daney 	 * Disable All CIU Interrupts. The ones we need will be
11160c326387SDavid Daney 	 * enabled later.  Read the SUM register so we know the write
11170c326387SDavid Daney 	 * completed.
1118cd847b78SDavid Daney 	 */
11190c326387SDavid Daney 	cvmx_write_csr(CVMX_CIU_INTX_EN0((coreid * 2)), 0);
11200c326387SDavid Daney 	cvmx_write_csr(CVMX_CIU_INTX_EN0((coreid * 2 + 1)), 0);
11210c326387SDavid Daney 	cvmx_write_csr(CVMX_CIU_INTX_EN1((coreid * 2)), 0);
11220c326387SDavid Daney 	cvmx_write_csr(CVMX_CIU_INTX_EN1((coreid * 2 + 1)), 0);
11230c326387SDavid Daney 	cvmx_read_csr(CVMX_CIU_INTX_SUM0((coreid * 2)));
11240c326387SDavid Daney }
1125cd847b78SDavid Daney 
11260c326387SDavid Daney static void __cpuinit octeon_irq_setup_secondary_ciu(void)
11270c326387SDavid Daney {
11285b3b1688SDavid Daney 
11290c326387SDavid Daney 	__get_cpu_var(octeon_irq_ciu0_en_mirror) = 0;
11300c326387SDavid Daney 	__get_cpu_var(octeon_irq_ciu1_en_mirror) = 0;
11315aae1fd4SDavid Daney 
11320c326387SDavid Daney 	octeon_irq_init_ciu_percpu();
11330c326387SDavid Daney 	octeon_irq_percpu_enable();
11345aae1fd4SDavid Daney 
11350c326387SDavid Daney 	/* Enable the CIU lines */
11360c326387SDavid Daney 	set_c0_status(STATUSF_IP3 | STATUSF_IP2);
11370c326387SDavid Daney 	clear_c0_status(STATUSF_IP4);
11380c326387SDavid Daney }
11390c326387SDavid Daney 
11400c326387SDavid Daney static void __init octeon_irq_init_ciu(void)
11410c326387SDavid Daney {
11420c326387SDavid Daney 	unsigned int i;
11430c326387SDavid Daney 	struct irq_chip *chip;
11440c326387SDavid Daney 	struct irq_chip *chip_mbox;
11450c326387SDavid Daney 	struct irq_chip *chip_wd;
1146a0c16582SDavid Daney 	struct device_node *gpio_node;
1147a0c16582SDavid Daney 	struct device_node *ciu_node;
1148*87161ccdSDavid Daney 	struct irq_domain *ciu_domain = NULL;
11490c326387SDavid Daney 
11500c326387SDavid Daney 	octeon_irq_init_ciu_percpu();
11510c326387SDavid Daney 	octeon_irq_setup_secondary = octeon_irq_setup_secondary_ciu;
11520c326387SDavid Daney 
11530c326387SDavid Daney 	if (OCTEON_IS_MODEL(OCTEON_CN58XX_PASS2_X) ||
11540c326387SDavid Daney 	    OCTEON_IS_MODEL(OCTEON_CN56XX_PASS2_X) ||
11550c326387SDavid Daney 	    OCTEON_IS_MODEL(OCTEON_CN52XX_PASS2_X) ||
11560c326387SDavid Daney 	    OCTEON_IS_MODEL(OCTEON_CN6XXX)) {
11570c326387SDavid Daney 		octeon_irq_ip2 = octeon_irq_ip2_v2;
11580c326387SDavid Daney 		octeon_irq_ip3 = octeon_irq_ip3_v2;
11590c326387SDavid Daney 		chip = &octeon_irq_chip_ciu_v2;
11600c326387SDavid Daney 		chip_mbox = &octeon_irq_chip_ciu_mbox_v2;
11610c326387SDavid Daney 		chip_wd = &octeon_irq_chip_ciu_wd_v2;
1162a0c16582SDavid Daney 		octeon_irq_gpio_chip = &octeon_irq_chip_ciu_gpio_v2;
11630c326387SDavid Daney 	} else {
11640c326387SDavid Daney 		octeon_irq_ip2 = octeon_irq_ip2_v1;
11650c326387SDavid Daney 		octeon_irq_ip3 = octeon_irq_ip3_v1;
11660c326387SDavid Daney 		chip = &octeon_irq_chip_ciu;
11670c326387SDavid Daney 		chip_mbox = &octeon_irq_chip_ciu_mbox;
11680c326387SDavid Daney 		chip_wd = &octeon_irq_chip_ciu_wd;
1169a0c16582SDavid Daney 		octeon_irq_gpio_chip = &octeon_irq_chip_ciu_gpio;
11700c326387SDavid Daney 	}
1171a0c16582SDavid Daney 	octeon_irq_ciu_chip = chip;
11720c326387SDavid Daney 	octeon_irq_ip4 = octeon_irq_ip4_mask;
11730c326387SDavid Daney 
11740c326387SDavid Daney 	/* Mips internal */
11750c326387SDavid Daney 	octeon_irq_init_core();
11760c326387SDavid Daney 
1177a0c16582SDavid Daney 	gpio_node = of_find_compatible_node(NULL, NULL, "cavium,octeon-3860-gpio");
1178a0c16582SDavid Daney 	if (gpio_node) {
1179a0c16582SDavid Daney 		struct octeon_irq_gpio_domain_data *gpiod;
1180a0c16582SDavid Daney 
1181a0c16582SDavid Daney 		gpiod = kzalloc(sizeof(*gpiod), GFP_KERNEL);
1182a0c16582SDavid Daney 		if (gpiod) {
1183a0c16582SDavid Daney 			/* gpio domain host_data is the base hwirq number. */
1184a0c16582SDavid Daney 			gpiod->base_hwirq = 16;
1185a0c16582SDavid Daney 			irq_domain_add_linear(gpio_node, 16, &octeon_irq_domain_gpio_ops, gpiod);
1186a0c16582SDavid Daney 			of_node_put(gpio_node);
1187a0c16582SDavid Daney 		} else
1188a0c16582SDavid Daney 			pr_warn("Cannot allocate memory for GPIO irq_domain.\n");
1189a0c16582SDavid Daney 	} else
1190a0c16582SDavid Daney 		pr_warn("Cannot find device node for cavium,octeon-3860-gpio.\n");
1191a0c16582SDavid Daney 
1192a0c16582SDavid Daney 	ciu_node = of_find_compatible_node(NULL, NULL, "cavium,octeon-3860-ciu");
1193a0c16582SDavid Daney 	if (ciu_node) {
1194*87161ccdSDavid Daney 		ciu_domain = irq_domain_add_tree(ciu_node, &octeon_irq_domain_ciu_ops, NULL);
1195a0c16582SDavid Daney 		of_node_put(ciu_node);
1196a0c16582SDavid Daney 	} else
1197*87161ccdSDavid Daney 		panic("Cannot find device node for cavium,octeon-3860-ciu.");
1198*87161ccdSDavid Daney 
1199*87161ccdSDavid Daney 	/* CIU_0 */
1200*87161ccdSDavid Daney 	for (i = 0; i < 16; i++)
1201*87161ccdSDavid Daney 		octeon_irq_force_ciu_mapping(ciu_domain, i + OCTEON_IRQ_WORKQ0, 0, i + 0);
1202*87161ccdSDavid Daney 
1203*87161ccdSDavid Daney 	octeon_irq_set_ciu_mapping(OCTEON_IRQ_MBOX0, 0, 32, chip_mbox, handle_percpu_irq);
1204*87161ccdSDavid Daney 	octeon_irq_set_ciu_mapping(OCTEON_IRQ_MBOX1, 0, 33, chip_mbox, handle_percpu_irq);
1205*87161ccdSDavid Daney 
1206*87161ccdSDavid Daney 	for (i = 0; i < 4; i++)
1207*87161ccdSDavid Daney 		octeon_irq_force_ciu_mapping(ciu_domain, i + OCTEON_IRQ_PCI_INT0, 0, i + 36);
1208*87161ccdSDavid Daney 	for (i = 0; i < 4; i++)
1209*87161ccdSDavid Daney 		octeon_irq_force_ciu_mapping(ciu_domain, i + OCTEON_IRQ_PCI_MSI0, 0, i + 40);
1210*87161ccdSDavid Daney 
1211*87161ccdSDavid Daney 	octeon_irq_force_ciu_mapping(ciu_domain, OCTEON_IRQ_RML, 0, 46);
1212*87161ccdSDavid Daney 	for (i = 0; i < 4; i++)
1213*87161ccdSDavid Daney 		octeon_irq_force_ciu_mapping(ciu_domain, i + OCTEON_IRQ_TIMER0, 0, i + 52);
1214*87161ccdSDavid Daney 
1215*87161ccdSDavid Daney 	octeon_irq_force_ciu_mapping(ciu_domain, OCTEON_IRQ_USB0, 0, 56);
1216*87161ccdSDavid Daney 	octeon_irq_force_ciu_mapping(ciu_domain, OCTEON_IRQ_BOOTDMA, 0, 63);
1217*87161ccdSDavid Daney 
1218*87161ccdSDavid Daney 	/* CIU_1 */
1219*87161ccdSDavid Daney 	for (i = 0; i < 16; i++)
1220*87161ccdSDavid Daney 		octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_WDOG0, 1, i + 0, chip_wd, handle_level_irq);
1221*87161ccdSDavid Daney 
1222*87161ccdSDavid Daney 	octeon_irq_force_ciu_mapping(ciu_domain, OCTEON_IRQ_USB1, 1, 17);
1223a0c16582SDavid Daney 
12240c326387SDavid Daney 	/* Enable the CIU lines */
12250c326387SDavid Daney 	set_c0_status(STATUSF_IP3 | STATUSF_IP2);
12260c326387SDavid Daney 	clear_c0_status(STATUSF_IP4);
12270c326387SDavid Daney }
12285aae1fd4SDavid Daney 
12295b3b1688SDavid Daney void __init arch_init_irq(void)
12305b3b1688SDavid Daney {
12315b3b1688SDavid Daney #ifdef CONFIG_SMP
12325b3b1688SDavid Daney 	/* Set the default affinity to the boot cpu. */
12335b3b1688SDavid Daney 	cpumask_clear(irq_default_affinity);
12345b3b1688SDavid Daney 	cpumask_set_cpu(smp_processor_id(), irq_default_affinity);
12355b3b1688SDavid Daney #endif
12360c326387SDavid Daney 	octeon_irq_init_ciu();
12375b3b1688SDavid Daney }
12385b3b1688SDavid Daney 
12395b3b1688SDavid Daney asmlinkage void plat_irq_dispatch(void)
12405b3b1688SDavid Daney {
12415b3b1688SDavid Daney 	unsigned long cop0_cause;
12425b3b1688SDavid Daney 	unsigned long cop0_status;
12435b3b1688SDavid Daney 
12445b3b1688SDavid Daney 	while (1) {
12455b3b1688SDavid Daney 		cop0_cause = read_c0_cause();
12465b3b1688SDavid Daney 		cop0_status = read_c0_status();
12475b3b1688SDavid Daney 		cop0_cause &= cop0_status;
12485b3b1688SDavid Daney 		cop0_cause &= ST0_IM;
12495b3b1688SDavid Daney 
12500c326387SDavid Daney 		if (unlikely(cop0_cause & STATUSF_IP2))
12510c326387SDavid Daney 			octeon_irq_ip2();
12520c326387SDavid Daney 		else if (unlikely(cop0_cause & STATUSF_IP3))
12530c326387SDavid Daney 			octeon_irq_ip3();
12540c326387SDavid Daney 		else if (unlikely(cop0_cause & STATUSF_IP4))
12550c326387SDavid Daney 			octeon_irq_ip4();
12560c326387SDavid Daney 		else if (likely(cop0_cause))
12575b3b1688SDavid Daney 			do_IRQ(fls(cop0_cause) - 9 + MIPS_CPU_IRQ_BASE);
12580c326387SDavid Daney 		else
12595b3b1688SDavid Daney 			break;
12605b3b1688SDavid Daney 	}
12615b3b1688SDavid Daney }
1262773cb77dSRalf Baechle 
1263773cb77dSRalf Baechle #ifdef CONFIG_HOTPLUG_CPU
1264773cb77dSRalf Baechle 
1265773cb77dSRalf Baechle void fixup_irqs(void)
1266773cb77dSRalf Baechle {
12670c326387SDavid Daney 	irq_cpu_offline();
1268773cb77dSRalf Baechle }
1269773cb77dSRalf Baechle 
1270773cb77dSRalf Baechle #endif /* CONFIG_HOTPLUG_CPU */
1271