xref: /linux/arch/mips/cavium-octeon/octeon-irq.c (revision ba1fc93457e3506aaab88b67b5404a8915ea8233)
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  *
62253e0b9SDavid Daney  * Copyright (C) 2004-2016 Cavium, Inc.
75b3b1688SDavid Daney  */
80c326387SDavid Daney 
964b139f9SDavid Daney #include <linux/of_address.h>
105b3b1688SDavid Daney #include <linux/interrupt.h>
11a0c16582SDavid Daney #include <linux/irqdomain.h>
120c326387SDavid Daney #include <linux/bitops.h>
1364b139f9SDavid Daney #include <linux/of_irq.h>
140c326387SDavid Daney #include <linux/percpu.h>
15a0c16582SDavid Daney #include <linux/slab.h>
160c326387SDavid Daney #include <linux/irq.h>
17631330f5SRalf Baechle #include <linux/smp.h>
18a0c16582SDavid Daney #include <linux/of.h>
195b3b1688SDavid Daney 
205b3b1688SDavid Daney #include <asm/octeon/octeon.h>
2188fd8589SDavid Daney #include <asm/octeon/cvmx-ciu2-defs.h>
22ce210d35SDavid Daney #include <asm/octeon/cvmx-ciu3-defs.h>
235b3b1688SDavid Daney 
240c326387SDavid Daney static DEFINE_PER_CPU(unsigned long, octeon_irq_ciu0_en_mirror);
250c326387SDavid Daney static DEFINE_PER_CPU(unsigned long, octeon_irq_ciu1_en_mirror);
261a7e68f2SDavid Daney static DEFINE_PER_CPU(raw_spinlock_t, octeon_irq_ciu_spinlock);
27ce210d35SDavid Daney static DEFINE_PER_CPU(unsigned int, octeon_irq_ciu3_idt_ip2);
28ce210d35SDavid Daney 
29ce210d35SDavid Daney static DEFINE_PER_CPU(unsigned int, octeon_irq_ciu3_idt_ip3);
30ce210d35SDavid Daney static DEFINE_PER_CPU(struct octeon_ciu3_info *, octeon_ciu3_info);
31ce210d35SDavid Daney #define CIU3_MBOX_PER_CORE 10
32ce210d35SDavid Daney 
33ce210d35SDavid Daney /*
34ce210d35SDavid Daney  * The 8 most significant bits of the intsn identify the interrupt major block.
35ce210d35SDavid Daney  * Each major block might use its own interrupt domain. Thus 256 domains are
36ce210d35SDavid Daney  * needed.
37ce210d35SDavid Daney  */
38ce210d35SDavid Daney #define MAX_CIU3_DOMAINS		256
39ce210d35SDavid Daney 
40ce210d35SDavid Daney typedef irq_hw_number_t (*octeon_ciu3_intsn2hw_t)(struct irq_domain *, unsigned int);
41ce210d35SDavid Daney 
42ce210d35SDavid Daney /* Information for each ciu3 in the system */
43ce210d35SDavid Daney struct octeon_ciu3_info {
44ce210d35SDavid Daney 	u64			ciu3_addr;
45ce210d35SDavid Daney 	int			node;
46ce210d35SDavid Daney 	struct irq_domain	*domain[MAX_CIU3_DOMAINS];
47ce210d35SDavid Daney 	octeon_ciu3_intsn2hw_t	intsn2hw[MAX_CIU3_DOMAINS];
48ce210d35SDavid Daney };
49ce210d35SDavid Daney 
50ce210d35SDavid Daney /* Each ciu3 in the system uses its own data (one ciu3 per node) */
51ce210d35SDavid Daney static struct octeon_ciu3_info	*octeon_ciu3_info_per_node[4];
520c326387SDavid Daney 
5364b139f9SDavid Daney struct octeon_irq_ciu_domain_data {
5464b139f9SDavid Daney 	int num_sum;  /* number of sum registers (2 or 3). */
5564b139f9SDavid Daney };
5664b139f9SDavid Daney 
57ce210d35SDavid Daney /* Register offsets from ciu3_addr */
58ce210d35SDavid Daney #define CIU3_CONST		0x220
59ce210d35SDavid Daney #define CIU3_IDT_CTL(_idt)	((_idt) * 8 + 0x110000)
60ce210d35SDavid Daney #define CIU3_IDT_PP(_idt, _idx)	((_idt) * 32 + (_idx) * 8 + 0x120000)
61ce210d35SDavid Daney #define CIU3_IDT_IO(_idt)	((_idt) * 8 + 0x130000)
62ce210d35SDavid Daney #define CIU3_DEST_PP_INT(_pp_ip) ((_pp_ip) * 8 + 0x200000)
63ce210d35SDavid Daney #define CIU3_DEST_IO_INT(_io)	((_io) * 8 + 0x210000)
64ce210d35SDavid Daney #define CIU3_ISC_CTL(_intsn)	((_intsn) * 8 + 0x80000000)
65ce210d35SDavid Daney #define CIU3_ISC_W1C(_intsn)	((_intsn) * 8 + 0x90000000)
66ce210d35SDavid Daney #define CIU3_ISC_W1S(_intsn)	((_intsn) * 8 + 0xa0000000)
67ce210d35SDavid Daney 
682253e0b9SDavid Daney static __read_mostly int octeon_irq_ciu_to_irq[8][64];
690c326387SDavid Daney 
7064b139f9SDavid Daney struct octeon_ciu_chip_data {
7164b139f9SDavid Daney 	union {
7264b139f9SDavid Daney 		struct {		/* only used for ciu3 */
7364b139f9SDavid Daney 			u64 ciu3_addr;
7464b139f9SDavid Daney 			unsigned int intsn;
7564b139f9SDavid Daney 		};
7664b139f9SDavid Daney 		struct {		/* only used for ciu/ciu2 */
7764b139f9SDavid Daney 			u8 line;
7864b139f9SDavid Daney 			u8 bit;
7964b139f9SDavid Daney 		};
8064b139f9SDavid Daney 	};
81ce210d35SDavid Daney 	int gpio_line;
8264b139f9SDavid Daney 	int current_cpu;	/* Next CPU expected to take this irq */
83ce210d35SDavid Daney 	int ciu_node; /* NUMA node number of the CIU */
840c326387SDavid Daney };
850c326387SDavid Daney 
860c326387SDavid Daney struct octeon_core_chip_data {
870c326387SDavid Daney 	struct mutex core_irq_mutex;
880c326387SDavid Daney 	bool current_en;
890c326387SDavid Daney 	bool desired_en;
900c326387SDavid Daney 	u8 bit;
910c326387SDavid Daney };
920c326387SDavid Daney 
930c326387SDavid Daney #define MIPS_CORE_IRQ_LINES 8
940c326387SDavid Daney 
950c326387SDavid Daney static struct octeon_core_chip_data octeon_irq_core_chip_data[MIPS_CORE_IRQ_LINES];
960c326387SDavid Daney 
9764b139f9SDavid Daney static int octeon_irq_set_ciu_mapping(int irq, int line, int bit, int gpio_line,
980c326387SDavid Daney 				      struct irq_chip *chip,
990c326387SDavid Daney 				      irq_flow_handler_t handler)
1000c326387SDavid Daney {
10164b139f9SDavid Daney 	struct octeon_ciu_chip_data *cd;
10264b139f9SDavid Daney 
10364b139f9SDavid Daney 	cd = kzalloc(sizeof(*cd), GFP_KERNEL);
10464b139f9SDavid Daney 	if (!cd)
10564b139f9SDavid Daney 		return -ENOMEM;
1060c326387SDavid Daney 
1070c326387SDavid Daney 	irq_set_chip_and_handler(irq, chip, handler);
1080c326387SDavid Daney 
10964b139f9SDavid Daney 	cd->line = line;
11064b139f9SDavid Daney 	cd->bit = bit;
11164b139f9SDavid Daney 	cd->gpio_line = gpio_line;
1120c326387SDavid Daney 
11364b139f9SDavid Daney 	irq_set_chip_data(irq, cd);
1140c326387SDavid Daney 	octeon_irq_ciu_to_irq[line][bit] = irq;
11564b139f9SDavid Daney 	return 0;
1160c326387SDavid Daney }
1170c326387SDavid Daney 
11864b139f9SDavid Daney static void octeon_irq_free_cd(struct irq_domain *d, unsigned int irq)
11964b139f9SDavid Daney {
12064b139f9SDavid Daney 	struct irq_data *data = irq_get_irq_data(irq);
12164b139f9SDavid Daney 	struct octeon_ciu_chip_data *cd = irq_data_get_irq_chip_data(data);
12264b139f9SDavid Daney 
12364b139f9SDavid Daney 	irq_set_chip_data(irq, NULL);
12464b139f9SDavid Daney 	kfree(cd);
12564b139f9SDavid Daney }
12664b139f9SDavid Daney 
12764b139f9SDavid Daney static int octeon_irq_force_ciu_mapping(struct irq_domain *domain,
12887161ccdSDavid Daney 					int irq, int line, int bit)
12987161ccdSDavid Daney {
13064b139f9SDavid Daney 	return irq_domain_associate(domain, irq, line << 6 | bit);
13187161ccdSDavid Daney }
13287161ccdSDavid Daney 
133cd847b78SDavid Daney static int octeon_coreid_for_cpu(int cpu)
134cd847b78SDavid Daney {
135cd847b78SDavid Daney #ifdef CONFIG_SMP
136cd847b78SDavid Daney 	return cpu_logical_map(cpu);
137cd847b78SDavid Daney #else
138cd847b78SDavid Daney 	return cvmx_get_core_num();
139cd847b78SDavid Daney #endif
140cd847b78SDavid Daney }
141cd847b78SDavid Daney 
1420c326387SDavid Daney static int octeon_cpu_for_coreid(int coreid)
1435b3b1688SDavid Daney {
1440c326387SDavid Daney #ifdef CONFIG_SMP
1450c326387SDavid Daney 	return cpu_number_map(coreid);
1460c326387SDavid Daney #else
1470c326387SDavid Daney 	return smp_processor_id();
1480c326387SDavid Daney #endif
1490c326387SDavid Daney }
1500c326387SDavid Daney 
1510c326387SDavid Daney static void octeon_irq_core_ack(struct irq_data *data)
1520c326387SDavid Daney {
1530c326387SDavid Daney 	struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data);
1540c326387SDavid Daney 	unsigned int bit = cd->bit;
1550c326387SDavid Daney 
1565b3b1688SDavid Daney 	/*
1575b3b1688SDavid Daney 	 * We don't need to disable IRQs to make these atomic since
1585b3b1688SDavid Daney 	 * they are already disabled earlier in the low level
1595b3b1688SDavid Daney 	 * interrupt code.
1605b3b1688SDavid Daney 	 */
1615b3b1688SDavid Daney 	clear_c0_status(0x100 << bit);
1625b3b1688SDavid Daney 	/* The two user interrupts must be cleared manually. */
1635b3b1688SDavid Daney 	if (bit < 2)
1645b3b1688SDavid Daney 		clear_c0_cause(0x100 << bit);
1655b3b1688SDavid Daney }
1665b3b1688SDavid Daney 
1670c326387SDavid Daney static void octeon_irq_core_eoi(struct irq_data *data)
1685b3b1688SDavid Daney {
1690c326387SDavid Daney 	struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data);
1700c326387SDavid Daney 
1715b3b1688SDavid Daney 	/*
1725b3b1688SDavid Daney 	 * We don't need to disable IRQs to make these atomic since
1735b3b1688SDavid Daney 	 * they are already disabled earlier in the low level
1745b3b1688SDavid Daney 	 * interrupt code.
1755b3b1688SDavid Daney 	 */
1760c326387SDavid Daney 	set_c0_status(0x100 << cd->bit);
1775b3b1688SDavid Daney }
1785b3b1688SDavid Daney 
1790c326387SDavid Daney static void octeon_irq_core_set_enable_local(void *arg)
1805b3b1688SDavid Daney {
1810c326387SDavid Daney 	struct irq_data *data = arg;
1820c326387SDavid Daney 	struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data);
1830c326387SDavid Daney 	unsigned int mask = 0x100 << cd->bit;
1845b3b1688SDavid Daney 
1855b3b1688SDavid Daney 	/*
1860c326387SDavid Daney 	 * Interrupts are already disabled, so these are atomic.
1875b3b1688SDavid Daney 	 */
1880c326387SDavid Daney 	if (cd->desired_en)
1890c326387SDavid Daney 		set_c0_status(mask);
1900c326387SDavid Daney 	else
1910c326387SDavid Daney 		clear_c0_status(mask);
1920c326387SDavid Daney 
1935b3b1688SDavid Daney }
1945b3b1688SDavid Daney 
1950c326387SDavid Daney static void octeon_irq_core_disable(struct irq_data *data)
1965b3b1688SDavid Daney {
1970c326387SDavid Daney 	struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data);
1980c326387SDavid Daney 	cd->desired_en = false;
1995b3b1688SDavid Daney }
2005b3b1688SDavid Daney 
2010c326387SDavid Daney static void octeon_irq_core_enable(struct irq_data *data)
2025b3b1688SDavid Daney {
2030c326387SDavid Daney 	struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data);
2040c326387SDavid Daney 	cd->desired_en = true;
2050c326387SDavid Daney }
2060c326387SDavid Daney 
2070c326387SDavid Daney static void octeon_irq_core_bus_lock(struct irq_data *data)
2080c326387SDavid Daney {
2090c326387SDavid Daney 	struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data);
2100c326387SDavid Daney 
2110c326387SDavid Daney 	mutex_lock(&cd->core_irq_mutex);
2120c326387SDavid Daney }
2130c326387SDavid Daney 
2140c326387SDavid Daney static void octeon_irq_core_bus_sync_unlock(struct irq_data *data)
2150c326387SDavid Daney {
2160c326387SDavid Daney 	struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data);
2170c326387SDavid Daney 
2180c326387SDavid Daney 	if (cd->desired_en != cd->current_en) {
2190c326387SDavid Daney 		on_each_cpu(octeon_irq_core_set_enable_local, data, 1);
2200c326387SDavid Daney 
2210c326387SDavid Daney 		cd->current_en = cd->desired_en;
2220c326387SDavid Daney 	}
2230c326387SDavid Daney 
2240c326387SDavid Daney 	mutex_unlock(&cd->core_irq_mutex);
2250c326387SDavid Daney }
2260c326387SDavid Daney 
2275b3b1688SDavid Daney static struct irq_chip octeon_irq_chip_core = {
2285b3b1688SDavid Daney 	.name = "Core",
2290c326387SDavid Daney 	.irq_enable = octeon_irq_core_enable,
2300c326387SDavid Daney 	.irq_disable = octeon_irq_core_disable,
2310c326387SDavid Daney 	.irq_ack = octeon_irq_core_ack,
2320c326387SDavid Daney 	.irq_eoi = octeon_irq_core_eoi,
2330c326387SDavid Daney 	.irq_bus_lock = octeon_irq_core_bus_lock,
2340c326387SDavid Daney 	.irq_bus_sync_unlock = octeon_irq_core_bus_sync_unlock,
2350c326387SDavid Daney 
2365b7cd6fdSThomas Gleixner 	.irq_cpu_online = octeon_irq_core_eoi,
2375b7cd6fdSThomas Gleixner 	.irq_cpu_offline = octeon_irq_core_ack,
2385b7cd6fdSThomas Gleixner 	.flags = IRQCHIP_ONOFFLINE_ENABLED,
2395b3b1688SDavid Daney };
2405b3b1688SDavid Daney 
2410c326387SDavid Daney static void __init octeon_irq_init_core(void)
2420c326387SDavid Daney {
2430c326387SDavid Daney 	int i;
2440c326387SDavid Daney 	int irq;
2450c326387SDavid Daney 	struct octeon_core_chip_data *cd;
2465b3b1688SDavid Daney 
2470c326387SDavid Daney 	for (i = 0; i < MIPS_CORE_IRQ_LINES; i++) {
2480c326387SDavid Daney 		cd = &octeon_irq_core_chip_data[i];
2490c326387SDavid Daney 		cd->current_en = false;
2500c326387SDavid Daney 		cd->desired_en = false;
2510c326387SDavid Daney 		cd->bit = i;
2520c326387SDavid Daney 		mutex_init(&cd->core_irq_mutex);
2530c326387SDavid Daney 
2540c326387SDavid Daney 		irq = OCTEON_IRQ_SW0 + i;
2550c326387SDavid Daney 		irq_set_chip_data(irq, cd);
2560c326387SDavid Daney 		irq_set_chip_and_handler(irq, &octeon_irq_chip_core,
2570c326387SDavid Daney 					 handle_percpu_irq);
2580c326387SDavid Daney 	}
2595b3b1688SDavid Daney }
2605b3b1688SDavid Daney 
2610c326387SDavid Daney static int next_cpu_for_irq(struct irq_data *data)
2625aae1fd4SDavid Daney {
2635aae1fd4SDavid Daney 
2645aae1fd4SDavid Daney #ifdef CONFIG_SMP
2650c326387SDavid Daney 	int cpu;
2665c159422SJiang Liu 	struct cpumask *mask = irq_data_get_affinity_mask(data);
2675c159422SJiang Liu 	int weight = cpumask_weight(mask);
26864b139f9SDavid Daney 	struct octeon_ciu_chip_data *cd = irq_data_get_irq_chip_data(data);
2695aae1fd4SDavid Daney 
2705aae1fd4SDavid Daney 	if (weight > 1) {
27164b139f9SDavid Daney 		cpu = cd->current_cpu;
2725aae1fd4SDavid Daney 		for (;;) {
2735c159422SJiang Liu 			cpu = cpumask_next(cpu, mask);
2745aae1fd4SDavid Daney 			if (cpu >= nr_cpu_ids) {
2755aae1fd4SDavid Daney 				cpu = -1;
2765aae1fd4SDavid Daney 				continue;
2775aae1fd4SDavid Daney 			} else if (cpumask_test_cpu(cpu, cpu_online_mask)) {
2785aae1fd4SDavid Daney 				break;
2795aae1fd4SDavid Daney 			}
2805aae1fd4SDavid Daney 		}
2815aae1fd4SDavid Daney 	} else if (weight == 1) {
2825c159422SJiang Liu 		cpu = cpumask_first(mask);
2835aae1fd4SDavid Daney 	} else {
2840c326387SDavid Daney 		cpu = smp_processor_id();
2855aae1fd4SDavid Daney 	}
28664b139f9SDavid Daney 	cd->current_cpu = cpu;
2870c326387SDavid Daney 	return cpu;
2885aae1fd4SDavid Daney #else
2890c326387SDavid Daney 	return smp_processor_id();
2905aae1fd4SDavid Daney #endif
2915aae1fd4SDavid Daney }
2925aae1fd4SDavid Daney 
2930c326387SDavid Daney static void octeon_irq_ciu_enable(struct irq_data *data)
2945b3b1688SDavid Daney {
2950c326387SDavid Daney 	int cpu = next_cpu_for_irq(data);
2960c326387SDavid Daney 	int coreid = octeon_coreid_for_cpu(cpu);
2970c326387SDavid Daney 	unsigned long *pen;
2985aae1fd4SDavid Daney 	unsigned long flags;
29964b139f9SDavid Daney 	struct octeon_ciu_chip_data *cd;
3001a7e68f2SDavid Daney 	raw_spinlock_t *lock = &per_cpu(octeon_irq_ciu_spinlock, cpu);
3015aae1fd4SDavid Daney 
30264b139f9SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
3030c326387SDavid Daney 
3041a7e68f2SDavid Daney 	raw_spin_lock_irqsave(lock, flags);
30564b139f9SDavid Daney 	if (cd->line == 0) {
3060c326387SDavid Daney 		pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu);
30764b139f9SDavid Daney 		__set_bit(cd->bit, pen);
3081a7e68f2SDavid Daney 		/*
3091a7e68f2SDavid Daney 		 * Must be visible to octeon_irq_ip{2,3}_ciu() before
3101a7e68f2SDavid Daney 		 * enabling the irq.
3111a7e68f2SDavid Daney 		 */
3121a7e68f2SDavid Daney 		wmb();
3130c326387SDavid Daney 		cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), *pen);
3140c326387SDavid Daney 	} else {
3150c326387SDavid Daney 		pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu);
31664b139f9SDavid Daney 		__set_bit(cd->bit, pen);
3171a7e68f2SDavid Daney 		/*
3181a7e68f2SDavid Daney 		 * Must be visible to octeon_irq_ip{2,3}_ciu() before
3191a7e68f2SDavid Daney 		 * enabling the irq.
3201a7e68f2SDavid Daney 		 */
3211a7e68f2SDavid Daney 		wmb();
3220c326387SDavid Daney 		cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen);
3230c326387SDavid Daney 	}
3241a7e68f2SDavid Daney 	raw_spin_unlock_irqrestore(lock, flags);
3255aae1fd4SDavid Daney }
3265aae1fd4SDavid Daney 
3270c326387SDavid Daney static void octeon_irq_ciu_enable_local(struct irq_data *data)
3285aae1fd4SDavid Daney {
3290c326387SDavid Daney 	unsigned long *pen;
3305b3b1688SDavid Daney 	unsigned long flags;
33164b139f9SDavid Daney 	struct octeon_ciu_chip_data *cd;
33235898716SChristoph Lameter 	raw_spinlock_t *lock = this_cpu_ptr(&octeon_irq_ciu_spinlock);
3335b3b1688SDavid Daney 
33464b139f9SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
3350c326387SDavid Daney 
3361a7e68f2SDavid Daney 	raw_spin_lock_irqsave(lock, flags);
33764b139f9SDavid Daney 	if (cd->line == 0) {
33835898716SChristoph Lameter 		pen = this_cpu_ptr(&octeon_irq_ciu0_en_mirror);
33964b139f9SDavid Daney 		__set_bit(cd->bit, pen);
3401a7e68f2SDavid Daney 		/*
3411a7e68f2SDavid Daney 		 * Must be visible to octeon_irq_ip{2,3}_ciu() before
3421a7e68f2SDavid Daney 		 * enabling the irq.
3431a7e68f2SDavid Daney 		 */
3441a7e68f2SDavid Daney 		wmb();
3450c326387SDavid Daney 		cvmx_write_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num() * 2), *pen);
3460c326387SDavid Daney 	} else {
34735898716SChristoph Lameter 		pen = this_cpu_ptr(&octeon_irq_ciu1_en_mirror);
34864b139f9SDavid Daney 		__set_bit(cd->bit, pen);
3491a7e68f2SDavid Daney 		/*
3501a7e68f2SDavid Daney 		 * Must be visible to octeon_irq_ip{2,3}_ciu() before
3511a7e68f2SDavid Daney 		 * enabling the irq.
3521a7e68f2SDavid Daney 		 */
3531a7e68f2SDavid Daney 		wmb();
3540c326387SDavid Daney 		cvmx_write_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num() * 2 + 1), *pen);
3550c326387SDavid Daney 	}
3561a7e68f2SDavid Daney 	raw_spin_unlock_irqrestore(lock, flags);
3575b3b1688SDavid Daney }
3585b3b1688SDavid Daney 
3590c326387SDavid Daney static void octeon_irq_ciu_disable_local(struct irq_data *data)
3605b3b1688SDavid Daney {
3610c326387SDavid Daney 	unsigned long *pen;
3625b3b1688SDavid Daney 	unsigned long flags;
36364b139f9SDavid Daney 	struct octeon_ciu_chip_data *cd;
36435898716SChristoph Lameter 	raw_spinlock_t *lock = this_cpu_ptr(&octeon_irq_ciu_spinlock);
3650c326387SDavid Daney 
36664b139f9SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
3670c326387SDavid Daney 
3681a7e68f2SDavid Daney 	raw_spin_lock_irqsave(lock, flags);
36964b139f9SDavid Daney 	if (cd->line == 0) {
37035898716SChristoph Lameter 		pen = this_cpu_ptr(&octeon_irq_ciu0_en_mirror);
37164b139f9SDavid Daney 		__clear_bit(cd->bit, pen);
3721a7e68f2SDavid Daney 		/*
3731a7e68f2SDavid Daney 		 * Must be visible to octeon_irq_ip{2,3}_ciu() before
3741a7e68f2SDavid Daney 		 * enabling the irq.
3751a7e68f2SDavid Daney 		 */
3761a7e68f2SDavid Daney 		wmb();
3770c326387SDavid Daney 		cvmx_write_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num() * 2), *pen);
3780c326387SDavid Daney 	} else {
37935898716SChristoph Lameter 		pen = this_cpu_ptr(&octeon_irq_ciu1_en_mirror);
38064b139f9SDavid Daney 		__clear_bit(cd->bit, pen);
3811a7e68f2SDavid Daney 		/*
3821a7e68f2SDavid Daney 		 * Must be visible to octeon_irq_ip{2,3}_ciu() before
3831a7e68f2SDavid Daney 		 * enabling the irq.
3841a7e68f2SDavid Daney 		 */
3851a7e68f2SDavid Daney 		wmb();
3860c326387SDavid Daney 		cvmx_write_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num() * 2 + 1), *pen);
3870c326387SDavid Daney 	}
3881a7e68f2SDavid Daney 	raw_spin_unlock_irqrestore(lock, flags);
3890c326387SDavid Daney }
3900c326387SDavid Daney 
3910c326387SDavid Daney static void octeon_irq_ciu_disable_all(struct irq_data *data)
3920c326387SDavid Daney {
3930c326387SDavid Daney 	unsigned long flags;
3940c326387SDavid Daney 	unsigned long *pen;
3955b3b1688SDavid Daney 	int cpu;
39664b139f9SDavid Daney 	struct octeon_ciu_chip_data *cd;
3971a7e68f2SDavid Daney 	raw_spinlock_t *lock;
3980c326387SDavid Daney 
39964b139f9SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
4000c326387SDavid Daney 
4015b3b1688SDavid Daney 	for_each_online_cpu(cpu) {
402cd847b78SDavid Daney 		int coreid = octeon_coreid_for_cpu(cpu);
4031a7e68f2SDavid Daney 		lock = &per_cpu(octeon_irq_ciu_spinlock, cpu);
40464b139f9SDavid Daney 		if (cd->line == 0)
4050c326387SDavid Daney 			pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu);
4061a7e68f2SDavid Daney 		else
4070c326387SDavid Daney 			pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu);
4081a7e68f2SDavid Daney 
4091a7e68f2SDavid Daney 		raw_spin_lock_irqsave(lock, flags);
41064b139f9SDavid Daney 		__clear_bit(cd->bit, pen);
4111a7e68f2SDavid Daney 		/*
4121a7e68f2SDavid Daney 		 * Must be visible to octeon_irq_ip{2,3}_ciu() before
4131a7e68f2SDavid Daney 		 * enabling the irq.
4141a7e68f2SDavid Daney 		 */
4151a7e68f2SDavid Daney 		wmb();
41664b139f9SDavid Daney 		if (cd->line == 0)
4171a7e68f2SDavid Daney 			cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), *pen);
4181a7e68f2SDavid Daney 		else
4190c326387SDavid Daney 			cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen);
4201a7e68f2SDavid Daney 		raw_spin_unlock_irqrestore(lock, flags);
4210c326387SDavid Daney 	}
4220c326387SDavid Daney }
4230c326387SDavid Daney 
4240c326387SDavid Daney static void octeon_irq_ciu_enable_all(struct irq_data *data)
4250c326387SDavid Daney {
4260c326387SDavid Daney 	unsigned long flags;
4270c326387SDavid Daney 	unsigned long *pen;
4280c326387SDavid Daney 	int cpu;
42964b139f9SDavid Daney 	struct octeon_ciu_chip_data *cd;
4301a7e68f2SDavid Daney 	raw_spinlock_t *lock;
4310c326387SDavid Daney 
43264b139f9SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
4330c326387SDavid Daney 
4340c326387SDavid Daney 	for_each_online_cpu(cpu) {
4350c326387SDavid Daney 		int coreid = octeon_coreid_for_cpu(cpu);
4361a7e68f2SDavid Daney 		lock = &per_cpu(octeon_irq_ciu_spinlock, cpu);
43764b139f9SDavid Daney 		if (cd->line == 0)
4380c326387SDavid Daney 			pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu);
4391a7e68f2SDavid Daney 		else
4400c326387SDavid Daney 			pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu);
4411a7e68f2SDavid Daney 
4421a7e68f2SDavid Daney 		raw_spin_lock_irqsave(lock, flags);
44364b139f9SDavid Daney 		__set_bit(cd->bit, pen);
4441a7e68f2SDavid Daney 		/*
4451a7e68f2SDavid Daney 		 * Must be visible to octeon_irq_ip{2,3}_ciu() before
4461a7e68f2SDavid Daney 		 * enabling the irq.
4471a7e68f2SDavid Daney 		 */
4481a7e68f2SDavid Daney 		wmb();
44964b139f9SDavid Daney 		if (cd->line == 0)
4501a7e68f2SDavid Daney 			cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), *pen);
4511a7e68f2SDavid Daney 		else
4520c326387SDavid Daney 			cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen);
4531a7e68f2SDavid Daney 		raw_spin_unlock_irqrestore(lock, flags);
4540c326387SDavid Daney 	}
455cd847b78SDavid Daney }
456cd847b78SDavid Daney 
457cd847b78SDavid Daney /*
4585aae1fd4SDavid Daney  * Enable the irq on the next core in the affinity set for chips that
4595aae1fd4SDavid Daney  * have the EN*_W1{S,C} registers.
460cd847b78SDavid Daney  */
4610c326387SDavid Daney static void octeon_irq_ciu_enable_v2(struct irq_data *data)
462cd847b78SDavid Daney {
4630c326387SDavid Daney 	u64 mask;
4640c326387SDavid Daney 	int cpu = next_cpu_for_irq(data);
46564b139f9SDavid Daney 	struct octeon_ciu_chip_data *cd;
4665aae1fd4SDavid Daney 
46764b139f9SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
46864b139f9SDavid Daney 	mask = 1ull << (cd->bit);
4690c326387SDavid Daney 
4700c326387SDavid Daney 	/*
4710c326387SDavid Daney 	 * Called under the desc lock, so these should never get out
4720c326387SDavid Daney 	 * of sync.
4730c326387SDavid Daney 	 */
47464b139f9SDavid Daney 	if (cd->line == 0) {
4750c326387SDavid Daney 		int index = octeon_coreid_for_cpu(cpu) * 2;
47664b139f9SDavid Daney 		set_bit(cd->bit, &per_cpu(octeon_irq_ciu0_en_mirror, cpu));
4775aae1fd4SDavid Daney 		cvmx_write_csr(CVMX_CIU_INTX_EN0_W1S(index), mask);
4780c326387SDavid Daney 	} else {
4790c326387SDavid Daney 		int index = octeon_coreid_for_cpu(cpu) * 2 + 1;
48064b139f9SDavid Daney 		set_bit(cd->bit, &per_cpu(octeon_irq_ciu1_en_mirror, cpu));
4810c326387SDavid Daney 		cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(index), mask);
4825aae1fd4SDavid Daney 	}
4835aae1fd4SDavid Daney }
4845aae1fd4SDavid Daney 
4855aae1fd4SDavid Daney /*
48664b139f9SDavid Daney  * Enable the irq in the sum2 registers.
48764b139f9SDavid Daney  */
48864b139f9SDavid Daney static void octeon_irq_ciu_enable_sum2(struct irq_data *data)
48964b139f9SDavid Daney {
49064b139f9SDavid Daney 	u64 mask;
49164b139f9SDavid Daney 	int cpu = next_cpu_for_irq(data);
49264b139f9SDavid Daney 	int index = octeon_coreid_for_cpu(cpu);
49364b139f9SDavid Daney 	struct octeon_ciu_chip_data *cd;
49464b139f9SDavid Daney 
49564b139f9SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
49664b139f9SDavid Daney 	mask = 1ull << (cd->bit);
49764b139f9SDavid Daney 
49864b139f9SDavid Daney 	cvmx_write_csr(CVMX_CIU_EN2_PPX_IP4_W1S(index), mask);
49964b139f9SDavid Daney }
50064b139f9SDavid Daney 
50164b139f9SDavid Daney /*
50264b139f9SDavid Daney  * Disable the irq in the sum2 registers.
50364b139f9SDavid Daney  */
50464b139f9SDavid Daney static void octeon_irq_ciu_disable_local_sum2(struct irq_data *data)
50564b139f9SDavid Daney {
50664b139f9SDavid Daney 	u64 mask;
50764b139f9SDavid Daney 	int cpu = next_cpu_for_irq(data);
50864b139f9SDavid Daney 	int index = octeon_coreid_for_cpu(cpu);
50964b139f9SDavid Daney 	struct octeon_ciu_chip_data *cd;
51064b139f9SDavid Daney 
51164b139f9SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
51264b139f9SDavid Daney 	mask = 1ull << (cd->bit);
51364b139f9SDavid Daney 
51464b139f9SDavid Daney 	cvmx_write_csr(CVMX_CIU_EN2_PPX_IP4_W1C(index), mask);
51564b139f9SDavid Daney }
51664b139f9SDavid Daney 
51764b139f9SDavid Daney static void octeon_irq_ciu_ack_sum2(struct irq_data *data)
51864b139f9SDavid Daney {
51964b139f9SDavid Daney 	u64 mask;
52064b139f9SDavid Daney 	int cpu = next_cpu_for_irq(data);
52164b139f9SDavid Daney 	int index = octeon_coreid_for_cpu(cpu);
52264b139f9SDavid Daney 	struct octeon_ciu_chip_data *cd;
52364b139f9SDavid Daney 
52464b139f9SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
52564b139f9SDavid Daney 	mask = 1ull << (cd->bit);
52664b139f9SDavid Daney 
52764b139f9SDavid Daney 	cvmx_write_csr(CVMX_CIU_SUM2_PPX_IP4(index), mask);
52864b139f9SDavid Daney }
52964b139f9SDavid Daney 
53064b139f9SDavid Daney static void octeon_irq_ciu_disable_all_sum2(struct irq_data *data)
53164b139f9SDavid Daney {
53264b139f9SDavid Daney 	int cpu;
53364b139f9SDavid Daney 	struct octeon_ciu_chip_data *cd;
53464b139f9SDavid Daney 	u64 mask;
53564b139f9SDavid Daney 
53664b139f9SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
53764b139f9SDavid Daney 	mask = 1ull << (cd->bit);
53864b139f9SDavid Daney 
53964b139f9SDavid Daney 	for_each_online_cpu(cpu) {
54064b139f9SDavid Daney 		int coreid = octeon_coreid_for_cpu(cpu);
54164b139f9SDavid Daney 
54264b139f9SDavid Daney 		cvmx_write_csr(CVMX_CIU_EN2_PPX_IP4_W1C(coreid), mask);
54364b139f9SDavid Daney 	}
54464b139f9SDavid Daney }
54564b139f9SDavid Daney 
54664b139f9SDavid Daney /*
5475aae1fd4SDavid Daney  * Enable the irq on the current CPU for chips that
5485aae1fd4SDavid Daney  * have the EN*_W1{S,C} registers.
5495aae1fd4SDavid Daney  */
5500c326387SDavid Daney static void octeon_irq_ciu_enable_local_v2(struct irq_data *data)
5515aae1fd4SDavid Daney {
5520c326387SDavid Daney 	u64 mask;
55364b139f9SDavid Daney 	struct octeon_ciu_chip_data *cd;
554cd847b78SDavid Daney 
55564b139f9SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
55664b139f9SDavid Daney 	mask = 1ull << (cd->bit);
557cd847b78SDavid Daney 
55864b139f9SDavid Daney 	if (cd->line == 0) {
559cd847b78SDavid Daney 		int index = cvmx_get_core_num() * 2;
56064b139f9SDavid Daney 		set_bit(cd->bit, this_cpu_ptr(&octeon_irq_ciu0_en_mirror));
5610c326387SDavid Daney 		cvmx_write_csr(CVMX_CIU_INTX_EN0_W1S(index), mask);
5620c326387SDavid Daney 	} else {
5630c326387SDavid Daney 		int index = cvmx_get_core_num() * 2 + 1;
56464b139f9SDavid Daney 		set_bit(cd->bit, this_cpu_ptr(&octeon_irq_ciu1_en_mirror));
5650c326387SDavid Daney 		cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(index), mask);
5660c326387SDavid Daney 	}
56786568dc4SDavid Daney }
56886568dc4SDavid Daney 
5690c326387SDavid Daney static void octeon_irq_ciu_disable_local_v2(struct irq_data *data)
5700c326387SDavid Daney {
5710c326387SDavid Daney 	u64 mask;
57264b139f9SDavid Daney 	struct octeon_ciu_chip_data *cd;
5730c326387SDavid Daney 
57464b139f9SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
57564b139f9SDavid Daney 	mask = 1ull << (cd->bit);
5760c326387SDavid Daney 
57764b139f9SDavid Daney 	if (cd->line == 0) {
5780c326387SDavid Daney 		int index = cvmx_get_core_num() * 2;
57964b139f9SDavid Daney 		clear_bit(cd->bit, this_cpu_ptr(&octeon_irq_ciu0_en_mirror));
5805aae1fd4SDavid Daney 		cvmx_write_csr(CVMX_CIU_INTX_EN0_W1C(index), mask);
5810c326387SDavid Daney 	} else {
5820c326387SDavid Daney 		int index = cvmx_get_core_num() * 2 + 1;
58364b139f9SDavid Daney 		clear_bit(cd->bit, this_cpu_ptr(&octeon_irq_ciu1_en_mirror));
5840c326387SDavid Daney 		cvmx_write_csr(CVMX_CIU_INTX_EN1_W1C(index), mask);
5850c326387SDavid Daney 	}
58686568dc4SDavid Daney }
58786568dc4SDavid Daney 
58886568dc4SDavid Daney /*
5890c326387SDavid Daney  * Write to the W1C bit in CVMX_CIU_INTX_SUM0 to clear the irq.
590dbb103b2SDavid Daney  */
5910c326387SDavid Daney static void octeon_irq_ciu_ack(struct irq_data *data)
592dbb103b2SDavid Daney {
5930c326387SDavid Daney 	u64 mask;
59464b139f9SDavid Daney 	struct octeon_ciu_chip_data *cd;
595dbb103b2SDavid Daney 
59664b139f9SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
59764b139f9SDavid Daney 	mask = 1ull << (cd->bit);
5980c326387SDavid Daney 
59964b139f9SDavid Daney 	if (cd->line == 0) {
6000c326387SDavid Daney 		int index = cvmx_get_core_num() * 2;
6010c326387SDavid Daney 		cvmx_write_csr(CVMX_CIU_INTX_SUM0(index), mask);
6020c326387SDavid Daney 	} else {
6030c326387SDavid Daney 		cvmx_write_csr(CVMX_CIU_INT_SUM1, mask);
6040c326387SDavid Daney 	}
605dbb103b2SDavid Daney }
606dbb103b2SDavid Daney 
607dbb103b2SDavid Daney /*
608cd847b78SDavid Daney  * Disable the irq on the all cores for chips that have the EN*_W1{S,C}
609cd847b78SDavid Daney  * registers.
610cd847b78SDavid Daney  */
6110c326387SDavid Daney static void octeon_irq_ciu_disable_all_v2(struct irq_data *data)
612cd847b78SDavid Daney {
613cd847b78SDavid Daney 	int cpu;
6140c326387SDavid Daney 	u64 mask;
61564b139f9SDavid Daney 	struct octeon_ciu_chip_data *cd;
6160c326387SDavid Daney 
61764b139f9SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
61864b139f9SDavid Daney 	mask = 1ull << (cd->bit);
6190c326387SDavid Daney 
62064b139f9SDavid Daney 	if (cd->line == 0) {
621cd847b78SDavid Daney 		for_each_online_cpu(cpu) {
6220c326387SDavid Daney 			int index = octeon_coreid_for_cpu(cpu) * 2;
62364b139f9SDavid Daney 			clear_bit(cd->bit,
62464b139f9SDavid Daney 				&per_cpu(octeon_irq_ciu0_en_mirror, cpu));
625cd847b78SDavid Daney 			cvmx_write_csr(CVMX_CIU_INTX_EN0_W1C(index), mask);
626cd847b78SDavid Daney 		}
6270c326387SDavid Daney 	} else {
6280c326387SDavid Daney 		for_each_online_cpu(cpu) {
6290c326387SDavid Daney 			int index = octeon_coreid_for_cpu(cpu) * 2 + 1;
63064b139f9SDavid Daney 			clear_bit(cd->bit,
63164b139f9SDavid Daney 				&per_cpu(octeon_irq_ciu1_en_mirror, cpu));
6320c326387SDavid Daney 			cvmx_write_csr(CVMX_CIU_INTX_EN1_W1C(index), mask);
6330c326387SDavid Daney 		}
6340c326387SDavid Daney 	}
6355b3b1688SDavid Daney }
6365b3b1688SDavid Daney 
6370c326387SDavid Daney /*
6380c326387SDavid Daney  * Enable the irq on the all cores for chips that have the EN*_W1{S,C}
6390c326387SDavid Daney  * registers.
6400c326387SDavid Daney  */
6410c326387SDavid Daney static void octeon_irq_ciu_enable_all_v2(struct irq_data *data)
6425b3b1688SDavid Daney {
6435b3b1688SDavid Daney 	int cpu;
6440c326387SDavid Daney 	u64 mask;
64564b139f9SDavid Daney 	struct octeon_ciu_chip_data *cd;
6460c326387SDavid Daney 
64764b139f9SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
64864b139f9SDavid Daney 	mask = 1ull << (cd->bit);
6490c326387SDavid Daney 
65064b139f9SDavid Daney 	if (cd->line == 0) {
6510c326387SDavid Daney 		for_each_online_cpu(cpu) {
6520c326387SDavid Daney 			int index = octeon_coreid_for_cpu(cpu) * 2;
65364b139f9SDavid Daney 			set_bit(cd->bit,
65464b139f9SDavid Daney 				&per_cpu(octeon_irq_ciu0_en_mirror, cpu));
6550c326387SDavid Daney 			cvmx_write_csr(CVMX_CIU_INTX_EN0_W1S(index), mask);
6560c326387SDavid Daney 		}
6570c326387SDavid Daney 	} else {
6580c326387SDavid Daney 		for_each_online_cpu(cpu) {
6590c326387SDavid Daney 			int index = octeon_coreid_for_cpu(cpu) * 2 + 1;
66064b139f9SDavid Daney 			set_bit(cd->bit,
66164b139f9SDavid Daney 				&per_cpu(octeon_irq_ciu1_en_mirror, cpu));
6620c326387SDavid Daney 			cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(index), mask);
6630c326387SDavid Daney 		}
6640c326387SDavid Daney 	}
6650c326387SDavid Daney }
6660c326387SDavid Daney 
667ce210d35SDavid Daney static int octeon_irq_ciu_set_type(struct irq_data *data, unsigned int t)
668ce210d35SDavid Daney {
669ce210d35SDavid Daney 	irqd_set_trigger_type(data, t);
670ce210d35SDavid Daney 
671ce210d35SDavid Daney 	if (t & IRQ_TYPE_EDGE_BOTH)
672ce210d35SDavid Daney 		irq_set_handler_locked(data, handle_edge_irq);
673ce210d35SDavid Daney 	else
674ce210d35SDavid Daney 		irq_set_handler_locked(data, handle_level_irq);
675ce210d35SDavid Daney 
676ce210d35SDavid Daney 	return IRQ_SET_MASK_OK;
677ce210d35SDavid Daney }
678ce210d35SDavid Daney 
6796d1ab4c2SDavid Daney static void octeon_irq_gpio_setup(struct irq_data *data)
6806d1ab4c2SDavid Daney {
6816d1ab4c2SDavid Daney 	union cvmx_gpio_bit_cfgx cfg;
68264b139f9SDavid Daney 	struct octeon_ciu_chip_data *cd;
6836d1ab4c2SDavid Daney 	u32 t = irqd_get_trigger_type(data);
6846d1ab4c2SDavid Daney 
68564b139f9SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
6866d1ab4c2SDavid Daney 
6876d1ab4c2SDavid Daney 	cfg.u64 = 0;
6886d1ab4c2SDavid Daney 	cfg.s.int_en = 1;
6896d1ab4c2SDavid Daney 	cfg.s.int_type = (t & IRQ_TYPE_EDGE_BOTH) != 0;
6906d1ab4c2SDavid Daney 	cfg.s.rx_xor = (t & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_EDGE_FALLING)) != 0;
6916d1ab4c2SDavid Daney 
6926d1ab4c2SDavid Daney 	/* 140 nS glitch filter*/
6936d1ab4c2SDavid Daney 	cfg.s.fil_cnt = 7;
6946d1ab4c2SDavid Daney 	cfg.s.fil_sel = 3;
6956d1ab4c2SDavid Daney 
69664b139f9SDavid Daney 	cvmx_write_csr(CVMX_GPIO_BIT_CFGX(cd->gpio_line), cfg.u64);
6976d1ab4c2SDavid Daney }
6986d1ab4c2SDavid Daney 
6996d1ab4c2SDavid Daney static void octeon_irq_ciu_enable_gpio_v2(struct irq_data *data)
7006d1ab4c2SDavid Daney {
7016d1ab4c2SDavid Daney 	octeon_irq_gpio_setup(data);
7026d1ab4c2SDavid Daney 	octeon_irq_ciu_enable_v2(data);
7036d1ab4c2SDavid Daney }
7046d1ab4c2SDavid Daney 
7056d1ab4c2SDavid Daney static void octeon_irq_ciu_enable_gpio(struct irq_data *data)
7066d1ab4c2SDavid Daney {
7076d1ab4c2SDavid Daney 	octeon_irq_gpio_setup(data);
7086d1ab4c2SDavid Daney 	octeon_irq_ciu_enable(data);
7096d1ab4c2SDavid Daney }
7106d1ab4c2SDavid Daney 
7116d1ab4c2SDavid Daney static int octeon_irq_ciu_gpio_set_type(struct irq_data *data, unsigned int t)
7126d1ab4c2SDavid Daney {
7136d1ab4c2SDavid Daney 	irqd_set_trigger_type(data, t);
7146d1ab4c2SDavid Daney 	octeon_irq_gpio_setup(data);
7156d1ab4c2SDavid Daney 
716490f7548SDavid Daney 	if (t & IRQ_TYPE_EDGE_BOTH)
71756a86c35SThomas Gleixner 		irq_set_handler_locked(data, handle_edge_irq);
71856a86c35SThomas Gleixner 	else
71956a86c35SThomas Gleixner 		irq_set_handler_locked(data, handle_level_irq);
72056a86c35SThomas Gleixner 
7216d1ab4c2SDavid Daney 	return IRQ_SET_MASK_OK;
7226d1ab4c2SDavid Daney }
7236d1ab4c2SDavid Daney 
7246d1ab4c2SDavid Daney static void octeon_irq_ciu_disable_gpio_v2(struct irq_data *data)
7256d1ab4c2SDavid Daney {
72664b139f9SDavid Daney 	struct octeon_ciu_chip_data *cd;
7276d1ab4c2SDavid Daney 
72864b139f9SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
72964b139f9SDavid Daney 	cvmx_write_csr(CVMX_GPIO_BIT_CFGX(cd->gpio_line), 0);
7306d1ab4c2SDavid Daney 
7316d1ab4c2SDavid Daney 	octeon_irq_ciu_disable_all_v2(data);
7326d1ab4c2SDavid Daney }
7336d1ab4c2SDavid Daney 
7346d1ab4c2SDavid Daney static void octeon_irq_ciu_disable_gpio(struct irq_data *data)
7356d1ab4c2SDavid Daney {
73664b139f9SDavid Daney 	struct octeon_ciu_chip_data *cd;
7376d1ab4c2SDavid Daney 
73864b139f9SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
73964b139f9SDavid Daney 	cvmx_write_csr(CVMX_GPIO_BIT_CFGX(cd->gpio_line), 0);
7406d1ab4c2SDavid Daney 
7416d1ab4c2SDavid Daney 	octeon_irq_ciu_disable_all(data);
7426d1ab4c2SDavid Daney }
7436d1ab4c2SDavid Daney 
7446d1ab4c2SDavid Daney static void octeon_irq_ciu_gpio_ack(struct irq_data *data)
7456d1ab4c2SDavid Daney {
74664b139f9SDavid Daney 	struct octeon_ciu_chip_data *cd;
7476d1ab4c2SDavid Daney 	u64 mask;
7486d1ab4c2SDavid Daney 
74964b139f9SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
75064b139f9SDavid Daney 	mask = 1ull << (cd->gpio_line);
7516d1ab4c2SDavid Daney 
7526d1ab4c2SDavid Daney 	cvmx_write_csr(CVMX_GPIO_INT_CLR, mask);
7536d1ab4c2SDavid Daney }
7546d1ab4c2SDavid Daney 
7550c326387SDavid Daney #ifdef CONFIG_SMP
7560c326387SDavid Daney 
7570c326387SDavid Daney static void octeon_irq_cpu_offline_ciu(struct irq_data *data)
7580c326387SDavid Daney {
7590c326387SDavid Daney 	int cpu = smp_processor_id();
7600c326387SDavid Daney 	cpumask_t new_affinity;
7615c159422SJiang Liu 	struct cpumask *mask = irq_data_get_affinity_mask(data);
7620c326387SDavid Daney 
7635c159422SJiang Liu 	if (!cpumask_test_cpu(cpu, mask))
7640c326387SDavid Daney 		return;
7650c326387SDavid Daney 
7665c159422SJiang Liu 	if (cpumask_weight(mask) > 1) {
7670c326387SDavid Daney 		/*
7680c326387SDavid Daney 		 * It has multi CPU affinity, just remove this CPU
7690c326387SDavid Daney 		 * from the affinity set.
7700c326387SDavid Daney 		 */
7715c159422SJiang Liu 		cpumask_copy(&new_affinity, mask);
7720c326387SDavid Daney 		cpumask_clear_cpu(cpu, &new_affinity);
7730c326387SDavid Daney 	} else {
7740c326387SDavid Daney 		/* Otherwise, put it on lowest numbered online CPU. */
7750c326387SDavid Daney 		cpumask_clear(&new_affinity);
7760c326387SDavid Daney 		cpumask_set_cpu(cpumask_first(cpu_online_mask), &new_affinity);
7770c326387SDavid Daney 	}
77801f8fa4fSThomas Gleixner 	irq_set_affinity_locked(data, &new_affinity, false);
7790c326387SDavid Daney }
7800c326387SDavid Daney 
7810c326387SDavid Daney static int octeon_irq_ciu_set_affinity(struct irq_data *data,
7820c326387SDavid Daney 				       const struct cpumask *dest, bool force)
7830c326387SDavid Daney {
7840c326387SDavid Daney 	int cpu;
7855b7cd6fdSThomas Gleixner 	bool enable_one = !irqd_irq_disabled(data) && !irqd_irq_masked(data);
786b6b74d54SDavid Daney 	unsigned long flags;
78764b139f9SDavid Daney 	struct octeon_ciu_chip_data *cd;
7881a7e68f2SDavid Daney 	unsigned long *pen;
7891a7e68f2SDavid Daney 	raw_spinlock_t *lock;
7900c326387SDavid Daney 
79164b139f9SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
7925b3b1688SDavid Daney 
7935aae1fd4SDavid Daney 	/*
7945aae1fd4SDavid Daney 	 * For non-v2 CIU, we will allow only single CPU affinity.
7955aae1fd4SDavid Daney 	 * This removes the need to do locking in the .ack/.eoi
7965aae1fd4SDavid Daney 	 * functions.
7975aae1fd4SDavid Daney 	 */
7985aae1fd4SDavid Daney 	if (cpumask_weight(dest) != 1)
7995aae1fd4SDavid Daney 		return -EINVAL;
8005aae1fd4SDavid Daney 
8015b7cd6fdSThomas Gleixner 	if (!enable_one)
8020c326387SDavid Daney 		return 0;
8030c326387SDavid Daney 
8041a7e68f2SDavid Daney 
8055b3b1688SDavid Daney 	for_each_online_cpu(cpu) {
806cd847b78SDavid Daney 		int coreid = octeon_coreid_for_cpu(cpu);
8071a7e68f2SDavid Daney 
8081a7e68f2SDavid Daney 		lock = &per_cpu(octeon_irq_ciu_spinlock, cpu);
8091a7e68f2SDavid Daney 		raw_spin_lock_irqsave(lock, flags);
8101a7e68f2SDavid Daney 
81164b139f9SDavid Daney 		if (cd->line == 0)
8121a7e68f2SDavid Daney 			pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu);
8131a7e68f2SDavid Daney 		else
8141a7e68f2SDavid Daney 			pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu);
8150c326387SDavid Daney 
8165aae1fd4SDavid Daney 		if (cpumask_test_cpu(cpu, dest) && enable_one) {
8171a7e68f2SDavid Daney 			enable_one = 0;
81864b139f9SDavid Daney 			__set_bit(cd->bit, pen);
8195aae1fd4SDavid Daney 		} else {
82064b139f9SDavid Daney 			__clear_bit(cd->bit, pen);
8215aae1fd4SDavid Daney 		}
8221a7e68f2SDavid Daney 		/*
8231a7e68f2SDavid Daney 		 * Must be visible to octeon_irq_ip{2,3}_ciu() before
8241a7e68f2SDavid Daney 		 * enabling the irq.
8251a7e68f2SDavid Daney 		 */
8261a7e68f2SDavid Daney 		wmb();
8271a7e68f2SDavid Daney 
82864b139f9SDavid Daney 		if (cd->line == 0)
8290c326387SDavid Daney 			cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), *pen);
8301a7e68f2SDavid Daney 		else
8310c326387SDavid Daney 			cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen);
8321a7e68f2SDavid Daney 
8331a7e68f2SDavid Daney 		raw_spin_unlock_irqrestore(lock, flags);
8340c326387SDavid Daney 	}
835d5dedd45SYinghai Lu 	return 0;
8365b3b1688SDavid Daney }
837cd847b78SDavid Daney 
838cd847b78SDavid Daney /*
839cd847b78SDavid Daney  * Set affinity for the irq for chips that have the EN*_W1{S,C}
840cd847b78SDavid Daney  * registers.
841cd847b78SDavid Daney  */
8420c326387SDavid Daney static int octeon_irq_ciu_set_affinity_v2(struct irq_data *data,
8430c326387SDavid Daney 					  const struct cpumask *dest,
8440c326387SDavid Daney 					  bool force)
845cd847b78SDavid Daney {
846cd847b78SDavid Daney 	int cpu;
8475b7cd6fdSThomas Gleixner 	bool enable_one = !irqd_irq_disabled(data) && !irqd_irq_masked(data);
8480c326387SDavid Daney 	u64 mask;
84964b139f9SDavid Daney 	struct octeon_ciu_chip_data *cd;
8505aae1fd4SDavid Daney 
8515b7cd6fdSThomas Gleixner 	if (!enable_one)
8520c326387SDavid Daney 		return 0;
8530c326387SDavid Daney 
85464b139f9SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
85564b139f9SDavid Daney 	mask = 1ull << cd->bit;
8560c326387SDavid Daney 
85764b139f9SDavid Daney 	if (cd->line == 0) {
858cd847b78SDavid Daney 		for_each_online_cpu(cpu) {
8590c326387SDavid Daney 			unsigned long *pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu);
8600c326387SDavid Daney 			int index = octeon_coreid_for_cpu(cpu) * 2;
8615aae1fd4SDavid Daney 			if (cpumask_test_cpu(cpu, dest) && enable_one) {
8625b7cd6fdSThomas Gleixner 				enable_one = false;
86364b139f9SDavid Daney 				set_bit(cd->bit, pen);
864cd847b78SDavid Daney 				cvmx_write_csr(CVMX_CIU_INTX_EN0_W1S(index), mask);
8655aae1fd4SDavid Daney 			} else {
86664b139f9SDavid Daney 				clear_bit(cd->bit, pen);
867cd847b78SDavid Daney 				cvmx_write_csr(CVMX_CIU_INTX_EN0_W1C(index), mask);
868cd847b78SDavid Daney 			}
8695aae1fd4SDavid Daney 		}
8700c326387SDavid Daney 	} else {
8710c326387SDavid Daney 		for_each_online_cpu(cpu) {
8720c326387SDavid Daney 			unsigned long *pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu);
8730c326387SDavid Daney 			int index = octeon_coreid_for_cpu(cpu) * 2 + 1;
8740c326387SDavid Daney 			if (cpumask_test_cpu(cpu, dest) && enable_one) {
8755b7cd6fdSThomas Gleixner 				enable_one = false;
87664b139f9SDavid Daney 				set_bit(cd->bit, pen);
8770c326387SDavid Daney 				cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(index), mask);
8780c326387SDavid Daney 			} else {
87964b139f9SDavid Daney 				clear_bit(cd->bit, pen);
8800c326387SDavid Daney 				cvmx_write_csr(CVMX_CIU_INTX_EN1_W1C(index), mask);
8810c326387SDavid Daney 			}
8820c326387SDavid Daney 		}
8830c326387SDavid Daney 	}
884cd847b78SDavid Daney 	return 0;
885cd847b78SDavid Daney }
88664b139f9SDavid Daney 
88764b139f9SDavid Daney static int octeon_irq_ciu_set_affinity_sum2(struct irq_data *data,
88864b139f9SDavid Daney 					    const struct cpumask *dest,
88964b139f9SDavid Daney 					    bool force)
89064b139f9SDavid Daney {
89164b139f9SDavid Daney 	int cpu;
89264b139f9SDavid Daney 	bool enable_one = !irqd_irq_disabled(data) && !irqd_irq_masked(data);
89364b139f9SDavid Daney 	u64 mask;
89464b139f9SDavid Daney 	struct octeon_ciu_chip_data *cd;
89564b139f9SDavid Daney 
89664b139f9SDavid Daney 	if (!enable_one)
89764b139f9SDavid Daney 		return 0;
89864b139f9SDavid Daney 
89964b139f9SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
90064b139f9SDavid Daney 	mask = 1ull << cd->bit;
90164b139f9SDavid Daney 
90264b139f9SDavid Daney 	for_each_online_cpu(cpu) {
90364b139f9SDavid Daney 		int index = octeon_coreid_for_cpu(cpu);
90464b139f9SDavid Daney 
90564b139f9SDavid Daney 		if (cpumask_test_cpu(cpu, dest) && enable_one) {
90664b139f9SDavid Daney 			enable_one = false;
90764b139f9SDavid Daney 			cvmx_write_csr(CVMX_CIU_EN2_PPX_IP4_W1S(index), mask);
90864b139f9SDavid Daney 		} else {
90964b139f9SDavid Daney 			cvmx_write_csr(CVMX_CIU_EN2_PPX_IP4_W1C(index), mask);
91064b139f9SDavid Daney 		}
91164b139f9SDavid Daney 	}
91264b139f9SDavid Daney 	return 0;
91364b139f9SDavid Daney }
9145b3b1688SDavid Daney #endif
9155b3b1688SDavid Daney 
916ce210d35SDavid Daney static unsigned int edge_startup(struct irq_data *data)
917ce210d35SDavid Daney {
918ce210d35SDavid Daney 	/* ack any pending edge-irq at startup, so there is
919ce210d35SDavid Daney 	 * an _edge_ to fire on when the event reappears.
920ce210d35SDavid Daney 	 */
921ce210d35SDavid Daney 	data->chip->irq_ack(data);
922ce210d35SDavid Daney 	data->chip->irq_enable(data);
923ce210d35SDavid Daney 	return 0;
924ce210d35SDavid Daney }
925ce210d35SDavid Daney 
926cd847b78SDavid Daney /*
927cd847b78SDavid Daney  * Newer octeon chips have support for lockless CIU operation.
928cd847b78SDavid Daney  */
9290c326387SDavid Daney static struct irq_chip octeon_irq_chip_ciu_v2 = {
9300c326387SDavid Daney 	.name = "CIU",
9310c326387SDavid Daney 	.irq_enable = octeon_irq_ciu_enable_v2,
9320c326387SDavid Daney 	.irq_disable = octeon_irq_ciu_disable_all_v2,
9332e3ecab1SDavid Daney 	.irq_mask = octeon_irq_ciu_disable_local_v2,
9342e3ecab1SDavid Daney 	.irq_unmask = octeon_irq_ciu_enable_v2,
9352e3ecab1SDavid Daney #ifdef CONFIG_SMP
9362e3ecab1SDavid Daney 	.irq_set_affinity = octeon_irq_ciu_set_affinity_v2,
9372e3ecab1SDavid Daney 	.irq_cpu_offline = octeon_irq_cpu_offline_ciu,
9382e3ecab1SDavid Daney #endif
9392e3ecab1SDavid Daney };
9402e3ecab1SDavid Daney 
9412e3ecab1SDavid Daney static struct irq_chip octeon_irq_chip_ciu_v2_edge = {
9422e3ecab1SDavid Daney 	.name = "CIU",
9432e3ecab1SDavid Daney 	.irq_enable = octeon_irq_ciu_enable_v2,
9442e3ecab1SDavid Daney 	.irq_disable = octeon_irq_ciu_disable_all_v2,
9450c326387SDavid Daney 	.irq_ack = octeon_irq_ciu_ack,
9460c326387SDavid Daney 	.irq_mask = octeon_irq_ciu_disable_local_v2,
9470c326387SDavid Daney 	.irq_unmask = octeon_irq_ciu_enable_v2,
9485b3b1688SDavid Daney #ifdef CONFIG_SMP
9490c326387SDavid Daney 	.irq_set_affinity = octeon_irq_ciu_set_affinity_v2,
9500c326387SDavid Daney 	.irq_cpu_offline = octeon_irq_cpu_offline_ciu,
9510c326387SDavid Daney #endif
9520c326387SDavid Daney };
9530c326387SDavid Daney 
95464b139f9SDavid Daney /*
95564b139f9SDavid Daney  * Newer octeon chips have support for lockless CIU operation.
95664b139f9SDavid Daney  */
95764b139f9SDavid Daney static struct irq_chip octeon_irq_chip_ciu_sum2 = {
95864b139f9SDavid Daney 	.name = "CIU",
95964b139f9SDavid Daney 	.irq_enable = octeon_irq_ciu_enable_sum2,
96064b139f9SDavid Daney 	.irq_disable = octeon_irq_ciu_disable_all_sum2,
96164b139f9SDavid Daney 	.irq_mask = octeon_irq_ciu_disable_local_sum2,
96264b139f9SDavid Daney 	.irq_unmask = octeon_irq_ciu_enable_sum2,
96364b139f9SDavid Daney #ifdef CONFIG_SMP
96464b139f9SDavid Daney 	.irq_set_affinity = octeon_irq_ciu_set_affinity_sum2,
96564b139f9SDavid Daney 	.irq_cpu_offline = octeon_irq_cpu_offline_ciu,
96664b139f9SDavid Daney #endif
96764b139f9SDavid Daney };
96864b139f9SDavid Daney 
96964b139f9SDavid Daney static struct irq_chip octeon_irq_chip_ciu_sum2_edge = {
97064b139f9SDavid Daney 	.name = "CIU",
97164b139f9SDavid Daney 	.irq_enable = octeon_irq_ciu_enable_sum2,
97264b139f9SDavid Daney 	.irq_disable = octeon_irq_ciu_disable_all_sum2,
97364b139f9SDavid Daney 	.irq_ack = octeon_irq_ciu_ack_sum2,
97464b139f9SDavid Daney 	.irq_mask = octeon_irq_ciu_disable_local_sum2,
97564b139f9SDavid Daney 	.irq_unmask = octeon_irq_ciu_enable_sum2,
97664b139f9SDavid Daney #ifdef CONFIG_SMP
97764b139f9SDavid Daney 	.irq_set_affinity = octeon_irq_ciu_set_affinity_sum2,
97864b139f9SDavid Daney 	.irq_cpu_offline = octeon_irq_cpu_offline_ciu,
97964b139f9SDavid Daney #endif
98064b139f9SDavid Daney };
98164b139f9SDavid Daney 
9820c326387SDavid Daney static struct irq_chip octeon_irq_chip_ciu = {
9830c326387SDavid Daney 	.name = "CIU",
9840c326387SDavid Daney 	.irq_enable = octeon_irq_ciu_enable,
9850c326387SDavid Daney 	.irq_disable = octeon_irq_ciu_disable_all,
9862e3ecab1SDavid Daney 	.irq_mask = octeon_irq_ciu_disable_local,
9872e3ecab1SDavid Daney 	.irq_unmask = octeon_irq_ciu_enable,
9882e3ecab1SDavid Daney #ifdef CONFIG_SMP
9892e3ecab1SDavid Daney 	.irq_set_affinity = octeon_irq_ciu_set_affinity,
9902e3ecab1SDavid Daney 	.irq_cpu_offline = octeon_irq_cpu_offline_ciu,
9912e3ecab1SDavid Daney #endif
9922e3ecab1SDavid Daney };
9932e3ecab1SDavid Daney 
9942e3ecab1SDavid Daney static struct irq_chip octeon_irq_chip_ciu_edge = {
9952e3ecab1SDavid Daney 	.name = "CIU",
9962e3ecab1SDavid Daney 	.irq_enable = octeon_irq_ciu_enable,
9972e3ecab1SDavid Daney 	.irq_disable = octeon_irq_ciu_disable_all,
9980c326387SDavid Daney 	.irq_ack = octeon_irq_ciu_ack,
9991a7e68f2SDavid Daney 	.irq_mask = octeon_irq_ciu_disable_local,
10001a7e68f2SDavid Daney 	.irq_unmask = octeon_irq_ciu_enable,
10010c326387SDavid Daney #ifdef CONFIG_SMP
10020c326387SDavid Daney 	.irq_set_affinity = octeon_irq_ciu_set_affinity,
10030c326387SDavid Daney 	.irq_cpu_offline = octeon_irq_cpu_offline_ciu,
10045b3b1688SDavid Daney #endif
10055b3b1688SDavid Daney };
10065b3b1688SDavid Daney 
10075aae1fd4SDavid Daney /* The mbox versions don't do any affinity or round-robin. */
10080c326387SDavid Daney static struct irq_chip octeon_irq_chip_ciu_mbox_v2 = {
10090c326387SDavid Daney 	.name = "CIU-M",
10100c326387SDavid Daney 	.irq_enable = octeon_irq_ciu_enable_all_v2,
10110c326387SDavid Daney 	.irq_disable = octeon_irq_ciu_disable_all_v2,
10120c326387SDavid Daney 	.irq_ack = octeon_irq_ciu_disable_local_v2,
10130c326387SDavid Daney 	.irq_eoi = octeon_irq_ciu_enable_local_v2,
10140c326387SDavid Daney 
10155b7cd6fdSThomas Gleixner 	.irq_cpu_online = octeon_irq_ciu_enable_local_v2,
10165b7cd6fdSThomas Gleixner 	.irq_cpu_offline = octeon_irq_ciu_disable_local_v2,
10175b7cd6fdSThomas Gleixner 	.flags = IRQCHIP_ONOFFLINE_ENABLED,
101886568dc4SDavid Daney };
101986568dc4SDavid Daney 
10200c326387SDavid Daney static struct irq_chip octeon_irq_chip_ciu_mbox = {
10210c326387SDavid Daney 	.name = "CIU-M",
10220c326387SDavid Daney 	.irq_enable = octeon_irq_ciu_enable_all,
10230c326387SDavid Daney 	.irq_disable = octeon_irq_ciu_disable_all,
10241a7e68f2SDavid Daney 	.irq_ack = octeon_irq_ciu_disable_local,
10251a7e68f2SDavid Daney 	.irq_eoi = octeon_irq_ciu_enable_local,
10260c326387SDavid Daney 
10275b7cd6fdSThomas Gleixner 	.irq_cpu_online = octeon_irq_ciu_enable_local,
10285b7cd6fdSThomas Gleixner 	.irq_cpu_offline = octeon_irq_ciu_disable_local,
10295b7cd6fdSThomas Gleixner 	.flags = IRQCHIP_ONOFFLINE_ENABLED,
10305aae1fd4SDavid Daney };
10315b3b1688SDavid Daney 
10326d1ab4c2SDavid Daney static struct irq_chip octeon_irq_chip_ciu_gpio_v2 = {
10336d1ab4c2SDavid Daney 	.name = "CIU-GPIO",
10346d1ab4c2SDavid Daney 	.irq_enable = octeon_irq_ciu_enable_gpio_v2,
10356d1ab4c2SDavid Daney 	.irq_disable = octeon_irq_ciu_disable_gpio_v2,
10366d1ab4c2SDavid Daney 	.irq_ack = octeon_irq_ciu_gpio_ack,
10376d1ab4c2SDavid Daney 	.irq_mask = octeon_irq_ciu_disable_local_v2,
10386d1ab4c2SDavid Daney 	.irq_unmask = octeon_irq_ciu_enable_v2,
10396d1ab4c2SDavid Daney 	.irq_set_type = octeon_irq_ciu_gpio_set_type,
10406d1ab4c2SDavid Daney #ifdef CONFIG_SMP
10416d1ab4c2SDavid Daney 	.irq_set_affinity = octeon_irq_ciu_set_affinity_v2,
1042cf355704SAlexander Sverdlin 	.irq_cpu_offline = octeon_irq_cpu_offline_ciu,
10436d1ab4c2SDavid Daney #endif
10446d1ab4c2SDavid Daney 	.flags = IRQCHIP_SET_TYPE_MASKED,
10456d1ab4c2SDavid Daney };
10466d1ab4c2SDavid Daney 
10476d1ab4c2SDavid Daney static struct irq_chip octeon_irq_chip_ciu_gpio = {
10486d1ab4c2SDavid Daney 	.name = "CIU-GPIO",
10496d1ab4c2SDavid Daney 	.irq_enable = octeon_irq_ciu_enable_gpio,
10506d1ab4c2SDavid Daney 	.irq_disable = octeon_irq_ciu_disable_gpio,
10511a7e68f2SDavid Daney 	.irq_mask = octeon_irq_ciu_disable_local,
10521a7e68f2SDavid Daney 	.irq_unmask = octeon_irq_ciu_enable,
10536d1ab4c2SDavid Daney 	.irq_ack = octeon_irq_ciu_gpio_ack,
10546d1ab4c2SDavid Daney 	.irq_set_type = octeon_irq_ciu_gpio_set_type,
10556d1ab4c2SDavid Daney #ifdef CONFIG_SMP
10566d1ab4c2SDavid Daney 	.irq_set_affinity = octeon_irq_ciu_set_affinity,
1057cf355704SAlexander Sverdlin 	.irq_cpu_offline = octeon_irq_cpu_offline_ciu,
10586d1ab4c2SDavid Daney #endif
10596d1ab4c2SDavid Daney 	.flags = IRQCHIP_SET_TYPE_MASKED,
10606d1ab4c2SDavid Daney };
10616d1ab4c2SDavid Daney 
10625b3b1688SDavid Daney /*
10630c326387SDavid Daney  * Watchdog interrupts are special.  They are associated with a single
10640c326387SDavid Daney  * core, so we hardwire the affinity to that core.
10655b3b1688SDavid Daney  */
10660c326387SDavid Daney static void octeon_irq_ciu_wd_enable(struct irq_data *data)
10675b3b1688SDavid Daney {
10685b3b1688SDavid Daney 	unsigned long flags;
10690c326387SDavid Daney 	unsigned long *pen;
10700c326387SDavid Daney 	int coreid = data->irq - OCTEON_IRQ_WDOG0;	/* Bit 0-63 of EN1 */
10710c326387SDavid Daney 	int cpu = octeon_cpu_for_coreid(coreid);
10721a7e68f2SDavid Daney 	raw_spinlock_t *lock = &per_cpu(octeon_irq_ciu_spinlock, cpu);
10735b3b1688SDavid Daney 
10741a7e68f2SDavid Daney 	raw_spin_lock_irqsave(lock, flags);
10750c326387SDavid Daney 	pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu);
10761a7e68f2SDavid Daney 	__set_bit(coreid, pen);
10771a7e68f2SDavid Daney 	/*
10781a7e68f2SDavid Daney 	 * Must be visible to octeon_irq_ip{2,3}_ciu() before enabling
10791a7e68f2SDavid Daney 	 * the irq.
10801a7e68f2SDavid Daney 	 */
10811a7e68f2SDavid Daney 	wmb();
10820c326387SDavid Daney 	cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen);
10831a7e68f2SDavid Daney 	raw_spin_unlock_irqrestore(lock, flags);
10845b3b1688SDavid Daney }
10855b3b1688SDavid Daney 
10865aae1fd4SDavid Daney /*
10875aae1fd4SDavid Daney  * Watchdog interrupts are special.  They are associated with a single
10885aae1fd4SDavid Daney  * core, so we hardwire the affinity to that core.
10895aae1fd4SDavid Daney  */
10900c326387SDavid Daney static void octeon_irq_ciu1_wd_enable_v2(struct irq_data *data)
10915aae1fd4SDavid Daney {
10920c326387SDavid Daney 	int coreid = data->irq - OCTEON_IRQ_WDOG0;
10930c326387SDavid Daney 	int cpu = octeon_cpu_for_coreid(coreid);
10945aae1fd4SDavid Daney 
10950c326387SDavid Daney 	set_bit(coreid, &per_cpu(octeon_irq_ciu1_en_mirror, cpu));
10960c326387SDavid Daney 	cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(coreid * 2 + 1), 1ull << coreid);
10975aae1fd4SDavid Daney }
10985aae1fd4SDavid Daney 
10990c326387SDavid Daney 
11000c326387SDavid Daney static struct irq_chip octeon_irq_chip_ciu_wd_v2 = {
11010c326387SDavid Daney 	.name = "CIU-W",
11020c326387SDavid Daney 	.irq_enable = octeon_irq_ciu1_wd_enable_v2,
11030c326387SDavid Daney 	.irq_disable = octeon_irq_ciu_disable_all_v2,
11040c326387SDavid Daney 	.irq_mask = octeon_irq_ciu_disable_local_v2,
11050c326387SDavid Daney 	.irq_unmask = octeon_irq_ciu_enable_local_v2,
11060c326387SDavid Daney };
11070c326387SDavid Daney 
11080c326387SDavid Daney static struct irq_chip octeon_irq_chip_ciu_wd = {
11090c326387SDavid Daney 	.name = "CIU-W",
11100c326387SDavid Daney 	.irq_enable = octeon_irq_ciu_wd_enable,
11110c326387SDavid Daney 	.irq_disable = octeon_irq_ciu_disable_all,
11121a7e68f2SDavid Daney 	.irq_mask = octeon_irq_ciu_disable_local,
11131a7e68f2SDavid Daney 	.irq_unmask = octeon_irq_ciu_enable_local,
11140c326387SDavid Daney };
11150c326387SDavid Daney 
1116a0c16582SDavid Daney static bool octeon_irq_ciu_is_edge(unsigned int line, unsigned int bit)
1117a0c16582SDavid Daney {
1118a0c16582SDavid Daney 	bool edge = false;
1119a0c16582SDavid Daney 
1120a0c16582SDavid Daney 	if (line == 0)
1121a0c16582SDavid Daney 		switch (bit) {
1122a0c16582SDavid Daney 		case 48 ... 49: /* GMX DRP */
1123a0c16582SDavid Daney 		case 50: /* IPD_DRP */
1124a0c16582SDavid Daney 		case 52 ... 55: /* Timers */
1125a0c16582SDavid Daney 		case 58: /* MPI */
1126a0c16582SDavid Daney 			edge = true;
1127a0c16582SDavid Daney 			break;
1128a0c16582SDavid Daney 		default:
1129a0c16582SDavid Daney 			break;
1130a0c16582SDavid Daney 		}
1131a0c16582SDavid Daney 	else /* line == 1 */
1132a0c16582SDavid Daney 		switch (bit) {
1133a0c16582SDavid Daney 		case 47: /* PTP */
1134a0c16582SDavid Daney 			edge = true;
1135a0c16582SDavid Daney 			break;
1136a0c16582SDavid Daney 		default:
1137a0c16582SDavid Daney 			break;
1138a0c16582SDavid Daney 		}
1139a0c16582SDavid Daney 	return edge;
1140a0c16582SDavid Daney }
1141a0c16582SDavid Daney 
1142a0c16582SDavid Daney struct octeon_irq_gpio_domain_data {
1143a0c16582SDavid Daney 	unsigned int base_hwirq;
1144a0c16582SDavid Daney };
1145a0c16582SDavid Daney 
1146a0c16582SDavid Daney static int octeon_irq_gpio_xlat(struct irq_domain *d,
1147a0c16582SDavid Daney 				struct device_node *node,
1148a0c16582SDavid Daney 				const u32 *intspec,
1149a0c16582SDavid Daney 				unsigned int intsize,
1150a0c16582SDavid Daney 				unsigned long *out_hwirq,
1151a0c16582SDavid Daney 				unsigned int *out_type)
1152a0c16582SDavid Daney {
1153a0c16582SDavid Daney 	unsigned int type;
1154a0c16582SDavid Daney 	unsigned int pin;
1155a0c16582SDavid Daney 	unsigned int trigger;
1156a0c16582SDavid Daney 
11575d4c9bc7SMarc Zyngier 	if (irq_domain_get_of_node(d) != node)
1158a0c16582SDavid Daney 		return -EINVAL;
1159a0c16582SDavid Daney 
1160a0c16582SDavid Daney 	if (intsize < 2)
1161a0c16582SDavid Daney 		return -EINVAL;
1162a0c16582SDavid Daney 
1163a0c16582SDavid Daney 	pin = intspec[0];
1164a0c16582SDavid Daney 	if (pin >= 16)
1165a0c16582SDavid Daney 		return -EINVAL;
1166a0c16582SDavid Daney 
1167a0c16582SDavid Daney 	trigger = intspec[1];
1168a0c16582SDavid Daney 
1169a0c16582SDavid Daney 	switch (trigger) {
1170a0c16582SDavid Daney 	case 1:
1171a0c16582SDavid Daney 		type = IRQ_TYPE_EDGE_RISING;
1172a0c16582SDavid Daney 		break;
1173a0c16582SDavid Daney 	case 2:
1174a0c16582SDavid Daney 		type = IRQ_TYPE_EDGE_FALLING;
1175a0c16582SDavid Daney 		break;
1176a0c16582SDavid Daney 	case 4:
1177a0c16582SDavid Daney 		type = IRQ_TYPE_LEVEL_HIGH;
1178a0c16582SDavid Daney 		break;
1179a0c16582SDavid Daney 	case 8:
1180a0c16582SDavid Daney 		type = IRQ_TYPE_LEVEL_LOW;
1181a0c16582SDavid Daney 		break;
1182a0c16582SDavid Daney 	default:
1183a0c16582SDavid Daney 		pr_err("Error: (%s) Invalid irq trigger specification: %x\n",
1184a0c16582SDavid Daney 		       node->name,
1185a0c16582SDavid Daney 		       trigger);
1186a0c16582SDavid Daney 		type = IRQ_TYPE_LEVEL_LOW;
1187a0c16582SDavid Daney 		break;
1188a0c16582SDavid Daney 	}
1189a0c16582SDavid Daney 	*out_type = type;
119087161ccdSDavid Daney 	*out_hwirq = pin;
1191a0c16582SDavid Daney 
1192a0c16582SDavid Daney 	return 0;
1193a0c16582SDavid Daney }
1194a0c16582SDavid Daney 
1195a0c16582SDavid Daney static int octeon_irq_ciu_xlat(struct irq_domain *d,
1196a0c16582SDavid Daney 			       struct device_node *node,
1197a0c16582SDavid Daney 			       const u32 *intspec,
1198a0c16582SDavid Daney 			       unsigned int intsize,
1199a0c16582SDavid Daney 			       unsigned long *out_hwirq,
1200a0c16582SDavid Daney 			       unsigned int *out_type)
1201a0c16582SDavid Daney {
1202a0c16582SDavid Daney 	unsigned int ciu, bit;
120364b139f9SDavid Daney 	struct octeon_irq_ciu_domain_data *dd = d->host_data;
1204a0c16582SDavid Daney 
1205a0c16582SDavid Daney 	ciu = intspec[0];
1206a0c16582SDavid Daney 	bit = intspec[1];
1207a0c16582SDavid Daney 
120864b139f9SDavid Daney 	if (ciu >= dd->num_sum || bit > 63)
1209a0c16582SDavid Daney 		return -EINVAL;
1210a0c16582SDavid Daney 
1211a0c16582SDavid Daney 	*out_hwirq = (ciu << 6) | bit;
1212a0c16582SDavid Daney 	*out_type = 0;
1213a0c16582SDavid Daney 
1214a0c16582SDavid Daney 	return 0;
1215a0c16582SDavid Daney }
1216a0c16582SDavid Daney 
1217a0c16582SDavid Daney static struct irq_chip *octeon_irq_ciu_chip;
12182e3ecab1SDavid Daney static struct irq_chip *octeon_irq_ciu_chip_edge;
1219a0c16582SDavid Daney static struct irq_chip *octeon_irq_gpio_chip;
1220a0c16582SDavid Daney 
1221a0c16582SDavid Daney static int octeon_irq_ciu_map(struct irq_domain *d,
1222a0c16582SDavid Daney 			      unsigned int virq, irq_hw_number_t hw)
1223a0c16582SDavid Daney {
122464b139f9SDavid Daney 	int rv;
1225a0c16582SDavid Daney 	unsigned int line = hw >> 6;
1226a0c16582SDavid Daney 	unsigned int bit = hw & 63;
122764b139f9SDavid Daney 	struct octeon_irq_ciu_domain_data *dd = d->host_data;
1228a0c16582SDavid Daney 
122964b139f9SDavid Daney 	if (line >= dd->num_sum || octeon_irq_ciu_to_irq[line][bit] != 0)
1230a0c16582SDavid Daney 		return -EINVAL;
1231a0c16582SDavid Daney 
123264b139f9SDavid Daney 	if (line == 2) {
1233a0c16582SDavid Daney 		if (octeon_irq_ciu_is_edge(line, bit))
123464b139f9SDavid Daney 			rv = octeon_irq_set_ciu_mapping(virq, line, bit, 0,
123564b139f9SDavid Daney 				&octeon_irq_chip_ciu_sum2_edge,
123664b139f9SDavid Daney 				handle_edge_irq);
123764b139f9SDavid Daney 		else
123864b139f9SDavid Daney 			rv = octeon_irq_set_ciu_mapping(virq, line, bit, 0,
123964b139f9SDavid Daney 				&octeon_irq_chip_ciu_sum2,
124064b139f9SDavid Daney 				handle_level_irq);
124164b139f9SDavid Daney 	} else {
124264b139f9SDavid Daney 		if (octeon_irq_ciu_is_edge(line, bit))
124364b139f9SDavid Daney 			rv = octeon_irq_set_ciu_mapping(virq, line, bit, 0,
12442e3ecab1SDavid Daney 				octeon_irq_ciu_chip_edge,
1245a0c16582SDavid Daney 				handle_edge_irq);
1246a0c16582SDavid Daney 		else
124764b139f9SDavid Daney 			rv = octeon_irq_set_ciu_mapping(virq, line, bit, 0,
1248a0c16582SDavid Daney 				octeon_irq_ciu_chip,
1249a0c16582SDavid Daney 				handle_level_irq);
125064b139f9SDavid Daney 	}
125164b139f9SDavid Daney 	return rv;
1252a0c16582SDavid Daney }
1253a0c16582SDavid Daney 
125464b139f9SDavid Daney static int octeon_irq_gpio_map(struct irq_domain *d,
125564b139f9SDavid Daney 			       unsigned int virq, irq_hw_number_t hw)
1256a0c16582SDavid Daney {
125787161ccdSDavid Daney 	struct octeon_irq_gpio_domain_data *gpiod = d->host_data;
125887161ccdSDavid Daney 	unsigned int line, bit;
125964b139f9SDavid Daney 	int r;
1260a0c16582SDavid Daney 
1261d41d547aSAlexander Sverdlin 	line = (hw + gpiod->base_hwirq) >> 6;
1262d41d547aSAlexander Sverdlin 	bit = (hw + gpiod->base_hwirq) & 63;
1263008d0cf1SDan Carpenter 	if (line >= ARRAY_SIZE(octeon_irq_ciu_to_irq) ||
126464b139f9SDavid Daney 		octeon_irq_ciu_to_irq[line][bit] != 0)
1265a0c16582SDavid Daney 		return -EINVAL;
1266a0c16582SDavid Daney 
126756a86c35SThomas Gleixner 	/*
126856a86c35SThomas Gleixner 	 * Default to handle_level_irq. If the DT contains a different
126956a86c35SThomas Gleixner 	 * trigger type, it will call the irq_set_type callback and
127056a86c35SThomas Gleixner 	 * the handler gets updated.
127156a86c35SThomas Gleixner 	 */
127264b139f9SDavid Daney 	r = octeon_irq_set_ciu_mapping(virq, line, bit, hw,
127356a86c35SThomas Gleixner 				       octeon_irq_gpio_chip, handle_level_irq);
127464b139f9SDavid Daney 	return r;
127588fd8589SDavid Daney }
127688fd8589SDavid Daney 
1277a0c16582SDavid Daney static struct irq_domain_ops octeon_irq_domain_ciu_ops = {
1278a0c16582SDavid Daney 	.map = octeon_irq_ciu_map,
127964b139f9SDavid Daney 	.unmap = octeon_irq_free_cd,
1280a0c16582SDavid Daney 	.xlate = octeon_irq_ciu_xlat,
1281a0c16582SDavid Daney };
1282a0c16582SDavid Daney 
1283a0c16582SDavid Daney static struct irq_domain_ops octeon_irq_domain_gpio_ops = {
1284a0c16582SDavid Daney 	.map = octeon_irq_gpio_map,
128564b139f9SDavid Daney 	.unmap = octeon_irq_free_cd,
1286a0c16582SDavid Daney 	.xlate = octeon_irq_gpio_xlat,
1287a0c16582SDavid Daney };
1288a0c16582SDavid Daney 
12891a7e68f2SDavid Daney static void octeon_irq_ip2_ciu(void)
1290cd847b78SDavid Daney {
12910c326387SDavid Daney 	const unsigned long core_id = cvmx_get_core_num();
12920c326387SDavid Daney 	u64 ciu_sum = cvmx_read_csr(CVMX_CIU_INTX_SUM0(core_id * 2));
12930c326387SDavid Daney 
129435898716SChristoph Lameter 	ciu_sum &= __this_cpu_read(octeon_irq_ciu0_en_mirror);
12950c326387SDavid Daney 	if (likely(ciu_sum)) {
12960c326387SDavid Daney 		int bit = fls64(ciu_sum) - 1;
12970c326387SDavid Daney 		int irq = octeon_irq_ciu_to_irq[0][bit];
12980c326387SDavid Daney 		if (likely(irq))
12990c326387SDavid Daney 			do_IRQ(irq);
13000c326387SDavid Daney 		else
13010c326387SDavid Daney 			spurious_interrupt();
13025aae1fd4SDavid Daney 	} else {
13030c326387SDavid Daney 		spurious_interrupt();
1304cd847b78SDavid Daney 	}
13055aae1fd4SDavid Daney }
13065b3b1688SDavid Daney 
13071a7e68f2SDavid Daney static void octeon_irq_ip3_ciu(void)
13080c326387SDavid Daney {
13090c326387SDavid Daney 	u64 ciu_sum = cvmx_read_csr(CVMX_CIU_INT_SUM1);
13100c326387SDavid Daney 
131135898716SChristoph Lameter 	ciu_sum &= __this_cpu_read(octeon_irq_ciu1_en_mirror);
13120c326387SDavid Daney 	if (likely(ciu_sum)) {
13130c326387SDavid Daney 		int bit = fls64(ciu_sum) - 1;
13140c326387SDavid Daney 		int irq = octeon_irq_ciu_to_irq[1][bit];
13150c326387SDavid Daney 		if (likely(irq))
13160c326387SDavid Daney 			do_IRQ(irq);
13170c326387SDavid Daney 		else
13180c326387SDavid Daney 			spurious_interrupt();
13190c326387SDavid Daney 	} else {
13200c326387SDavid Daney 		spurious_interrupt();
13210c326387SDavid Daney 	}
13220c326387SDavid Daney }
13230c326387SDavid Daney 
132464b139f9SDavid Daney static void octeon_irq_ip4_ciu(void)
132564b139f9SDavid Daney {
132664b139f9SDavid Daney 	int coreid = cvmx_get_core_num();
132764b139f9SDavid Daney 	u64 ciu_sum = cvmx_read_csr(CVMX_CIU_SUM2_PPX_IP4(coreid));
132864b139f9SDavid Daney 	u64 ciu_en = cvmx_read_csr(CVMX_CIU_EN2_PPX_IP4(coreid));
132964b139f9SDavid Daney 
133064b139f9SDavid Daney 	ciu_sum &= ciu_en;
133164b139f9SDavid Daney 	if (likely(ciu_sum)) {
133264b139f9SDavid Daney 		int bit = fls64(ciu_sum) - 1;
133364b139f9SDavid Daney 		int irq = octeon_irq_ciu_to_irq[2][bit];
133464b139f9SDavid Daney 
133564b139f9SDavid Daney 		if (likely(irq))
133664b139f9SDavid Daney 			do_IRQ(irq);
133764b139f9SDavid Daney 		else
133864b139f9SDavid Daney 			spurious_interrupt();
133964b139f9SDavid Daney 	} else {
134064b139f9SDavid Daney 		spurious_interrupt();
134164b139f9SDavid Daney 	}
134264b139f9SDavid Daney }
134364b139f9SDavid Daney 
134488fd8589SDavid Daney static bool octeon_irq_use_ip4;
134588fd8589SDavid Daney 
1346078a55fcSPaul Gortmaker static void octeon_irq_local_enable_ip4(void *arg)
134788fd8589SDavid Daney {
134888fd8589SDavid Daney 	set_c0_status(STATUSF_IP4);
134988fd8589SDavid Daney }
135088fd8589SDavid Daney 
13510c326387SDavid Daney static void octeon_irq_ip4_mask(void)
13520c326387SDavid Daney {
13530c326387SDavid Daney 	clear_c0_status(STATUSF_IP4);
13540c326387SDavid Daney 	spurious_interrupt();
13550c326387SDavid Daney }
13560c326387SDavid Daney 
13570c326387SDavid Daney static void (*octeon_irq_ip2)(void);
13580c326387SDavid Daney static void (*octeon_irq_ip3)(void);
13590c326387SDavid Daney static void (*octeon_irq_ip4)(void);
13600c326387SDavid Daney 
1361078a55fcSPaul Gortmaker void (*octeon_irq_setup_secondary)(void);
13620c326387SDavid Daney 
1363078a55fcSPaul Gortmaker void octeon_irq_set_ip4_handler(octeon_irq_ip4_handler_t h)
136488fd8589SDavid Daney {
136588fd8589SDavid Daney 	octeon_irq_ip4 = h;
136688fd8589SDavid Daney 	octeon_irq_use_ip4 = true;
136788fd8589SDavid Daney 	on_each_cpu(octeon_irq_local_enable_ip4, NULL, 1);
136888fd8589SDavid Daney }
136988fd8589SDavid Daney 
1370078a55fcSPaul Gortmaker static void octeon_irq_percpu_enable(void)
13710c326387SDavid Daney {
13720c326387SDavid Daney 	irq_cpu_online();
13730c326387SDavid Daney }
13740c326387SDavid Daney 
1375078a55fcSPaul Gortmaker static void octeon_irq_init_ciu_percpu(void)
13760c326387SDavid Daney {
13770c326387SDavid Daney 	int coreid = cvmx_get_core_num();
13781a7e68f2SDavid Daney 
13791a7e68f2SDavid Daney 
138035898716SChristoph Lameter 	__this_cpu_write(octeon_irq_ciu0_en_mirror, 0);
138135898716SChristoph Lameter 	__this_cpu_write(octeon_irq_ciu1_en_mirror, 0);
13821a7e68f2SDavid Daney 	wmb();
138335898716SChristoph Lameter 	raw_spin_lock_init(this_cpu_ptr(&octeon_irq_ciu_spinlock));
1384cd847b78SDavid Daney 	/*
13850c326387SDavid Daney 	 * Disable All CIU Interrupts. The ones we need will be
13860c326387SDavid Daney 	 * enabled later.  Read the SUM register so we know the write
13870c326387SDavid Daney 	 * completed.
1388cd847b78SDavid Daney 	 */
13890c326387SDavid Daney 	cvmx_write_csr(CVMX_CIU_INTX_EN0((coreid * 2)), 0);
13900c326387SDavid Daney 	cvmx_write_csr(CVMX_CIU_INTX_EN0((coreid * 2 + 1)), 0);
13910c326387SDavid Daney 	cvmx_write_csr(CVMX_CIU_INTX_EN1((coreid * 2)), 0);
13920c326387SDavid Daney 	cvmx_write_csr(CVMX_CIU_INTX_EN1((coreid * 2 + 1)), 0);
13930c326387SDavid Daney 	cvmx_read_csr(CVMX_CIU_INTX_SUM0((coreid * 2)));
13940c326387SDavid Daney }
1395cd847b78SDavid Daney 
139688fd8589SDavid Daney static void octeon_irq_init_ciu2_percpu(void)
139788fd8589SDavid Daney {
139888fd8589SDavid Daney 	u64 regx, ipx;
139988fd8589SDavid Daney 	int coreid = cvmx_get_core_num();
140088fd8589SDavid Daney 	u64 base = CVMX_CIU2_EN_PPX_IP2_WRKQ(coreid);
140188fd8589SDavid Daney 
140288fd8589SDavid Daney 	/*
140388fd8589SDavid Daney 	 * Disable All CIU2 Interrupts. The ones we need will be
140488fd8589SDavid Daney 	 * enabled later.  Read the SUM register so we know the write
140588fd8589SDavid Daney 	 * completed.
140688fd8589SDavid Daney 	 *
140788fd8589SDavid Daney 	 * There are 9 registers and 3 IPX levels with strides 0x1000
140888fd8589SDavid Daney 	 * and 0x200 respectivly.  Use loops to clear them.
140988fd8589SDavid Daney 	 */
141088fd8589SDavid Daney 	for (regx = 0; regx <= 0x8000; regx += 0x1000) {
141188fd8589SDavid Daney 		for (ipx = 0; ipx <= 0x400; ipx += 0x200)
141288fd8589SDavid Daney 			cvmx_write_csr(base + regx + ipx, 0);
141388fd8589SDavid Daney 	}
141488fd8589SDavid Daney 
141588fd8589SDavid Daney 	cvmx_read_csr(CVMX_CIU2_SUM_PPX_IP2(coreid));
141688fd8589SDavid Daney }
141788fd8589SDavid Daney 
1418078a55fcSPaul Gortmaker static void octeon_irq_setup_secondary_ciu(void)
14190c326387SDavid Daney {
14200c326387SDavid Daney 	octeon_irq_init_ciu_percpu();
14210c326387SDavid Daney 	octeon_irq_percpu_enable();
14225aae1fd4SDavid Daney 
14230c326387SDavid Daney 	/* Enable the CIU lines */
14240c326387SDavid Daney 	set_c0_status(STATUSF_IP3 | STATUSF_IP2);
142564b139f9SDavid Daney 	if (octeon_irq_use_ip4)
142664b139f9SDavid Daney 		set_c0_status(STATUSF_IP4);
142764b139f9SDavid Daney 	else
14280c326387SDavid Daney 		clear_c0_status(STATUSF_IP4);
14290c326387SDavid Daney }
14300c326387SDavid Daney 
143188fd8589SDavid Daney static void octeon_irq_setup_secondary_ciu2(void)
143288fd8589SDavid Daney {
143388fd8589SDavid Daney 	octeon_irq_init_ciu2_percpu();
143488fd8589SDavid Daney 	octeon_irq_percpu_enable();
143588fd8589SDavid Daney 
143688fd8589SDavid Daney 	/* Enable the CIU lines */
143788fd8589SDavid Daney 	set_c0_status(STATUSF_IP3 | STATUSF_IP2);
143888fd8589SDavid Daney 	if (octeon_irq_use_ip4)
143988fd8589SDavid Daney 		set_c0_status(STATUSF_IP4);
144088fd8589SDavid Daney 	else
144188fd8589SDavid Daney 		clear_c0_status(STATUSF_IP4);
144288fd8589SDavid Daney }
144388fd8589SDavid Daney 
144464b139f9SDavid Daney static int __init octeon_irq_init_ciu(
144564b139f9SDavid Daney 	struct device_node *ciu_node, struct device_node *parent)
14460c326387SDavid Daney {
144764b139f9SDavid Daney 	unsigned int i, r;
14480c326387SDavid Daney 	struct irq_chip *chip;
14492e3ecab1SDavid Daney 	struct irq_chip *chip_edge;
14500c326387SDavid Daney 	struct irq_chip *chip_mbox;
14510c326387SDavid Daney 	struct irq_chip *chip_wd;
145287161ccdSDavid Daney 	struct irq_domain *ciu_domain = NULL;
145364b139f9SDavid Daney 	struct octeon_irq_ciu_domain_data *dd;
145464b139f9SDavid Daney 
145564b139f9SDavid Daney 	dd = kzalloc(sizeof(*dd), GFP_KERNEL);
145664b139f9SDavid Daney 	if (!dd)
145764b139f9SDavid Daney 		return -ENOMEM;
14580c326387SDavid Daney 
14590c326387SDavid Daney 	octeon_irq_init_ciu_percpu();
14600c326387SDavid Daney 	octeon_irq_setup_secondary = octeon_irq_setup_secondary_ciu;
14610c326387SDavid Daney 
14621a7e68f2SDavid Daney 	octeon_irq_ip2 = octeon_irq_ip2_ciu;
14631a7e68f2SDavid Daney 	octeon_irq_ip3 = octeon_irq_ip3_ciu;
146464b139f9SDavid Daney 	if ((OCTEON_IS_OCTEON2() || OCTEON_IS_OCTEON3())
146564b139f9SDavid Daney 		&& !OCTEON_IS_MODEL(OCTEON_CN63XX)) {
146664b139f9SDavid Daney 		octeon_irq_ip4 =  octeon_irq_ip4_ciu;
146764b139f9SDavid Daney 		dd->num_sum = 3;
146864b139f9SDavid Daney 		octeon_irq_use_ip4 = true;
146964b139f9SDavid Daney 	} else {
147064b139f9SDavid Daney 		octeon_irq_ip4 = octeon_irq_ip4_mask;
147164b139f9SDavid Daney 		dd->num_sum = 2;
147264b139f9SDavid Daney 		octeon_irq_use_ip4 = false;
147364b139f9SDavid Daney 	}
14740c326387SDavid Daney 	if (OCTEON_IS_MODEL(OCTEON_CN58XX_PASS2_X) ||
14750c326387SDavid Daney 	    OCTEON_IS_MODEL(OCTEON_CN56XX_PASS2_X) ||
14760c326387SDavid Daney 	    OCTEON_IS_MODEL(OCTEON_CN52XX_PASS2_X) ||
1477debe6a62SDavid Daney 	    OCTEON_IS_OCTEON2() || OCTEON_IS_OCTEON3()) {
14780c326387SDavid Daney 		chip = &octeon_irq_chip_ciu_v2;
14792e3ecab1SDavid Daney 		chip_edge = &octeon_irq_chip_ciu_v2_edge;
14800c326387SDavid Daney 		chip_mbox = &octeon_irq_chip_ciu_mbox_v2;
14810c326387SDavid Daney 		chip_wd = &octeon_irq_chip_ciu_wd_v2;
1482a0c16582SDavid Daney 		octeon_irq_gpio_chip = &octeon_irq_chip_ciu_gpio_v2;
14830c326387SDavid Daney 	} else {
14840c326387SDavid Daney 		chip = &octeon_irq_chip_ciu;
14852e3ecab1SDavid Daney 		chip_edge = &octeon_irq_chip_ciu_edge;
14860c326387SDavid Daney 		chip_mbox = &octeon_irq_chip_ciu_mbox;
14870c326387SDavid Daney 		chip_wd = &octeon_irq_chip_ciu_wd;
1488a0c16582SDavid Daney 		octeon_irq_gpio_chip = &octeon_irq_chip_ciu_gpio;
14890c326387SDavid Daney 	}
1490a0c16582SDavid Daney 	octeon_irq_ciu_chip = chip;
14912e3ecab1SDavid Daney 	octeon_irq_ciu_chip_edge = chip_edge;
14920c326387SDavid Daney 
14930c326387SDavid Daney 	/* Mips internal */
14940c326387SDavid Daney 	octeon_irq_init_core();
14950c326387SDavid Daney 
149664b139f9SDavid Daney 	ciu_domain = irq_domain_add_tree(
149764b139f9SDavid Daney 		ciu_node, &octeon_irq_domain_ciu_ops, dd);
149864b139f9SDavid Daney 	irq_set_default_host(ciu_domain);
149964b139f9SDavid Daney 
150064b139f9SDavid Daney 	/* CIU_0 */
150164b139f9SDavid Daney 	for (i = 0; i < 16; i++) {
150264b139f9SDavid Daney 		r = octeon_irq_force_ciu_mapping(
150364b139f9SDavid Daney 			ciu_domain, i + OCTEON_IRQ_WORKQ0, 0, i + 0);
150464b139f9SDavid Daney 		if (r)
150564b139f9SDavid Daney 			goto err;
150664b139f9SDavid Daney 	}
150764b139f9SDavid Daney 
150864b139f9SDavid Daney 	r = octeon_irq_set_ciu_mapping(
150964b139f9SDavid Daney 		OCTEON_IRQ_MBOX0, 0, 32, 0, chip_mbox, handle_percpu_irq);
151064b139f9SDavid Daney 	if (r)
151164b139f9SDavid Daney 		goto err;
151264b139f9SDavid Daney 	r = octeon_irq_set_ciu_mapping(
151364b139f9SDavid Daney 		OCTEON_IRQ_MBOX1, 0, 33, 0, chip_mbox, handle_percpu_irq);
151464b139f9SDavid Daney 	if (r)
151564b139f9SDavid Daney 		goto err;
151664b139f9SDavid Daney 
151764b139f9SDavid Daney 	for (i = 0; i < 4; i++) {
151864b139f9SDavid Daney 		r = octeon_irq_force_ciu_mapping(
151964b139f9SDavid Daney 			ciu_domain, i + OCTEON_IRQ_PCI_INT0, 0, i + 36);
152064b139f9SDavid Daney 		if (r)
152164b139f9SDavid Daney 			goto err;
152264b139f9SDavid Daney 	}
152364b139f9SDavid Daney 	for (i = 0; i < 4; i++) {
152464b139f9SDavid Daney 		r = octeon_irq_force_ciu_mapping(
152564b139f9SDavid Daney 			ciu_domain, i + OCTEON_IRQ_PCI_MSI0, 0, i + 40);
152664b139f9SDavid Daney 		if (r)
152764b139f9SDavid Daney 			goto err;
152864b139f9SDavid Daney 	}
152964b139f9SDavid Daney 
153064b139f9SDavid Daney 	r = octeon_irq_force_ciu_mapping(ciu_domain, OCTEON_IRQ_TWSI, 0, 45);
153164b139f9SDavid Daney 	if (r)
153264b139f9SDavid Daney 		goto err;
153364b139f9SDavid Daney 
153464b139f9SDavid Daney 	r = octeon_irq_force_ciu_mapping(ciu_domain, OCTEON_IRQ_RML, 0, 46);
153564b139f9SDavid Daney 	if (r)
153664b139f9SDavid Daney 		goto err;
153764b139f9SDavid Daney 
153864b139f9SDavid Daney 	for (i = 0; i < 4; i++) {
153964b139f9SDavid Daney 		r = octeon_irq_force_ciu_mapping(
154064b139f9SDavid Daney 			ciu_domain, i + OCTEON_IRQ_TIMER0, 0, i + 52);
154164b139f9SDavid Daney 		if (r)
154264b139f9SDavid Daney 			goto err;
154364b139f9SDavid Daney 	}
154464b139f9SDavid Daney 
154564b139f9SDavid Daney 	r = octeon_irq_force_ciu_mapping(ciu_domain, OCTEON_IRQ_TWSI2, 0, 59);
154664b139f9SDavid Daney 	if (r)
154764b139f9SDavid Daney 		goto err;
154864b139f9SDavid Daney 
154964b139f9SDavid Daney 	/* CIU_1 */
155064b139f9SDavid Daney 	for (i = 0; i < 16; i++) {
155164b139f9SDavid Daney 		r = octeon_irq_set_ciu_mapping(
155264b139f9SDavid Daney 			i + OCTEON_IRQ_WDOG0, 1, i + 0, 0, chip_wd,
155364b139f9SDavid Daney 			handle_level_irq);
155464b139f9SDavid Daney 		if (r)
155564b139f9SDavid Daney 			goto err;
155664b139f9SDavid Daney 	}
155764b139f9SDavid Daney 
155864b139f9SDavid Daney 	/* Enable the CIU lines */
155964b139f9SDavid Daney 	set_c0_status(STATUSF_IP3 | STATUSF_IP2);
156064b139f9SDavid Daney 	if (octeon_irq_use_ip4)
156164b139f9SDavid Daney 		set_c0_status(STATUSF_IP4);
156264b139f9SDavid Daney 	else
156364b139f9SDavid Daney 		clear_c0_status(STATUSF_IP4);
156464b139f9SDavid Daney 
156564b139f9SDavid Daney 	return 0;
156664b139f9SDavid Daney err:
156764b139f9SDavid Daney 	return r;
156864b139f9SDavid Daney }
156964b139f9SDavid Daney 
157064b139f9SDavid Daney static int __init octeon_irq_init_gpio(
157164b139f9SDavid Daney 	struct device_node *gpio_node, struct device_node *parent)
157264b139f9SDavid Daney {
1573a0c16582SDavid Daney 	struct octeon_irq_gpio_domain_data *gpiod;
157464b139f9SDavid Daney 	u32 interrupt_cells;
157564b139f9SDavid Daney 	unsigned int base_hwirq;
157664b139f9SDavid Daney 	int r;
157764b139f9SDavid Daney 
157864b139f9SDavid Daney 	r = of_property_read_u32(parent, "#interrupt-cells", &interrupt_cells);
157964b139f9SDavid Daney 	if (r)
158064b139f9SDavid Daney 		return r;
158164b139f9SDavid Daney 
158264b139f9SDavid Daney 	if (interrupt_cells == 1) {
158364b139f9SDavid Daney 		u32 v;
158464b139f9SDavid Daney 
158564b139f9SDavid Daney 		r = of_property_read_u32_index(gpio_node, "interrupts", 0, &v);
158664b139f9SDavid Daney 		if (r) {
158764b139f9SDavid Daney 			pr_warn("No \"interrupts\" property.\n");
158864b139f9SDavid Daney 			return r;
158964b139f9SDavid Daney 		}
159064b139f9SDavid Daney 		base_hwirq = v;
159164b139f9SDavid Daney 	} else if (interrupt_cells == 2) {
159264b139f9SDavid Daney 		u32 v0, v1;
159364b139f9SDavid Daney 
159464b139f9SDavid Daney 		r = of_property_read_u32_index(gpio_node, "interrupts", 0, &v0);
159564b139f9SDavid Daney 		if (r) {
159664b139f9SDavid Daney 			pr_warn("No \"interrupts\" property.\n");
159764b139f9SDavid Daney 			return r;
159864b139f9SDavid Daney 		}
159964b139f9SDavid Daney 		r = of_property_read_u32_index(gpio_node, "interrupts", 1, &v1);
160064b139f9SDavid Daney 		if (r) {
160164b139f9SDavid Daney 			pr_warn("No \"interrupts\" property.\n");
160264b139f9SDavid Daney 			return r;
160364b139f9SDavid Daney 		}
160464b139f9SDavid Daney 		base_hwirq = (v0 << 6) | v1;
160564b139f9SDavid Daney 	} else {
160664b139f9SDavid Daney 		pr_warn("Bad \"#interrupt-cells\" property: %u\n",
160764b139f9SDavid Daney 			interrupt_cells);
160864b139f9SDavid Daney 		return -EINVAL;
160964b139f9SDavid Daney 	}
1610a0c16582SDavid Daney 
1611a0c16582SDavid Daney 	gpiod = kzalloc(sizeof(*gpiod), GFP_KERNEL);
1612a0c16582SDavid Daney 	if (gpiod) {
1613a0c16582SDavid Daney 		/* gpio domain host_data is the base hwirq number. */
161464b139f9SDavid Daney 		gpiod->base_hwirq = base_hwirq;
161564b139f9SDavid Daney 		irq_domain_add_linear(
161664b139f9SDavid Daney 			gpio_node, 16, &octeon_irq_domain_gpio_ops, gpiod);
161764b139f9SDavid Daney 	} else {
1618a0c16582SDavid Daney 		pr_warn("Cannot allocate memory for GPIO irq_domain.\n");
161964b139f9SDavid Daney 		return -ENOMEM;
16200c326387SDavid Daney 	}
16215aae1fd4SDavid Daney 
16220a900553SSteven J. Hill 	/*
16230a900553SSteven J. Hill 	 * Clear the OF_POPULATED flag that was set by of_irq_init()
16240a900553SSteven J. Hill 	 * so that all GPIO devices will be probed.
16250a900553SSteven J. Hill 	 */
16260a900553SSteven J. Hill 	of_node_clear_flag(gpio_node, OF_POPULATED);
16270a900553SSteven J. Hill 
162864b139f9SDavid Daney 	return 0;
162964b139f9SDavid Daney }
163088fd8589SDavid Daney /*
163188fd8589SDavid Daney  * Watchdog interrupts are special.  They are associated with a single
163288fd8589SDavid Daney  * core, so we hardwire the affinity to that core.
163388fd8589SDavid Daney  */
163488fd8589SDavid Daney static void octeon_irq_ciu2_wd_enable(struct irq_data *data)
163588fd8589SDavid Daney {
163688fd8589SDavid Daney 	u64 mask;
163788fd8589SDavid Daney 	u64 en_addr;
163888fd8589SDavid Daney 	int coreid = data->irq - OCTEON_IRQ_WDOG0;
163964b139f9SDavid Daney 	struct octeon_ciu_chip_data *cd;
164088fd8589SDavid Daney 
164164b139f9SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
164264b139f9SDavid Daney 	mask = 1ull << (cd->bit);
164388fd8589SDavid Daney 
164464b139f9SDavid Daney 	en_addr = CVMX_CIU2_EN_PPX_IP2_WRKQ_W1S(coreid) +
164564b139f9SDavid Daney 		(0x1000ull * cd->line);
164688fd8589SDavid Daney 	cvmx_write_csr(en_addr, mask);
164788fd8589SDavid Daney 
164888fd8589SDavid Daney }
164988fd8589SDavid Daney 
165088fd8589SDavid Daney static void octeon_irq_ciu2_enable(struct irq_data *data)
165188fd8589SDavid Daney {
165288fd8589SDavid Daney 	u64 mask;
165388fd8589SDavid Daney 	u64 en_addr;
165488fd8589SDavid Daney 	int cpu = next_cpu_for_irq(data);
165588fd8589SDavid Daney 	int coreid = octeon_coreid_for_cpu(cpu);
165664b139f9SDavid Daney 	struct octeon_ciu_chip_data *cd;
165788fd8589SDavid Daney 
165864b139f9SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
165964b139f9SDavid Daney 	mask = 1ull << (cd->bit);
166088fd8589SDavid Daney 
166164b139f9SDavid Daney 	en_addr = CVMX_CIU2_EN_PPX_IP2_WRKQ_W1S(coreid) +
166264b139f9SDavid Daney 		(0x1000ull * cd->line);
166388fd8589SDavid Daney 	cvmx_write_csr(en_addr, mask);
166488fd8589SDavid Daney }
166588fd8589SDavid Daney 
166688fd8589SDavid Daney static void octeon_irq_ciu2_enable_local(struct irq_data *data)
166788fd8589SDavid Daney {
166888fd8589SDavid Daney 	u64 mask;
166988fd8589SDavid Daney 	u64 en_addr;
167088fd8589SDavid Daney 	int coreid = cvmx_get_core_num();
167164b139f9SDavid Daney 	struct octeon_ciu_chip_data *cd;
167288fd8589SDavid Daney 
167364b139f9SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
167464b139f9SDavid Daney 	mask = 1ull << (cd->bit);
167588fd8589SDavid Daney 
167664b139f9SDavid Daney 	en_addr = CVMX_CIU2_EN_PPX_IP2_WRKQ_W1S(coreid) +
167764b139f9SDavid Daney 		(0x1000ull * cd->line);
167888fd8589SDavid Daney 	cvmx_write_csr(en_addr, mask);
167988fd8589SDavid Daney 
168088fd8589SDavid Daney }
168188fd8589SDavid Daney 
168288fd8589SDavid Daney static void octeon_irq_ciu2_disable_local(struct irq_data *data)
168388fd8589SDavid Daney {
168488fd8589SDavid Daney 	u64 mask;
168588fd8589SDavid Daney 	u64 en_addr;
168688fd8589SDavid Daney 	int coreid = cvmx_get_core_num();
168764b139f9SDavid Daney 	struct octeon_ciu_chip_data *cd;
168888fd8589SDavid Daney 
168964b139f9SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
169064b139f9SDavid Daney 	mask = 1ull << (cd->bit);
169188fd8589SDavid Daney 
169264b139f9SDavid Daney 	en_addr = CVMX_CIU2_EN_PPX_IP2_WRKQ_W1C(coreid) +
169364b139f9SDavid Daney 		(0x1000ull * cd->line);
169488fd8589SDavid Daney 	cvmx_write_csr(en_addr, mask);
169588fd8589SDavid Daney 
169688fd8589SDavid Daney }
169788fd8589SDavid Daney 
169888fd8589SDavid Daney static void octeon_irq_ciu2_ack(struct irq_data *data)
169988fd8589SDavid Daney {
170088fd8589SDavid Daney 	u64 mask;
170188fd8589SDavid Daney 	u64 en_addr;
170288fd8589SDavid Daney 	int coreid = cvmx_get_core_num();
170364b139f9SDavid Daney 	struct octeon_ciu_chip_data *cd;
170488fd8589SDavid Daney 
170564b139f9SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
170664b139f9SDavid Daney 	mask = 1ull << (cd->bit);
170788fd8589SDavid Daney 
170864b139f9SDavid Daney 	en_addr = CVMX_CIU2_RAW_PPX_IP2_WRKQ(coreid) + (0x1000ull * cd->line);
170988fd8589SDavid Daney 	cvmx_write_csr(en_addr, mask);
171088fd8589SDavid Daney 
171188fd8589SDavid Daney }
171288fd8589SDavid Daney 
171388fd8589SDavid Daney static void octeon_irq_ciu2_disable_all(struct irq_data *data)
171488fd8589SDavid Daney {
171588fd8589SDavid Daney 	int cpu;
171688fd8589SDavid Daney 	u64 mask;
171764b139f9SDavid Daney 	struct octeon_ciu_chip_data *cd;
171888fd8589SDavid Daney 
171964b139f9SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
172064b139f9SDavid Daney 	mask = 1ull << (cd->bit);
172188fd8589SDavid Daney 
172288fd8589SDavid Daney 	for_each_online_cpu(cpu) {
172364b139f9SDavid Daney 		u64 en_addr = CVMX_CIU2_EN_PPX_IP2_WRKQ_W1C(
172464b139f9SDavid Daney 			octeon_coreid_for_cpu(cpu)) + (0x1000ull * cd->line);
172588fd8589SDavid Daney 		cvmx_write_csr(en_addr, mask);
172688fd8589SDavid Daney 	}
172788fd8589SDavid Daney }
172888fd8589SDavid Daney 
172988fd8589SDavid Daney static void octeon_irq_ciu2_mbox_enable_all(struct irq_data *data)
173088fd8589SDavid Daney {
173188fd8589SDavid Daney 	int cpu;
173288fd8589SDavid Daney 	u64 mask;
173388fd8589SDavid Daney 
173488fd8589SDavid Daney 	mask = 1ull << (data->irq - OCTEON_IRQ_MBOX0);
173588fd8589SDavid Daney 
173688fd8589SDavid Daney 	for_each_online_cpu(cpu) {
173764b139f9SDavid Daney 		u64 en_addr = CVMX_CIU2_EN_PPX_IP3_MBOX_W1S(
173864b139f9SDavid Daney 			octeon_coreid_for_cpu(cpu));
173988fd8589SDavid Daney 		cvmx_write_csr(en_addr, mask);
174088fd8589SDavid Daney 	}
174188fd8589SDavid Daney }
174288fd8589SDavid Daney 
174388fd8589SDavid Daney static void octeon_irq_ciu2_mbox_disable_all(struct irq_data *data)
174488fd8589SDavid Daney {
174588fd8589SDavid Daney 	int cpu;
174688fd8589SDavid Daney 	u64 mask;
174788fd8589SDavid Daney 
174888fd8589SDavid Daney 	mask = 1ull << (data->irq - OCTEON_IRQ_MBOX0);
174988fd8589SDavid Daney 
175088fd8589SDavid Daney 	for_each_online_cpu(cpu) {
175164b139f9SDavid Daney 		u64 en_addr = CVMX_CIU2_EN_PPX_IP3_MBOX_W1C(
175264b139f9SDavid Daney 			octeon_coreid_for_cpu(cpu));
175388fd8589SDavid Daney 		cvmx_write_csr(en_addr, mask);
175488fd8589SDavid Daney 	}
175588fd8589SDavid Daney }
175688fd8589SDavid Daney 
175788fd8589SDavid Daney static void octeon_irq_ciu2_mbox_enable_local(struct irq_data *data)
175888fd8589SDavid Daney {
175988fd8589SDavid Daney 	u64 mask;
176088fd8589SDavid Daney 	u64 en_addr;
176188fd8589SDavid Daney 	int coreid = cvmx_get_core_num();
176288fd8589SDavid Daney 
176388fd8589SDavid Daney 	mask = 1ull << (data->irq - OCTEON_IRQ_MBOX0);
176488fd8589SDavid Daney 	en_addr = CVMX_CIU2_EN_PPX_IP3_MBOX_W1S(coreid);
176588fd8589SDavid Daney 	cvmx_write_csr(en_addr, mask);
176688fd8589SDavid Daney }
176788fd8589SDavid Daney 
176888fd8589SDavid Daney static void octeon_irq_ciu2_mbox_disable_local(struct irq_data *data)
176988fd8589SDavid Daney {
177088fd8589SDavid Daney 	u64 mask;
177188fd8589SDavid Daney 	u64 en_addr;
177288fd8589SDavid Daney 	int coreid = cvmx_get_core_num();
177388fd8589SDavid Daney 
177488fd8589SDavid Daney 	mask = 1ull << (data->irq - OCTEON_IRQ_MBOX0);
177588fd8589SDavid Daney 	en_addr = CVMX_CIU2_EN_PPX_IP3_MBOX_W1C(coreid);
177688fd8589SDavid Daney 	cvmx_write_csr(en_addr, mask);
177788fd8589SDavid Daney }
177888fd8589SDavid Daney 
177988fd8589SDavid Daney #ifdef CONFIG_SMP
178088fd8589SDavid Daney static int octeon_irq_ciu2_set_affinity(struct irq_data *data,
178188fd8589SDavid Daney 					const struct cpumask *dest, bool force)
178288fd8589SDavid Daney {
178388fd8589SDavid Daney 	int cpu;
178488fd8589SDavid Daney 	bool enable_one = !irqd_irq_disabled(data) && !irqd_irq_masked(data);
178588fd8589SDavid Daney 	u64 mask;
178664b139f9SDavid Daney 	struct octeon_ciu_chip_data *cd;
178788fd8589SDavid Daney 
178888fd8589SDavid Daney 	if (!enable_one)
178988fd8589SDavid Daney 		return 0;
179088fd8589SDavid Daney 
179164b139f9SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
179264b139f9SDavid Daney 	mask = 1ull << cd->bit;
179388fd8589SDavid Daney 
179488fd8589SDavid Daney 	for_each_online_cpu(cpu) {
179588fd8589SDavid Daney 		u64 en_addr;
179688fd8589SDavid Daney 		if (cpumask_test_cpu(cpu, dest) && enable_one) {
179788fd8589SDavid Daney 			enable_one = false;
179864b139f9SDavid Daney 			en_addr = CVMX_CIU2_EN_PPX_IP2_WRKQ_W1S(
179964b139f9SDavid Daney 				octeon_coreid_for_cpu(cpu)) +
180064b139f9SDavid Daney 				(0x1000ull * cd->line);
180188fd8589SDavid Daney 		} else {
180264b139f9SDavid Daney 			en_addr = CVMX_CIU2_EN_PPX_IP2_WRKQ_W1C(
180364b139f9SDavid Daney 				octeon_coreid_for_cpu(cpu)) +
180464b139f9SDavid Daney 				(0x1000ull * cd->line);
180588fd8589SDavid Daney 		}
180688fd8589SDavid Daney 		cvmx_write_csr(en_addr, mask);
180788fd8589SDavid Daney 	}
180888fd8589SDavid Daney 
180988fd8589SDavid Daney 	return 0;
181088fd8589SDavid Daney }
181188fd8589SDavid Daney #endif
181288fd8589SDavid Daney 
181388fd8589SDavid Daney static void octeon_irq_ciu2_enable_gpio(struct irq_data *data)
181488fd8589SDavid Daney {
181588fd8589SDavid Daney 	octeon_irq_gpio_setup(data);
181688fd8589SDavid Daney 	octeon_irq_ciu2_enable(data);
181788fd8589SDavid Daney }
181888fd8589SDavid Daney 
181988fd8589SDavid Daney static void octeon_irq_ciu2_disable_gpio(struct irq_data *data)
182088fd8589SDavid Daney {
182164b139f9SDavid Daney 	struct octeon_ciu_chip_data *cd;
182288fd8589SDavid Daney 
182364b139f9SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
182464b139f9SDavid Daney 
182564b139f9SDavid Daney 	cvmx_write_csr(CVMX_GPIO_BIT_CFGX(cd->gpio_line), 0);
182688fd8589SDavid Daney 
182788fd8589SDavid Daney 	octeon_irq_ciu2_disable_all(data);
182888fd8589SDavid Daney }
182988fd8589SDavid Daney 
183088fd8589SDavid Daney static struct irq_chip octeon_irq_chip_ciu2 = {
183188fd8589SDavid Daney 	.name = "CIU2-E",
183288fd8589SDavid Daney 	.irq_enable = octeon_irq_ciu2_enable,
183388fd8589SDavid Daney 	.irq_disable = octeon_irq_ciu2_disable_all,
18342e3ecab1SDavid Daney 	.irq_mask = octeon_irq_ciu2_disable_local,
18352e3ecab1SDavid Daney 	.irq_unmask = octeon_irq_ciu2_enable,
18362e3ecab1SDavid Daney #ifdef CONFIG_SMP
18372e3ecab1SDavid Daney 	.irq_set_affinity = octeon_irq_ciu2_set_affinity,
18382e3ecab1SDavid Daney 	.irq_cpu_offline = octeon_irq_cpu_offline_ciu,
18392e3ecab1SDavid Daney #endif
18402e3ecab1SDavid Daney };
18412e3ecab1SDavid Daney 
18422e3ecab1SDavid Daney static struct irq_chip octeon_irq_chip_ciu2_edge = {
18432e3ecab1SDavid Daney 	.name = "CIU2-E",
18442e3ecab1SDavid Daney 	.irq_enable = octeon_irq_ciu2_enable,
18452e3ecab1SDavid Daney 	.irq_disable = octeon_irq_ciu2_disable_all,
184688fd8589SDavid Daney 	.irq_ack = octeon_irq_ciu2_ack,
184788fd8589SDavid Daney 	.irq_mask = octeon_irq_ciu2_disable_local,
184888fd8589SDavid Daney 	.irq_unmask = octeon_irq_ciu2_enable,
184988fd8589SDavid Daney #ifdef CONFIG_SMP
185088fd8589SDavid Daney 	.irq_set_affinity = octeon_irq_ciu2_set_affinity,
185188fd8589SDavid Daney 	.irq_cpu_offline = octeon_irq_cpu_offline_ciu,
185288fd8589SDavid Daney #endif
185388fd8589SDavid Daney };
185488fd8589SDavid Daney 
185588fd8589SDavid Daney static struct irq_chip octeon_irq_chip_ciu2_mbox = {
185688fd8589SDavid Daney 	.name = "CIU2-M",
185788fd8589SDavid Daney 	.irq_enable = octeon_irq_ciu2_mbox_enable_all,
185888fd8589SDavid Daney 	.irq_disable = octeon_irq_ciu2_mbox_disable_all,
185988fd8589SDavid Daney 	.irq_ack = octeon_irq_ciu2_mbox_disable_local,
186088fd8589SDavid Daney 	.irq_eoi = octeon_irq_ciu2_mbox_enable_local,
186188fd8589SDavid Daney 
186288fd8589SDavid Daney 	.irq_cpu_online = octeon_irq_ciu2_mbox_enable_local,
186388fd8589SDavid Daney 	.irq_cpu_offline = octeon_irq_ciu2_mbox_disable_local,
186488fd8589SDavid Daney 	.flags = IRQCHIP_ONOFFLINE_ENABLED,
186588fd8589SDavid Daney };
186688fd8589SDavid Daney 
186788fd8589SDavid Daney static struct irq_chip octeon_irq_chip_ciu2_wd = {
186888fd8589SDavid Daney 	.name = "CIU2-W",
186988fd8589SDavid Daney 	.irq_enable = octeon_irq_ciu2_wd_enable,
187088fd8589SDavid Daney 	.irq_disable = octeon_irq_ciu2_disable_all,
187188fd8589SDavid Daney 	.irq_mask = octeon_irq_ciu2_disable_local,
187288fd8589SDavid Daney 	.irq_unmask = octeon_irq_ciu2_enable_local,
187388fd8589SDavid Daney };
187488fd8589SDavid Daney 
187588fd8589SDavid Daney static struct irq_chip octeon_irq_chip_ciu2_gpio = {
187688fd8589SDavid Daney 	.name = "CIU-GPIO",
187788fd8589SDavid Daney 	.irq_enable = octeon_irq_ciu2_enable_gpio,
187888fd8589SDavid Daney 	.irq_disable = octeon_irq_ciu2_disable_gpio,
187988fd8589SDavid Daney 	.irq_ack = octeon_irq_ciu_gpio_ack,
188088fd8589SDavid Daney 	.irq_mask = octeon_irq_ciu2_disable_local,
188188fd8589SDavid Daney 	.irq_unmask = octeon_irq_ciu2_enable,
188288fd8589SDavid Daney 	.irq_set_type = octeon_irq_ciu_gpio_set_type,
188388fd8589SDavid Daney #ifdef CONFIG_SMP
188488fd8589SDavid Daney 	.irq_set_affinity = octeon_irq_ciu2_set_affinity,
188588fd8589SDavid Daney 	.irq_cpu_offline = octeon_irq_cpu_offline_ciu,
188688fd8589SDavid Daney #endif
188788fd8589SDavid Daney 	.flags = IRQCHIP_SET_TYPE_MASKED,
188888fd8589SDavid Daney };
188988fd8589SDavid Daney 
189088fd8589SDavid Daney static int octeon_irq_ciu2_xlat(struct irq_domain *d,
189188fd8589SDavid Daney 				struct device_node *node,
189288fd8589SDavid Daney 				const u32 *intspec,
189388fd8589SDavid Daney 				unsigned int intsize,
189488fd8589SDavid Daney 				unsigned long *out_hwirq,
189588fd8589SDavid Daney 				unsigned int *out_type)
189688fd8589SDavid Daney {
189788fd8589SDavid Daney 	unsigned int ciu, bit;
189888fd8589SDavid Daney 
189988fd8589SDavid Daney 	ciu = intspec[0];
190088fd8589SDavid Daney 	bit = intspec[1];
190188fd8589SDavid Daney 
190288fd8589SDavid Daney 	*out_hwirq = (ciu << 6) | bit;
190388fd8589SDavid Daney 	*out_type = 0;
190488fd8589SDavid Daney 
190588fd8589SDavid Daney 	return 0;
190688fd8589SDavid Daney }
190788fd8589SDavid Daney 
190888fd8589SDavid Daney static bool octeon_irq_ciu2_is_edge(unsigned int line, unsigned int bit)
190988fd8589SDavid Daney {
191088fd8589SDavid Daney 	bool edge = false;
191188fd8589SDavid Daney 
191288fd8589SDavid Daney 	if (line == 3) /* MIO */
191388fd8589SDavid Daney 		switch (bit) {
191488fd8589SDavid Daney 		case 2:	 /* IPD_DRP */
191588fd8589SDavid Daney 		case 8 ... 11: /* Timers */
191688fd8589SDavid Daney 		case 48: /* PTP */
191788fd8589SDavid Daney 			edge = true;
191888fd8589SDavid Daney 			break;
191988fd8589SDavid Daney 		default:
192088fd8589SDavid Daney 			break;
192188fd8589SDavid Daney 		}
192288fd8589SDavid Daney 	else if (line == 6) /* PKT */
192388fd8589SDavid Daney 		switch (bit) {
192488fd8589SDavid Daney 		case 52 ... 53: /* ILK_DRP */
192588fd8589SDavid Daney 		case 8 ... 12:	/* GMX_DRP */
192688fd8589SDavid Daney 			edge = true;
192788fd8589SDavid Daney 			break;
192888fd8589SDavid Daney 		default:
192988fd8589SDavid Daney 			break;
193088fd8589SDavid Daney 		}
193188fd8589SDavid Daney 	return edge;
193288fd8589SDavid Daney }
193388fd8589SDavid Daney 
193488fd8589SDavid Daney static int octeon_irq_ciu2_map(struct irq_domain *d,
193588fd8589SDavid Daney 			       unsigned int virq, irq_hw_number_t hw)
193688fd8589SDavid Daney {
193788fd8589SDavid Daney 	unsigned int line = hw >> 6;
193888fd8589SDavid Daney 	unsigned int bit = hw & 63;
193988fd8589SDavid Daney 
19402eddb708SAndreas Herrmann 	/*
19412eddb708SAndreas Herrmann 	 * Don't map irq if it is reserved for GPIO.
19422eddb708SAndreas Herrmann 	 * (Line 7 are the GPIO lines.)
19432eddb708SAndreas Herrmann 	 */
19442eddb708SAndreas Herrmann 	if (line == 7)
19452eddb708SAndreas Herrmann 		return 0;
19462eddb708SAndreas Herrmann 
19472eddb708SAndreas Herrmann 	if (line > 7 || octeon_irq_ciu_to_irq[line][bit] != 0)
194888fd8589SDavid Daney 		return -EINVAL;
194988fd8589SDavid Daney 
195088fd8589SDavid Daney 	if (octeon_irq_ciu2_is_edge(line, bit))
195188fd8589SDavid Daney 		octeon_irq_set_ciu_mapping(virq, line, bit, 0,
19522e3ecab1SDavid Daney 					   &octeon_irq_chip_ciu2_edge,
195388fd8589SDavid Daney 					   handle_edge_irq);
195488fd8589SDavid Daney 	else
195588fd8589SDavid Daney 		octeon_irq_set_ciu_mapping(virq, line, bit, 0,
195688fd8589SDavid Daney 					   &octeon_irq_chip_ciu2,
195788fd8589SDavid Daney 					   handle_level_irq);
195888fd8589SDavid Daney 
195988fd8589SDavid Daney 	return 0;
196088fd8589SDavid Daney }
196188fd8589SDavid Daney 
196288fd8589SDavid Daney static struct irq_domain_ops octeon_irq_domain_ciu2_ops = {
196388fd8589SDavid Daney 	.map = octeon_irq_ciu2_map,
196464b139f9SDavid Daney 	.unmap = octeon_irq_free_cd,
196588fd8589SDavid Daney 	.xlate = octeon_irq_ciu2_xlat,
196688fd8589SDavid Daney };
196788fd8589SDavid Daney 
196888fd8589SDavid Daney static void octeon_irq_ciu2(void)
196988fd8589SDavid Daney {
197088fd8589SDavid Daney 	int line;
197188fd8589SDavid Daney 	int bit;
197288fd8589SDavid Daney 	int irq;
197388fd8589SDavid Daney 	u64 src_reg, src, sum;
197488fd8589SDavid Daney 	const unsigned long core_id = cvmx_get_core_num();
197588fd8589SDavid Daney 
197688fd8589SDavid Daney 	sum = cvmx_read_csr(CVMX_CIU2_SUM_PPX_IP2(core_id)) & 0xfful;
197788fd8589SDavid Daney 
197888fd8589SDavid Daney 	if (unlikely(!sum))
197988fd8589SDavid Daney 		goto spurious;
198088fd8589SDavid Daney 
198188fd8589SDavid Daney 	line = fls64(sum) - 1;
198288fd8589SDavid Daney 	src_reg = CVMX_CIU2_SRC_PPX_IP2_WRKQ(core_id) + (0x1000 * line);
198388fd8589SDavid Daney 	src = cvmx_read_csr(src_reg);
198488fd8589SDavid Daney 
198588fd8589SDavid Daney 	if (unlikely(!src))
198688fd8589SDavid Daney 		goto spurious;
198788fd8589SDavid Daney 
198888fd8589SDavid Daney 	bit = fls64(src) - 1;
198988fd8589SDavid Daney 	irq = octeon_irq_ciu_to_irq[line][bit];
199088fd8589SDavid Daney 	if (unlikely(!irq))
199188fd8589SDavid Daney 		goto spurious;
199288fd8589SDavid Daney 
199388fd8589SDavid Daney 	do_IRQ(irq);
199488fd8589SDavid Daney 	goto out;
199588fd8589SDavid Daney 
199688fd8589SDavid Daney spurious:
199788fd8589SDavid Daney 	spurious_interrupt();
199888fd8589SDavid Daney out:
199988fd8589SDavid Daney 	/* CN68XX pass 1.x has an errata that accessing the ACK registers
200088fd8589SDavid Daney 		can stop interrupts from propagating */
200188fd8589SDavid Daney 	if (OCTEON_IS_MODEL(OCTEON_CN68XX))
200288fd8589SDavid Daney 		cvmx_read_csr(CVMX_CIU2_INTR_CIU_READY);
200388fd8589SDavid Daney 	else
200488fd8589SDavid Daney 		cvmx_read_csr(CVMX_CIU2_ACK_PPX_IP2(core_id));
200588fd8589SDavid Daney 	return;
200688fd8589SDavid Daney }
200788fd8589SDavid Daney 
200888fd8589SDavid Daney static void octeon_irq_ciu2_mbox(void)
200988fd8589SDavid Daney {
201088fd8589SDavid Daney 	int line;
201188fd8589SDavid Daney 
201288fd8589SDavid Daney 	const unsigned long core_id = cvmx_get_core_num();
201388fd8589SDavid Daney 	u64 sum = cvmx_read_csr(CVMX_CIU2_SUM_PPX_IP3(core_id)) >> 60;
201488fd8589SDavid Daney 
201588fd8589SDavid Daney 	if (unlikely(!sum))
201688fd8589SDavid Daney 		goto spurious;
201788fd8589SDavid Daney 
201888fd8589SDavid Daney 	line = fls64(sum) - 1;
201988fd8589SDavid Daney 
202088fd8589SDavid Daney 	do_IRQ(OCTEON_IRQ_MBOX0 + line);
202188fd8589SDavid Daney 	goto out;
202288fd8589SDavid Daney 
202388fd8589SDavid Daney spurious:
202488fd8589SDavid Daney 	spurious_interrupt();
202588fd8589SDavid Daney out:
202688fd8589SDavid Daney 	/* CN68XX pass 1.x has an errata that accessing the ACK registers
202788fd8589SDavid Daney 		can stop interrupts from propagating */
202888fd8589SDavid Daney 	if (OCTEON_IS_MODEL(OCTEON_CN68XX))
202988fd8589SDavid Daney 		cvmx_read_csr(CVMX_CIU2_INTR_CIU_READY);
203088fd8589SDavid Daney 	else
203188fd8589SDavid Daney 		cvmx_read_csr(CVMX_CIU2_ACK_PPX_IP3(core_id));
203288fd8589SDavid Daney 	return;
203388fd8589SDavid Daney }
203488fd8589SDavid Daney 
203564b139f9SDavid Daney static int __init octeon_irq_init_ciu2(
203664b139f9SDavid Daney 	struct device_node *ciu_node, struct device_node *parent)
203788fd8589SDavid Daney {
203864b139f9SDavid Daney 	unsigned int i, r;
203988fd8589SDavid Daney 	struct irq_domain *ciu_domain = NULL;
204088fd8589SDavid Daney 
204188fd8589SDavid Daney 	octeon_irq_init_ciu2_percpu();
204288fd8589SDavid Daney 	octeon_irq_setup_secondary = octeon_irq_setup_secondary_ciu2;
204388fd8589SDavid Daney 
204464b139f9SDavid Daney 	octeon_irq_gpio_chip = &octeon_irq_chip_ciu2_gpio;
204588fd8589SDavid Daney 	octeon_irq_ip2 = octeon_irq_ciu2;
204688fd8589SDavid Daney 	octeon_irq_ip3 = octeon_irq_ciu2_mbox;
204788fd8589SDavid Daney 	octeon_irq_ip4 = octeon_irq_ip4_mask;
204888fd8589SDavid Daney 
204988fd8589SDavid Daney 	/* Mips internal */
205088fd8589SDavid Daney 	octeon_irq_init_core();
205188fd8589SDavid Daney 
205264b139f9SDavid Daney 	ciu_domain = irq_domain_add_tree(
205364b139f9SDavid Daney 		ciu_node, &octeon_irq_domain_ciu2_ops, NULL);
2054c9f0f0c0SDavid Daney 	irq_set_default_host(ciu_domain);
205588fd8589SDavid Daney 
205688fd8589SDavid Daney 	/* CUI2 */
205764b139f9SDavid Daney 	for (i = 0; i < 64; i++) {
205864b139f9SDavid Daney 		r = octeon_irq_force_ciu_mapping(
205964b139f9SDavid Daney 			ciu_domain, i + OCTEON_IRQ_WORKQ0, 0, i);
206064b139f9SDavid Daney 		if (r)
206164b139f9SDavid Daney 			goto err;
206264b139f9SDavid Daney 	}
206388fd8589SDavid Daney 
206464b139f9SDavid Daney 	for (i = 0; i < 32; i++) {
206564b139f9SDavid Daney 		r = octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_WDOG0, 1, i, 0,
206688fd8589SDavid Daney 			&octeon_irq_chip_ciu2_wd, handle_level_irq);
206764b139f9SDavid Daney 		if (r)
206864b139f9SDavid Daney 			goto err;
206964b139f9SDavid Daney 	}
207088fd8589SDavid Daney 
207164b139f9SDavid Daney 	for (i = 0; i < 4; i++) {
207264b139f9SDavid Daney 		r = octeon_irq_force_ciu_mapping(
207364b139f9SDavid Daney 			ciu_domain, i + OCTEON_IRQ_TIMER0, 3, i + 8);
207464b139f9SDavid Daney 		if (r)
207564b139f9SDavid Daney 			goto err;
207664b139f9SDavid Daney 	}
207788fd8589SDavid Daney 
207864b139f9SDavid Daney 	for (i = 0; i < 4; i++) {
207964b139f9SDavid Daney 		r = octeon_irq_force_ciu_mapping(
208064b139f9SDavid Daney 			ciu_domain, i + OCTEON_IRQ_PCI_INT0, 4, i);
208164b139f9SDavid Daney 		if (r)
208264b139f9SDavid Daney 			goto err;
208364b139f9SDavid Daney 	}
208488fd8589SDavid Daney 
208564b139f9SDavid Daney 	for (i = 0; i < 4; i++) {
208664b139f9SDavid Daney 		r = octeon_irq_force_ciu_mapping(
208764b139f9SDavid Daney 			ciu_domain, i + OCTEON_IRQ_PCI_MSI0, 4, i + 8);
208864b139f9SDavid Daney 		if (r)
208964b139f9SDavid Daney 			goto err;
209064b139f9SDavid Daney 	}
209188fd8589SDavid Daney 
209288fd8589SDavid Daney 	irq_set_chip_and_handler(OCTEON_IRQ_MBOX0, &octeon_irq_chip_ciu2_mbox, handle_percpu_irq);
209388fd8589SDavid Daney 	irq_set_chip_and_handler(OCTEON_IRQ_MBOX1, &octeon_irq_chip_ciu2_mbox, handle_percpu_irq);
209488fd8589SDavid Daney 	irq_set_chip_and_handler(OCTEON_IRQ_MBOX2, &octeon_irq_chip_ciu2_mbox, handle_percpu_irq);
209588fd8589SDavid Daney 	irq_set_chip_and_handler(OCTEON_IRQ_MBOX3, &octeon_irq_chip_ciu2_mbox, handle_percpu_irq);
209688fd8589SDavid Daney 
209788fd8589SDavid Daney 	/* Enable the CIU lines */
209888fd8589SDavid Daney 	set_c0_status(STATUSF_IP3 | STATUSF_IP2);
209988fd8589SDavid Daney 	clear_c0_status(STATUSF_IP4);
210064b139f9SDavid Daney 	return 0;
210164b139f9SDavid Daney err:
210264b139f9SDavid Daney 	return r;
210388fd8589SDavid Daney }
210488fd8589SDavid Daney 
210564b139f9SDavid Daney struct octeon_irq_cib_host_data {
210664b139f9SDavid Daney 	raw_spinlock_t lock;
210764b139f9SDavid Daney 	u64 raw_reg;
210864b139f9SDavid Daney 	u64 en_reg;
210964b139f9SDavid Daney 	int max_bits;
211064b139f9SDavid Daney };
211164b139f9SDavid Daney 
211264b139f9SDavid Daney struct octeon_irq_cib_chip_data {
211364b139f9SDavid Daney 	struct octeon_irq_cib_host_data *host_data;
211464b139f9SDavid Daney 	int bit;
211564b139f9SDavid Daney };
211664b139f9SDavid Daney 
211764b139f9SDavid Daney static void octeon_irq_cib_enable(struct irq_data *data)
211864b139f9SDavid Daney {
211964b139f9SDavid Daney 	unsigned long flags;
212064b139f9SDavid Daney 	u64 en;
212164b139f9SDavid Daney 	struct octeon_irq_cib_chip_data *cd = irq_data_get_irq_chip_data(data);
212264b139f9SDavid Daney 	struct octeon_irq_cib_host_data *host_data = cd->host_data;
212364b139f9SDavid Daney 
212464b139f9SDavid Daney 	raw_spin_lock_irqsave(&host_data->lock, flags);
212564b139f9SDavid Daney 	en = cvmx_read_csr(host_data->en_reg);
212664b139f9SDavid Daney 	en |= 1ull << cd->bit;
212764b139f9SDavid Daney 	cvmx_write_csr(host_data->en_reg, en);
212864b139f9SDavid Daney 	raw_spin_unlock_irqrestore(&host_data->lock, flags);
212964b139f9SDavid Daney }
213064b139f9SDavid Daney 
213164b139f9SDavid Daney static void octeon_irq_cib_disable(struct irq_data *data)
213264b139f9SDavid Daney {
213364b139f9SDavid Daney 	unsigned long flags;
213464b139f9SDavid Daney 	u64 en;
213564b139f9SDavid Daney 	struct octeon_irq_cib_chip_data *cd = irq_data_get_irq_chip_data(data);
213664b139f9SDavid Daney 	struct octeon_irq_cib_host_data *host_data = cd->host_data;
213764b139f9SDavid Daney 
213864b139f9SDavid Daney 	raw_spin_lock_irqsave(&host_data->lock, flags);
213964b139f9SDavid Daney 	en = cvmx_read_csr(host_data->en_reg);
214064b139f9SDavid Daney 	en &= ~(1ull << cd->bit);
214164b139f9SDavid Daney 	cvmx_write_csr(host_data->en_reg, en);
214264b139f9SDavid Daney 	raw_spin_unlock_irqrestore(&host_data->lock, flags);
214364b139f9SDavid Daney }
214464b139f9SDavid Daney 
214564b139f9SDavid Daney static int octeon_irq_cib_set_type(struct irq_data *data, unsigned int t)
214664b139f9SDavid Daney {
214764b139f9SDavid Daney 	irqd_set_trigger_type(data, t);
214864b139f9SDavid Daney 	return IRQ_SET_MASK_OK;
214964b139f9SDavid Daney }
215064b139f9SDavid Daney 
215164b139f9SDavid Daney static struct irq_chip octeon_irq_chip_cib = {
215264b139f9SDavid Daney 	.name = "CIB",
215364b139f9SDavid Daney 	.irq_enable = octeon_irq_cib_enable,
215464b139f9SDavid Daney 	.irq_disable = octeon_irq_cib_disable,
215564b139f9SDavid Daney 	.irq_mask = octeon_irq_cib_disable,
215664b139f9SDavid Daney 	.irq_unmask = octeon_irq_cib_enable,
215764b139f9SDavid Daney 	.irq_set_type = octeon_irq_cib_set_type,
215864b139f9SDavid Daney };
215964b139f9SDavid Daney 
216064b139f9SDavid Daney static int octeon_irq_cib_xlat(struct irq_domain *d,
216164b139f9SDavid Daney 				   struct device_node *node,
216264b139f9SDavid Daney 				   const u32 *intspec,
216364b139f9SDavid Daney 				   unsigned int intsize,
216464b139f9SDavid Daney 				   unsigned long *out_hwirq,
216564b139f9SDavid Daney 				   unsigned int *out_type)
216664b139f9SDavid Daney {
216764b139f9SDavid Daney 	unsigned int type = 0;
216864b139f9SDavid Daney 
216964b139f9SDavid Daney 	if (intsize == 2)
217064b139f9SDavid Daney 		type = intspec[1];
217164b139f9SDavid Daney 
217264b139f9SDavid Daney 	switch (type) {
217364b139f9SDavid Daney 	case 0: /* unofficial value, but we might as well let it work. */
217464b139f9SDavid Daney 	case 4: /* official value for level triggering. */
217564b139f9SDavid Daney 		*out_type = IRQ_TYPE_LEVEL_HIGH;
217664b139f9SDavid Daney 		break;
217764b139f9SDavid Daney 	case 1: /* official value for edge triggering. */
217864b139f9SDavid Daney 		*out_type = IRQ_TYPE_EDGE_RISING;
217964b139f9SDavid Daney 		break;
218064b139f9SDavid Daney 	default: /* Nothing else is acceptable. */
218164b139f9SDavid Daney 		return -EINVAL;
218264b139f9SDavid Daney 	}
218364b139f9SDavid Daney 
218464b139f9SDavid Daney 	*out_hwirq = intspec[0];
218564b139f9SDavid Daney 
218664b139f9SDavid Daney 	return 0;
218764b139f9SDavid Daney }
218864b139f9SDavid Daney 
218964b139f9SDavid Daney static int octeon_irq_cib_map(struct irq_domain *d,
219064b139f9SDavid Daney 			      unsigned int virq, irq_hw_number_t hw)
219164b139f9SDavid Daney {
219264b139f9SDavid Daney 	struct octeon_irq_cib_host_data *host_data = d->host_data;
219364b139f9SDavid Daney 	struct octeon_irq_cib_chip_data *cd;
219464b139f9SDavid Daney 
219564b139f9SDavid Daney 	if (hw >= host_data->max_bits) {
219664b139f9SDavid Daney 		pr_err("ERROR: %s mapping %u is to big!\n",
21975d4c9bc7SMarc Zyngier 		       irq_domain_get_of_node(d)->name, (unsigned)hw);
219864b139f9SDavid Daney 		return -EINVAL;
219964b139f9SDavid Daney 	}
220064b139f9SDavid Daney 
220164b139f9SDavid Daney 	cd = kzalloc(sizeof(*cd), GFP_KERNEL);
220264b139f9SDavid Daney 	cd->host_data = host_data;
220364b139f9SDavid Daney 	cd->bit = hw;
220464b139f9SDavid Daney 
220564b139f9SDavid Daney 	irq_set_chip_and_handler(virq, &octeon_irq_chip_cib,
220664b139f9SDavid Daney 				 handle_simple_irq);
220764b139f9SDavid Daney 	irq_set_chip_data(virq, cd);
220864b139f9SDavid Daney 	return 0;
220964b139f9SDavid Daney }
221064b139f9SDavid Daney 
221164b139f9SDavid Daney static struct irq_domain_ops octeon_irq_domain_cib_ops = {
221264b139f9SDavid Daney 	.map = octeon_irq_cib_map,
221364b139f9SDavid Daney 	.unmap = octeon_irq_free_cd,
221464b139f9SDavid Daney 	.xlate = octeon_irq_cib_xlat,
221564b139f9SDavid Daney };
221664b139f9SDavid Daney 
221764b139f9SDavid Daney /* Chain to real handler. */
221864b139f9SDavid Daney static irqreturn_t octeon_irq_cib_handler(int my_irq, void *data)
221964b139f9SDavid Daney {
222064b139f9SDavid Daney 	u64 en;
222164b139f9SDavid Daney 	u64 raw;
222264b139f9SDavid Daney 	u64 bits;
222364b139f9SDavid Daney 	int i;
222464b139f9SDavid Daney 	int irq;
222564b139f9SDavid Daney 	struct irq_domain *cib_domain = data;
222664b139f9SDavid Daney 	struct octeon_irq_cib_host_data *host_data = cib_domain->host_data;
222764b139f9SDavid Daney 
222864b139f9SDavid Daney 	en = cvmx_read_csr(host_data->en_reg);
222964b139f9SDavid Daney 	raw = cvmx_read_csr(host_data->raw_reg);
223064b139f9SDavid Daney 
223164b139f9SDavid Daney 	bits = en & raw;
223264b139f9SDavid Daney 
223364b139f9SDavid Daney 	for (i = 0; i < host_data->max_bits; i++) {
223464b139f9SDavid Daney 		if ((bits & 1ull << i) == 0)
223564b139f9SDavid Daney 			continue;
223664b139f9SDavid Daney 		irq = irq_find_mapping(cib_domain, i);
223764b139f9SDavid Daney 		if (!irq) {
223864b139f9SDavid Daney 			unsigned long flags;
223964b139f9SDavid Daney 
224064b139f9SDavid Daney 			pr_err("ERROR: CIB bit %d@%llx IRQ unhandled, disabling\n",
224164b139f9SDavid Daney 				i, host_data->raw_reg);
224264b139f9SDavid Daney 			raw_spin_lock_irqsave(&host_data->lock, flags);
224364b139f9SDavid Daney 			en = cvmx_read_csr(host_data->en_reg);
224464b139f9SDavid Daney 			en &= ~(1ull << i);
224564b139f9SDavid Daney 			cvmx_write_csr(host_data->en_reg, en);
224664b139f9SDavid Daney 			cvmx_write_csr(host_data->raw_reg, 1ull << i);
224764b139f9SDavid Daney 			raw_spin_unlock_irqrestore(&host_data->lock, flags);
224864b139f9SDavid Daney 		} else {
224964b139f9SDavid Daney 			struct irq_desc *desc = irq_to_desc(irq);
225064b139f9SDavid Daney 			struct irq_data *irq_data = irq_desc_get_irq_data(desc);
225164b139f9SDavid Daney 			/* If edge, acknowledge the bit we will be sending. */
225264b139f9SDavid Daney 			if (irqd_get_trigger_type(irq_data) &
225364b139f9SDavid Daney 				IRQ_TYPE_EDGE_BOTH)
225464b139f9SDavid Daney 				cvmx_write_csr(host_data->raw_reg, 1ull << i);
2255bd0b9ac4SThomas Gleixner 			generic_handle_irq_desc(desc);
225664b139f9SDavid Daney 		}
225764b139f9SDavid Daney 	}
225864b139f9SDavid Daney 
225964b139f9SDavid Daney 	return IRQ_HANDLED;
226064b139f9SDavid Daney }
226164b139f9SDavid Daney 
226264b139f9SDavid Daney static int __init octeon_irq_init_cib(struct device_node *ciu_node,
226364b139f9SDavid Daney 				      struct device_node *parent)
226464b139f9SDavid Daney {
226564b139f9SDavid Daney 	const __be32 *addr;
226664b139f9SDavid Daney 	u32 val;
226764b139f9SDavid Daney 	struct octeon_irq_cib_host_data *host_data;
226864b139f9SDavid Daney 	int parent_irq;
226964b139f9SDavid Daney 	int r;
227064b139f9SDavid Daney 	struct irq_domain *cib_domain;
227164b139f9SDavid Daney 
227264b139f9SDavid Daney 	parent_irq = irq_of_parse_and_map(ciu_node, 0);
227364b139f9SDavid Daney 	if (!parent_irq) {
227464b139f9SDavid Daney 		pr_err("ERROR: Couldn't acquire parent_irq for %s\n.",
227564b139f9SDavid Daney 			ciu_node->name);
227664b139f9SDavid Daney 		return -EINVAL;
227764b139f9SDavid Daney 	}
227864b139f9SDavid Daney 
227964b139f9SDavid Daney 	host_data = kzalloc(sizeof(*host_data), GFP_KERNEL);
228064b139f9SDavid Daney 	raw_spin_lock_init(&host_data->lock);
228164b139f9SDavid Daney 
228264b139f9SDavid Daney 	addr = of_get_address(ciu_node, 0, NULL, NULL);
228364b139f9SDavid Daney 	if (!addr) {
228464b139f9SDavid Daney 		pr_err("ERROR: Couldn't acquire reg(0) %s\n.", ciu_node->name);
228564b139f9SDavid Daney 		return -EINVAL;
228664b139f9SDavid Daney 	}
228764b139f9SDavid Daney 	host_data->raw_reg = (u64)phys_to_virt(
228864b139f9SDavid Daney 		of_translate_address(ciu_node, addr));
228964b139f9SDavid Daney 
229064b139f9SDavid Daney 	addr = of_get_address(ciu_node, 1, NULL, NULL);
229164b139f9SDavid Daney 	if (!addr) {
229264b139f9SDavid Daney 		pr_err("ERROR: Couldn't acquire reg(1) %s\n.", ciu_node->name);
229364b139f9SDavid Daney 		return -EINVAL;
229464b139f9SDavid Daney 	}
229564b139f9SDavid Daney 	host_data->en_reg = (u64)phys_to_virt(
229664b139f9SDavid Daney 		of_translate_address(ciu_node, addr));
229764b139f9SDavid Daney 
229864b139f9SDavid Daney 	r = of_property_read_u32(ciu_node, "cavium,max-bits", &val);
229964b139f9SDavid Daney 	if (r) {
230064b139f9SDavid Daney 		pr_err("ERROR: Couldn't read cavium,max-bits from %s\n.",
230164b139f9SDavid Daney 			ciu_node->name);
230264b139f9SDavid Daney 		return r;
230364b139f9SDavid Daney 	}
230464b139f9SDavid Daney 	host_data->max_bits = val;
230564b139f9SDavid Daney 
230664b139f9SDavid Daney 	cib_domain = irq_domain_add_linear(ciu_node, host_data->max_bits,
230764b139f9SDavid Daney 					   &octeon_irq_domain_cib_ops,
230864b139f9SDavid Daney 					   host_data);
230964b139f9SDavid Daney 	if (!cib_domain) {
231064b139f9SDavid Daney 		pr_err("ERROR: Couldn't irq_domain_add_linear()\n.");
231164b139f9SDavid Daney 		return -ENOMEM;
231264b139f9SDavid Daney 	}
231364b139f9SDavid Daney 
231464b139f9SDavid Daney 	cvmx_write_csr(host_data->en_reg, 0); /* disable all IRQs */
231564b139f9SDavid Daney 	cvmx_write_csr(host_data->raw_reg, ~0); /* ack any outstanding */
231664b139f9SDavid Daney 
231764b139f9SDavid Daney 	r = request_irq(parent_irq, octeon_irq_cib_handler,
231864b139f9SDavid Daney 			IRQF_NO_THREAD, "cib", cib_domain);
231964b139f9SDavid Daney 	if (r) {
232064b139f9SDavid Daney 		pr_err("request_irq cib failed %d\n", r);
232164b139f9SDavid Daney 		return r;
232264b139f9SDavid Daney 	}
232364b139f9SDavid Daney 	pr_info("CIB interrupt controller probed: %llx %d\n",
232464b139f9SDavid Daney 		host_data->raw_reg, host_data->max_bits);
232564b139f9SDavid Daney 	return 0;
232664b139f9SDavid Daney }
232764b139f9SDavid Daney 
2328ce210d35SDavid Daney int octeon_irq_ciu3_xlat(struct irq_domain *d,
2329ce210d35SDavid Daney 			 struct device_node *node,
2330ce210d35SDavid Daney 			 const u32 *intspec,
2331ce210d35SDavid Daney 			 unsigned int intsize,
2332ce210d35SDavid Daney 			 unsigned long *out_hwirq,
2333ce210d35SDavid Daney 			 unsigned int *out_type)
2334ce210d35SDavid Daney {
2335ce210d35SDavid Daney 	struct octeon_ciu3_info *ciu3_info = d->host_data;
2336ce210d35SDavid Daney 	unsigned int hwirq, type, intsn_major;
2337ce210d35SDavid Daney 	union cvmx_ciu3_iscx_ctl isc;
2338ce210d35SDavid Daney 
2339ce210d35SDavid Daney 	if (intsize < 2)
2340ce210d35SDavid Daney 		return -EINVAL;
2341ce210d35SDavid Daney 	hwirq = intspec[0];
2342ce210d35SDavid Daney 	type = intspec[1];
2343ce210d35SDavid Daney 
2344ce210d35SDavid Daney 	if (hwirq >= (1 << 20))
2345ce210d35SDavid Daney 		return -EINVAL;
2346ce210d35SDavid Daney 
2347ce210d35SDavid Daney 	intsn_major = hwirq >> 12;
2348ce210d35SDavid Daney 	switch (intsn_major) {
2349ce210d35SDavid Daney 	case 0x04: /* Software handled separately. */
2350ce210d35SDavid Daney 		return -EINVAL;
2351ce210d35SDavid Daney 	default:
2352ce210d35SDavid Daney 		break;
2353ce210d35SDavid Daney 	}
2354ce210d35SDavid Daney 
2355ce210d35SDavid Daney 	isc.u64 =  cvmx_read_csr(ciu3_info->ciu3_addr + CIU3_ISC_CTL(hwirq));
2356ce210d35SDavid Daney 	if (!isc.s.imp)
2357ce210d35SDavid Daney 		return -EINVAL;
2358ce210d35SDavid Daney 
2359ce210d35SDavid Daney 	switch (type) {
2360ce210d35SDavid Daney 	case 4: /* official value for level triggering. */
2361ce210d35SDavid Daney 		*out_type = IRQ_TYPE_LEVEL_HIGH;
2362ce210d35SDavid Daney 		break;
2363ce210d35SDavid Daney 	case 0: /* unofficial value, but we might as well let it work. */
2364ce210d35SDavid Daney 	case 1: /* official value for edge triggering. */
2365ce210d35SDavid Daney 		*out_type = IRQ_TYPE_EDGE_RISING;
2366ce210d35SDavid Daney 		break;
2367ce210d35SDavid Daney 	default: /* Nothing else is acceptable. */
2368ce210d35SDavid Daney 		return -EINVAL;
2369ce210d35SDavid Daney 	}
2370ce210d35SDavid Daney 
2371ce210d35SDavid Daney 	*out_hwirq = hwirq;
2372ce210d35SDavid Daney 
2373ce210d35SDavid Daney 	return 0;
2374ce210d35SDavid Daney }
2375ce210d35SDavid Daney 
2376ce210d35SDavid Daney void octeon_irq_ciu3_enable(struct irq_data *data)
2377ce210d35SDavid Daney {
2378ce210d35SDavid Daney 	int cpu;
2379ce210d35SDavid Daney 	union cvmx_ciu3_iscx_ctl isc_ctl;
2380ce210d35SDavid Daney 	union cvmx_ciu3_iscx_w1c isc_w1c;
2381ce210d35SDavid Daney 	u64 isc_ctl_addr;
2382ce210d35SDavid Daney 
2383ce210d35SDavid Daney 	struct octeon_ciu_chip_data *cd;
2384ce210d35SDavid Daney 
2385ce210d35SDavid Daney 	cpu = next_cpu_for_irq(data);
2386ce210d35SDavid Daney 
2387ce210d35SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
2388ce210d35SDavid Daney 
2389ce210d35SDavid Daney 	isc_w1c.u64 = 0;
2390ce210d35SDavid Daney 	isc_w1c.s.en = 1;
2391ce210d35SDavid Daney 	cvmx_write_csr(cd->ciu3_addr + CIU3_ISC_W1C(cd->intsn), isc_w1c.u64);
2392ce210d35SDavid Daney 
2393ce210d35SDavid Daney 	isc_ctl_addr = cd->ciu3_addr + CIU3_ISC_CTL(cd->intsn);
2394ce210d35SDavid Daney 	isc_ctl.u64 = 0;
2395ce210d35SDavid Daney 	isc_ctl.s.en = 1;
2396ce210d35SDavid Daney 	isc_ctl.s.idt = per_cpu(octeon_irq_ciu3_idt_ip2, cpu);
2397ce210d35SDavid Daney 	cvmx_write_csr(isc_ctl_addr, isc_ctl.u64);
2398ce210d35SDavid Daney 	cvmx_read_csr(isc_ctl_addr);
2399ce210d35SDavid Daney }
2400ce210d35SDavid Daney 
2401ce210d35SDavid Daney void octeon_irq_ciu3_disable(struct irq_data *data)
2402ce210d35SDavid Daney {
2403ce210d35SDavid Daney 	u64 isc_ctl_addr;
2404ce210d35SDavid Daney 	union cvmx_ciu3_iscx_w1c isc_w1c;
2405ce210d35SDavid Daney 
2406ce210d35SDavid Daney 	struct octeon_ciu_chip_data *cd;
2407ce210d35SDavid Daney 
2408ce210d35SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
2409ce210d35SDavid Daney 
2410ce210d35SDavid Daney 	isc_w1c.u64 = 0;
2411ce210d35SDavid Daney 	isc_w1c.s.en = 1;
2412ce210d35SDavid Daney 
2413ce210d35SDavid Daney 	isc_ctl_addr = cd->ciu3_addr + CIU3_ISC_CTL(cd->intsn);
2414ce210d35SDavid Daney 	cvmx_write_csr(cd->ciu3_addr + CIU3_ISC_W1C(cd->intsn), isc_w1c.u64);
2415ce210d35SDavid Daney 	cvmx_write_csr(isc_ctl_addr, 0);
2416ce210d35SDavid Daney 	cvmx_read_csr(isc_ctl_addr);
2417ce210d35SDavid Daney }
2418ce210d35SDavid Daney 
2419ce210d35SDavid Daney void octeon_irq_ciu3_ack(struct irq_data *data)
2420ce210d35SDavid Daney {
2421ce210d35SDavid Daney 	u64 isc_w1c_addr;
2422ce210d35SDavid Daney 	union cvmx_ciu3_iscx_w1c isc_w1c;
2423ce210d35SDavid Daney 	struct octeon_ciu_chip_data *cd;
2424ce210d35SDavid Daney 	u32 trigger_type = irqd_get_trigger_type(data);
2425ce210d35SDavid Daney 
2426ce210d35SDavid Daney 	/*
2427ce210d35SDavid Daney 	 * We use a single irq_chip, so we have to do nothing to ack a
2428ce210d35SDavid Daney 	 * level interrupt.
2429ce210d35SDavid Daney 	 */
2430ce210d35SDavid Daney 	if (!(trigger_type & IRQ_TYPE_EDGE_BOTH))
2431ce210d35SDavid Daney 		return;
2432ce210d35SDavid Daney 
2433ce210d35SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
2434ce210d35SDavid Daney 
2435ce210d35SDavid Daney 	isc_w1c.u64 = 0;
2436ce210d35SDavid Daney 	isc_w1c.s.raw = 1;
2437ce210d35SDavid Daney 
2438ce210d35SDavid Daney 	isc_w1c_addr = cd->ciu3_addr + CIU3_ISC_W1C(cd->intsn);
2439ce210d35SDavid Daney 	cvmx_write_csr(isc_w1c_addr, isc_w1c.u64);
2440ce210d35SDavid Daney 	cvmx_read_csr(isc_w1c_addr);
2441ce210d35SDavid Daney }
2442ce210d35SDavid Daney 
2443ce210d35SDavid Daney void octeon_irq_ciu3_mask(struct irq_data *data)
2444ce210d35SDavid Daney {
2445ce210d35SDavid Daney 	union cvmx_ciu3_iscx_w1c isc_w1c;
2446ce210d35SDavid Daney 	u64 isc_w1c_addr;
2447ce210d35SDavid Daney 	struct octeon_ciu_chip_data *cd;
2448ce210d35SDavid Daney 
2449ce210d35SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
2450ce210d35SDavid Daney 
2451ce210d35SDavid Daney 	isc_w1c.u64 = 0;
2452ce210d35SDavid Daney 	isc_w1c.s.en = 1;
2453ce210d35SDavid Daney 
2454ce210d35SDavid Daney 	isc_w1c_addr = cd->ciu3_addr + CIU3_ISC_W1C(cd->intsn);
2455ce210d35SDavid Daney 	cvmx_write_csr(isc_w1c_addr, isc_w1c.u64);
2456ce210d35SDavid Daney 	cvmx_read_csr(isc_w1c_addr);
2457ce210d35SDavid Daney }
2458ce210d35SDavid Daney 
2459ce210d35SDavid Daney void octeon_irq_ciu3_mask_ack(struct irq_data *data)
2460ce210d35SDavid Daney {
2461ce210d35SDavid Daney 	union cvmx_ciu3_iscx_w1c isc_w1c;
2462ce210d35SDavid Daney 	u64 isc_w1c_addr;
2463ce210d35SDavid Daney 	struct octeon_ciu_chip_data *cd;
2464ce210d35SDavid Daney 	u32 trigger_type = irqd_get_trigger_type(data);
2465ce210d35SDavid Daney 
2466ce210d35SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
2467ce210d35SDavid Daney 
2468ce210d35SDavid Daney 	isc_w1c.u64 = 0;
2469ce210d35SDavid Daney 	isc_w1c.s.en = 1;
2470ce210d35SDavid Daney 
2471ce210d35SDavid Daney 	/*
2472ce210d35SDavid Daney 	 * We use a single irq_chip, so only ack an edge (!level)
2473ce210d35SDavid Daney 	 * interrupt.
2474ce210d35SDavid Daney 	 */
2475ce210d35SDavid Daney 	if (trigger_type & IRQ_TYPE_EDGE_BOTH)
2476ce210d35SDavid Daney 		isc_w1c.s.raw = 1;
2477ce210d35SDavid Daney 
2478ce210d35SDavid Daney 	isc_w1c_addr = cd->ciu3_addr + CIU3_ISC_W1C(cd->intsn);
2479ce210d35SDavid Daney 	cvmx_write_csr(isc_w1c_addr, isc_w1c.u64);
2480ce210d35SDavid Daney 	cvmx_read_csr(isc_w1c_addr);
2481ce210d35SDavid Daney }
2482ce210d35SDavid Daney 
2483ce210d35SDavid Daney #ifdef CONFIG_SMP
2484ce210d35SDavid Daney int octeon_irq_ciu3_set_affinity(struct irq_data *data,
2485ce210d35SDavid Daney 				 const struct cpumask *dest, bool force)
2486ce210d35SDavid Daney {
2487ce210d35SDavid Daney 	union cvmx_ciu3_iscx_ctl isc_ctl;
2488ce210d35SDavid Daney 	union cvmx_ciu3_iscx_w1c isc_w1c;
2489ce210d35SDavid Daney 	u64 isc_ctl_addr;
2490ce210d35SDavid Daney 	int cpu;
2491ce210d35SDavid Daney 	bool enable_one = !irqd_irq_disabled(data) && !irqd_irq_masked(data);
2492ce210d35SDavid Daney 	struct octeon_ciu_chip_data *cd = irq_data_get_irq_chip_data(data);
2493ce210d35SDavid Daney 
2494ce210d35SDavid Daney 	if (!cpumask_subset(dest, cpumask_of_node(cd->ciu_node)))
2495ce210d35SDavid Daney 		return -EINVAL;
2496ce210d35SDavid Daney 
2497ce210d35SDavid Daney 	if (!enable_one)
2498ce210d35SDavid Daney 		return IRQ_SET_MASK_OK;
2499ce210d35SDavid Daney 
2500ce210d35SDavid Daney 	cd = irq_data_get_irq_chip_data(data);
2501ce210d35SDavid Daney 	cpu = cpumask_first(dest);
2502ce210d35SDavid Daney 	if (cpu >= nr_cpu_ids)
2503ce210d35SDavid Daney 		cpu = smp_processor_id();
2504ce210d35SDavid Daney 	cd->current_cpu = cpu;
2505ce210d35SDavid Daney 
2506ce210d35SDavid Daney 	isc_w1c.u64 = 0;
2507ce210d35SDavid Daney 	isc_w1c.s.en = 1;
2508ce210d35SDavid Daney 	cvmx_write_csr(cd->ciu3_addr + CIU3_ISC_W1C(cd->intsn), isc_w1c.u64);
2509ce210d35SDavid Daney 
2510ce210d35SDavid Daney 	isc_ctl_addr = cd->ciu3_addr + CIU3_ISC_CTL(cd->intsn);
2511ce210d35SDavid Daney 	isc_ctl.u64 = 0;
2512ce210d35SDavid Daney 	isc_ctl.s.en = 1;
2513ce210d35SDavid Daney 	isc_ctl.s.idt = per_cpu(octeon_irq_ciu3_idt_ip2, cpu);
2514ce210d35SDavid Daney 	cvmx_write_csr(isc_ctl_addr, isc_ctl.u64);
2515ce210d35SDavid Daney 	cvmx_read_csr(isc_ctl_addr);
2516ce210d35SDavid Daney 
2517ce210d35SDavid Daney 	return IRQ_SET_MASK_OK;
2518ce210d35SDavid Daney }
2519ce210d35SDavid Daney #endif
2520ce210d35SDavid Daney 
2521ce210d35SDavid Daney static struct irq_chip octeon_irq_chip_ciu3 = {
2522ce210d35SDavid Daney 	.name = "CIU3",
2523ce210d35SDavid Daney 	.irq_startup = edge_startup,
2524ce210d35SDavid Daney 	.irq_enable = octeon_irq_ciu3_enable,
2525ce210d35SDavid Daney 	.irq_disable = octeon_irq_ciu3_disable,
2526ce210d35SDavid Daney 	.irq_ack = octeon_irq_ciu3_ack,
2527ce210d35SDavid Daney 	.irq_mask = octeon_irq_ciu3_mask,
2528ce210d35SDavid Daney 	.irq_mask_ack = octeon_irq_ciu3_mask_ack,
2529ce210d35SDavid Daney 	.irq_unmask = octeon_irq_ciu3_enable,
2530ce210d35SDavid Daney 	.irq_set_type = octeon_irq_ciu_set_type,
2531ce210d35SDavid Daney #ifdef CONFIG_SMP
2532ce210d35SDavid Daney 	.irq_set_affinity = octeon_irq_ciu3_set_affinity,
2533ce210d35SDavid Daney 	.irq_cpu_offline = octeon_irq_cpu_offline_ciu,
2534ce210d35SDavid Daney #endif
2535ce210d35SDavid Daney };
2536ce210d35SDavid Daney 
2537ce210d35SDavid Daney int octeon_irq_ciu3_mapx(struct irq_domain *d, unsigned int virq,
2538ce210d35SDavid Daney 			 irq_hw_number_t hw, struct irq_chip *chip)
2539ce210d35SDavid Daney {
2540ce210d35SDavid Daney 	struct octeon_ciu3_info *ciu3_info = d->host_data;
2541ce210d35SDavid Daney 	struct octeon_ciu_chip_data *cd = kzalloc_node(sizeof(*cd), GFP_KERNEL,
2542ce210d35SDavid Daney 						       ciu3_info->node);
2543ce210d35SDavid Daney 	if (!cd)
2544ce210d35SDavid Daney 		return -ENOMEM;
2545ce210d35SDavid Daney 	cd->intsn = hw;
2546ce210d35SDavid Daney 	cd->current_cpu = -1;
2547ce210d35SDavid Daney 	cd->ciu3_addr = ciu3_info->ciu3_addr;
2548ce210d35SDavid Daney 	cd->ciu_node = ciu3_info->node;
2549ce210d35SDavid Daney 	irq_set_chip_and_handler(virq, chip, handle_edge_irq);
2550ce210d35SDavid Daney 	irq_set_chip_data(virq, cd);
2551ce210d35SDavid Daney 
2552ce210d35SDavid Daney 	return 0;
2553ce210d35SDavid Daney }
2554ce210d35SDavid Daney 
2555ce210d35SDavid Daney static int octeon_irq_ciu3_map(struct irq_domain *d,
2556ce210d35SDavid Daney 			       unsigned int virq, irq_hw_number_t hw)
2557ce210d35SDavid Daney {
2558ce210d35SDavid Daney 	return octeon_irq_ciu3_mapx(d, virq, hw, &octeon_irq_chip_ciu3);
2559ce210d35SDavid Daney }
2560ce210d35SDavid Daney 
2561ce210d35SDavid Daney static struct irq_domain_ops octeon_dflt_domain_ciu3_ops = {
2562ce210d35SDavid Daney 	.map = octeon_irq_ciu3_map,
2563ce210d35SDavid Daney 	.unmap = octeon_irq_free_cd,
2564ce210d35SDavid Daney 	.xlate = octeon_irq_ciu3_xlat,
2565ce210d35SDavid Daney };
2566ce210d35SDavid Daney 
2567ce210d35SDavid Daney static void octeon_irq_ciu3_ip2(void)
2568ce210d35SDavid Daney {
2569ce210d35SDavid Daney 	union cvmx_ciu3_destx_pp_int dest_pp_int;
2570ce210d35SDavid Daney 	struct octeon_ciu3_info *ciu3_info;
2571ce210d35SDavid Daney 	u64 ciu3_addr;
2572ce210d35SDavid Daney 
2573ce210d35SDavid Daney 	ciu3_info = __this_cpu_read(octeon_ciu3_info);
2574ce210d35SDavid Daney 	ciu3_addr = ciu3_info->ciu3_addr;
2575ce210d35SDavid Daney 
2576ce210d35SDavid Daney 	dest_pp_int.u64 = cvmx_read_csr(ciu3_addr + CIU3_DEST_PP_INT(3 * cvmx_get_local_core_num()));
2577ce210d35SDavid Daney 
2578ce210d35SDavid Daney 	if (likely(dest_pp_int.s.intr)) {
2579ce210d35SDavid Daney 		irq_hw_number_t intsn = dest_pp_int.s.intsn;
2580ce210d35SDavid Daney 		irq_hw_number_t hw;
2581ce210d35SDavid Daney 		struct irq_domain *domain;
2582ce210d35SDavid Daney 		/* Get the domain to use from the major block */
2583ce210d35SDavid Daney 		int block = intsn >> 12;
2584ce210d35SDavid Daney 		int ret;
2585ce210d35SDavid Daney 
2586ce210d35SDavid Daney 		domain = ciu3_info->domain[block];
2587ce210d35SDavid Daney 		if (ciu3_info->intsn2hw[block])
2588ce210d35SDavid Daney 			hw = ciu3_info->intsn2hw[block](domain, intsn);
2589ce210d35SDavid Daney 		else
2590ce210d35SDavid Daney 			hw = intsn;
2591ce210d35SDavid Daney 
2592ce210d35SDavid Daney 		ret = handle_domain_irq(domain, hw, NULL);
2593ce210d35SDavid Daney 		if (ret < 0) {
2594ce210d35SDavid Daney 			union cvmx_ciu3_iscx_w1c isc_w1c;
2595ce210d35SDavid Daney 			u64 isc_w1c_addr = ciu3_addr + CIU3_ISC_W1C(intsn);
2596ce210d35SDavid Daney 
2597ce210d35SDavid Daney 			isc_w1c.u64 = 0;
2598ce210d35SDavid Daney 			isc_w1c.s.en = 1;
2599ce210d35SDavid Daney 			cvmx_write_csr(isc_w1c_addr, isc_w1c.u64);
2600ce210d35SDavid Daney 			cvmx_read_csr(isc_w1c_addr);
2601ce210d35SDavid Daney 			spurious_interrupt();
2602ce210d35SDavid Daney 		}
2603ce210d35SDavid Daney 	} else {
2604ce210d35SDavid Daney 		spurious_interrupt();
2605ce210d35SDavid Daney 	}
2606ce210d35SDavid Daney }
2607ce210d35SDavid Daney 
2608ce210d35SDavid Daney /*
2609ce210d35SDavid Daney  * 10 mbox per core starting from zero.
2610ce210d35SDavid Daney  * Base mbox is core * 10
2611ce210d35SDavid Daney  */
2612ce210d35SDavid Daney static unsigned int octeon_irq_ciu3_base_mbox_intsn(int core)
2613ce210d35SDavid Daney {
2614ce210d35SDavid Daney 	/* SW (mbox) are 0x04 in bits 12..19 */
2615ce210d35SDavid Daney 	return 0x04000 + CIU3_MBOX_PER_CORE * core;
2616ce210d35SDavid Daney }
2617ce210d35SDavid Daney 
2618ce210d35SDavid Daney static unsigned int octeon_irq_ciu3_mbox_intsn_for_core(int core, unsigned int mbox)
2619ce210d35SDavid Daney {
2620ce210d35SDavid Daney 	return octeon_irq_ciu3_base_mbox_intsn(core) + mbox;
2621ce210d35SDavid Daney }
2622ce210d35SDavid Daney 
2623ce210d35SDavid Daney static unsigned int octeon_irq_ciu3_mbox_intsn_for_cpu(int cpu, unsigned int mbox)
2624ce210d35SDavid Daney {
2625ce210d35SDavid Daney 	int local_core = octeon_coreid_for_cpu(cpu) & 0x3f;
2626ce210d35SDavid Daney 
2627ce210d35SDavid Daney 	return octeon_irq_ciu3_mbox_intsn_for_core(local_core, mbox);
2628ce210d35SDavid Daney }
2629ce210d35SDavid Daney 
2630ce210d35SDavid Daney static void octeon_irq_ciu3_mbox(void)
2631ce210d35SDavid Daney {
2632ce210d35SDavid Daney 	union cvmx_ciu3_destx_pp_int dest_pp_int;
2633ce210d35SDavid Daney 	struct octeon_ciu3_info *ciu3_info;
2634ce210d35SDavid Daney 	u64 ciu3_addr;
2635ce210d35SDavid Daney 	int core = cvmx_get_local_core_num();
2636ce210d35SDavid Daney 
2637ce210d35SDavid Daney 	ciu3_info = __this_cpu_read(octeon_ciu3_info);
2638ce210d35SDavid Daney 	ciu3_addr = ciu3_info->ciu3_addr;
2639ce210d35SDavid Daney 
2640ce210d35SDavid Daney 	dest_pp_int.u64 = cvmx_read_csr(ciu3_addr + CIU3_DEST_PP_INT(1 + 3 * core));
2641ce210d35SDavid Daney 
2642ce210d35SDavid Daney 	if (likely(dest_pp_int.s.intr)) {
2643ce210d35SDavid Daney 		irq_hw_number_t intsn = dest_pp_int.s.intsn;
2644ce210d35SDavid Daney 		int mbox = intsn - octeon_irq_ciu3_base_mbox_intsn(core);
2645ce210d35SDavid Daney 
2646ce210d35SDavid Daney 		if (likely(mbox >= 0 && mbox < CIU3_MBOX_PER_CORE)) {
2647ce210d35SDavid Daney 			do_IRQ(mbox + OCTEON_IRQ_MBOX0);
2648ce210d35SDavid Daney 		} else {
2649ce210d35SDavid Daney 			union cvmx_ciu3_iscx_w1c isc_w1c;
2650ce210d35SDavid Daney 			u64 isc_w1c_addr = ciu3_addr + CIU3_ISC_W1C(intsn);
2651ce210d35SDavid Daney 
2652ce210d35SDavid Daney 			isc_w1c.u64 = 0;
2653ce210d35SDavid Daney 			isc_w1c.s.en = 1;
2654ce210d35SDavid Daney 			cvmx_write_csr(isc_w1c_addr, isc_w1c.u64);
2655ce210d35SDavid Daney 			cvmx_read_csr(isc_w1c_addr);
2656ce210d35SDavid Daney 			spurious_interrupt();
2657ce210d35SDavid Daney 		}
2658ce210d35SDavid Daney 	} else {
2659ce210d35SDavid Daney 		spurious_interrupt();
2660ce210d35SDavid Daney 	}
2661ce210d35SDavid Daney }
2662ce210d35SDavid Daney 
2663ce210d35SDavid Daney void octeon_ciu3_mbox_send(int cpu, unsigned int mbox)
2664ce210d35SDavid Daney {
2665ce210d35SDavid Daney 	struct octeon_ciu3_info *ciu3_info;
2666ce210d35SDavid Daney 	unsigned int intsn;
2667ce210d35SDavid Daney 	union cvmx_ciu3_iscx_w1s isc_w1s;
2668ce210d35SDavid Daney 	u64 isc_w1s_addr;
2669ce210d35SDavid Daney 
2670ce210d35SDavid Daney 	if (WARN_ON_ONCE(mbox >= CIU3_MBOX_PER_CORE))
2671ce210d35SDavid Daney 		return;
2672ce210d35SDavid Daney 
2673ce210d35SDavid Daney 	intsn = octeon_irq_ciu3_mbox_intsn_for_cpu(cpu, mbox);
2674ce210d35SDavid Daney 	ciu3_info = per_cpu(octeon_ciu3_info, cpu);
2675ce210d35SDavid Daney 	isc_w1s_addr = ciu3_info->ciu3_addr + CIU3_ISC_W1S(intsn);
2676ce210d35SDavid Daney 
2677ce210d35SDavid Daney 	isc_w1s.u64 = 0;
2678ce210d35SDavid Daney 	isc_w1s.s.raw = 1;
2679ce210d35SDavid Daney 
2680ce210d35SDavid Daney 	cvmx_write_csr(isc_w1s_addr, isc_w1s.u64);
2681ce210d35SDavid Daney 	cvmx_read_csr(isc_w1s_addr);
2682ce210d35SDavid Daney }
2683ce210d35SDavid Daney 
2684ce210d35SDavid Daney static void octeon_irq_ciu3_mbox_set_enable(struct irq_data *data, int cpu, bool en)
2685ce210d35SDavid Daney {
2686ce210d35SDavid Daney 	struct octeon_ciu3_info *ciu3_info;
2687ce210d35SDavid Daney 	unsigned int intsn;
2688ce210d35SDavid Daney 	u64 isc_ctl_addr, isc_w1c_addr;
2689ce210d35SDavid Daney 	union cvmx_ciu3_iscx_ctl isc_ctl;
2690ce210d35SDavid Daney 	unsigned int mbox = data->irq - OCTEON_IRQ_MBOX0;
2691ce210d35SDavid Daney 
2692ce210d35SDavid Daney 	intsn = octeon_irq_ciu3_mbox_intsn_for_cpu(cpu, mbox);
2693ce210d35SDavid Daney 	ciu3_info = per_cpu(octeon_ciu3_info, cpu);
2694ce210d35SDavid Daney 	isc_w1c_addr = ciu3_info->ciu3_addr + CIU3_ISC_W1C(intsn);
2695ce210d35SDavid Daney 	isc_ctl_addr = ciu3_info->ciu3_addr + CIU3_ISC_CTL(intsn);
2696ce210d35SDavid Daney 
2697ce210d35SDavid Daney 	isc_ctl.u64 = 0;
2698ce210d35SDavid Daney 	isc_ctl.s.en = 1;
2699ce210d35SDavid Daney 
2700ce210d35SDavid Daney 	cvmx_write_csr(isc_w1c_addr, isc_ctl.u64);
2701ce210d35SDavid Daney 	cvmx_write_csr(isc_ctl_addr, 0);
2702ce210d35SDavid Daney 	if (en) {
2703ce210d35SDavid Daney 		unsigned int idt = per_cpu(octeon_irq_ciu3_idt_ip3, cpu);
2704ce210d35SDavid Daney 
2705ce210d35SDavid Daney 		isc_ctl.u64 = 0;
2706ce210d35SDavid Daney 		isc_ctl.s.en = 1;
2707ce210d35SDavid Daney 		isc_ctl.s.idt = idt;
2708ce210d35SDavid Daney 		cvmx_write_csr(isc_ctl_addr, isc_ctl.u64);
2709ce210d35SDavid Daney 	}
2710ce210d35SDavid Daney 	cvmx_read_csr(isc_ctl_addr);
2711ce210d35SDavid Daney }
2712ce210d35SDavid Daney 
2713ce210d35SDavid Daney static void octeon_irq_ciu3_mbox_enable(struct irq_data *data)
2714ce210d35SDavid Daney {
2715ce210d35SDavid Daney 	int cpu;
2716ce210d35SDavid Daney 	unsigned int mbox = data->irq - OCTEON_IRQ_MBOX0;
2717ce210d35SDavid Daney 
2718ce210d35SDavid Daney 	WARN_ON(mbox >= CIU3_MBOX_PER_CORE);
2719ce210d35SDavid Daney 
2720ce210d35SDavid Daney 	for_each_online_cpu(cpu)
2721ce210d35SDavid Daney 		octeon_irq_ciu3_mbox_set_enable(data, cpu, true);
2722ce210d35SDavid Daney }
2723ce210d35SDavid Daney 
2724ce210d35SDavid Daney static void octeon_irq_ciu3_mbox_disable(struct irq_data *data)
2725ce210d35SDavid Daney {
2726ce210d35SDavid Daney 	int cpu;
2727ce210d35SDavid Daney 	unsigned int mbox = data->irq - OCTEON_IRQ_MBOX0;
2728ce210d35SDavid Daney 
2729ce210d35SDavid Daney 	WARN_ON(mbox >= CIU3_MBOX_PER_CORE);
2730ce210d35SDavid Daney 
2731ce210d35SDavid Daney 	for_each_online_cpu(cpu)
2732ce210d35SDavid Daney 		octeon_irq_ciu3_mbox_set_enable(data, cpu, false);
2733ce210d35SDavid Daney }
2734ce210d35SDavid Daney 
2735ce210d35SDavid Daney static void octeon_irq_ciu3_mbox_ack(struct irq_data *data)
2736ce210d35SDavid Daney {
2737ce210d35SDavid Daney 	struct octeon_ciu3_info *ciu3_info;
2738ce210d35SDavid Daney 	unsigned int intsn;
2739ce210d35SDavid Daney 	u64 isc_w1c_addr;
2740ce210d35SDavid Daney 	union cvmx_ciu3_iscx_w1c isc_w1c;
2741ce210d35SDavid Daney 	unsigned int mbox = data->irq - OCTEON_IRQ_MBOX0;
2742ce210d35SDavid Daney 
2743ce210d35SDavid Daney 	intsn = octeon_irq_ciu3_mbox_intsn_for_core(cvmx_get_local_core_num(), mbox);
2744ce210d35SDavid Daney 
2745ce210d35SDavid Daney 	isc_w1c.u64 = 0;
2746ce210d35SDavid Daney 	isc_w1c.s.raw = 1;
2747ce210d35SDavid Daney 
2748ce210d35SDavid Daney 	ciu3_info = __this_cpu_read(octeon_ciu3_info);
2749ce210d35SDavid Daney 	isc_w1c_addr = ciu3_info->ciu3_addr + CIU3_ISC_W1C(intsn);
2750ce210d35SDavid Daney 	cvmx_write_csr(isc_w1c_addr, isc_w1c.u64);
2751ce210d35SDavid Daney 	cvmx_read_csr(isc_w1c_addr);
2752ce210d35SDavid Daney }
2753ce210d35SDavid Daney 
2754ce210d35SDavid Daney static void octeon_irq_ciu3_mbox_cpu_online(struct irq_data *data)
2755ce210d35SDavid Daney {
2756ce210d35SDavid Daney 	octeon_irq_ciu3_mbox_set_enable(data, smp_processor_id(), true);
2757ce210d35SDavid Daney }
2758ce210d35SDavid Daney 
2759ce210d35SDavid Daney static void octeon_irq_ciu3_mbox_cpu_offline(struct irq_data *data)
2760ce210d35SDavid Daney {
2761ce210d35SDavid Daney 	octeon_irq_ciu3_mbox_set_enable(data, smp_processor_id(), false);
2762ce210d35SDavid Daney }
2763ce210d35SDavid Daney 
2764ce210d35SDavid Daney static int octeon_irq_ciu3_alloc_resources(struct octeon_ciu3_info *ciu3_info)
2765ce210d35SDavid Daney {
2766ce210d35SDavid Daney 	u64 b = ciu3_info->ciu3_addr;
2767ce210d35SDavid Daney 	int idt_ip2, idt_ip3, idt_ip4;
2768ce210d35SDavid Daney 	int unused_idt2;
2769ce210d35SDavid Daney 	int core = cvmx_get_local_core_num();
2770ce210d35SDavid Daney 	int i;
2771ce210d35SDavid Daney 
2772ce210d35SDavid Daney 	__this_cpu_write(octeon_ciu3_info, ciu3_info);
2773ce210d35SDavid Daney 
2774ce210d35SDavid Daney 	/*
2775ce210d35SDavid Daney 	 * 4 idt per core starting from 1 because zero is reserved.
2776ce210d35SDavid Daney 	 * Base idt per core is 4 * core + 1
2777ce210d35SDavid Daney 	 */
2778ce210d35SDavid Daney 	idt_ip2 = core * 4 + 1;
2779ce210d35SDavid Daney 	idt_ip3 = core * 4 + 2;
2780ce210d35SDavid Daney 	idt_ip4 = core * 4 + 3;
2781ce210d35SDavid Daney 	unused_idt2 = core * 4 + 4;
2782ce210d35SDavid Daney 	__this_cpu_write(octeon_irq_ciu3_idt_ip2, idt_ip2);
2783ce210d35SDavid Daney 	__this_cpu_write(octeon_irq_ciu3_idt_ip3, idt_ip3);
2784ce210d35SDavid Daney 
2785ce210d35SDavid Daney 	/* ip2 interrupts for this CPU */
2786ce210d35SDavid Daney 	cvmx_write_csr(b + CIU3_IDT_CTL(idt_ip2), 0);
2787ce210d35SDavid Daney 	cvmx_write_csr(b + CIU3_IDT_PP(idt_ip2, 0), 1ull << core);
2788ce210d35SDavid Daney 	cvmx_write_csr(b + CIU3_IDT_IO(idt_ip2), 0);
2789ce210d35SDavid Daney 
2790ce210d35SDavid Daney 	/* ip3 interrupts for this CPU */
2791ce210d35SDavid Daney 	cvmx_write_csr(b + CIU3_IDT_CTL(idt_ip3), 1);
2792ce210d35SDavid Daney 	cvmx_write_csr(b + CIU3_IDT_PP(idt_ip3, 0), 1ull << core);
2793ce210d35SDavid Daney 	cvmx_write_csr(b + CIU3_IDT_IO(idt_ip3), 0);
2794ce210d35SDavid Daney 
2795ce210d35SDavid Daney 	/* ip4 interrupts for this CPU */
2796ce210d35SDavid Daney 	cvmx_write_csr(b + CIU3_IDT_CTL(idt_ip4), 2);
2797ce210d35SDavid Daney 	cvmx_write_csr(b + CIU3_IDT_PP(idt_ip4, 0), 0);
2798ce210d35SDavid Daney 	cvmx_write_csr(b + CIU3_IDT_IO(idt_ip4), 0);
2799ce210d35SDavid Daney 
2800ce210d35SDavid Daney 	cvmx_write_csr(b + CIU3_IDT_CTL(unused_idt2), 0);
2801ce210d35SDavid Daney 	cvmx_write_csr(b + CIU3_IDT_PP(unused_idt2, 0), 0);
2802ce210d35SDavid Daney 	cvmx_write_csr(b + CIU3_IDT_IO(unused_idt2), 0);
2803ce210d35SDavid Daney 
2804ce210d35SDavid Daney 	for (i = 0; i < CIU3_MBOX_PER_CORE; i++) {
2805ce210d35SDavid Daney 		unsigned int intsn = octeon_irq_ciu3_mbox_intsn_for_core(core, i);
2806ce210d35SDavid Daney 
2807ce210d35SDavid Daney 		cvmx_write_csr(b + CIU3_ISC_W1C(intsn), 2);
2808ce210d35SDavid Daney 		cvmx_write_csr(b + CIU3_ISC_CTL(intsn), 0);
2809ce210d35SDavid Daney 	}
2810ce210d35SDavid Daney 
2811ce210d35SDavid Daney 	return 0;
2812ce210d35SDavid Daney }
2813ce210d35SDavid Daney 
2814ce210d35SDavid Daney static void octeon_irq_setup_secondary_ciu3(void)
2815ce210d35SDavid Daney {
2816ce210d35SDavid Daney 	struct octeon_ciu3_info *ciu3_info;
2817ce210d35SDavid Daney 
2818ce210d35SDavid Daney 	ciu3_info = octeon_ciu3_info_per_node[cvmx_get_node_num()];
2819ce210d35SDavid Daney 	octeon_irq_ciu3_alloc_resources(ciu3_info);
2820ce210d35SDavid Daney 	irq_cpu_online();
2821ce210d35SDavid Daney 
2822ce210d35SDavid Daney 	/* Enable the CIU lines */
2823ce210d35SDavid Daney 	set_c0_status(STATUSF_IP3 | STATUSF_IP2);
2824ce210d35SDavid Daney 	if (octeon_irq_use_ip4)
2825ce210d35SDavid Daney 		set_c0_status(STATUSF_IP4);
2826ce210d35SDavid Daney 	else
2827ce210d35SDavid Daney 		clear_c0_status(STATUSF_IP4);
2828ce210d35SDavid Daney }
2829ce210d35SDavid Daney 
2830ce210d35SDavid Daney static struct irq_chip octeon_irq_chip_ciu3_mbox = {
2831ce210d35SDavid Daney 	.name = "CIU3-M",
2832ce210d35SDavid Daney 	.irq_enable = octeon_irq_ciu3_mbox_enable,
2833ce210d35SDavid Daney 	.irq_disable = octeon_irq_ciu3_mbox_disable,
2834ce210d35SDavid Daney 	.irq_ack = octeon_irq_ciu3_mbox_ack,
2835ce210d35SDavid Daney 
2836ce210d35SDavid Daney 	.irq_cpu_online = octeon_irq_ciu3_mbox_cpu_online,
2837ce210d35SDavid Daney 	.irq_cpu_offline = octeon_irq_ciu3_mbox_cpu_offline,
2838ce210d35SDavid Daney 	.flags = IRQCHIP_ONOFFLINE_ENABLED,
2839ce210d35SDavid Daney };
2840ce210d35SDavid Daney 
2841ce210d35SDavid Daney static int __init octeon_irq_init_ciu3(struct device_node *ciu_node,
2842ce210d35SDavid Daney 				       struct device_node *parent)
2843ce210d35SDavid Daney {
2844ce210d35SDavid Daney 	int i;
2845ce210d35SDavid Daney 	int node;
2846ce210d35SDavid Daney 	struct irq_domain *domain;
2847ce210d35SDavid Daney 	struct octeon_ciu3_info *ciu3_info;
2848ce210d35SDavid Daney 	const __be32 *zero_addr;
2849ce210d35SDavid Daney 	u64 base_addr;
2850ce210d35SDavid Daney 	union cvmx_ciu3_const consts;
2851ce210d35SDavid Daney 
2852ce210d35SDavid Daney 	node = 0; /* of_node_to_nid(ciu_node); */
2853ce210d35SDavid Daney 	ciu3_info = kzalloc_node(sizeof(*ciu3_info), GFP_KERNEL, node);
2854ce210d35SDavid Daney 
2855ce210d35SDavid Daney 	if (!ciu3_info)
2856ce210d35SDavid Daney 		return -ENOMEM;
2857ce210d35SDavid Daney 
2858ce210d35SDavid Daney 	zero_addr = of_get_address(ciu_node, 0, NULL, NULL);
2859ce210d35SDavid Daney 	if (WARN_ON(!zero_addr))
2860ce210d35SDavid Daney 		return -EINVAL;
2861ce210d35SDavid Daney 
2862ce210d35SDavid Daney 	base_addr = of_translate_address(ciu_node, zero_addr);
2863ce210d35SDavid Daney 	base_addr = (u64)phys_to_virt(base_addr);
2864ce210d35SDavid Daney 
2865ce210d35SDavid Daney 	ciu3_info->ciu3_addr = base_addr;
2866ce210d35SDavid Daney 	ciu3_info->node = node;
2867ce210d35SDavid Daney 
2868ce210d35SDavid Daney 	consts.u64 = cvmx_read_csr(base_addr + CIU3_CONST);
2869ce210d35SDavid Daney 
2870ce210d35SDavid Daney 	octeon_irq_setup_secondary = octeon_irq_setup_secondary_ciu3;
2871ce210d35SDavid Daney 
2872ce210d35SDavid Daney 	octeon_irq_ip2 = octeon_irq_ciu3_ip2;
2873ce210d35SDavid Daney 	octeon_irq_ip3 = octeon_irq_ciu3_mbox;
2874ce210d35SDavid Daney 	octeon_irq_ip4 = octeon_irq_ip4_mask;
2875ce210d35SDavid Daney 
2876ce210d35SDavid Daney 	if (node == cvmx_get_node_num()) {
2877ce210d35SDavid Daney 		/* Mips internal */
2878ce210d35SDavid Daney 		octeon_irq_init_core();
2879ce210d35SDavid Daney 
2880ce210d35SDavid Daney 		/* Only do per CPU things if it is the CIU of the boot node. */
2881ce210d35SDavid Daney 		i = irq_alloc_descs_from(OCTEON_IRQ_MBOX0, 8, node);
2882ce210d35SDavid Daney 		WARN_ON(i < 0);
2883ce210d35SDavid Daney 
2884ce210d35SDavid Daney 		for (i = 0; i < 8; i++)
2885ce210d35SDavid Daney 			irq_set_chip_and_handler(i + OCTEON_IRQ_MBOX0,
2886ce210d35SDavid Daney 						 &octeon_irq_chip_ciu3_mbox, handle_percpu_irq);
2887ce210d35SDavid Daney 	}
2888ce210d35SDavid Daney 
2889ce210d35SDavid Daney 	/*
2890ce210d35SDavid Daney 	 * Initialize all domains to use the default domain. Specific major
2891ce210d35SDavid Daney 	 * blocks will overwrite the default domain as needed.
2892ce210d35SDavid Daney 	 */
2893ce210d35SDavid Daney 	domain = irq_domain_add_tree(ciu_node, &octeon_dflt_domain_ciu3_ops,
2894ce210d35SDavid Daney 				     ciu3_info);
2895ce210d35SDavid Daney 	for (i = 0; i < MAX_CIU3_DOMAINS; i++)
2896ce210d35SDavid Daney 		ciu3_info->domain[i] = domain;
2897ce210d35SDavid Daney 
2898ce210d35SDavid Daney 	octeon_ciu3_info_per_node[node] = ciu3_info;
2899ce210d35SDavid Daney 
2900ce210d35SDavid Daney 	if (node == cvmx_get_node_num()) {
2901ce210d35SDavid Daney 		/* Only do per CPU things if it is the CIU of the boot node. */
2902ce210d35SDavid Daney 		octeon_irq_ciu3_alloc_resources(ciu3_info);
2903ce210d35SDavid Daney 		if (node == 0)
2904ce210d35SDavid Daney 			irq_set_default_host(domain);
2905ce210d35SDavid Daney 
2906ce210d35SDavid Daney 		octeon_irq_use_ip4 = false;
2907ce210d35SDavid Daney 		/* Enable the CIU lines */
2908ce210d35SDavid Daney 		set_c0_status(STATUSF_IP2 | STATUSF_IP3);
2909ce210d35SDavid Daney 		clear_c0_status(STATUSF_IP4);
2910ce210d35SDavid Daney 	}
2911ce210d35SDavid Daney 
2912ce210d35SDavid Daney 	return 0;
2913ce210d35SDavid Daney }
2914ce210d35SDavid Daney 
291564b139f9SDavid Daney static struct of_device_id ciu_types[] __initdata = {
291664b139f9SDavid Daney 	{.compatible = "cavium,octeon-3860-ciu", .data = octeon_irq_init_ciu},
291764b139f9SDavid Daney 	{.compatible = "cavium,octeon-3860-gpio", .data = octeon_irq_init_gpio},
291864b139f9SDavid Daney 	{.compatible = "cavium,octeon-6880-ciu2", .data = octeon_irq_init_ciu2},
2919ce210d35SDavid Daney 	{.compatible = "cavium,octeon-7890-ciu3", .data = octeon_irq_init_ciu3},
292064b139f9SDavid Daney 	{.compatible = "cavium,octeon-7130-cib", .data = octeon_irq_init_cib},
292164b139f9SDavid Daney 	{}
292264b139f9SDavid Daney };
292364b139f9SDavid Daney 
29245b3b1688SDavid Daney void __init arch_init_irq(void)
29255b3b1688SDavid Daney {
29265b3b1688SDavid Daney #ifdef CONFIG_SMP
29275b3b1688SDavid Daney 	/* Set the default affinity to the boot cpu. */
29285b3b1688SDavid Daney 	cpumask_clear(irq_default_affinity);
29295b3b1688SDavid Daney 	cpumask_set_cpu(smp_processor_id(), irq_default_affinity);
29305b3b1688SDavid Daney #endif
293164b139f9SDavid Daney 	of_irq_init(ciu_types);
29325b3b1688SDavid Daney }
29335b3b1688SDavid Daney 
29345b3b1688SDavid Daney asmlinkage void plat_irq_dispatch(void)
29355b3b1688SDavid Daney {
29365b3b1688SDavid Daney 	unsigned long cop0_cause;
29375b3b1688SDavid Daney 	unsigned long cop0_status;
29385b3b1688SDavid Daney 
29395b3b1688SDavid Daney 	while (1) {
29405b3b1688SDavid Daney 		cop0_cause = read_c0_cause();
29415b3b1688SDavid Daney 		cop0_status = read_c0_status();
29425b3b1688SDavid Daney 		cop0_cause &= cop0_status;
29435b3b1688SDavid Daney 		cop0_cause &= ST0_IM;
29445b3b1688SDavid Daney 
294564b139f9SDavid Daney 		if (cop0_cause & STATUSF_IP2)
29460c326387SDavid Daney 			octeon_irq_ip2();
294764b139f9SDavid Daney 		else if (cop0_cause & STATUSF_IP3)
29480c326387SDavid Daney 			octeon_irq_ip3();
294964b139f9SDavid Daney 		else if (cop0_cause & STATUSF_IP4)
29500c326387SDavid Daney 			octeon_irq_ip4();
295164b139f9SDavid Daney 		else if (cop0_cause)
29525b3b1688SDavid Daney 			do_IRQ(fls(cop0_cause) - 9 + MIPS_CPU_IRQ_BASE);
29530c326387SDavid Daney 		else
29545b3b1688SDavid Daney 			break;
29555b3b1688SDavid Daney 	}
29565b3b1688SDavid Daney }
2957773cb77dSRalf Baechle 
2958773cb77dSRalf Baechle #ifdef CONFIG_HOTPLUG_CPU
2959773cb77dSRalf Baechle 
296017efb59aSRalf Baechle void octeon_fixup_irqs(void)
2961773cb77dSRalf Baechle {
29620c326387SDavid Daney 	irq_cpu_offline();
2963773cb77dSRalf Baechle }
2964773cb77dSRalf Baechle 
2965773cb77dSRalf Baechle #endif /* CONFIG_HOTPLUG_CPU */
2966*ba1fc934SSteven J. Hill 
2967*ba1fc934SSteven J. Hill struct irq_domain *octeon_irq_get_block_domain(int node, uint8_t block)
2968*ba1fc934SSteven J. Hill {
2969*ba1fc934SSteven J. Hill 	struct octeon_ciu3_info *ciu3_info;
2970*ba1fc934SSteven J. Hill 
2971*ba1fc934SSteven J. Hill 	ciu3_info = octeon_ciu3_info_per_node[node & CVMX_NODE_MASK];
2972*ba1fc934SSteven J. Hill 	return ciu3_info->domain[block];
2973*ba1fc934SSteven J. Hill }
2974*ba1fc934SSteven J. Hill EXPORT_SYMBOL(octeon_irq_get_block_domain);
2975