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