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