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 /** 392832a81dSGeoff Levand * ps3_alloc_io_irq - Assign a virq to a system bus device. 402832a81dSGeoff Levand * interrupt_id: The device interrupt id read from the system repository. 412832a81dSGeoff Levand * @virq: The assigned Linux virq. 422832a81dSGeoff Levand * 432832a81dSGeoff Levand * An io irq represents a non-virtualized device interrupt. interrupt_id 442832a81dSGeoff Levand * coresponds to the interrupt number of the interrupt controller. 452832a81dSGeoff Levand */ 462832a81dSGeoff Levand 472832a81dSGeoff Levand int ps3_alloc_io_irq(unsigned int interrupt_id, unsigned int *virq) 482832a81dSGeoff Levand { 492832a81dSGeoff Levand int result; 502832a81dSGeoff Levand unsigned long outlet; 512832a81dSGeoff Levand 522832a81dSGeoff Levand result = lv1_construct_io_irq_outlet(interrupt_id, &outlet); 532832a81dSGeoff Levand 542832a81dSGeoff Levand if (result) { 552832a81dSGeoff Levand pr_debug("%s:%d: lv1_construct_io_irq_outlet failed: %s\n", 562832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 572832a81dSGeoff Levand return result; 582832a81dSGeoff Levand } 592832a81dSGeoff Levand 602832a81dSGeoff Levand *virq = irq_create_mapping(NULL, outlet); 612832a81dSGeoff Levand 622832a81dSGeoff Levand pr_debug("%s:%d: interrupt_id %u => outlet %lu, virq %u\n", 632832a81dSGeoff Levand __func__, __LINE__, interrupt_id, outlet, *virq); 642832a81dSGeoff Levand 652832a81dSGeoff Levand return 0; 662832a81dSGeoff Levand } 672832a81dSGeoff Levand 682832a81dSGeoff Levand int ps3_free_io_irq(unsigned int virq) 692832a81dSGeoff Levand { 702832a81dSGeoff Levand int result; 712832a81dSGeoff Levand 722832a81dSGeoff Levand result = lv1_destruct_io_irq_outlet(virq_to_hw(virq)); 732832a81dSGeoff Levand 74ded84bcbSGeert Uytterhoeven if (result) 752832a81dSGeoff Levand pr_debug("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n", 762832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 772832a81dSGeoff Levand 782832a81dSGeoff Levand irq_dispose_mapping(virq); 792832a81dSGeoff Levand 802832a81dSGeoff Levand return result; 812832a81dSGeoff Levand } 822832a81dSGeoff Levand 832832a81dSGeoff Levand /** 842832a81dSGeoff Levand * ps3_alloc_event_irq - Allocate a virq for use with a system event. 852832a81dSGeoff Levand * @virq: The assigned Linux virq. 862832a81dSGeoff Levand * 872832a81dSGeoff Levand * The virq can be used with lv1_connect_interrupt_event_receive_port() to 882832a81dSGeoff Levand * arrange to receive events, or with ps3_send_event_locally() to signal 892832a81dSGeoff Levand * events. 902832a81dSGeoff Levand */ 912832a81dSGeoff Levand 922832a81dSGeoff Levand int ps3_alloc_event_irq(unsigned int *virq) 932832a81dSGeoff Levand { 942832a81dSGeoff Levand int result; 952832a81dSGeoff Levand unsigned long outlet; 962832a81dSGeoff Levand 972832a81dSGeoff Levand result = lv1_construct_event_receive_port(&outlet); 982832a81dSGeoff Levand 992832a81dSGeoff Levand if (result) { 1002832a81dSGeoff Levand pr_debug("%s:%d: lv1_construct_event_receive_port failed: %s\n", 1012832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 1022832a81dSGeoff Levand *virq = NO_IRQ; 1032832a81dSGeoff Levand return result; 1042832a81dSGeoff Levand } 1052832a81dSGeoff Levand 1062832a81dSGeoff Levand *virq = irq_create_mapping(NULL, outlet); 1072832a81dSGeoff Levand 1082832a81dSGeoff Levand pr_debug("%s:%d: outlet %lu, virq %u\n", __func__, __LINE__, outlet, 1092832a81dSGeoff Levand *virq); 1102832a81dSGeoff Levand 1112832a81dSGeoff Levand return 0; 1122832a81dSGeoff Levand } 1132832a81dSGeoff Levand 1142832a81dSGeoff Levand int ps3_free_event_irq(unsigned int virq) 1152832a81dSGeoff Levand { 1162832a81dSGeoff Levand int result; 1172832a81dSGeoff Levand 1182832a81dSGeoff Levand pr_debug(" -> %s:%d\n", __func__, __LINE__); 1192832a81dSGeoff Levand 1202832a81dSGeoff Levand result = lv1_destruct_event_receive_port(virq_to_hw(virq)); 1212832a81dSGeoff Levand 1222832a81dSGeoff Levand if (result) 1232832a81dSGeoff Levand pr_debug("%s:%d: lv1_destruct_event_receive_port failed: %s\n", 1242832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 1252832a81dSGeoff Levand 1262832a81dSGeoff Levand irq_dispose_mapping(virq); 1272832a81dSGeoff Levand 1282832a81dSGeoff Levand pr_debug(" <- %s:%d\n", __func__, __LINE__); 1292832a81dSGeoff Levand return result; 1302832a81dSGeoff Levand } 1312832a81dSGeoff Levand 1322832a81dSGeoff Levand int ps3_send_event_locally(unsigned int virq) 1332832a81dSGeoff Levand { 1342832a81dSGeoff Levand return lv1_send_event_locally(virq_to_hw(virq)); 1352832a81dSGeoff Levand } 1362832a81dSGeoff Levand 1372832a81dSGeoff Levand /** 1382832a81dSGeoff Levand * ps3_connect_event_irq - Assign a virq to a system bus device. 1392832a81dSGeoff Levand * @did: The HV device identifier read from the system repository. 1402832a81dSGeoff Levand * @interrupt_id: The device interrupt id read from the system repository. 1412832a81dSGeoff Levand * @virq: The assigned Linux virq. 1422832a81dSGeoff Levand * 1432832a81dSGeoff Levand * An event irq represents a virtual device interrupt. The interrupt_id 1442832a81dSGeoff Levand * coresponds to the software interrupt number. 1452832a81dSGeoff Levand */ 1462832a81dSGeoff Levand 1472832a81dSGeoff Levand int ps3_connect_event_irq(const struct ps3_device_id *did, 1482832a81dSGeoff Levand unsigned int interrupt_id, unsigned int *virq) 1492832a81dSGeoff Levand { 1502832a81dSGeoff Levand int result; 1512832a81dSGeoff Levand 1522832a81dSGeoff Levand result = ps3_alloc_event_irq(virq); 1532832a81dSGeoff Levand 1542832a81dSGeoff Levand if (result) 1552832a81dSGeoff Levand return result; 1562832a81dSGeoff Levand 1572832a81dSGeoff Levand result = lv1_connect_interrupt_event_receive_port(did->bus_id, 1582832a81dSGeoff Levand did->dev_id, virq_to_hw(*virq), interrupt_id); 1592832a81dSGeoff Levand 1602832a81dSGeoff Levand if (result) { 1612832a81dSGeoff Levand pr_debug("%s:%d: lv1_connect_interrupt_event_receive_port" 1622832a81dSGeoff Levand " failed: %s\n", __func__, __LINE__, 1632832a81dSGeoff Levand ps3_result(result)); 1642832a81dSGeoff Levand ps3_free_event_irq(*virq); 1652832a81dSGeoff Levand *virq = NO_IRQ; 1662832a81dSGeoff Levand return result; 1672832a81dSGeoff Levand } 1682832a81dSGeoff Levand 1692832a81dSGeoff Levand pr_debug("%s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__, 1702832a81dSGeoff Levand interrupt_id, *virq); 1712832a81dSGeoff Levand 1722832a81dSGeoff Levand return 0; 1732832a81dSGeoff Levand } 1742832a81dSGeoff Levand 1752832a81dSGeoff Levand int ps3_disconnect_event_irq(const struct ps3_device_id *did, 1762832a81dSGeoff Levand unsigned int interrupt_id, unsigned int virq) 1772832a81dSGeoff Levand { 1782832a81dSGeoff Levand int result; 1792832a81dSGeoff Levand 1802832a81dSGeoff Levand pr_debug(" -> %s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__, 1812832a81dSGeoff Levand interrupt_id, virq); 1822832a81dSGeoff Levand 1832832a81dSGeoff Levand result = lv1_disconnect_interrupt_event_receive_port(did->bus_id, 1842832a81dSGeoff Levand did->dev_id, virq_to_hw(virq), interrupt_id); 1852832a81dSGeoff Levand 1862832a81dSGeoff Levand if (result) 1872832a81dSGeoff Levand pr_debug("%s:%d: lv1_disconnect_interrupt_event_receive_port" 1882832a81dSGeoff Levand " failed: %s\n", __func__, __LINE__, 1892832a81dSGeoff Levand ps3_result(result)); 1902832a81dSGeoff Levand 1912832a81dSGeoff Levand ps3_free_event_irq(virq); 1922832a81dSGeoff Levand 1932832a81dSGeoff Levand pr_debug(" <- %s:%d\n", __func__, __LINE__); 1942832a81dSGeoff Levand return result; 1952832a81dSGeoff Levand } 1962832a81dSGeoff Levand 1972832a81dSGeoff Levand /** 1982832a81dSGeoff Levand * ps3_alloc_vuart_irq - Configure the system virtual uart virq. 1992832a81dSGeoff Levand * @virt_addr_bmp: The caller supplied virtual uart interrupt bitmap. 2002832a81dSGeoff Levand * @virq: The assigned Linux virq. 2012832a81dSGeoff Levand * 2022832a81dSGeoff Levand * The system supports only a single virtual uart, so multiple calls without 2032832a81dSGeoff Levand * freeing the interrupt will return a wrong state error. 2042832a81dSGeoff Levand */ 2052832a81dSGeoff Levand 2062832a81dSGeoff Levand int ps3_alloc_vuart_irq(void* virt_addr_bmp, unsigned int *virq) 2072832a81dSGeoff Levand { 2082832a81dSGeoff Levand int result; 2092832a81dSGeoff Levand unsigned long outlet; 2102832a81dSGeoff Levand unsigned long lpar_addr; 2112832a81dSGeoff Levand 2122832a81dSGeoff Levand BUG_ON(!is_kernel_addr((unsigned long)virt_addr_bmp)); 2132832a81dSGeoff Levand 2142832a81dSGeoff Levand lpar_addr = ps3_mm_phys_to_lpar(__pa(virt_addr_bmp)); 2152832a81dSGeoff Levand 2162832a81dSGeoff Levand result = lv1_configure_virtual_uart_irq(lpar_addr, &outlet); 2172832a81dSGeoff Levand 2182832a81dSGeoff Levand if (result) { 2192832a81dSGeoff Levand pr_debug("%s:%d: lv1_configure_virtual_uart_irq failed: %s\n", 2202832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 2212832a81dSGeoff Levand return result; 2222832a81dSGeoff Levand } 2232832a81dSGeoff Levand 2242832a81dSGeoff Levand *virq = irq_create_mapping(NULL, outlet); 2252832a81dSGeoff Levand 2262832a81dSGeoff Levand pr_debug("%s:%d: outlet %lu, virq %u\n", __func__, __LINE__, 2272832a81dSGeoff Levand outlet, *virq); 2282832a81dSGeoff Levand 2292832a81dSGeoff Levand return 0; 2302832a81dSGeoff Levand } 2312832a81dSGeoff Levand 2322832a81dSGeoff Levand int ps3_free_vuart_irq(unsigned int virq) 2332832a81dSGeoff Levand { 2342832a81dSGeoff Levand int result; 2352832a81dSGeoff Levand 2362832a81dSGeoff Levand result = lv1_deconfigure_virtual_uart_irq(); 2372832a81dSGeoff Levand 2382832a81dSGeoff Levand if (result) { 2392832a81dSGeoff Levand pr_debug("%s:%d: lv1_configure_virtual_uart_irq failed: %s\n", 2402832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 2412832a81dSGeoff Levand return result; 2422832a81dSGeoff Levand } 2432832a81dSGeoff Levand 2442832a81dSGeoff Levand irq_dispose_mapping(virq); 2452832a81dSGeoff Levand 2462832a81dSGeoff Levand return result; 2472832a81dSGeoff Levand } 2482832a81dSGeoff Levand 2492832a81dSGeoff Levand /** 2502832a81dSGeoff Levand * ps3_alloc_spe_irq - Configure an spe virq. 2512832a81dSGeoff Levand * @spe_id: The spe_id returned from lv1_construct_logical_spe(). 2522832a81dSGeoff Levand * @class: The spe interrupt class {0,1,2}. 2532832a81dSGeoff Levand * @virq: The assigned Linux virq. 2542832a81dSGeoff Levand * 2552832a81dSGeoff Levand */ 2562832a81dSGeoff Levand 2572832a81dSGeoff Levand int ps3_alloc_spe_irq(unsigned long spe_id, unsigned int class, 2582832a81dSGeoff Levand unsigned int *virq) 2592832a81dSGeoff Levand { 2602832a81dSGeoff Levand int result; 2612832a81dSGeoff Levand unsigned long outlet; 2622832a81dSGeoff Levand 2632832a81dSGeoff Levand BUG_ON(class > 2); 2642832a81dSGeoff Levand 2652832a81dSGeoff Levand result = lv1_get_spe_irq_outlet(spe_id, class, &outlet); 2662832a81dSGeoff Levand 2672832a81dSGeoff Levand if (result) { 2682832a81dSGeoff Levand pr_debug("%s:%d: lv1_get_spe_irq_outlet failed: %s\n", 2692832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 2702832a81dSGeoff Levand return result; 2712832a81dSGeoff Levand } 2722832a81dSGeoff Levand 2732832a81dSGeoff Levand *virq = irq_create_mapping(NULL, outlet); 2742832a81dSGeoff Levand 2752832a81dSGeoff Levand pr_debug("%s:%d: spe_id %lu, class %u, outlet %lu, virq %u\n", 2762832a81dSGeoff Levand __func__, __LINE__, spe_id, class, outlet, *virq); 2772832a81dSGeoff Levand 2782832a81dSGeoff Levand return 0; 2792832a81dSGeoff Levand } 2802832a81dSGeoff Levand 2812832a81dSGeoff Levand int ps3_free_spe_irq(unsigned int virq) 2822832a81dSGeoff Levand { 2832832a81dSGeoff Levand irq_dispose_mapping(virq); 2842832a81dSGeoff Levand return 0; 2852832a81dSGeoff Levand } 2862832a81dSGeoff Levand 2872832a81dSGeoff Levand #define PS3_INVALID_OUTLET ((irq_hw_number_t)-1) 2882832a81dSGeoff Levand #define PS3_PLUG_MAX 63 2892832a81dSGeoff Levand 2902832a81dSGeoff Levand /** 2919633ac8dSGeoff Levand * struct ps3_bmp - a per cpu irq status and mask bitmap structure 2922832a81dSGeoff Levand * @status: 256 bit status bitmap indexed by plug 2932832a81dSGeoff Levand * @unused_1: 2942832a81dSGeoff Levand * @mask: 256 bit mask bitmap indexed by plug 2952832a81dSGeoff Levand * @unused_2: 2962832a81dSGeoff Levand * @lock: 2972832a81dSGeoff Levand * @ipi_debug_brk_mask: 2982832a81dSGeoff Levand * 2992832a81dSGeoff Levand * The HV mantains per SMT thread mappings of HV outlet to HV plug on 3002832a81dSGeoff Levand * behalf of the guest. These mappings are implemented as 256 bit guest 3012832a81dSGeoff Levand * supplied bitmaps indexed by plug number. The address of the bitmaps are 3022832a81dSGeoff Levand * registered with the HV through lv1_configure_irq_state_bitmap(). 3032832a81dSGeoff Levand * 3042832a81dSGeoff Levand * The HV supports 256 plugs per thread, assigned as {0..255}, for a total 3052832a81dSGeoff Levand * of 512 plugs supported on a processor. To simplify the logic this 3062832a81dSGeoff Levand * implementation equates HV plug value to linux virq value, constrains each 3072832a81dSGeoff Levand * interrupt to have a system wide unique plug number, and limits the range 3082832a81dSGeoff Levand * of the plug values to map into the first dword of the bitmaps. This 3092832a81dSGeoff Levand * gives a usable range of plug values of {NUM_ISA_INTERRUPTS..63}. Note 3102832a81dSGeoff Levand * that there is no constraint on how many in this set an individual thread 3112832a81dSGeoff Levand * can aquire. 3122832a81dSGeoff Levand */ 3132832a81dSGeoff Levand 3149633ac8dSGeoff Levand struct ps3_bmp { 3152832a81dSGeoff Levand struct { 3162832a81dSGeoff Levand unsigned long status; 3172832a81dSGeoff Levand unsigned long unused_1[3]; 3182832a81dSGeoff Levand unsigned long mask; 3192832a81dSGeoff Levand unsigned long unused_2[3]; 320*407e24a0SGeoff Levand } __attribute__ ((aligned (64))); 321*407e24a0SGeoff Levand 3222832a81dSGeoff Levand spinlock_t lock; 3232832a81dSGeoff Levand unsigned long ipi_debug_brk_mask; 3242832a81dSGeoff Levand }; 3252832a81dSGeoff Levand 3262832a81dSGeoff Levand /** 3279633ac8dSGeoff Levand * struct ps3_private - a per cpu data structure 328*407e24a0SGeoff Levand * @bmp: ps3_bmp structure 329*407e24a0SGeoff Levand * @node: HV logical_ppe_id 330*407e24a0SGeoff Levand * @cpu: HV thread_id 3312832a81dSGeoff Levand */ 3322832a81dSGeoff Levand 3339633ac8dSGeoff Levand struct ps3_private { 334*407e24a0SGeoff Levand struct ps3_bmp bmp; 3352832a81dSGeoff Levand unsigned long node; 3362832a81dSGeoff Levand unsigned int cpu; 3372832a81dSGeoff Levand }; 3382832a81dSGeoff Levand 3392832a81dSGeoff Levand #if defined(DEBUG) 3402832a81dSGeoff Levand static void _dump_64_bmp(const char *header, const unsigned long *p, unsigned cpu, 3412832a81dSGeoff Levand const char* func, int line) 3422832a81dSGeoff Levand { 3432832a81dSGeoff Levand pr_debug("%s:%d: %s %u {%04lx_%04lx_%04lx_%04lx}\n", 3442832a81dSGeoff Levand func, line, header, cpu, 3452832a81dSGeoff Levand *p >> 48, (*p >> 32) & 0xffff, (*p >> 16) & 0xffff, 3462832a81dSGeoff Levand *p & 0xffff); 3472832a81dSGeoff Levand } 3482832a81dSGeoff Levand 3492832a81dSGeoff Levand static void __attribute__ ((unused)) _dump_256_bmp(const char *header, 3502832a81dSGeoff Levand const unsigned long *p, unsigned cpu, const char* func, int line) 3512832a81dSGeoff Levand { 3522832a81dSGeoff Levand pr_debug("%s:%d: %s %u {%016lx:%016lx:%016lx:%016lx}\n", 3532832a81dSGeoff Levand func, line, header, cpu, p[0], p[1], p[2], p[3]); 3542832a81dSGeoff Levand } 3552832a81dSGeoff Levand 3562832a81dSGeoff Levand #define dump_bmp(_x) _dump_bmp(_x, __func__, __LINE__) 3579633ac8dSGeoff Levand static void _dump_bmp(struct ps3_private* pd, const char* func, int line) 3582832a81dSGeoff Levand { 3592832a81dSGeoff Levand unsigned long flags; 3602832a81dSGeoff Levand 3612832a81dSGeoff Levand spin_lock_irqsave(&pd->bmp.lock, flags); 3622832a81dSGeoff Levand _dump_64_bmp("stat", &pd->bmp.status, pd->cpu, func, line); 3632832a81dSGeoff Levand _dump_64_bmp("mask", &pd->bmp.mask, pd->cpu, func, line); 3642832a81dSGeoff Levand spin_unlock_irqrestore(&pd->bmp.lock, flags); 3652832a81dSGeoff Levand } 3662832a81dSGeoff Levand 3672832a81dSGeoff Levand #define dump_mask(_x) _dump_mask(_x, __func__, __LINE__) 3689633ac8dSGeoff Levand static void __attribute__ ((unused)) _dump_mask(struct ps3_private* pd, 3692832a81dSGeoff Levand const char* func, int line) 3702832a81dSGeoff Levand { 3712832a81dSGeoff Levand unsigned long flags; 3722832a81dSGeoff Levand 3732832a81dSGeoff Levand spin_lock_irqsave(&pd->bmp.lock, flags); 3742832a81dSGeoff Levand _dump_64_bmp("mask", &pd->bmp.mask, pd->cpu, func, line); 3752832a81dSGeoff Levand spin_unlock_irqrestore(&pd->bmp.lock, flags); 3762832a81dSGeoff Levand } 3772832a81dSGeoff Levand #else 3789633ac8dSGeoff Levand static void dump_bmp(struct ps3_private* pd) {}; 3792832a81dSGeoff Levand #endif /* defined(DEBUG) */ 3802832a81dSGeoff Levand 3819633ac8dSGeoff Levand static void ps3_chip_mask(unsigned int virq) 3822832a81dSGeoff Levand { 3832832a81dSGeoff Levand unsigned long flags; 3849633ac8dSGeoff Levand struct ps3_private *pd = get_irq_chip_data(virq); 3852832a81dSGeoff Levand 3862832a81dSGeoff Levand pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq); 3872832a81dSGeoff Levand 3882832a81dSGeoff Levand BUG_ON(virq < NUM_ISA_INTERRUPTS); 3892832a81dSGeoff Levand BUG_ON(virq > PS3_PLUG_MAX); 3902832a81dSGeoff Levand 3912832a81dSGeoff Levand spin_lock_irqsave(&pd->bmp.lock, flags); 3922832a81dSGeoff Levand pd->bmp.mask &= ~(0x8000000000000000UL >> virq); 3932832a81dSGeoff Levand lv1_did_update_interrupt_mask(pd->node, pd->cpu); 394*407e24a0SGeoff Levand spin_unlock_irqrestore(&pd->bmp.lock, flags); 3952832a81dSGeoff Levand } 3962832a81dSGeoff Levand 3979633ac8dSGeoff Levand static void ps3_chip_unmask(unsigned int virq) 3982832a81dSGeoff Levand { 3992832a81dSGeoff Levand unsigned long flags; 4009633ac8dSGeoff Levand struct ps3_private *pd = get_irq_chip_data(virq); 4012832a81dSGeoff Levand 4022832a81dSGeoff Levand pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq); 4032832a81dSGeoff Levand 4042832a81dSGeoff Levand BUG_ON(virq < NUM_ISA_INTERRUPTS); 4052832a81dSGeoff Levand BUG_ON(virq > PS3_PLUG_MAX); 4062832a81dSGeoff Levand 4072832a81dSGeoff Levand spin_lock_irqsave(&pd->bmp.lock, flags); 4082832a81dSGeoff Levand pd->bmp.mask |= (0x8000000000000000UL >> virq); 4092832a81dSGeoff Levand lv1_did_update_interrupt_mask(pd->node, pd->cpu); 410*407e24a0SGeoff Levand spin_unlock_irqrestore(&pd->bmp.lock, flags); 4112832a81dSGeoff Levand } 4122832a81dSGeoff Levand 4139633ac8dSGeoff Levand static void ps3_chip_eoi(unsigned int virq) 4142832a81dSGeoff Levand { 415*407e24a0SGeoff Levand const struct ps3_private *pd = get_irq_chip_data(virq); 416*407e24a0SGeoff Levand lv1_end_of_interrupt_ext(pd->node, pd->cpu, virq); 4172832a81dSGeoff Levand } 4182832a81dSGeoff Levand 4192832a81dSGeoff Levand static struct irq_chip irq_chip = { 4202832a81dSGeoff Levand .typename = "ps3", 4219633ac8dSGeoff Levand .mask = ps3_chip_mask, 4229633ac8dSGeoff Levand .unmask = ps3_chip_unmask, 4239633ac8dSGeoff Levand .eoi = ps3_chip_eoi, 4242832a81dSGeoff Levand }; 4252832a81dSGeoff Levand 4269633ac8dSGeoff Levand static void ps3_host_unmap(struct irq_host *h, unsigned int virq) 4272832a81dSGeoff Levand { 4282832a81dSGeoff Levand int result; 429*407e24a0SGeoff Levand const struct ps3_private *pd = get_irq_chip_data(virq); 4302832a81dSGeoff Levand 431*407e24a0SGeoff Levand pr_debug("%s:%d: node %lu, cpu %d, virq %u\n", __func__, __LINE__, 432*407e24a0SGeoff Levand pd->node, pd->cpu, virq); 4332832a81dSGeoff Levand 434*407e24a0SGeoff Levand lv1_disconnect_irq_plug_ext(pd->node, pd->cpu, virq); 4352832a81dSGeoff Levand 4362832a81dSGeoff Levand result = set_irq_chip_data(virq, NULL); 4372832a81dSGeoff Levand BUG_ON(result); 4382832a81dSGeoff Levand } 4392832a81dSGeoff Levand 4409633ac8dSGeoff Levand static DEFINE_PER_CPU(struct ps3_private, ps3_private); 4412832a81dSGeoff Levand 4429633ac8dSGeoff Levand static int ps3_host_map(struct irq_host *h, unsigned int virq, 4432832a81dSGeoff Levand irq_hw_number_t hwirq) 4442832a81dSGeoff Levand { 4452832a81dSGeoff Levand int result; 446*407e24a0SGeoff Levand struct ps3_private *pd = &__get_cpu_var(ps3_private); 4472832a81dSGeoff Levand 448*407e24a0SGeoff Levand pr_debug("%s:%d: node %lu, cpu %d, hwirq %lu => virq %u\n", __func__, 449*407e24a0SGeoff Levand __LINE__, pd->node, pd->cpu, hwirq, virq); 4502832a81dSGeoff Levand 451*407e24a0SGeoff Levand /* Binds this virq to pd->cpu (current cpu) */ 4522832a81dSGeoff Levand 453*407e24a0SGeoff Levand result = lv1_connect_irq_plug_ext(pd->node, pd->cpu, virq, hwirq, 0); 4542832a81dSGeoff Levand 4552832a81dSGeoff Levand if (result) { 456*407e24a0SGeoff Levand pr_info("%s:%d: lv1_connect_irq_plug_ext failed:" 4572832a81dSGeoff Levand " %s\n", __func__, __LINE__, ps3_result(result)); 4582832a81dSGeoff Levand return -EPERM; 4592832a81dSGeoff Levand } 4602832a81dSGeoff Levand 461*407e24a0SGeoff Levand result = set_irq_chip_data(virq, pd); 4622832a81dSGeoff Levand BUG_ON(result); 4632832a81dSGeoff Levand 4642832a81dSGeoff Levand set_irq_chip_and_handler(virq, &irq_chip, handle_fasteoi_irq); 4652832a81dSGeoff Levand 4662832a81dSGeoff Levand return result; 4672832a81dSGeoff Levand } 4682832a81dSGeoff Levand 4699633ac8dSGeoff Levand static struct irq_host_ops ps3_host_ops = { 4709633ac8dSGeoff Levand .map = ps3_host_map, 4719633ac8dSGeoff Levand .unmap = ps3_host_unmap, 4722832a81dSGeoff Levand }; 4732832a81dSGeoff Levand 4742832a81dSGeoff Levand void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq) 4752832a81dSGeoff Levand { 4769633ac8dSGeoff Levand struct ps3_private *pd = &per_cpu(ps3_private, cpu); 4772832a81dSGeoff Levand 4782832a81dSGeoff Levand pd->bmp.ipi_debug_brk_mask = 0x8000000000000000UL >> virq; 4792832a81dSGeoff Levand 4802832a81dSGeoff Levand pr_debug("%s:%d: cpu %u, virq %u, mask %lxh\n", __func__, __LINE__, 4812832a81dSGeoff Levand cpu, virq, pd->bmp.ipi_debug_brk_mask); 4822832a81dSGeoff Levand } 4832832a81dSGeoff Levand 4849633ac8dSGeoff Levand static int bmp_get_and_clear_status_bit(struct ps3_bmp *m) 4852832a81dSGeoff Levand { 4862832a81dSGeoff Levand unsigned long flags; 4872832a81dSGeoff Levand unsigned int bit; 4882832a81dSGeoff Levand unsigned long x; 4892832a81dSGeoff Levand 4902832a81dSGeoff Levand spin_lock_irqsave(&m->lock, flags); 4912832a81dSGeoff Levand 4922832a81dSGeoff Levand /* check for ipi break first to stop this cpu ASAP */ 4932832a81dSGeoff Levand 4942832a81dSGeoff Levand if (m->status & m->ipi_debug_brk_mask) { 4952832a81dSGeoff Levand m->status &= ~m->ipi_debug_brk_mask; 4962832a81dSGeoff Levand spin_unlock_irqrestore(&m->lock, flags); 4972832a81dSGeoff Levand return __ilog2(m->ipi_debug_brk_mask); 4982832a81dSGeoff Levand } 4992832a81dSGeoff Levand 5002832a81dSGeoff Levand x = (m->status & m->mask); 5012832a81dSGeoff Levand 5022832a81dSGeoff Levand for (bit = NUM_ISA_INTERRUPTS, x <<= bit; x; bit++, x <<= 1) 5032832a81dSGeoff Levand if (x & 0x8000000000000000UL) { 5042832a81dSGeoff Levand m->status &= ~(0x8000000000000000UL >> bit); 5052832a81dSGeoff Levand spin_unlock_irqrestore(&m->lock, flags); 5062832a81dSGeoff Levand return bit; 5072832a81dSGeoff Levand } 5082832a81dSGeoff Levand 5092832a81dSGeoff Levand spin_unlock_irqrestore(&m->lock, flags); 5102832a81dSGeoff Levand 5112832a81dSGeoff Levand pr_debug("%s:%d: not found\n", __func__, __LINE__); 5122832a81dSGeoff Levand return -1; 5132832a81dSGeoff Levand } 5142832a81dSGeoff Levand 5152832a81dSGeoff Levand unsigned int ps3_get_irq(void) 5162832a81dSGeoff Levand { 5172832a81dSGeoff Levand int plug; 5182832a81dSGeoff Levand 5199633ac8dSGeoff Levand struct ps3_private *pd = &__get_cpu_var(ps3_private); 5202832a81dSGeoff Levand 5212832a81dSGeoff Levand plug = bmp_get_and_clear_status_bit(&pd->bmp); 5222832a81dSGeoff Levand 5232832a81dSGeoff Levand if (plug < 1) { 5242832a81dSGeoff Levand pr_debug("%s:%d: no plug found: cpu %u\n", __func__, __LINE__, 5252832a81dSGeoff Levand pd->cpu); 5269633ac8dSGeoff Levand dump_bmp(&per_cpu(ps3_private, 0)); 5279633ac8dSGeoff Levand dump_bmp(&per_cpu(ps3_private, 1)); 5282832a81dSGeoff Levand return NO_IRQ; 5292832a81dSGeoff Levand } 5302832a81dSGeoff Levand 5312832a81dSGeoff Levand #if defined(DEBUG) 5322832a81dSGeoff Levand if (plug < NUM_ISA_INTERRUPTS || plug > PS3_PLUG_MAX) { 5339633ac8dSGeoff Levand dump_bmp(&per_cpu(ps3_private, 0)); 5349633ac8dSGeoff Levand dump_bmp(&per_cpu(ps3_private, 1)); 5352832a81dSGeoff Levand BUG(); 5362832a81dSGeoff Levand } 5372832a81dSGeoff Levand #endif 5382832a81dSGeoff Levand return plug; 5392832a81dSGeoff Levand } 5402832a81dSGeoff Levand 5412832a81dSGeoff Levand void __init ps3_init_IRQ(void) 5422832a81dSGeoff Levand { 5432832a81dSGeoff Levand int result; 5442832a81dSGeoff Levand unsigned cpu; 5452832a81dSGeoff Levand struct irq_host *host; 5462832a81dSGeoff Levand 5479633ac8dSGeoff Levand host = irq_alloc_host(IRQ_HOST_MAP_NOMAP, 0, &ps3_host_ops, 5482832a81dSGeoff Levand PS3_INVALID_OUTLET); 5492832a81dSGeoff Levand irq_set_default_host(host); 5502832a81dSGeoff Levand irq_set_virq_count(PS3_PLUG_MAX + 1); 5512832a81dSGeoff Levand 5522832a81dSGeoff Levand for_each_possible_cpu(cpu) { 5539633ac8dSGeoff Levand struct ps3_private *pd = &per_cpu(ps3_private, cpu); 5542832a81dSGeoff Levand 555*407e24a0SGeoff Levand lv1_get_logical_ppe_id(&pd->node); 556*407e24a0SGeoff Levand pd->cpu = get_hard_smp_processor_id(cpu); 5572832a81dSGeoff Levand spin_lock_init(&pd->bmp.lock); 5582832a81dSGeoff Levand 559*407e24a0SGeoff Levand pr_debug("%s:%d: node %lu, cpu %d, bmp %lxh\n", __func__, 560*407e24a0SGeoff Levand __LINE__, pd->node, pd->cpu, 561*407e24a0SGeoff Levand ps3_mm_phys_to_lpar(__pa(&pd->bmp))); 562*407e24a0SGeoff Levand 563*407e24a0SGeoff Levand result = lv1_configure_irq_state_bitmap(pd->node, pd->cpu, 564*407e24a0SGeoff Levand ps3_mm_phys_to_lpar(__pa(&pd->bmp))); 5652832a81dSGeoff Levand 5662832a81dSGeoff Levand if (result) 5672832a81dSGeoff Levand pr_debug("%s:%d: lv1_configure_irq_state_bitmap failed:" 5682832a81dSGeoff Levand " %s\n", __func__, __LINE__, 5692832a81dSGeoff Levand ps3_result(result)); 5702832a81dSGeoff Levand } 5712832a81dSGeoff Levand 5722832a81dSGeoff Levand ppc_md.get_irq = ps3_get_irq; 5732832a81dSGeoff Levand } 574