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