12832a81dSGeoff Levand /* 22832a81dSGeoff Levand * PS3 interrupt routines. 32832a81dSGeoff Levand * 42832a81dSGeoff Levand * Copyright (C) 2006 Sony Computer Entertainment Inc. 52832a81dSGeoff Levand * Copyright 2006 Sony Corp. 62832a81dSGeoff Levand * 72832a81dSGeoff Levand * This program is free software; you can redistribute it and/or modify 82832a81dSGeoff Levand * it under the terms of the GNU General Public License as published by 92832a81dSGeoff Levand * the Free Software Foundation; version 2 of the License. 102832a81dSGeoff Levand * 112832a81dSGeoff Levand * This program is distributed in the hope that it will be useful, 122832a81dSGeoff Levand * but WITHOUT ANY WARRANTY; without even the implied warranty of 132832a81dSGeoff Levand * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 142832a81dSGeoff Levand * GNU General Public License for more details. 152832a81dSGeoff Levand * 162832a81dSGeoff Levand * You should have received a copy of the GNU General Public License 172832a81dSGeoff Levand * along with this program; if not, write to the Free Software 182832a81dSGeoff Levand * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 192832a81dSGeoff Levand */ 202832a81dSGeoff Levand 212832a81dSGeoff Levand #include <linux/kernel.h> 222832a81dSGeoff Levand #include <linux/module.h> 232832a81dSGeoff Levand #include <linux/irq.h> 242832a81dSGeoff Levand 252832a81dSGeoff Levand #include <asm/machdep.h> 262832a81dSGeoff Levand #include <asm/udbg.h> 272832a81dSGeoff Levand #include <asm/ps3.h> 282832a81dSGeoff Levand #include <asm/lv1call.h> 292832a81dSGeoff Levand 302832a81dSGeoff Levand #include "platform.h" 312832a81dSGeoff Levand 322832a81dSGeoff Levand #if defined(DEBUG) 332832a81dSGeoff Levand #define DBG(fmt...) udbg_printf(fmt) 342832a81dSGeoff Levand #else 352832a81dSGeoff Levand #define DBG(fmt...) do{if(0)printk(fmt);}while(0) 362832a81dSGeoff Levand #endif 372832a81dSGeoff Levand 382832a81dSGeoff Levand /** 39*861be32cSGeoff Levand * struct ps3_bmp - a per cpu irq status and mask bitmap structure 40*861be32cSGeoff Levand * @status: 256 bit status bitmap indexed by plug 41*861be32cSGeoff Levand * @unused_1: 42*861be32cSGeoff Levand * @mask: 256 bit mask bitmap indexed by plug 43*861be32cSGeoff Levand * @unused_2: 44*861be32cSGeoff Levand * @lock: 45*861be32cSGeoff Levand * @ipi_debug_brk_mask: 46*861be32cSGeoff Levand * 47*861be32cSGeoff Levand * The HV mantains per SMT thread mappings of HV outlet to HV plug on 48*861be32cSGeoff Levand * behalf of the guest. These mappings are implemented as 256 bit guest 49*861be32cSGeoff Levand * supplied bitmaps indexed by plug number. The addresses of the bitmaps 50*861be32cSGeoff Levand * are registered with the HV through lv1_configure_irq_state_bitmap(). 51*861be32cSGeoff Levand * 52*861be32cSGeoff Levand * The HV supports 256 plugs per thread, assigned as {0..255}, for a total 53*861be32cSGeoff Levand * of 512 plugs supported on a processor. To simplify the logic this 54*861be32cSGeoff Levand * implementation equates HV plug value to Linux virq value, constrains each 55*861be32cSGeoff Levand * interrupt to have a system wide unique plug number, and limits the range 56*861be32cSGeoff Levand * of the plug values to map into the first dword of the bitmaps. This 57*861be32cSGeoff Levand * gives a usable range of plug values of {NUM_ISA_INTERRUPTS..63}. Note 58*861be32cSGeoff Levand * that there is no constraint on how many in this set an individual thread 59*861be32cSGeoff Levand * can acquire. 60*861be32cSGeoff Levand */ 61*861be32cSGeoff Levand 62*861be32cSGeoff Levand struct ps3_bmp { 63*861be32cSGeoff Levand struct { 64*861be32cSGeoff Levand u64 status; 65*861be32cSGeoff Levand u64 unused_1[3]; 66*861be32cSGeoff Levand u64 mask; 67*861be32cSGeoff Levand u64 unused_2[3]; 68*861be32cSGeoff Levand }; 69*861be32cSGeoff Levand u64 ipi_debug_brk_mask; 70*861be32cSGeoff Levand spinlock_t lock; 71*861be32cSGeoff Levand }; 72*861be32cSGeoff Levand 73*861be32cSGeoff Levand /** 74*861be32cSGeoff Levand * struct ps3_private - a per cpu data structure 75*861be32cSGeoff Levand * @bmp: ps3_bmp structure 76*861be32cSGeoff Levand * @node: HV logical_ppe_id 77*861be32cSGeoff Levand * @cpu: HV thread_id 78*861be32cSGeoff Levand */ 79*861be32cSGeoff Levand 80*861be32cSGeoff Levand struct ps3_private { 81*861be32cSGeoff Levand struct ps3_bmp bmp __attribute__ ((aligned (64))); 82*861be32cSGeoff Levand u64 node; 83*861be32cSGeoff Levand unsigned int cpu; 84*861be32cSGeoff Levand }; 85*861be32cSGeoff Levand 86*861be32cSGeoff Levand static DEFINE_PER_CPU(struct ps3_private, ps3_private); 87*861be32cSGeoff Levand 88*861be32cSGeoff Levand static int ps3_connect_irq(enum ps3_cpu_binding cpu, unsigned long outlet, 89*861be32cSGeoff Levand unsigned int *virq) 90*861be32cSGeoff Levand { 91*861be32cSGeoff Levand int result; 92*861be32cSGeoff Levand struct ps3_private *pd; 93*861be32cSGeoff Levand 94*861be32cSGeoff Levand /* This defines the default interrupt distribution policy. */ 95*861be32cSGeoff Levand 96*861be32cSGeoff Levand if (cpu == PS3_BINDING_CPU_ANY) 97*861be32cSGeoff Levand cpu = 0; 98*861be32cSGeoff Levand 99*861be32cSGeoff Levand pd = &per_cpu(ps3_private, cpu); 100*861be32cSGeoff Levand 101*861be32cSGeoff Levand *virq = irq_create_mapping(NULL, outlet); 102*861be32cSGeoff Levand 103*861be32cSGeoff Levand if (*virq == NO_IRQ) { 104*861be32cSGeoff Levand pr_debug("%s:%d: irq_create_mapping failed: outlet %lu\n", 105*861be32cSGeoff Levand __func__, __LINE__, outlet); 106*861be32cSGeoff Levand result = -ENOMEM; 107*861be32cSGeoff Levand goto fail_create; 108*861be32cSGeoff Levand } 109*861be32cSGeoff Levand 110*861be32cSGeoff Levand /* Binds outlet to cpu + virq. */ 111*861be32cSGeoff Levand 112*861be32cSGeoff Levand result = lv1_connect_irq_plug_ext(pd->node, pd->cpu, *virq, outlet, 0); 113*861be32cSGeoff Levand 114*861be32cSGeoff Levand if (result) { 115*861be32cSGeoff Levand pr_info("%s:%d: lv1_connect_irq_plug_ext failed: %s\n", 116*861be32cSGeoff Levand __func__, __LINE__, ps3_result(result)); 117*861be32cSGeoff Levand result = -EPERM; 118*861be32cSGeoff Levand goto fail_connect; 119*861be32cSGeoff Levand } 120*861be32cSGeoff Levand 121*861be32cSGeoff Levand pr_debug("%s:%d: outlet %lu => cpu %u, virq %u\n", __func__, __LINE__, 122*861be32cSGeoff Levand outlet, cpu, *virq); 123*861be32cSGeoff Levand 124*861be32cSGeoff Levand result = set_irq_chip_data(*virq, pd); 125*861be32cSGeoff Levand 126*861be32cSGeoff Levand if (result) { 127*861be32cSGeoff Levand pr_debug("%s:%d: set_irq_chip_data failed\n", 128*861be32cSGeoff Levand __func__, __LINE__); 129*861be32cSGeoff Levand goto fail_set; 130*861be32cSGeoff Levand } 131*861be32cSGeoff Levand 132*861be32cSGeoff Levand return result; 133*861be32cSGeoff Levand 134*861be32cSGeoff Levand fail_set: 135*861be32cSGeoff Levand lv1_disconnect_irq_plug_ext(pd->node, pd->cpu, *virq); 136*861be32cSGeoff Levand fail_connect: 137*861be32cSGeoff Levand irq_dispose_mapping(*virq); 138*861be32cSGeoff Levand fail_create: 139*861be32cSGeoff Levand return result; 140*861be32cSGeoff Levand } 141*861be32cSGeoff Levand 142*861be32cSGeoff Levand static void ps3_disconnect_irq(unsigned int virq) 143*861be32cSGeoff Levand { 144*861be32cSGeoff Levand int result; 145*861be32cSGeoff Levand const struct ps3_private *pd = get_irq_chip_data(virq); 146*861be32cSGeoff Levand 147*861be32cSGeoff Levand pr_debug("%s:%d: node %lu, cpu %d, virq %u\n", __func__, __LINE__, 148*861be32cSGeoff Levand pd->node, pd->cpu, virq); 149*861be32cSGeoff Levand 150*861be32cSGeoff Levand result = lv1_disconnect_irq_plug_ext(pd->node, pd->cpu, virq); 151*861be32cSGeoff Levand 152*861be32cSGeoff Levand if (result) 153*861be32cSGeoff Levand pr_info("%s:%d: lv1_disconnect_irq_plug_ext failed: %s\n", 154*861be32cSGeoff Levand __func__, __LINE__, ps3_result(result)); 155*861be32cSGeoff Levand 156*861be32cSGeoff Levand set_irq_chip_data(virq, NULL); 157*861be32cSGeoff Levand irq_dispose_mapping(virq); 158*861be32cSGeoff Levand } 159*861be32cSGeoff Levand 160*861be32cSGeoff Levand /** 1612832a81dSGeoff Levand * ps3_alloc_io_irq - Assign a virq to a system bus device. 162*861be32cSGeoff Levand * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 163*861be32cSGeoff Levand * serviced on. 164*861be32cSGeoff Levand * @interrupt_id: The device interrupt id read from the system repository. 1652832a81dSGeoff Levand * @virq: The assigned Linux virq. 1662832a81dSGeoff Levand * 1672832a81dSGeoff Levand * An io irq represents a non-virtualized device interrupt. interrupt_id 1682832a81dSGeoff Levand * coresponds to the interrupt number of the interrupt controller. 1692832a81dSGeoff Levand */ 1702832a81dSGeoff Levand 171*861be32cSGeoff Levand int ps3_alloc_io_irq(enum ps3_cpu_binding cpu, unsigned int interrupt_id, 172*861be32cSGeoff Levand unsigned int *virq) 1732832a81dSGeoff Levand { 1742832a81dSGeoff Levand int result; 1752832a81dSGeoff Levand unsigned long outlet; 1762832a81dSGeoff Levand 1772832a81dSGeoff Levand result = lv1_construct_io_irq_outlet(interrupt_id, &outlet); 1782832a81dSGeoff Levand 1792832a81dSGeoff Levand if (result) { 1802832a81dSGeoff Levand pr_debug("%s:%d: lv1_construct_io_irq_outlet failed: %s\n", 1812832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 1822832a81dSGeoff Levand return result; 1832832a81dSGeoff Levand } 1842832a81dSGeoff Levand 185*861be32cSGeoff Levand result = ps3_connect_irq(cpu, outlet, virq); 186*861be32cSGeoff Levand BUG_ON(result); 1872832a81dSGeoff Levand 188*861be32cSGeoff Levand return result; 1892832a81dSGeoff Levand } 1902832a81dSGeoff Levand 1912832a81dSGeoff Levand int ps3_free_io_irq(unsigned int virq) 1922832a81dSGeoff Levand { 1932832a81dSGeoff Levand int result; 1942832a81dSGeoff Levand 1952832a81dSGeoff Levand result = lv1_destruct_io_irq_outlet(virq_to_hw(virq)); 1962832a81dSGeoff Levand 197ded84bcbSGeert Uytterhoeven if (result) 1982832a81dSGeoff Levand pr_debug("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n", 1992832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 2002832a81dSGeoff Levand 201*861be32cSGeoff Levand ps3_disconnect_irq(virq); 2022832a81dSGeoff Levand 2032832a81dSGeoff Levand return result; 2042832a81dSGeoff Levand } 2052832a81dSGeoff Levand 2062832a81dSGeoff Levand /** 2072832a81dSGeoff Levand * ps3_alloc_event_irq - Allocate a virq for use with a system event. 208*861be32cSGeoff Levand * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 209*861be32cSGeoff Levand * serviced on. 2102832a81dSGeoff Levand * @virq: The assigned Linux virq. 2112832a81dSGeoff Levand * 2122832a81dSGeoff Levand * The virq can be used with lv1_connect_interrupt_event_receive_port() to 2132832a81dSGeoff Levand * arrange to receive events, or with ps3_send_event_locally() to signal 2142832a81dSGeoff Levand * events. 2152832a81dSGeoff Levand */ 2162832a81dSGeoff Levand 217*861be32cSGeoff Levand int ps3_alloc_event_irq(enum ps3_cpu_binding cpu, unsigned int *virq) 2182832a81dSGeoff Levand { 2192832a81dSGeoff Levand int result; 2202832a81dSGeoff Levand unsigned long outlet; 2212832a81dSGeoff Levand 2222832a81dSGeoff Levand result = lv1_construct_event_receive_port(&outlet); 2232832a81dSGeoff Levand 2242832a81dSGeoff Levand if (result) { 2252832a81dSGeoff Levand pr_debug("%s:%d: lv1_construct_event_receive_port failed: %s\n", 2262832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 2272832a81dSGeoff Levand *virq = NO_IRQ; 2282832a81dSGeoff Levand return result; 2292832a81dSGeoff Levand } 2302832a81dSGeoff Levand 231*861be32cSGeoff Levand result = ps3_connect_irq(cpu, outlet, virq); 232*861be32cSGeoff Levand BUG_ON(result); 2332832a81dSGeoff Levand 234*861be32cSGeoff Levand return result; 2352832a81dSGeoff Levand } 2362832a81dSGeoff Levand 2372832a81dSGeoff Levand int ps3_free_event_irq(unsigned int virq) 2382832a81dSGeoff Levand { 2392832a81dSGeoff Levand int result; 2402832a81dSGeoff Levand 2412832a81dSGeoff Levand pr_debug(" -> %s:%d\n", __func__, __LINE__); 2422832a81dSGeoff Levand 2432832a81dSGeoff Levand result = lv1_destruct_event_receive_port(virq_to_hw(virq)); 2442832a81dSGeoff Levand 2452832a81dSGeoff Levand if (result) 2462832a81dSGeoff Levand pr_debug("%s:%d: lv1_destruct_event_receive_port failed: %s\n", 2472832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 2482832a81dSGeoff Levand 249*861be32cSGeoff Levand ps3_disconnect_irq(virq); 2502832a81dSGeoff Levand 2512832a81dSGeoff Levand pr_debug(" <- %s:%d\n", __func__, __LINE__); 2522832a81dSGeoff Levand return result; 2532832a81dSGeoff Levand } 2542832a81dSGeoff Levand 2552832a81dSGeoff Levand int ps3_send_event_locally(unsigned int virq) 2562832a81dSGeoff Levand { 2572832a81dSGeoff Levand return lv1_send_event_locally(virq_to_hw(virq)); 2582832a81dSGeoff Levand } 2592832a81dSGeoff Levand 2602832a81dSGeoff Levand /** 2612832a81dSGeoff Levand * ps3_connect_event_irq - Assign a virq to a system bus device. 262*861be32cSGeoff Levand * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 263*861be32cSGeoff Levand * serviced on. 2642832a81dSGeoff Levand * @did: The HV device identifier read from the system repository. 2652832a81dSGeoff Levand * @interrupt_id: The device interrupt id read from the system repository. 2662832a81dSGeoff Levand * @virq: The assigned Linux virq. 2672832a81dSGeoff Levand * 2682832a81dSGeoff Levand * An event irq represents a virtual device interrupt. The interrupt_id 2692832a81dSGeoff Levand * coresponds to the software interrupt number. 2702832a81dSGeoff Levand */ 2712832a81dSGeoff Levand 272*861be32cSGeoff Levand int ps3_connect_event_irq(enum ps3_cpu_binding cpu, 273*861be32cSGeoff Levand const struct ps3_device_id *did, unsigned int interrupt_id, 274*861be32cSGeoff Levand unsigned int *virq) 2752832a81dSGeoff Levand { 2762832a81dSGeoff Levand int result; 2772832a81dSGeoff Levand 278*861be32cSGeoff Levand result = ps3_alloc_event_irq(cpu, virq); 2792832a81dSGeoff Levand 2802832a81dSGeoff Levand if (result) 2812832a81dSGeoff Levand return result; 2822832a81dSGeoff Levand 2832832a81dSGeoff Levand result = lv1_connect_interrupt_event_receive_port(did->bus_id, 2842832a81dSGeoff Levand did->dev_id, virq_to_hw(*virq), interrupt_id); 2852832a81dSGeoff Levand 2862832a81dSGeoff Levand if (result) { 2872832a81dSGeoff Levand pr_debug("%s:%d: lv1_connect_interrupt_event_receive_port" 2882832a81dSGeoff Levand " failed: %s\n", __func__, __LINE__, 2892832a81dSGeoff Levand ps3_result(result)); 2902832a81dSGeoff Levand ps3_free_event_irq(*virq); 2912832a81dSGeoff Levand *virq = NO_IRQ; 2922832a81dSGeoff Levand return result; 2932832a81dSGeoff Levand } 2942832a81dSGeoff Levand 2952832a81dSGeoff Levand pr_debug("%s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__, 2962832a81dSGeoff Levand interrupt_id, *virq); 2972832a81dSGeoff Levand 2982832a81dSGeoff Levand return 0; 2992832a81dSGeoff Levand } 3002832a81dSGeoff Levand 3012832a81dSGeoff Levand int ps3_disconnect_event_irq(const struct ps3_device_id *did, 3022832a81dSGeoff Levand unsigned int interrupt_id, unsigned int virq) 3032832a81dSGeoff Levand { 3042832a81dSGeoff Levand int result; 3052832a81dSGeoff Levand 3062832a81dSGeoff Levand pr_debug(" -> %s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__, 3072832a81dSGeoff Levand interrupt_id, virq); 3082832a81dSGeoff Levand 3092832a81dSGeoff Levand result = lv1_disconnect_interrupt_event_receive_port(did->bus_id, 3102832a81dSGeoff Levand did->dev_id, virq_to_hw(virq), interrupt_id); 3112832a81dSGeoff Levand 3122832a81dSGeoff Levand if (result) 3132832a81dSGeoff Levand pr_debug("%s:%d: lv1_disconnect_interrupt_event_receive_port" 3142832a81dSGeoff Levand " failed: %s\n", __func__, __LINE__, 3152832a81dSGeoff Levand ps3_result(result)); 3162832a81dSGeoff Levand 3172832a81dSGeoff Levand ps3_free_event_irq(virq); 3182832a81dSGeoff Levand 3192832a81dSGeoff Levand pr_debug(" <- %s:%d\n", __func__, __LINE__); 3202832a81dSGeoff Levand return result; 3212832a81dSGeoff Levand } 3222832a81dSGeoff Levand 3232832a81dSGeoff Levand /** 3242832a81dSGeoff Levand * ps3_alloc_vuart_irq - Configure the system virtual uart virq. 325*861be32cSGeoff Levand * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 326*861be32cSGeoff Levand * serviced on. 3272832a81dSGeoff Levand * @virt_addr_bmp: The caller supplied virtual uart interrupt bitmap. 3282832a81dSGeoff Levand * @virq: The assigned Linux virq. 3292832a81dSGeoff Levand * 3302832a81dSGeoff Levand * The system supports only a single virtual uart, so multiple calls without 3312832a81dSGeoff Levand * freeing the interrupt will return a wrong state error. 3322832a81dSGeoff Levand */ 3332832a81dSGeoff Levand 334*861be32cSGeoff Levand int ps3_alloc_vuart_irq(enum ps3_cpu_binding cpu, void* virt_addr_bmp, 335*861be32cSGeoff Levand unsigned int *virq) 3362832a81dSGeoff Levand { 3372832a81dSGeoff Levand int result; 3382832a81dSGeoff Levand unsigned long outlet; 339*861be32cSGeoff Levand u64 lpar_addr; 3402832a81dSGeoff Levand 341*861be32cSGeoff Levand BUG_ON(!is_kernel_addr((u64)virt_addr_bmp)); 3422832a81dSGeoff Levand 3432832a81dSGeoff Levand lpar_addr = ps3_mm_phys_to_lpar(__pa(virt_addr_bmp)); 3442832a81dSGeoff Levand 3452832a81dSGeoff Levand result = lv1_configure_virtual_uart_irq(lpar_addr, &outlet); 3462832a81dSGeoff Levand 3472832a81dSGeoff Levand if (result) { 3482832a81dSGeoff Levand pr_debug("%s:%d: lv1_configure_virtual_uart_irq failed: %s\n", 3492832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 3502832a81dSGeoff Levand return result; 3512832a81dSGeoff Levand } 3522832a81dSGeoff Levand 353*861be32cSGeoff Levand result = ps3_connect_irq(cpu, outlet, virq); 354*861be32cSGeoff Levand BUG_ON(result); 3552832a81dSGeoff Levand 356*861be32cSGeoff Levand return result; 3572832a81dSGeoff Levand } 3582832a81dSGeoff Levand 3592832a81dSGeoff Levand int ps3_free_vuart_irq(unsigned int virq) 3602832a81dSGeoff Levand { 3612832a81dSGeoff Levand int result; 3622832a81dSGeoff Levand 3632832a81dSGeoff Levand result = lv1_deconfigure_virtual_uart_irq(); 3642832a81dSGeoff Levand 3652832a81dSGeoff Levand if (result) { 3662832a81dSGeoff Levand pr_debug("%s:%d: lv1_configure_virtual_uart_irq failed: %s\n", 3672832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 3682832a81dSGeoff Levand return result; 3692832a81dSGeoff Levand } 3702832a81dSGeoff Levand 371*861be32cSGeoff Levand ps3_disconnect_irq(virq); 3722832a81dSGeoff Levand 3732832a81dSGeoff Levand return result; 3742832a81dSGeoff Levand } 3752832a81dSGeoff Levand 3762832a81dSGeoff Levand /** 3772832a81dSGeoff Levand * ps3_alloc_spe_irq - Configure an spe virq. 378*861be32cSGeoff Levand * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 379*861be32cSGeoff Levand * serviced on. 3802832a81dSGeoff Levand * @spe_id: The spe_id returned from lv1_construct_logical_spe(). 3812832a81dSGeoff Levand * @class: The spe interrupt class {0,1,2}. 3822832a81dSGeoff Levand * @virq: The assigned Linux virq. 3832832a81dSGeoff Levand * 3842832a81dSGeoff Levand */ 3852832a81dSGeoff Levand 386*861be32cSGeoff Levand int ps3_alloc_spe_irq(enum ps3_cpu_binding cpu, unsigned long spe_id, 387*861be32cSGeoff Levand unsigned int class, unsigned int *virq) 3882832a81dSGeoff Levand { 3892832a81dSGeoff Levand int result; 3902832a81dSGeoff Levand unsigned long outlet; 3912832a81dSGeoff Levand 3922832a81dSGeoff Levand BUG_ON(class > 2); 3932832a81dSGeoff Levand 3942832a81dSGeoff Levand result = lv1_get_spe_irq_outlet(spe_id, class, &outlet); 3952832a81dSGeoff Levand 3962832a81dSGeoff Levand if (result) { 3972832a81dSGeoff Levand pr_debug("%s:%d: lv1_get_spe_irq_outlet failed: %s\n", 3982832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 3992832a81dSGeoff Levand return result; 4002832a81dSGeoff Levand } 4012832a81dSGeoff Levand 402*861be32cSGeoff Levand result = ps3_connect_irq(cpu, outlet, virq); 403*861be32cSGeoff Levand BUG_ON(result); 4042832a81dSGeoff Levand 405*861be32cSGeoff Levand return result; 4062832a81dSGeoff Levand } 4072832a81dSGeoff Levand 4082832a81dSGeoff Levand int ps3_free_spe_irq(unsigned int virq) 4092832a81dSGeoff Levand { 410*861be32cSGeoff Levand ps3_disconnect_irq(virq); 4112832a81dSGeoff Levand return 0; 4122832a81dSGeoff Levand } 4132832a81dSGeoff Levand 4142832a81dSGeoff Levand #define PS3_INVALID_OUTLET ((irq_hw_number_t)-1) 4152832a81dSGeoff Levand #define PS3_PLUG_MAX 63 4162832a81dSGeoff Levand 4172832a81dSGeoff Levand #if defined(DEBUG) 418*861be32cSGeoff Levand static void _dump_64_bmp(const char *header, const u64 *p, unsigned cpu, 4192832a81dSGeoff Levand const char* func, int line) 4202832a81dSGeoff Levand { 4212832a81dSGeoff Levand pr_debug("%s:%d: %s %u {%04lx_%04lx_%04lx_%04lx}\n", 4222832a81dSGeoff Levand func, line, header, cpu, 4232832a81dSGeoff Levand *p >> 48, (*p >> 32) & 0xffff, (*p >> 16) & 0xffff, 4242832a81dSGeoff Levand *p & 0xffff); 4252832a81dSGeoff Levand } 4262832a81dSGeoff Levand 4272832a81dSGeoff Levand static void __attribute__ ((unused)) _dump_256_bmp(const char *header, 428*861be32cSGeoff Levand const u64 *p, unsigned cpu, const char* func, int line) 4292832a81dSGeoff Levand { 4302832a81dSGeoff Levand pr_debug("%s:%d: %s %u {%016lx:%016lx:%016lx:%016lx}\n", 4312832a81dSGeoff Levand func, line, header, cpu, p[0], p[1], p[2], p[3]); 4322832a81dSGeoff Levand } 4332832a81dSGeoff Levand 4342832a81dSGeoff Levand #define dump_bmp(_x) _dump_bmp(_x, __func__, __LINE__) 4359633ac8dSGeoff Levand static void _dump_bmp(struct ps3_private* pd, const char* func, int line) 4362832a81dSGeoff Levand { 4372832a81dSGeoff Levand unsigned long flags; 4382832a81dSGeoff Levand 4392832a81dSGeoff Levand spin_lock_irqsave(&pd->bmp.lock, flags); 4402832a81dSGeoff Levand _dump_64_bmp("stat", &pd->bmp.status, pd->cpu, func, line); 4412832a81dSGeoff Levand _dump_64_bmp("mask", &pd->bmp.mask, pd->cpu, func, line); 4422832a81dSGeoff Levand spin_unlock_irqrestore(&pd->bmp.lock, flags); 4432832a81dSGeoff Levand } 4442832a81dSGeoff Levand 4452832a81dSGeoff Levand #define dump_mask(_x) _dump_mask(_x, __func__, __LINE__) 4469633ac8dSGeoff Levand static void __attribute__ ((unused)) _dump_mask(struct ps3_private* pd, 4472832a81dSGeoff Levand const char* func, int line) 4482832a81dSGeoff Levand { 4492832a81dSGeoff Levand unsigned long flags; 4502832a81dSGeoff Levand 4512832a81dSGeoff Levand spin_lock_irqsave(&pd->bmp.lock, flags); 4522832a81dSGeoff Levand _dump_64_bmp("mask", &pd->bmp.mask, pd->cpu, func, line); 4532832a81dSGeoff Levand spin_unlock_irqrestore(&pd->bmp.lock, flags); 4542832a81dSGeoff Levand } 4552832a81dSGeoff Levand #else 4569633ac8dSGeoff Levand static void dump_bmp(struct ps3_private* pd) {}; 4572832a81dSGeoff Levand #endif /* defined(DEBUG) */ 4582832a81dSGeoff Levand 4599633ac8dSGeoff Levand static void ps3_chip_mask(unsigned int virq) 4602832a81dSGeoff Levand { 4619633ac8dSGeoff Levand struct ps3_private *pd = get_irq_chip_data(virq); 462*861be32cSGeoff Levand u64 bit = 0x8000000000000000UL >> virq; 463*861be32cSGeoff Levand u64 *p = &pd->bmp.mask; 464*861be32cSGeoff Levand u64 old; 4659cf9e196SBenjamin Herrenschmidt unsigned long flags; 4662832a81dSGeoff Levand 4672832a81dSGeoff Levand pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq); 4682832a81dSGeoff Levand 4699cf9e196SBenjamin Herrenschmidt local_irq_save(flags); 4709cf9e196SBenjamin Herrenschmidt asm volatile( 4719cf9e196SBenjamin Herrenschmidt "1: ldarx %0,0,%3\n" 4729cf9e196SBenjamin Herrenschmidt "andc %0,%0,%2\n" 4739cf9e196SBenjamin Herrenschmidt "stdcx. %0,0,%3\n" 4749cf9e196SBenjamin Herrenschmidt "bne- 1b" 4759cf9e196SBenjamin Herrenschmidt : "=&r" (old), "+m" (*p) 4769cf9e196SBenjamin Herrenschmidt : "r" (bit), "r" (p) 4779cf9e196SBenjamin Herrenschmidt : "cc" ); 4782832a81dSGeoff Levand 4792832a81dSGeoff Levand lv1_did_update_interrupt_mask(pd->node, pd->cpu); 4809cf9e196SBenjamin Herrenschmidt local_irq_restore(flags); 4812832a81dSGeoff Levand } 4822832a81dSGeoff Levand 4839633ac8dSGeoff Levand static void ps3_chip_unmask(unsigned int virq) 4842832a81dSGeoff Levand { 4859633ac8dSGeoff Levand struct ps3_private *pd = get_irq_chip_data(virq); 486*861be32cSGeoff Levand u64 bit = 0x8000000000000000UL >> virq; 487*861be32cSGeoff Levand u64 *p = &pd->bmp.mask; 488*861be32cSGeoff Levand u64 old; 4899cf9e196SBenjamin Herrenschmidt unsigned long flags; 4902832a81dSGeoff Levand 4912832a81dSGeoff Levand pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq); 4922832a81dSGeoff Levand 4939cf9e196SBenjamin Herrenschmidt local_irq_save(flags); 4949cf9e196SBenjamin Herrenschmidt asm volatile( 4959cf9e196SBenjamin Herrenschmidt "1: ldarx %0,0,%3\n" 4969cf9e196SBenjamin Herrenschmidt "or %0,%0,%2\n" 4979cf9e196SBenjamin Herrenschmidt "stdcx. %0,0,%3\n" 4989cf9e196SBenjamin Herrenschmidt "bne- 1b" 4999cf9e196SBenjamin Herrenschmidt : "=&r" (old), "+m" (*p) 5009cf9e196SBenjamin Herrenschmidt : "r" (bit), "r" (p) 5019cf9e196SBenjamin Herrenschmidt : "cc" ); 5022832a81dSGeoff Levand 5032832a81dSGeoff Levand lv1_did_update_interrupt_mask(pd->node, pd->cpu); 5049cf9e196SBenjamin Herrenschmidt local_irq_restore(flags); 5052832a81dSGeoff Levand } 5062832a81dSGeoff Levand 5079633ac8dSGeoff Levand static void ps3_chip_eoi(unsigned int virq) 5082832a81dSGeoff Levand { 509407e24a0SGeoff Levand const struct ps3_private *pd = get_irq_chip_data(virq); 510407e24a0SGeoff Levand lv1_end_of_interrupt_ext(pd->node, pd->cpu, virq); 5112832a81dSGeoff Levand } 5122832a81dSGeoff Levand 5132832a81dSGeoff Levand static struct irq_chip irq_chip = { 5142832a81dSGeoff Levand .typename = "ps3", 5159633ac8dSGeoff Levand .mask = ps3_chip_mask, 5169633ac8dSGeoff Levand .unmask = ps3_chip_unmask, 5179633ac8dSGeoff Levand .eoi = ps3_chip_eoi, 5182832a81dSGeoff Levand }; 5192832a81dSGeoff Levand 5209633ac8dSGeoff Levand static void ps3_host_unmap(struct irq_host *h, unsigned int virq) 5212832a81dSGeoff Levand { 522*861be32cSGeoff Levand set_irq_chip_data(virq, NULL); 5232832a81dSGeoff Levand } 5242832a81dSGeoff Levand 5259633ac8dSGeoff Levand static int ps3_host_map(struct irq_host *h, unsigned int virq, 5262832a81dSGeoff Levand irq_hw_number_t hwirq) 5272832a81dSGeoff Levand { 528*861be32cSGeoff Levand pr_debug("%s:%d: hwirq %lu, virq %u\n", __func__, __LINE__, hwirq, 529*861be32cSGeoff Levand virq); 5302832a81dSGeoff Levand 5312832a81dSGeoff Levand set_irq_chip_and_handler(virq, &irq_chip, handle_fasteoi_irq); 5322832a81dSGeoff Levand 533*861be32cSGeoff Levand return 0; 5342832a81dSGeoff Levand } 5352832a81dSGeoff Levand 5369633ac8dSGeoff Levand static struct irq_host_ops ps3_host_ops = { 5379633ac8dSGeoff Levand .map = ps3_host_map, 5389633ac8dSGeoff Levand .unmap = ps3_host_unmap, 5392832a81dSGeoff Levand }; 5402832a81dSGeoff Levand 5412832a81dSGeoff Levand void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq) 5422832a81dSGeoff Levand { 5439633ac8dSGeoff Levand struct ps3_private *pd = &per_cpu(ps3_private, cpu); 5442832a81dSGeoff Levand 5452832a81dSGeoff Levand pd->bmp.ipi_debug_brk_mask = 0x8000000000000000UL >> virq; 5462832a81dSGeoff Levand 5472832a81dSGeoff Levand pr_debug("%s:%d: cpu %u, virq %u, mask %lxh\n", __func__, __LINE__, 5482832a81dSGeoff Levand cpu, virq, pd->bmp.ipi_debug_brk_mask); 5492832a81dSGeoff Levand } 5502832a81dSGeoff Levand 5519cf9e196SBenjamin Herrenschmidt unsigned int ps3_get_irq(void) 5522832a81dSGeoff Levand { 5539cf9e196SBenjamin Herrenschmidt struct ps3_private *pd = &__get_cpu_var(ps3_private); 554*861be32cSGeoff Levand u64 x = (pd->bmp.status & pd->bmp.mask); 5559cf9e196SBenjamin Herrenschmidt unsigned int plug; 5562832a81dSGeoff Levand 5572832a81dSGeoff Levand /* check for ipi break first to stop this cpu ASAP */ 5582832a81dSGeoff Levand 5599cf9e196SBenjamin Herrenschmidt if (x & pd->bmp.ipi_debug_brk_mask) 5609cf9e196SBenjamin Herrenschmidt x &= pd->bmp.ipi_debug_brk_mask; 5612832a81dSGeoff Levand 5629cf9e196SBenjamin Herrenschmidt asm volatile("cntlzd %0,%1" : "=r" (plug) : "r" (x)); 5639cf9e196SBenjamin Herrenschmidt plug &= 0x3f; 5642832a81dSGeoff Levand 5659cf9e196SBenjamin Herrenschmidt if (unlikely(plug) == NO_IRQ) { 5662832a81dSGeoff Levand pr_debug("%s:%d: no plug found: cpu %u\n", __func__, __LINE__, 5672832a81dSGeoff Levand pd->cpu); 5689633ac8dSGeoff Levand dump_bmp(&per_cpu(ps3_private, 0)); 5699633ac8dSGeoff Levand dump_bmp(&per_cpu(ps3_private, 1)); 5702832a81dSGeoff Levand return NO_IRQ; 5712832a81dSGeoff Levand } 5722832a81dSGeoff Levand 5732832a81dSGeoff Levand #if defined(DEBUG) 5749cf9e196SBenjamin Herrenschmidt if (unlikely(plug < NUM_ISA_INTERRUPTS || plug > PS3_PLUG_MAX)) { 5759633ac8dSGeoff Levand dump_bmp(&per_cpu(ps3_private, 0)); 5769633ac8dSGeoff Levand dump_bmp(&per_cpu(ps3_private, 1)); 5772832a81dSGeoff Levand BUG(); 5782832a81dSGeoff Levand } 5792832a81dSGeoff Levand #endif 5802832a81dSGeoff Levand return plug; 5812832a81dSGeoff Levand } 5822832a81dSGeoff Levand 5832832a81dSGeoff Levand void __init ps3_init_IRQ(void) 5842832a81dSGeoff Levand { 5852832a81dSGeoff Levand int result; 5862832a81dSGeoff Levand unsigned cpu; 5872832a81dSGeoff Levand struct irq_host *host; 5882832a81dSGeoff Levand 5899633ac8dSGeoff Levand host = irq_alloc_host(IRQ_HOST_MAP_NOMAP, 0, &ps3_host_ops, 5902832a81dSGeoff Levand PS3_INVALID_OUTLET); 5912832a81dSGeoff Levand irq_set_default_host(host); 5922832a81dSGeoff Levand irq_set_virq_count(PS3_PLUG_MAX + 1); 5932832a81dSGeoff Levand 5942832a81dSGeoff Levand for_each_possible_cpu(cpu) { 5959633ac8dSGeoff Levand struct ps3_private *pd = &per_cpu(ps3_private, cpu); 5962832a81dSGeoff Levand 597407e24a0SGeoff Levand lv1_get_logical_ppe_id(&pd->node); 598407e24a0SGeoff Levand pd->cpu = get_hard_smp_processor_id(cpu); 5992832a81dSGeoff Levand spin_lock_init(&pd->bmp.lock); 6002832a81dSGeoff Levand 601407e24a0SGeoff Levand pr_debug("%s:%d: node %lu, cpu %d, bmp %lxh\n", __func__, 602407e24a0SGeoff Levand __LINE__, pd->node, pd->cpu, 603407e24a0SGeoff Levand ps3_mm_phys_to_lpar(__pa(&pd->bmp))); 604407e24a0SGeoff Levand 605407e24a0SGeoff Levand result = lv1_configure_irq_state_bitmap(pd->node, pd->cpu, 606407e24a0SGeoff Levand ps3_mm_phys_to_lpar(__pa(&pd->bmp))); 6072832a81dSGeoff Levand 6082832a81dSGeoff Levand if (result) 6092832a81dSGeoff Levand pr_debug("%s:%d: lv1_configure_irq_state_bitmap failed:" 6102832a81dSGeoff Levand " %s\n", __func__, __LINE__, 6112832a81dSGeoff Levand ps3_result(result)); 6122832a81dSGeoff Levand } 6132832a81dSGeoff Levand 6142832a81dSGeoff Levand ppc_md.get_irq = ps3_get_irq; 6152832a81dSGeoff Levand } 616