1*2832a81dSGeoff Levand /* 2*2832a81dSGeoff Levand * PS3 interrupt routines. 3*2832a81dSGeoff Levand * 4*2832a81dSGeoff Levand * Copyright (C) 2006 Sony Computer Entertainment Inc. 5*2832a81dSGeoff Levand * Copyright 2006 Sony Corp. 6*2832a81dSGeoff Levand * 7*2832a81dSGeoff Levand * This program is free software; you can redistribute it and/or modify 8*2832a81dSGeoff Levand * it under the terms of the GNU General Public License as published by 9*2832a81dSGeoff Levand * the Free Software Foundation; version 2 of the License. 10*2832a81dSGeoff Levand * 11*2832a81dSGeoff Levand * This program is distributed in the hope that it will be useful, 12*2832a81dSGeoff Levand * but WITHOUT ANY WARRANTY; without even the implied warranty of 13*2832a81dSGeoff Levand * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14*2832a81dSGeoff Levand * GNU General Public License for more details. 15*2832a81dSGeoff Levand * 16*2832a81dSGeoff Levand * You should have received a copy of the GNU General Public License 17*2832a81dSGeoff Levand * along with this program; if not, write to the Free Software 18*2832a81dSGeoff Levand * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19*2832a81dSGeoff Levand */ 20*2832a81dSGeoff Levand 21*2832a81dSGeoff Levand #include <linux/kernel.h> 22*2832a81dSGeoff Levand #include <linux/module.h> 23*2832a81dSGeoff Levand #include <linux/irq.h> 24*2832a81dSGeoff Levand 25*2832a81dSGeoff Levand #include <asm/machdep.h> 26*2832a81dSGeoff Levand #include <asm/udbg.h> 27*2832a81dSGeoff Levand #include <asm/ps3.h> 28*2832a81dSGeoff Levand #include <asm/lv1call.h> 29*2832a81dSGeoff Levand 30*2832a81dSGeoff Levand #include "platform.h" 31*2832a81dSGeoff Levand 32*2832a81dSGeoff Levand #if defined(DEBUG) 33*2832a81dSGeoff Levand #define DBG(fmt...) udbg_printf(fmt) 34*2832a81dSGeoff Levand #else 35*2832a81dSGeoff Levand #define DBG(fmt...) do{if(0)printk(fmt);}while(0) 36*2832a81dSGeoff Levand #endif 37*2832a81dSGeoff Levand 38*2832a81dSGeoff Levand /** 39*2832a81dSGeoff Levand * ps3_alloc_io_irq - Assign a virq to a system bus device. 40*2832a81dSGeoff Levand * interrupt_id: The device interrupt id read from the system repository. 41*2832a81dSGeoff Levand * @virq: The assigned Linux virq. 42*2832a81dSGeoff Levand * 43*2832a81dSGeoff Levand * An io irq represents a non-virtualized device interrupt. interrupt_id 44*2832a81dSGeoff Levand * coresponds to the interrupt number of the interrupt controller. 45*2832a81dSGeoff Levand */ 46*2832a81dSGeoff Levand 47*2832a81dSGeoff Levand int ps3_alloc_io_irq(unsigned int interrupt_id, unsigned int *virq) 48*2832a81dSGeoff Levand { 49*2832a81dSGeoff Levand int result; 50*2832a81dSGeoff Levand unsigned long outlet; 51*2832a81dSGeoff Levand 52*2832a81dSGeoff Levand result = lv1_construct_io_irq_outlet(interrupt_id, &outlet); 53*2832a81dSGeoff Levand 54*2832a81dSGeoff Levand if (result) { 55*2832a81dSGeoff Levand pr_debug("%s:%d: lv1_construct_io_irq_outlet failed: %s\n", 56*2832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 57*2832a81dSGeoff Levand return result; 58*2832a81dSGeoff Levand } 59*2832a81dSGeoff Levand 60*2832a81dSGeoff Levand *virq = irq_create_mapping(NULL, outlet); 61*2832a81dSGeoff Levand 62*2832a81dSGeoff Levand pr_debug("%s:%d: interrupt_id %u => outlet %lu, virq %u\n", 63*2832a81dSGeoff Levand __func__, __LINE__, interrupt_id, outlet, *virq); 64*2832a81dSGeoff Levand 65*2832a81dSGeoff Levand return 0; 66*2832a81dSGeoff Levand } 67*2832a81dSGeoff Levand 68*2832a81dSGeoff Levand int ps3_free_io_irq(unsigned int virq) 69*2832a81dSGeoff Levand { 70*2832a81dSGeoff Levand int result; 71*2832a81dSGeoff Levand 72*2832a81dSGeoff Levand result = lv1_destruct_io_irq_outlet(virq_to_hw(virq)); 73*2832a81dSGeoff Levand 74*2832a81dSGeoff Levand if (!result) 75*2832a81dSGeoff Levand pr_debug("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n", 76*2832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 77*2832a81dSGeoff Levand 78*2832a81dSGeoff Levand irq_dispose_mapping(virq); 79*2832a81dSGeoff Levand 80*2832a81dSGeoff Levand return result; 81*2832a81dSGeoff Levand } 82*2832a81dSGeoff Levand 83*2832a81dSGeoff Levand /** 84*2832a81dSGeoff Levand * ps3_alloc_event_irq - Allocate a virq for use with a system event. 85*2832a81dSGeoff Levand * @virq: The assigned Linux virq. 86*2832a81dSGeoff Levand * 87*2832a81dSGeoff Levand * The virq can be used with lv1_connect_interrupt_event_receive_port() to 88*2832a81dSGeoff Levand * arrange to receive events, or with ps3_send_event_locally() to signal 89*2832a81dSGeoff Levand * events. 90*2832a81dSGeoff Levand */ 91*2832a81dSGeoff Levand 92*2832a81dSGeoff Levand int ps3_alloc_event_irq(unsigned int *virq) 93*2832a81dSGeoff Levand { 94*2832a81dSGeoff Levand int result; 95*2832a81dSGeoff Levand unsigned long outlet; 96*2832a81dSGeoff Levand 97*2832a81dSGeoff Levand result = lv1_construct_event_receive_port(&outlet); 98*2832a81dSGeoff Levand 99*2832a81dSGeoff Levand if (result) { 100*2832a81dSGeoff Levand pr_debug("%s:%d: lv1_construct_event_receive_port failed: %s\n", 101*2832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 102*2832a81dSGeoff Levand *virq = NO_IRQ; 103*2832a81dSGeoff Levand return result; 104*2832a81dSGeoff Levand } 105*2832a81dSGeoff Levand 106*2832a81dSGeoff Levand *virq = irq_create_mapping(NULL, outlet); 107*2832a81dSGeoff Levand 108*2832a81dSGeoff Levand pr_debug("%s:%d: outlet %lu, virq %u\n", __func__, __LINE__, outlet, 109*2832a81dSGeoff Levand *virq); 110*2832a81dSGeoff Levand 111*2832a81dSGeoff Levand return 0; 112*2832a81dSGeoff Levand } 113*2832a81dSGeoff Levand 114*2832a81dSGeoff Levand int ps3_free_event_irq(unsigned int virq) 115*2832a81dSGeoff Levand { 116*2832a81dSGeoff Levand int result; 117*2832a81dSGeoff Levand 118*2832a81dSGeoff Levand pr_debug(" -> %s:%d\n", __func__, __LINE__); 119*2832a81dSGeoff Levand 120*2832a81dSGeoff Levand result = lv1_destruct_event_receive_port(virq_to_hw(virq)); 121*2832a81dSGeoff Levand 122*2832a81dSGeoff Levand if (result) 123*2832a81dSGeoff Levand pr_debug("%s:%d: lv1_destruct_event_receive_port failed: %s\n", 124*2832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 125*2832a81dSGeoff Levand 126*2832a81dSGeoff Levand irq_dispose_mapping(virq); 127*2832a81dSGeoff Levand 128*2832a81dSGeoff Levand pr_debug(" <- %s:%d\n", __func__, __LINE__); 129*2832a81dSGeoff Levand return result; 130*2832a81dSGeoff Levand } 131*2832a81dSGeoff Levand 132*2832a81dSGeoff Levand int ps3_send_event_locally(unsigned int virq) 133*2832a81dSGeoff Levand { 134*2832a81dSGeoff Levand return lv1_send_event_locally(virq_to_hw(virq)); 135*2832a81dSGeoff Levand } 136*2832a81dSGeoff Levand 137*2832a81dSGeoff Levand /** 138*2832a81dSGeoff Levand * ps3_connect_event_irq - Assign a virq to a system bus device. 139*2832a81dSGeoff Levand * @did: The HV device identifier read from the system repository. 140*2832a81dSGeoff Levand * @interrupt_id: The device interrupt id read from the system repository. 141*2832a81dSGeoff Levand * @virq: The assigned Linux virq. 142*2832a81dSGeoff Levand * 143*2832a81dSGeoff Levand * An event irq represents a virtual device interrupt. The interrupt_id 144*2832a81dSGeoff Levand * coresponds to the software interrupt number. 145*2832a81dSGeoff Levand */ 146*2832a81dSGeoff Levand 147*2832a81dSGeoff Levand int ps3_connect_event_irq(const struct ps3_device_id *did, 148*2832a81dSGeoff Levand unsigned int interrupt_id, unsigned int *virq) 149*2832a81dSGeoff Levand { 150*2832a81dSGeoff Levand int result; 151*2832a81dSGeoff Levand 152*2832a81dSGeoff Levand result = ps3_alloc_event_irq(virq); 153*2832a81dSGeoff Levand 154*2832a81dSGeoff Levand if (result) 155*2832a81dSGeoff Levand return result; 156*2832a81dSGeoff Levand 157*2832a81dSGeoff Levand result = lv1_connect_interrupt_event_receive_port(did->bus_id, 158*2832a81dSGeoff Levand did->dev_id, virq_to_hw(*virq), interrupt_id); 159*2832a81dSGeoff Levand 160*2832a81dSGeoff Levand if (result) { 161*2832a81dSGeoff Levand pr_debug("%s:%d: lv1_connect_interrupt_event_receive_port" 162*2832a81dSGeoff Levand " failed: %s\n", __func__, __LINE__, 163*2832a81dSGeoff Levand ps3_result(result)); 164*2832a81dSGeoff Levand ps3_free_event_irq(*virq); 165*2832a81dSGeoff Levand *virq = NO_IRQ; 166*2832a81dSGeoff Levand return result; 167*2832a81dSGeoff Levand } 168*2832a81dSGeoff Levand 169*2832a81dSGeoff Levand pr_debug("%s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__, 170*2832a81dSGeoff Levand interrupt_id, *virq); 171*2832a81dSGeoff Levand 172*2832a81dSGeoff Levand return 0; 173*2832a81dSGeoff Levand } 174*2832a81dSGeoff Levand 175*2832a81dSGeoff Levand int ps3_disconnect_event_irq(const struct ps3_device_id *did, 176*2832a81dSGeoff Levand unsigned int interrupt_id, unsigned int virq) 177*2832a81dSGeoff Levand { 178*2832a81dSGeoff Levand int result; 179*2832a81dSGeoff Levand 180*2832a81dSGeoff Levand pr_debug(" -> %s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__, 181*2832a81dSGeoff Levand interrupt_id, virq); 182*2832a81dSGeoff Levand 183*2832a81dSGeoff Levand result = lv1_disconnect_interrupt_event_receive_port(did->bus_id, 184*2832a81dSGeoff Levand did->dev_id, virq_to_hw(virq), interrupt_id); 185*2832a81dSGeoff Levand 186*2832a81dSGeoff Levand if (result) 187*2832a81dSGeoff Levand pr_debug("%s:%d: lv1_disconnect_interrupt_event_receive_port" 188*2832a81dSGeoff Levand " failed: %s\n", __func__, __LINE__, 189*2832a81dSGeoff Levand ps3_result(result)); 190*2832a81dSGeoff Levand 191*2832a81dSGeoff Levand ps3_free_event_irq(virq); 192*2832a81dSGeoff Levand 193*2832a81dSGeoff Levand pr_debug(" <- %s:%d\n", __func__, __LINE__); 194*2832a81dSGeoff Levand return result; 195*2832a81dSGeoff Levand } 196*2832a81dSGeoff Levand 197*2832a81dSGeoff Levand /** 198*2832a81dSGeoff Levand * ps3_alloc_vuart_irq - Configure the system virtual uart virq. 199*2832a81dSGeoff Levand * @virt_addr_bmp: The caller supplied virtual uart interrupt bitmap. 200*2832a81dSGeoff Levand * @virq: The assigned Linux virq. 201*2832a81dSGeoff Levand * 202*2832a81dSGeoff Levand * The system supports only a single virtual uart, so multiple calls without 203*2832a81dSGeoff Levand * freeing the interrupt will return a wrong state error. 204*2832a81dSGeoff Levand */ 205*2832a81dSGeoff Levand 206*2832a81dSGeoff Levand int ps3_alloc_vuart_irq(void* virt_addr_bmp, unsigned int *virq) 207*2832a81dSGeoff Levand { 208*2832a81dSGeoff Levand int result; 209*2832a81dSGeoff Levand unsigned long outlet; 210*2832a81dSGeoff Levand unsigned long lpar_addr; 211*2832a81dSGeoff Levand 212*2832a81dSGeoff Levand BUG_ON(!is_kernel_addr((unsigned long)virt_addr_bmp)); 213*2832a81dSGeoff Levand 214*2832a81dSGeoff Levand lpar_addr = ps3_mm_phys_to_lpar(__pa(virt_addr_bmp)); 215*2832a81dSGeoff Levand 216*2832a81dSGeoff Levand result = lv1_configure_virtual_uart_irq(lpar_addr, &outlet); 217*2832a81dSGeoff Levand 218*2832a81dSGeoff Levand if (result) { 219*2832a81dSGeoff Levand pr_debug("%s:%d: lv1_configure_virtual_uart_irq failed: %s\n", 220*2832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 221*2832a81dSGeoff Levand return result; 222*2832a81dSGeoff Levand } 223*2832a81dSGeoff Levand 224*2832a81dSGeoff Levand *virq = irq_create_mapping(NULL, outlet); 225*2832a81dSGeoff Levand 226*2832a81dSGeoff Levand pr_debug("%s:%d: outlet %lu, virq %u\n", __func__, __LINE__, 227*2832a81dSGeoff Levand outlet, *virq); 228*2832a81dSGeoff Levand 229*2832a81dSGeoff Levand return 0; 230*2832a81dSGeoff Levand } 231*2832a81dSGeoff Levand 232*2832a81dSGeoff Levand int ps3_free_vuart_irq(unsigned int virq) 233*2832a81dSGeoff Levand { 234*2832a81dSGeoff Levand int result; 235*2832a81dSGeoff Levand 236*2832a81dSGeoff Levand result = lv1_deconfigure_virtual_uart_irq(); 237*2832a81dSGeoff Levand 238*2832a81dSGeoff Levand if (result) { 239*2832a81dSGeoff Levand pr_debug("%s:%d: lv1_configure_virtual_uart_irq failed: %s\n", 240*2832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 241*2832a81dSGeoff Levand return result; 242*2832a81dSGeoff Levand } 243*2832a81dSGeoff Levand 244*2832a81dSGeoff Levand irq_dispose_mapping(virq); 245*2832a81dSGeoff Levand 246*2832a81dSGeoff Levand return result; 247*2832a81dSGeoff Levand } 248*2832a81dSGeoff Levand 249*2832a81dSGeoff Levand /** 250*2832a81dSGeoff Levand * ps3_alloc_spe_irq - Configure an spe virq. 251*2832a81dSGeoff Levand * @spe_id: The spe_id returned from lv1_construct_logical_spe(). 252*2832a81dSGeoff Levand * @class: The spe interrupt class {0,1,2}. 253*2832a81dSGeoff Levand * @virq: The assigned Linux virq. 254*2832a81dSGeoff Levand * 255*2832a81dSGeoff Levand */ 256*2832a81dSGeoff Levand 257*2832a81dSGeoff Levand int ps3_alloc_spe_irq(unsigned long spe_id, unsigned int class, 258*2832a81dSGeoff Levand unsigned int *virq) 259*2832a81dSGeoff Levand { 260*2832a81dSGeoff Levand int result; 261*2832a81dSGeoff Levand unsigned long outlet; 262*2832a81dSGeoff Levand 263*2832a81dSGeoff Levand BUG_ON(class > 2); 264*2832a81dSGeoff Levand 265*2832a81dSGeoff Levand result = lv1_get_spe_irq_outlet(spe_id, class, &outlet); 266*2832a81dSGeoff Levand 267*2832a81dSGeoff Levand if (result) { 268*2832a81dSGeoff Levand pr_debug("%s:%d: lv1_get_spe_irq_outlet failed: %s\n", 269*2832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 270*2832a81dSGeoff Levand return result; 271*2832a81dSGeoff Levand } 272*2832a81dSGeoff Levand 273*2832a81dSGeoff Levand *virq = irq_create_mapping(NULL, outlet); 274*2832a81dSGeoff Levand 275*2832a81dSGeoff Levand pr_debug("%s:%d: spe_id %lu, class %u, outlet %lu, virq %u\n", 276*2832a81dSGeoff Levand __func__, __LINE__, spe_id, class, outlet, *virq); 277*2832a81dSGeoff Levand 278*2832a81dSGeoff Levand return 0; 279*2832a81dSGeoff Levand } 280*2832a81dSGeoff Levand 281*2832a81dSGeoff Levand int ps3_free_spe_irq(unsigned int virq) 282*2832a81dSGeoff Levand { 283*2832a81dSGeoff Levand irq_dispose_mapping(virq); 284*2832a81dSGeoff Levand return 0; 285*2832a81dSGeoff Levand } 286*2832a81dSGeoff Levand 287*2832a81dSGeoff Levand #define PS3_INVALID_OUTLET ((irq_hw_number_t)-1) 288*2832a81dSGeoff Levand #define PS3_PLUG_MAX 63 289*2832a81dSGeoff Levand 290*2832a81dSGeoff Levand /** 291*2832a81dSGeoff Levand * struct bmp - a per cpu irq status and mask bitmap structure 292*2832a81dSGeoff Levand * @status: 256 bit status bitmap indexed by plug 293*2832a81dSGeoff Levand * @unused_1: 294*2832a81dSGeoff Levand * @mask: 256 bit mask bitmap indexed by plug 295*2832a81dSGeoff Levand * @unused_2: 296*2832a81dSGeoff Levand * @lock: 297*2832a81dSGeoff Levand * @ipi_debug_brk_mask: 298*2832a81dSGeoff Levand * 299*2832a81dSGeoff Levand * The HV mantains per SMT thread mappings of HV outlet to HV plug on 300*2832a81dSGeoff Levand * behalf of the guest. These mappings are implemented as 256 bit guest 301*2832a81dSGeoff Levand * supplied bitmaps indexed by plug number. The address of the bitmaps are 302*2832a81dSGeoff Levand * registered with the HV through lv1_configure_irq_state_bitmap(). 303*2832a81dSGeoff Levand * 304*2832a81dSGeoff Levand * The HV supports 256 plugs per thread, assigned as {0..255}, for a total 305*2832a81dSGeoff Levand * of 512 plugs supported on a processor. To simplify the logic this 306*2832a81dSGeoff Levand * implementation equates HV plug value to linux virq value, constrains each 307*2832a81dSGeoff Levand * interrupt to have a system wide unique plug number, and limits the range 308*2832a81dSGeoff Levand * of the plug values to map into the first dword of the bitmaps. This 309*2832a81dSGeoff Levand * gives a usable range of plug values of {NUM_ISA_INTERRUPTS..63}. Note 310*2832a81dSGeoff Levand * that there is no constraint on how many in this set an individual thread 311*2832a81dSGeoff Levand * can aquire. 312*2832a81dSGeoff Levand */ 313*2832a81dSGeoff Levand 314*2832a81dSGeoff Levand struct bmp { 315*2832a81dSGeoff Levand struct { 316*2832a81dSGeoff Levand unsigned long status; 317*2832a81dSGeoff Levand unsigned long unused_1[3]; 318*2832a81dSGeoff Levand unsigned long mask; 319*2832a81dSGeoff Levand unsigned long unused_2[3]; 320*2832a81dSGeoff Levand } __attribute__ ((packed)); 321*2832a81dSGeoff Levand spinlock_t lock; 322*2832a81dSGeoff Levand unsigned long ipi_debug_brk_mask; 323*2832a81dSGeoff Levand }; 324*2832a81dSGeoff Levand 325*2832a81dSGeoff Levand /** 326*2832a81dSGeoff Levand * struct private - a per cpu data structure 327*2832a81dSGeoff Levand * @node: HV node id 328*2832a81dSGeoff Levand * @cpu: HV thread id 329*2832a81dSGeoff Levand * @bmp: an HV bmp structure 330*2832a81dSGeoff Levand */ 331*2832a81dSGeoff Levand 332*2832a81dSGeoff Levand struct private { 333*2832a81dSGeoff Levand unsigned long node; 334*2832a81dSGeoff Levand unsigned int cpu; 335*2832a81dSGeoff Levand struct bmp bmp; 336*2832a81dSGeoff Levand }; 337*2832a81dSGeoff Levand 338*2832a81dSGeoff Levand #if defined(DEBUG) 339*2832a81dSGeoff Levand static void _dump_64_bmp(const char *header, const unsigned long *p, unsigned cpu, 340*2832a81dSGeoff Levand const char* func, int line) 341*2832a81dSGeoff Levand { 342*2832a81dSGeoff Levand pr_debug("%s:%d: %s %u {%04lx_%04lx_%04lx_%04lx}\n", 343*2832a81dSGeoff Levand func, line, header, cpu, 344*2832a81dSGeoff Levand *p >> 48, (*p >> 32) & 0xffff, (*p >> 16) & 0xffff, 345*2832a81dSGeoff Levand *p & 0xffff); 346*2832a81dSGeoff Levand } 347*2832a81dSGeoff Levand 348*2832a81dSGeoff Levand static void __attribute__ ((unused)) _dump_256_bmp(const char *header, 349*2832a81dSGeoff Levand const unsigned long *p, unsigned cpu, const char* func, int line) 350*2832a81dSGeoff Levand { 351*2832a81dSGeoff Levand pr_debug("%s:%d: %s %u {%016lx:%016lx:%016lx:%016lx}\n", 352*2832a81dSGeoff Levand func, line, header, cpu, p[0], p[1], p[2], p[3]); 353*2832a81dSGeoff Levand } 354*2832a81dSGeoff Levand 355*2832a81dSGeoff Levand #define dump_bmp(_x) _dump_bmp(_x, __func__, __LINE__) 356*2832a81dSGeoff Levand static void _dump_bmp(struct private* pd, const char* func, int line) 357*2832a81dSGeoff Levand { 358*2832a81dSGeoff Levand unsigned long flags; 359*2832a81dSGeoff Levand 360*2832a81dSGeoff Levand spin_lock_irqsave(&pd->bmp.lock, flags); 361*2832a81dSGeoff Levand _dump_64_bmp("stat", &pd->bmp.status, pd->cpu, func, line); 362*2832a81dSGeoff Levand _dump_64_bmp("mask", &pd->bmp.mask, pd->cpu, func, line); 363*2832a81dSGeoff Levand spin_unlock_irqrestore(&pd->bmp.lock, flags); 364*2832a81dSGeoff Levand } 365*2832a81dSGeoff Levand 366*2832a81dSGeoff Levand #define dump_mask(_x) _dump_mask(_x, __func__, __LINE__) 367*2832a81dSGeoff Levand static void __attribute__ ((unused)) _dump_mask(struct private* pd, 368*2832a81dSGeoff Levand const char* func, int line) 369*2832a81dSGeoff Levand { 370*2832a81dSGeoff Levand unsigned long flags; 371*2832a81dSGeoff Levand 372*2832a81dSGeoff Levand spin_lock_irqsave(&pd->bmp.lock, flags); 373*2832a81dSGeoff Levand _dump_64_bmp("mask", &pd->bmp.mask, pd->cpu, func, line); 374*2832a81dSGeoff Levand spin_unlock_irqrestore(&pd->bmp.lock, flags); 375*2832a81dSGeoff Levand } 376*2832a81dSGeoff Levand #else 377*2832a81dSGeoff Levand static void dump_bmp(struct private* pd) {}; 378*2832a81dSGeoff Levand #endif /* defined(DEBUG) */ 379*2832a81dSGeoff Levand 380*2832a81dSGeoff Levand static void chip_mask(unsigned int virq) 381*2832a81dSGeoff Levand { 382*2832a81dSGeoff Levand unsigned long flags; 383*2832a81dSGeoff Levand struct private *pd = get_irq_chip_data(virq); 384*2832a81dSGeoff Levand 385*2832a81dSGeoff Levand pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq); 386*2832a81dSGeoff Levand 387*2832a81dSGeoff Levand BUG_ON(virq < NUM_ISA_INTERRUPTS); 388*2832a81dSGeoff Levand BUG_ON(virq > PS3_PLUG_MAX); 389*2832a81dSGeoff Levand 390*2832a81dSGeoff Levand spin_lock_irqsave(&pd->bmp.lock, flags); 391*2832a81dSGeoff Levand pd->bmp.mask &= ~(0x8000000000000000UL >> virq); 392*2832a81dSGeoff Levand spin_unlock_irqrestore(&pd->bmp.lock, flags); 393*2832a81dSGeoff Levand 394*2832a81dSGeoff Levand lv1_did_update_interrupt_mask(pd->node, pd->cpu); 395*2832a81dSGeoff Levand } 396*2832a81dSGeoff Levand 397*2832a81dSGeoff Levand static void chip_unmask(unsigned int virq) 398*2832a81dSGeoff Levand { 399*2832a81dSGeoff Levand unsigned long flags; 400*2832a81dSGeoff Levand struct private *pd = get_irq_chip_data(virq); 401*2832a81dSGeoff Levand 402*2832a81dSGeoff Levand pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq); 403*2832a81dSGeoff Levand 404*2832a81dSGeoff Levand BUG_ON(virq < NUM_ISA_INTERRUPTS); 405*2832a81dSGeoff Levand BUG_ON(virq > PS3_PLUG_MAX); 406*2832a81dSGeoff Levand 407*2832a81dSGeoff Levand spin_lock_irqsave(&pd->bmp.lock, flags); 408*2832a81dSGeoff Levand pd->bmp.mask |= (0x8000000000000000UL >> virq); 409*2832a81dSGeoff Levand spin_unlock_irqrestore(&pd->bmp.lock, flags); 410*2832a81dSGeoff Levand 411*2832a81dSGeoff Levand lv1_did_update_interrupt_mask(pd->node, pd->cpu); 412*2832a81dSGeoff Levand } 413*2832a81dSGeoff Levand 414*2832a81dSGeoff Levand static void chip_eoi(unsigned int virq) 415*2832a81dSGeoff Levand { 416*2832a81dSGeoff Levand lv1_end_of_interrupt(virq); 417*2832a81dSGeoff Levand } 418*2832a81dSGeoff Levand 419*2832a81dSGeoff Levand static struct irq_chip irq_chip = { 420*2832a81dSGeoff Levand .typename = "ps3", 421*2832a81dSGeoff Levand .mask = chip_mask, 422*2832a81dSGeoff Levand .unmask = chip_unmask, 423*2832a81dSGeoff Levand .eoi = chip_eoi, 424*2832a81dSGeoff Levand }; 425*2832a81dSGeoff Levand 426*2832a81dSGeoff Levand static void host_unmap(struct irq_host *h, unsigned int virq) 427*2832a81dSGeoff Levand { 428*2832a81dSGeoff Levand int result; 429*2832a81dSGeoff Levand 430*2832a81dSGeoff Levand pr_debug("%s:%d: virq %d\n", __func__, __LINE__, virq); 431*2832a81dSGeoff Levand 432*2832a81dSGeoff Levand lv1_disconnect_irq_plug(virq); 433*2832a81dSGeoff Levand 434*2832a81dSGeoff Levand result = set_irq_chip_data(virq, NULL); 435*2832a81dSGeoff Levand BUG_ON(result); 436*2832a81dSGeoff Levand } 437*2832a81dSGeoff Levand 438*2832a81dSGeoff Levand static DEFINE_PER_CPU(struct private, private); 439*2832a81dSGeoff Levand 440*2832a81dSGeoff Levand static int host_map(struct irq_host *h, unsigned int virq, 441*2832a81dSGeoff Levand irq_hw_number_t hwirq) 442*2832a81dSGeoff Levand { 443*2832a81dSGeoff Levand int result; 444*2832a81dSGeoff Levand unsigned int cpu; 445*2832a81dSGeoff Levand 446*2832a81dSGeoff Levand pr_debug(" -> %s:%d\n", __func__, __LINE__); 447*2832a81dSGeoff Levand pr_debug("%s:%d: hwirq %lu => virq %u\n", __func__, __LINE__, hwirq, 448*2832a81dSGeoff Levand virq); 449*2832a81dSGeoff Levand 450*2832a81dSGeoff Levand /* bind this virq to a cpu */ 451*2832a81dSGeoff Levand 452*2832a81dSGeoff Levand preempt_disable(); 453*2832a81dSGeoff Levand cpu = smp_processor_id(); 454*2832a81dSGeoff Levand result = lv1_connect_irq_plug(virq, hwirq); 455*2832a81dSGeoff Levand preempt_enable(); 456*2832a81dSGeoff Levand 457*2832a81dSGeoff Levand if (result) { 458*2832a81dSGeoff Levand pr_info("%s:%d: lv1_connect_irq_plug failed:" 459*2832a81dSGeoff Levand " %s\n", __func__, __LINE__, ps3_result(result)); 460*2832a81dSGeoff Levand return -EPERM; 461*2832a81dSGeoff Levand } 462*2832a81dSGeoff Levand 463*2832a81dSGeoff Levand result = set_irq_chip_data(virq, &per_cpu(private, cpu)); 464*2832a81dSGeoff Levand BUG_ON(result); 465*2832a81dSGeoff Levand 466*2832a81dSGeoff Levand set_irq_chip_and_handler(virq, &irq_chip, handle_fasteoi_irq); 467*2832a81dSGeoff Levand 468*2832a81dSGeoff Levand pr_debug(" <- %s:%d\n", __func__, __LINE__); 469*2832a81dSGeoff Levand return result; 470*2832a81dSGeoff Levand } 471*2832a81dSGeoff Levand 472*2832a81dSGeoff Levand static struct irq_host_ops host_ops = { 473*2832a81dSGeoff Levand .map = host_map, 474*2832a81dSGeoff Levand .unmap = host_unmap, 475*2832a81dSGeoff Levand }; 476*2832a81dSGeoff Levand 477*2832a81dSGeoff Levand void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq) 478*2832a81dSGeoff Levand { 479*2832a81dSGeoff Levand struct private *pd = &per_cpu(private, cpu); 480*2832a81dSGeoff Levand 481*2832a81dSGeoff Levand pd->bmp.ipi_debug_brk_mask = 0x8000000000000000UL >> virq; 482*2832a81dSGeoff Levand 483*2832a81dSGeoff Levand pr_debug("%s:%d: cpu %u, virq %u, mask %lxh\n", __func__, __LINE__, 484*2832a81dSGeoff Levand cpu, virq, pd->bmp.ipi_debug_brk_mask); 485*2832a81dSGeoff Levand } 486*2832a81dSGeoff Levand 487*2832a81dSGeoff Levand static int bmp_get_and_clear_status_bit(struct bmp *m) 488*2832a81dSGeoff Levand { 489*2832a81dSGeoff Levand unsigned long flags; 490*2832a81dSGeoff Levand unsigned int bit; 491*2832a81dSGeoff Levand unsigned long x; 492*2832a81dSGeoff Levand 493*2832a81dSGeoff Levand spin_lock_irqsave(&m->lock, flags); 494*2832a81dSGeoff Levand 495*2832a81dSGeoff Levand /* check for ipi break first to stop this cpu ASAP */ 496*2832a81dSGeoff Levand 497*2832a81dSGeoff Levand if (m->status & m->ipi_debug_brk_mask) { 498*2832a81dSGeoff Levand m->status &= ~m->ipi_debug_brk_mask; 499*2832a81dSGeoff Levand spin_unlock_irqrestore(&m->lock, flags); 500*2832a81dSGeoff Levand return __ilog2(m->ipi_debug_brk_mask); 501*2832a81dSGeoff Levand } 502*2832a81dSGeoff Levand 503*2832a81dSGeoff Levand x = (m->status & m->mask); 504*2832a81dSGeoff Levand 505*2832a81dSGeoff Levand for (bit = NUM_ISA_INTERRUPTS, x <<= bit; x; bit++, x <<= 1) 506*2832a81dSGeoff Levand if (x & 0x8000000000000000UL) { 507*2832a81dSGeoff Levand m->status &= ~(0x8000000000000000UL >> bit); 508*2832a81dSGeoff Levand spin_unlock_irqrestore(&m->lock, flags); 509*2832a81dSGeoff Levand return bit; 510*2832a81dSGeoff Levand } 511*2832a81dSGeoff Levand 512*2832a81dSGeoff Levand spin_unlock_irqrestore(&m->lock, flags); 513*2832a81dSGeoff Levand 514*2832a81dSGeoff Levand pr_debug("%s:%d: not found\n", __func__, __LINE__); 515*2832a81dSGeoff Levand return -1; 516*2832a81dSGeoff Levand } 517*2832a81dSGeoff Levand 518*2832a81dSGeoff Levand unsigned int ps3_get_irq(void) 519*2832a81dSGeoff Levand { 520*2832a81dSGeoff Levand int plug; 521*2832a81dSGeoff Levand 522*2832a81dSGeoff Levand struct private *pd = &__get_cpu_var(private); 523*2832a81dSGeoff Levand 524*2832a81dSGeoff Levand plug = bmp_get_and_clear_status_bit(&pd->bmp); 525*2832a81dSGeoff Levand 526*2832a81dSGeoff Levand if (plug < 1) { 527*2832a81dSGeoff Levand pr_debug("%s:%d: no plug found: cpu %u\n", __func__, __LINE__, 528*2832a81dSGeoff Levand pd->cpu); 529*2832a81dSGeoff Levand dump_bmp(&per_cpu(private, 0)); 530*2832a81dSGeoff Levand dump_bmp(&per_cpu(private, 1)); 531*2832a81dSGeoff Levand return NO_IRQ; 532*2832a81dSGeoff Levand } 533*2832a81dSGeoff Levand 534*2832a81dSGeoff Levand #if defined(DEBUG) 535*2832a81dSGeoff Levand if (plug < NUM_ISA_INTERRUPTS || plug > PS3_PLUG_MAX) { 536*2832a81dSGeoff Levand dump_bmp(&per_cpu(private, 0)); 537*2832a81dSGeoff Levand dump_bmp(&per_cpu(private, 1)); 538*2832a81dSGeoff Levand BUG(); 539*2832a81dSGeoff Levand } 540*2832a81dSGeoff Levand #endif 541*2832a81dSGeoff Levand return plug; 542*2832a81dSGeoff Levand } 543*2832a81dSGeoff Levand 544*2832a81dSGeoff Levand void __init ps3_init_IRQ(void) 545*2832a81dSGeoff Levand { 546*2832a81dSGeoff Levand int result; 547*2832a81dSGeoff Levand unsigned long node; 548*2832a81dSGeoff Levand unsigned cpu; 549*2832a81dSGeoff Levand struct irq_host *host; 550*2832a81dSGeoff Levand 551*2832a81dSGeoff Levand lv1_get_logical_ppe_id(&node); 552*2832a81dSGeoff Levand 553*2832a81dSGeoff Levand host = irq_alloc_host(IRQ_HOST_MAP_NOMAP, 0, &host_ops, 554*2832a81dSGeoff Levand PS3_INVALID_OUTLET); 555*2832a81dSGeoff Levand irq_set_default_host(host); 556*2832a81dSGeoff Levand irq_set_virq_count(PS3_PLUG_MAX + 1); 557*2832a81dSGeoff Levand 558*2832a81dSGeoff Levand for_each_possible_cpu(cpu) { 559*2832a81dSGeoff Levand struct private *pd = &per_cpu(private, cpu); 560*2832a81dSGeoff Levand 561*2832a81dSGeoff Levand pd->node = node; 562*2832a81dSGeoff Levand pd->cpu = cpu; 563*2832a81dSGeoff Levand spin_lock_init(&pd->bmp.lock); 564*2832a81dSGeoff Levand 565*2832a81dSGeoff Levand result = lv1_configure_irq_state_bitmap(node, cpu, 566*2832a81dSGeoff Levand ps3_mm_phys_to_lpar(__pa(&pd->bmp.status))); 567*2832a81dSGeoff Levand 568*2832a81dSGeoff Levand if (result) 569*2832a81dSGeoff Levand pr_debug("%s:%d: lv1_configure_irq_state_bitmap failed:" 570*2832a81dSGeoff Levand " %s\n", __func__, __LINE__, 571*2832a81dSGeoff Levand ps3_result(result)); 572*2832a81dSGeoff Levand } 573*2832a81dSGeoff Levand 574*2832a81dSGeoff Levand ppc_md.get_irq = ps3_get_irq; 575*2832a81dSGeoff Levand } 576