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/lv1call.h> 282832a81dSGeoff Levand 292832a81dSGeoff Levand #include "platform.h" 302832a81dSGeoff Levand 312832a81dSGeoff Levand #if defined(DEBUG) 322832a81dSGeoff Levand #define DBG(fmt...) udbg_printf(fmt) 332832a81dSGeoff Levand #else 342832a81dSGeoff Levand #define DBG(fmt...) do{if(0)printk(fmt);}while(0) 352832a81dSGeoff Levand #endif 362832a81dSGeoff Levand 372832a81dSGeoff Levand /** 38861be32cSGeoff Levand * struct ps3_bmp - a per cpu irq status and mask bitmap structure 39861be32cSGeoff Levand * @status: 256 bit status bitmap indexed by plug 40861be32cSGeoff Levand * @unused_1: 41861be32cSGeoff Levand * @mask: 256 bit mask bitmap indexed by plug 42861be32cSGeoff Levand * @unused_2: 43861be32cSGeoff Levand * @lock: 44861be32cSGeoff Levand * @ipi_debug_brk_mask: 45861be32cSGeoff Levand * 46861be32cSGeoff Levand * The HV mantains per SMT thread mappings of HV outlet to HV plug on 47861be32cSGeoff Levand * behalf of the guest. These mappings are implemented as 256 bit guest 48861be32cSGeoff Levand * supplied bitmaps indexed by plug number. The addresses of the bitmaps 49861be32cSGeoff Levand * are registered with the HV through lv1_configure_irq_state_bitmap(). 5057715765SGeoff Levand * The HV requires that the 512 bits of status + mask not cross a page 5157715765SGeoff Levand * boundary. PS3_BMP_MINALIGN is used to define this minimal 64 byte 5257715765SGeoff Levand * alignment. 53861be32cSGeoff Levand * 54861be32cSGeoff Levand * The HV supports 256 plugs per thread, assigned as {0..255}, for a total 55861be32cSGeoff Levand * of 512 plugs supported on a processor. To simplify the logic this 56861be32cSGeoff Levand * implementation equates HV plug value to Linux virq value, constrains each 57861be32cSGeoff Levand * interrupt to have a system wide unique plug number, and limits the range 58861be32cSGeoff Levand * of the plug values to map into the first dword of the bitmaps. This 59861be32cSGeoff Levand * gives a usable range of plug values of {NUM_ISA_INTERRUPTS..63}. Note 60861be32cSGeoff Levand * that there is no constraint on how many in this set an individual thread 61861be32cSGeoff Levand * can acquire. 62861be32cSGeoff Levand */ 63861be32cSGeoff Levand 6457715765SGeoff Levand #define PS3_BMP_MINALIGN 64 6557715765SGeoff Levand 66861be32cSGeoff Levand struct ps3_bmp { 67861be32cSGeoff Levand struct { 68861be32cSGeoff Levand u64 status; 69861be32cSGeoff Levand u64 unused_1[3]; 70861be32cSGeoff Levand u64 mask; 71861be32cSGeoff Levand u64 unused_2[3]; 72861be32cSGeoff Levand }; 73861be32cSGeoff Levand u64 ipi_debug_brk_mask; 74861be32cSGeoff Levand spinlock_t lock; 75861be32cSGeoff Levand }; 76861be32cSGeoff Levand 77861be32cSGeoff Levand /** 78861be32cSGeoff Levand * struct ps3_private - a per cpu data structure 79861be32cSGeoff Levand * @bmp: ps3_bmp structure 80861be32cSGeoff Levand * @node: HV logical_ppe_id 81861be32cSGeoff Levand * @cpu: HV thread_id 82861be32cSGeoff Levand */ 83861be32cSGeoff Levand 84861be32cSGeoff Levand struct ps3_private { 8557715765SGeoff Levand struct ps3_bmp bmp __attribute__ ((aligned (PS3_BMP_MINALIGN))); 86861be32cSGeoff Levand u64 node; 87861be32cSGeoff Levand unsigned int cpu; 88861be32cSGeoff Levand }; 89861be32cSGeoff Levand 90861be32cSGeoff Levand static DEFINE_PER_CPU(struct ps3_private, ps3_private); 91861be32cSGeoff Levand 92*dc4f60c2SGeoff Levand /** 93*dc4f60c2SGeoff Levand * ps3_virq_setup - virq related setup. 94*dc4f60c2SGeoff Levand * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 95*dc4f60c2SGeoff Levand * serviced on. 96*dc4f60c2SGeoff Levand * @outlet: The HV outlet from the various create outlet routines. 97*dc4f60c2SGeoff Levand * @virq: The assigned Linux virq. 98*dc4f60c2SGeoff Levand * 99*dc4f60c2SGeoff Levand * Calls irq_create_mapping() to get a virq and sets the chip data to 100*dc4f60c2SGeoff Levand * ps3_private data. 101*dc4f60c2SGeoff Levand */ 102*dc4f60c2SGeoff Levand 103*dc4f60c2SGeoff Levand int ps3_virq_setup(enum ps3_cpu_binding cpu, unsigned long outlet, 104861be32cSGeoff Levand unsigned int *virq) 105861be32cSGeoff Levand { 106861be32cSGeoff Levand int result; 107861be32cSGeoff Levand struct ps3_private *pd; 108861be32cSGeoff Levand 109861be32cSGeoff Levand /* This defines the default interrupt distribution policy. */ 110861be32cSGeoff Levand 111861be32cSGeoff Levand if (cpu == PS3_BINDING_CPU_ANY) 112861be32cSGeoff Levand cpu = 0; 113861be32cSGeoff Levand 114861be32cSGeoff Levand pd = &per_cpu(ps3_private, cpu); 115861be32cSGeoff Levand 116861be32cSGeoff Levand *virq = irq_create_mapping(NULL, outlet); 117861be32cSGeoff Levand 118861be32cSGeoff Levand if (*virq == NO_IRQ) { 119861be32cSGeoff Levand pr_debug("%s:%d: irq_create_mapping failed: outlet %lu\n", 120861be32cSGeoff Levand __func__, __LINE__, outlet); 121861be32cSGeoff Levand result = -ENOMEM; 122861be32cSGeoff Levand goto fail_create; 123861be32cSGeoff Levand } 124861be32cSGeoff Levand 125861be32cSGeoff Levand pr_debug("%s:%d: outlet %lu => cpu %u, virq %u\n", __func__, __LINE__, 126861be32cSGeoff Levand outlet, cpu, *virq); 127861be32cSGeoff Levand 128861be32cSGeoff Levand result = set_irq_chip_data(*virq, pd); 129861be32cSGeoff Levand 130861be32cSGeoff Levand if (result) { 131861be32cSGeoff Levand pr_debug("%s:%d: set_irq_chip_data failed\n", 132861be32cSGeoff Levand __func__, __LINE__); 133861be32cSGeoff Levand goto fail_set; 134861be32cSGeoff Levand } 135861be32cSGeoff Levand 136861be32cSGeoff Levand return result; 137861be32cSGeoff Levand 138861be32cSGeoff Levand fail_set: 139861be32cSGeoff Levand irq_dispose_mapping(*virq); 140861be32cSGeoff Levand fail_create: 141861be32cSGeoff Levand return result; 142861be32cSGeoff Levand } 143861be32cSGeoff Levand 144*dc4f60c2SGeoff Levand /** 145*dc4f60c2SGeoff Levand * ps3_virq_destroy - virq related teardown. 146*dc4f60c2SGeoff Levand * @virq: The assigned Linux virq. 147*dc4f60c2SGeoff Levand * 148*dc4f60c2SGeoff Levand * Clears chip data and calls irq_dispose_mapping() for the virq. 149*dc4f60c2SGeoff Levand */ 150*dc4f60c2SGeoff Levand 151*dc4f60c2SGeoff Levand int ps3_virq_destroy(unsigned int virq) 152*dc4f60c2SGeoff Levand { 153*dc4f60c2SGeoff Levand const struct ps3_private *pd = get_irq_chip_data(virq); 154*dc4f60c2SGeoff Levand 155*dc4f60c2SGeoff Levand pr_debug("%s:%d: node %lu, cpu %d, virq %u\n", __func__, __LINE__, 156*dc4f60c2SGeoff Levand pd->node, pd->cpu, virq); 157*dc4f60c2SGeoff Levand 158*dc4f60c2SGeoff Levand set_irq_chip_data(virq, NULL); 159*dc4f60c2SGeoff Levand irq_dispose_mapping(virq); 160*dc4f60c2SGeoff Levand 161*dc4f60c2SGeoff Levand pr_debug("%s:%d <-\n", __func__, __LINE__); 162*dc4f60c2SGeoff Levand return 0; 163*dc4f60c2SGeoff Levand } 164*dc4f60c2SGeoff Levand 165*dc4f60c2SGeoff Levand /** 166*dc4f60c2SGeoff Levand * ps3_irq_plug_setup - Generic outlet and virq related setup. 167*dc4f60c2SGeoff Levand * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 168*dc4f60c2SGeoff Levand * serviced on. 169*dc4f60c2SGeoff Levand * @outlet: The HV outlet from the various create outlet routines. 170*dc4f60c2SGeoff Levand * @virq: The assigned Linux virq. 171*dc4f60c2SGeoff Levand * 172*dc4f60c2SGeoff Levand * Sets up virq and connects the irq plug. 173*dc4f60c2SGeoff Levand */ 174*dc4f60c2SGeoff Levand 175*dc4f60c2SGeoff Levand int ps3_irq_plug_setup(enum ps3_cpu_binding cpu, unsigned long outlet, 176*dc4f60c2SGeoff Levand unsigned int *virq) 177*dc4f60c2SGeoff Levand { 178*dc4f60c2SGeoff Levand int result; 179*dc4f60c2SGeoff Levand struct ps3_private *pd; 180*dc4f60c2SGeoff Levand 181*dc4f60c2SGeoff Levand result = ps3_virq_setup(cpu, outlet, virq); 182*dc4f60c2SGeoff Levand 183*dc4f60c2SGeoff Levand if (result) { 184*dc4f60c2SGeoff Levand pr_debug("%s:%d: ps3_virq_setup failed\n", __func__, __LINE__); 185*dc4f60c2SGeoff Levand goto fail_setup; 186*dc4f60c2SGeoff Levand } 187*dc4f60c2SGeoff Levand 188*dc4f60c2SGeoff Levand pd = get_irq_chip_data(*virq); 189*dc4f60c2SGeoff Levand 190*dc4f60c2SGeoff Levand /* Binds outlet to cpu + virq. */ 191*dc4f60c2SGeoff Levand 192*dc4f60c2SGeoff Levand result = lv1_connect_irq_plug_ext(pd->node, pd->cpu, *virq, outlet, 0); 193*dc4f60c2SGeoff Levand 194*dc4f60c2SGeoff Levand if (result) { 195*dc4f60c2SGeoff Levand pr_info("%s:%d: lv1_connect_irq_plug_ext failed: %s\n", 196*dc4f60c2SGeoff Levand __func__, __LINE__, ps3_result(result)); 197*dc4f60c2SGeoff Levand result = -EPERM; 198*dc4f60c2SGeoff Levand goto fail_connect; 199*dc4f60c2SGeoff Levand } 200*dc4f60c2SGeoff Levand 201*dc4f60c2SGeoff Levand return result; 202*dc4f60c2SGeoff Levand 203*dc4f60c2SGeoff Levand fail_connect: 204*dc4f60c2SGeoff Levand ps3_virq_destroy(*virq); 205*dc4f60c2SGeoff Levand fail_setup: 206*dc4f60c2SGeoff Levand return result; 207*dc4f60c2SGeoff Levand } 208*dc4f60c2SGeoff Levand EXPORT_SYMBOL_GPL(ps3_irq_plug_setup); 209*dc4f60c2SGeoff Levand 210*dc4f60c2SGeoff Levand /** 211*dc4f60c2SGeoff Levand * ps3_irq_plug_destroy - Generic outlet and virq related teardown. 212*dc4f60c2SGeoff Levand * @virq: The assigned Linux virq. 213*dc4f60c2SGeoff Levand * 214*dc4f60c2SGeoff Levand * Disconnects the irq plug and tears down virq. 215*dc4f60c2SGeoff Levand * Do not call for system bus event interrupts setup with 216*dc4f60c2SGeoff Levand * ps3_sb_event_receive_port_setup(). 217*dc4f60c2SGeoff Levand */ 218*dc4f60c2SGeoff Levand 219*dc4f60c2SGeoff Levand int ps3_irq_plug_destroy(unsigned int virq) 220861be32cSGeoff Levand { 221861be32cSGeoff Levand int result; 222861be32cSGeoff Levand const struct ps3_private *pd = get_irq_chip_data(virq); 223861be32cSGeoff Levand 224861be32cSGeoff Levand pr_debug("%s:%d: node %lu, cpu %d, virq %u\n", __func__, __LINE__, 225861be32cSGeoff Levand pd->node, pd->cpu, virq); 226861be32cSGeoff Levand 227861be32cSGeoff Levand result = lv1_disconnect_irq_plug_ext(pd->node, pd->cpu, virq); 228861be32cSGeoff Levand 229861be32cSGeoff Levand if (result) 230861be32cSGeoff Levand pr_info("%s:%d: lv1_disconnect_irq_plug_ext failed: %s\n", 231861be32cSGeoff Levand __func__, __LINE__, ps3_result(result)); 232861be32cSGeoff Levand 233*dc4f60c2SGeoff Levand ps3_virq_destroy(virq); 234*dc4f60c2SGeoff Levand 235b1eeb38eSGeert Uytterhoeven return result; 236861be32cSGeoff Levand } 237*dc4f60c2SGeoff Levand EXPORT_SYMBOL_GPL(ps3_irq_plug_destroy); 238861be32cSGeoff Levand 239861be32cSGeoff Levand /** 240*dc4f60c2SGeoff Levand * ps3_event_receive_port_setup - Setup an event receive port. 241861be32cSGeoff Levand * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 242861be32cSGeoff Levand * serviced on. 2432832a81dSGeoff Levand * @virq: The assigned Linux virq. 2442832a81dSGeoff Levand * 2452832a81dSGeoff Levand * The virq can be used with lv1_connect_interrupt_event_receive_port() to 246*dc4f60c2SGeoff Levand * arrange to receive interrupts from system-bus devices, or with 247*dc4f60c2SGeoff Levand * ps3_send_event_locally() to signal events. 2482832a81dSGeoff Levand */ 2492832a81dSGeoff Levand 250*dc4f60c2SGeoff Levand int ps3_event_receive_port_setup(enum ps3_cpu_binding cpu, unsigned int *virq) 2512832a81dSGeoff Levand { 2522832a81dSGeoff Levand int result; 2532832a81dSGeoff Levand unsigned long outlet; 2542832a81dSGeoff Levand 2552832a81dSGeoff Levand result = lv1_construct_event_receive_port(&outlet); 2562832a81dSGeoff Levand 2572832a81dSGeoff Levand if (result) { 2582832a81dSGeoff Levand pr_debug("%s:%d: lv1_construct_event_receive_port failed: %s\n", 2592832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 2602832a81dSGeoff Levand *virq = NO_IRQ; 2612832a81dSGeoff Levand return result; 2622832a81dSGeoff Levand } 2632832a81dSGeoff Levand 264*dc4f60c2SGeoff Levand result = ps3_irq_plug_setup(cpu, outlet, virq); 265861be32cSGeoff Levand BUG_ON(result); 2662832a81dSGeoff Levand 267861be32cSGeoff Levand return result; 2682832a81dSGeoff Levand } 269*dc4f60c2SGeoff Levand EXPORT_SYMBOL_GPL(ps3_event_receive_port_setup); 2702832a81dSGeoff Levand 271*dc4f60c2SGeoff Levand /** 272*dc4f60c2SGeoff Levand * ps3_event_receive_port_destroy - Destroy an event receive port. 273*dc4f60c2SGeoff Levand * @virq: The assigned Linux virq. 274*dc4f60c2SGeoff Levand * 275*dc4f60c2SGeoff Levand * Since ps3_event_receive_port_destroy destroys the receive port outlet, 276*dc4f60c2SGeoff Levand * SB devices need to call disconnect_interrupt_event_receive_port() before 277*dc4f60c2SGeoff Levand * this. 278*dc4f60c2SGeoff Levand */ 279*dc4f60c2SGeoff Levand 280*dc4f60c2SGeoff Levand int ps3_event_receive_port_destroy(unsigned int virq) 2812832a81dSGeoff Levand { 2822832a81dSGeoff Levand int result; 2832832a81dSGeoff Levand 284*dc4f60c2SGeoff Levand pr_debug(" -> %s:%d virq: %u\n", __func__, __LINE__, virq); 2852832a81dSGeoff Levand 2862832a81dSGeoff Levand result = lv1_destruct_event_receive_port(virq_to_hw(virq)); 2872832a81dSGeoff Levand 2882832a81dSGeoff Levand if (result) 2892832a81dSGeoff Levand pr_debug("%s:%d: lv1_destruct_event_receive_port failed: %s\n", 2902832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 2912832a81dSGeoff Levand 292*dc4f60c2SGeoff Levand /* lv1_destruct_event_receive_port() destroys the IRQ plug, 293*dc4f60c2SGeoff Levand * so don't call ps3_irq_plug_destroy() here. 294*dc4f60c2SGeoff Levand */ 295*dc4f60c2SGeoff Levand 296*dc4f60c2SGeoff Levand result = ps3_virq_destroy(virq); 297*dc4f60c2SGeoff Levand BUG_ON(result); 2982832a81dSGeoff Levand 2992832a81dSGeoff Levand pr_debug(" <- %s:%d\n", __func__, __LINE__); 3002832a81dSGeoff Levand return result; 3012832a81dSGeoff Levand } 302*dc4f60c2SGeoff Levand EXPORT_SYMBOL_GPL(ps3_event_receive_port_destroy); 3032832a81dSGeoff Levand 3042832a81dSGeoff Levand int ps3_send_event_locally(unsigned int virq) 3052832a81dSGeoff Levand { 3062832a81dSGeoff Levand return lv1_send_event_locally(virq_to_hw(virq)); 3072832a81dSGeoff Levand } 3082832a81dSGeoff Levand 3092832a81dSGeoff Levand /** 310*dc4f60c2SGeoff Levand * ps3_sb_event_receive_port_setup - Setup a system bus event receive port. 311861be32cSGeoff Levand * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 312861be32cSGeoff Levand * serviced on. 3132832a81dSGeoff Levand * @did: The HV device identifier read from the system repository. 3142832a81dSGeoff Levand * @interrupt_id: The device interrupt id read from the system repository. 3152832a81dSGeoff Levand * @virq: The assigned Linux virq. 3162832a81dSGeoff Levand * 3172832a81dSGeoff Levand * An event irq represents a virtual device interrupt. The interrupt_id 3182832a81dSGeoff Levand * coresponds to the software interrupt number. 3192832a81dSGeoff Levand */ 3202832a81dSGeoff Levand 321*dc4f60c2SGeoff Levand int ps3_sb_event_receive_port_setup(enum ps3_cpu_binding cpu, 322861be32cSGeoff Levand const struct ps3_device_id *did, unsigned int interrupt_id, 323861be32cSGeoff Levand unsigned int *virq) 3242832a81dSGeoff Levand { 325*dc4f60c2SGeoff Levand /* this should go in system-bus.c */ 326*dc4f60c2SGeoff Levand 3272832a81dSGeoff Levand int result; 3282832a81dSGeoff Levand 329*dc4f60c2SGeoff Levand result = ps3_event_receive_port_setup(cpu, virq); 3302832a81dSGeoff Levand 3312832a81dSGeoff Levand if (result) 3322832a81dSGeoff Levand return result; 3332832a81dSGeoff Levand 3342832a81dSGeoff Levand result = lv1_connect_interrupt_event_receive_port(did->bus_id, 3352832a81dSGeoff Levand did->dev_id, virq_to_hw(*virq), interrupt_id); 3362832a81dSGeoff Levand 3372832a81dSGeoff Levand if (result) { 3382832a81dSGeoff Levand pr_debug("%s:%d: lv1_connect_interrupt_event_receive_port" 3392832a81dSGeoff Levand " failed: %s\n", __func__, __LINE__, 3402832a81dSGeoff Levand ps3_result(result)); 341*dc4f60c2SGeoff Levand ps3_event_receive_port_destroy(*virq); 3422832a81dSGeoff Levand *virq = NO_IRQ; 3432832a81dSGeoff Levand return result; 3442832a81dSGeoff Levand } 3452832a81dSGeoff Levand 3462832a81dSGeoff Levand pr_debug("%s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__, 3472832a81dSGeoff Levand interrupt_id, *virq); 3482832a81dSGeoff Levand 3492832a81dSGeoff Levand return 0; 3502832a81dSGeoff Levand } 351*dc4f60c2SGeoff Levand EXPORT_SYMBOL(ps3_sb_event_receive_port_setup); 3522832a81dSGeoff Levand 353*dc4f60c2SGeoff Levand int ps3_sb_event_receive_port_destroy(const struct ps3_device_id *did, 3542832a81dSGeoff Levand unsigned int interrupt_id, unsigned int virq) 3552832a81dSGeoff Levand { 356*dc4f60c2SGeoff Levand /* this should go in system-bus.c */ 357*dc4f60c2SGeoff Levand 3582832a81dSGeoff Levand int result; 3592832a81dSGeoff Levand 3602832a81dSGeoff Levand pr_debug(" -> %s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__, 3612832a81dSGeoff Levand interrupt_id, virq); 3622832a81dSGeoff Levand 3632832a81dSGeoff Levand result = lv1_disconnect_interrupt_event_receive_port(did->bus_id, 3642832a81dSGeoff Levand did->dev_id, virq_to_hw(virq), interrupt_id); 3652832a81dSGeoff Levand 3662832a81dSGeoff Levand if (result) 3672832a81dSGeoff Levand pr_debug("%s:%d: lv1_disconnect_interrupt_event_receive_port" 3682832a81dSGeoff Levand " failed: %s\n", __func__, __LINE__, 3692832a81dSGeoff Levand ps3_result(result)); 3702832a81dSGeoff Levand 371*dc4f60c2SGeoff Levand result = ps3_event_receive_port_destroy(virq); 372*dc4f60c2SGeoff Levand BUG_ON(result); 3732832a81dSGeoff Levand 3742832a81dSGeoff Levand pr_debug(" <- %s:%d\n", __func__, __LINE__); 3752832a81dSGeoff Levand return result; 3762832a81dSGeoff Levand } 377*dc4f60c2SGeoff Levand EXPORT_SYMBOL(ps3_sb_event_receive_port_destroy); 3782832a81dSGeoff Levand 3792832a81dSGeoff Levand /** 380*dc4f60c2SGeoff Levand * ps3_io_irq_setup - Setup a system bus io irq. 381*dc4f60c2SGeoff Levand * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 382*dc4f60c2SGeoff Levand * serviced on. 383*dc4f60c2SGeoff Levand * @interrupt_id: The device interrupt id read from the system repository. 384*dc4f60c2SGeoff Levand * @virq: The assigned Linux virq. 385*dc4f60c2SGeoff Levand * 386*dc4f60c2SGeoff Levand * An io irq represents a non-virtualized device interrupt. interrupt_id 387*dc4f60c2SGeoff Levand * coresponds to the interrupt number of the interrupt controller. 388*dc4f60c2SGeoff Levand */ 389*dc4f60c2SGeoff Levand 390*dc4f60c2SGeoff Levand int ps3_io_irq_setup(enum ps3_cpu_binding cpu, unsigned int interrupt_id, 391*dc4f60c2SGeoff Levand unsigned int *virq) 392*dc4f60c2SGeoff Levand { 393*dc4f60c2SGeoff Levand int result; 394*dc4f60c2SGeoff Levand unsigned long outlet; 395*dc4f60c2SGeoff Levand 396*dc4f60c2SGeoff Levand result = lv1_construct_io_irq_outlet(interrupt_id, &outlet); 397*dc4f60c2SGeoff Levand 398*dc4f60c2SGeoff Levand if (result) { 399*dc4f60c2SGeoff Levand pr_debug("%s:%d: lv1_construct_io_irq_outlet failed: %s\n", 400*dc4f60c2SGeoff Levand __func__, __LINE__, ps3_result(result)); 401*dc4f60c2SGeoff Levand return result; 402*dc4f60c2SGeoff Levand } 403*dc4f60c2SGeoff Levand 404*dc4f60c2SGeoff Levand result = ps3_irq_plug_setup(cpu, outlet, virq); 405*dc4f60c2SGeoff Levand BUG_ON(result); 406*dc4f60c2SGeoff Levand 407*dc4f60c2SGeoff Levand return result; 408*dc4f60c2SGeoff Levand } 409*dc4f60c2SGeoff Levand EXPORT_SYMBOL_GPL(ps3_io_irq_setup); 410*dc4f60c2SGeoff Levand 411*dc4f60c2SGeoff Levand int ps3_io_irq_destroy(unsigned int virq) 412*dc4f60c2SGeoff Levand { 413*dc4f60c2SGeoff Levand int result; 414*dc4f60c2SGeoff Levand 415*dc4f60c2SGeoff Levand result = lv1_destruct_io_irq_outlet(virq_to_hw(virq)); 416*dc4f60c2SGeoff Levand 417*dc4f60c2SGeoff Levand if (result) 418*dc4f60c2SGeoff Levand pr_debug("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n", 419*dc4f60c2SGeoff Levand __func__, __LINE__, ps3_result(result)); 420*dc4f60c2SGeoff Levand 421*dc4f60c2SGeoff Levand result = ps3_irq_plug_destroy(virq); 422*dc4f60c2SGeoff Levand BUG_ON(result); 423*dc4f60c2SGeoff Levand 424*dc4f60c2SGeoff Levand return result; 425*dc4f60c2SGeoff Levand } 426*dc4f60c2SGeoff Levand EXPORT_SYMBOL_GPL(ps3_io_irq_destroy); 427*dc4f60c2SGeoff Levand 428*dc4f60c2SGeoff Levand /** 429*dc4f60c2SGeoff Levand * ps3_vuart_irq_setup - Setup the system virtual uart virq. 430861be32cSGeoff Levand * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 431861be32cSGeoff Levand * serviced on. 4322832a81dSGeoff Levand * @virt_addr_bmp: The caller supplied virtual uart interrupt bitmap. 4332832a81dSGeoff Levand * @virq: The assigned Linux virq. 4342832a81dSGeoff Levand * 4352832a81dSGeoff Levand * The system supports only a single virtual uart, so multiple calls without 4362832a81dSGeoff Levand * freeing the interrupt will return a wrong state error. 4372832a81dSGeoff Levand */ 4382832a81dSGeoff Levand 439*dc4f60c2SGeoff Levand int ps3_vuart_irq_setup(enum ps3_cpu_binding cpu, void* virt_addr_bmp, 440861be32cSGeoff Levand unsigned int *virq) 4412832a81dSGeoff Levand { 4422832a81dSGeoff Levand int result; 4432832a81dSGeoff Levand unsigned long outlet; 444861be32cSGeoff Levand u64 lpar_addr; 4452832a81dSGeoff Levand 446861be32cSGeoff Levand BUG_ON(!is_kernel_addr((u64)virt_addr_bmp)); 4472832a81dSGeoff Levand 4482832a81dSGeoff Levand lpar_addr = ps3_mm_phys_to_lpar(__pa(virt_addr_bmp)); 4492832a81dSGeoff Levand 4502832a81dSGeoff Levand result = lv1_configure_virtual_uart_irq(lpar_addr, &outlet); 4512832a81dSGeoff Levand 4522832a81dSGeoff Levand if (result) { 4532832a81dSGeoff Levand pr_debug("%s:%d: lv1_configure_virtual_uart_irq failed: %s\n", 4542832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 4552832a81dSGeoff Levand return result; 4562832a81dSGeoff Levand } 4572832a81dSGeoff Levand 458*dc4f60c2SGeoff Levand result = ps3_irq_plug_setup(cpu, outlet, virq); 459861be32cSGeoff Levand BUG_ON(result); 4602832a81dSGeoff Levand 461861be32cSGeoff Levand return result; 4622832a81dSGeoff Levand } 4632832a81dSGeoff Levand 464*dc4f60c2SGeoff Levand int ps3_vuart_irq_destroy(unsigned int virq) 4652832a81dSGeoff Levand { 4662832a81dSGeoff Levand int result; 4672832a81dSGeoff Levand 4682832a81dSGeoff Levand result = lv1_deconfigure_virtual_uart_irq(); 4692832a81dSGeoff Levand 4702832a81dSGeoff Levand if (result) { 4712832a81dSGeoff Levand pr_debug("%s:%d: lv1_configure_virtual_uart_irq failed: %s\n", 4722832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 4732832a81dSGeoff Levand return result; 4742832a81dSGeoff Levand } 4752832a81dSGeoff Levand 476*dc4f60c2SGeoff Levand result = ps3_irq_plug_destroy(virq); 477*dc4f60c2SGeoff Levand BUG_ON(result); 4782832a81dSGeoff Levand 4792832a81dSGeoff Levand return result; 4802832a81dSGeoff Levand } 4812832a81dSGeoff Levand 4822832a81dSGeoff Levand /** 483*dc4f60c2SGeoff Levand * ps3_spe_irq_setup - Setup an spe virq. 484861be32cSGeoff Levand * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 485861be32cSGeoff Levand * serviced on. 4862832a81dSGeoff Levand * @spe_id: The spe_id returned from lv1_construct_logical_spe(). 4872832a81dSGeoff Levand * @class: The spe interrupt class {0,1,2}. 4882832a81dSGeoff Levand * @virq: The assigned Linux virq. 4892832a81dSGeoff Levand * 4902832a81dSGeoff Levand */ 4912832a81dSGeoff Levand 492*dc4f60c2SGeoff Levand int ps3_spe_irq_setup(enum ps3_cpu_binding cpu, unsigned long spe_id, 493861be32cSGeoff Levand unsigned int class, unsigned int *virq) 4942832a81dSGeoff Levand { 4952832a81dSGeoff Levand int result; 4962832a81dSGeoff Levand unsigned long outlet; 4972832a81dSGeoff Levand 4982832a81dSGeoff Levand BUG_ON(class > 2); 4992832a81dSGeoff Levand 5002832a81dSGeoff Levand result = lv1_get_spe_irq_outlet(spe_id, class, &outlet); 5012832a81dSGeoff Levand 5022832a81dSGeoff Levand if (result) { 5032832a81dSGeoff Levand pr_debug("%s:%d: lv1_get_spe_irq_outlet failed: %s\n", 5042832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 5052832a81dSGeoff Levand return result; 5062832a81dSGeoff Levand } 5072832a81dSGeoff Levand 508*dc4f60c2SGeoff Levand result = ps3_irq_plug_setup(cpu, outlet, virq); 509861be32cSGeoff Levand BUG_ON(result); 5102832a81dSGeoff Levand 511861be32cSGeoff Levand return result; 5122832a81dSGeoff Levand } 5132832a81dSGeoff Levand 514*dc4f60c2SGeoff Levand int ps3_spe_irq_destroy(unsigned int virq) 5152832a81dSGeoff Levand { 516*dc4f60c2SGeoff Levand int result = ps3_irq_plug_destroy(virq); 517*dc4f60c2SGeoff Levand BUG_ON(result); 5182832a81dSGeoff Levand return 0; 5192832a81dSGeoff Levand } 5202832a81dSGeoff Levand 521b1eeb38eSGeert Uytterhoeven 5222832a81dSGeoff Levand #define PS3_INVALID_OUTLET ((irq_hw_number_t)-1) 5232832a81dSGeoff Levand #define PS3_PLUG_MAX 63 5242832a81dSGeoff Levand 5252832a81dSGeoff Levand #if defined(DEBUG) 526861be32cSGeoff Levand static void _dump_64_bmp(const char *header, const u64 *p, unsigned cpu, 5272832a81dSGeoff Levand const char* func, int line) 5282832a81dSGeoff Levand { 5292832a81dSGeoff Levand pr_debug("%s:%d: %s %u {%04lx_%04lx_%04lx_%04lx}\n", 5302832a81dSGeoff Levand func, line, header, cpu, 5312832a81dSGeoff Levand *p >> 48, (*p >> 32) & 0xffff, (*p >> 16) & 0xffff, 5322832a81dSGeoff Levand *p & 0xffff); 5332832a81dSGeoff Levand } 5342832a81dSGeoff Levand 5352832a81dSGeoff Levand static void __attribute__ ((unused)) _dump_256_bmp(const char *header, 536861be32cSGeoff Levand const u64 *p, unsigned cpu, const char* func, int line) 5372832a81dSGeoff Levand { 5382832a81dSGeoff Levand pr_debug("%s:%d: %s %u {%016lx:%016lx:%016lx:%016lx}\n", 5392832a81dSGeoff Levand func, line, header, cpu, p[0], p[1], p[2], p[3]); 5402832a81dSGeoff Levand } 5412832a81dSGeoff Levand 5422832a81dSGeoff Levand #define dump_bmp(_x) _dump_bmp(_x, __func__, __LINE__) 5439633ac8dSGeoff Levand static void _dump_bmp(struct ps3_private* pd, const char* func, int line) 5442832a81dSGeoff Levand { 5452832a81dSGeoff Levand unsigned long flags; 5462832a81dSGeoff Levand 5472832a81dSGeoff Levand spin_lock_irqsave(&pd->bmp.lock, flags); 5482832a81dSGeoff Levand _dump_64_bmp("stat", &pd->bmp.status, pd->cpu, func, line); 5492832a81dSGeoff Levand _dump_64_bmp("mask", &pd->bmp.mask, pd->cpu, func, line); 5502832a81dSGeoff Levand spin_unlock_irqrestore(&pd->bmp.lock, flags); 5512832a81dSGeoff Levand } 5522832a81dSGeoff Levand 5532832a81dSGeoff Levand #define dump_mask(_x) _dump_mask(_x, __func__, __LINE__) 5549633ac8dSGeoff Levand static void __attribute__ ((unused)) _dump_mask(struct ps3_private* pd, 5552832a81dSGeoff Levand const char* func, int line) 5562832a81dSGeoff Levand { 5572832a81dSGeoff Levand unsigned long flags; 5582832a81dSGeoff Levand 5592832a81dSGeoff Levand spin_lock_irqsave(&pd->bmp.lock, flags); 5602832a81dSGeoff Levand _dump_64_bmp("mask", &pd->bmp.mask, pd->cpu, func, line); 5612832a81dSGeoff Levand spin_unlock_irqrestore(&pd->bmp.lock, flags); 5622832a81dSGeoff Levand } 5632832a81dSGeoff Levand #else 5649633ac8dSGeoff Levand static void dump_bmp(struct ps3_private* pd) {}; 5652832a81dSGeoff Levand #endif /* defined(DEBUG) */ 5662832a81dSGeoff Levand 5679633ac8dSGeoff Levand static void ps3_chip_mask(unsigned int virq) 5682832a81dSGeoff Levand { 5699633ac8dSGeoff Levand struct ps3_private *pd = get_irq_chip_data(virq); 570861be32cSGeoff Levand u64 bit = 0x8000000000000000UL >> virq; 571861be32cSGeoff Levand u64 *p = &pd->bmp.mask; 572861be32cSGeoff Levand u64 old; 5739cf9e196SBenjamin Herrenschmidt unsigned long flags; 5742832a81dSGeoff Levand 5752832a81dSGeoff Levand pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq); 5762832a81dSGeoff Levand 5779cf9e196SBenjamin Herrenschmidt local_irq_save(flags); 5789cf9e196SBenjamin Herrenschmidt asm volatile( 5799cf9e196SBenjamin Herrenschmidt "1: ldarx %0,0,%3\n" 5809cf9e196SBenjamin Herrenschmidt "andc %0,%0,%2\n" 5819cf9e196SBenjamin Herrenschmidt "stdcx. %0,0,%3\n" 5829cf9e196SBenjamin Herrenschmidt "bne- 1b" 5839cf9e196SBenjamin Herrenschmidt : "=&r" (old), "+m" (*p) 5849cf9e196SBenjamin Herrenschmidt : "r" (bit), "r" (p) 5859cf9e196SBenjamin Herrenschmidt : "cc" ); 5862832a81dSGeoff Levand 5872832a81dSGeoff Levand lv1_did_update_interrupt_mask(pd->node, pd->cpu); 5889cf9e196SBenjamin Herrenschmidt local_irq_restore(flags); 5892832a81dSGeoff Levand } 5902832a81dSGeoff Levand 5919633ac8dSGeoff Levand static void ps3_chip_unmask(unsigned int virq) 5922832a81dSGeoff Levand { 5939633ac8dSGeoff Levand struct ps3_private *pd = get_irq_chip_data(virq); 594861be32cSGeoff Levand u64 bit = 0x8000000000000000UL >> virq; 595861be32cSGeoff Levand u64 *p = &pd->bmp.mask; 596861be32cSGeoff Levand u64 old; 5979cf9e196SBenjamin Herrenschmidt unsigned long flags; 5982832a81dSGeoff Levand 5992832a81dSGeoff Levand pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq); 6002832a81dSGeoff Levand 6019cf9e196SBenjamin Herrenschmidt local_irq_save(flags); 6029cf9e196SBenjamin Herrenschmidt asm volatile( 6039cf9e196SBenjamin Herrenschmidt "1: ldarx %0,0,%3\n" 6049cf9e196SBenjamin Herrenschmidt "or %0,%0,%2\n" 6059cf9e196SBenjamin Herrenschmidt "stdcx. %0,0,%3\n" 6069cf9e196SBenjamin Herrenschmidt "bne- 1b" 6079cf9e196SBenjamin Herrenschmidt : "=&r" (old), "+m" (*p) 6089cf9e196SBenjamin Herrenschmidt : "r" (bit), "r" (p) 6099cf9e196SBenjamin Herrenschmidt : "cc" ); 6102832a81dSGeoff Levand 6112832a81dSGeoff Levand lv1_did_update_interrupt_mask(pd->node, pd->cpu); 6129cf9e196SBenjamin Herrenschmidt local_irq_restore(flags); 6132832a81dSGeoff Levand } 6142832a81dSGeoff Levand 6159633ac8dSGeoff Levand static void ps3_chip_eoi(unsigned int virq) 6162832a81dSGeoff Levand { 617407e24a0SGeoff Levand const struct ps3_private *pd = get_irq_chip_data(virq); 618407e24a0SGeoff Levand lv1_end_of_interrupt_ext(pd->node, pd->cpu, virq); 6192832a81dSGeoff Levand } 6202832a81dSGeoff Levand 6212832a81dSGeoff Levand static struct irq_chip irq_chip = { 6222832a81dSGeoff Levand .typename = "ps3", 6239633ac8dSGeoff Levand .mask = ps3_chip_mask, 6249633ac8dSGeoff Levand .unmask = ps3_chip_unmask, 6259633ac8dSGeoff Levand .eoi = ps3_chip_eoi, 6262832a81dSGeoff Levand }; 6272832a81dSGeoff Levand 6289633ac8dSGeoff Levand static void ps3_host_unmap(struct irq_host *h, unsigned int virq) 6292832a81dSGeoff Levand { 630861be32cSGeoff Levand set_irq_chip_data(virq, NULL); 6312832a81dSGeoff Levand } 6322832a81dSGeoff Levand 6339633ac8dSGeoff Levand static int ps3_host_map(struct irq_host *h, unsigned int virq, 6342832a81dSGeoff Levand irq_hw_number_t hwirq) 6352832a81dSGeoff Levand { 636861be32cSGeoff Levand pr_debug("%s:%d: hwirq %lu, virq %u\n", __func__, __LINE__, hwirq, 637861be32cSGeoff Levand virq); 6382832a81dSGeoff Levand 6392832a81dSGeoff Levand set_irq_chip_and_handler(virq, &irq_chip, handle_fasteoi_irq); 6402832a81dSGeoff Levand 641861be32cSGeoff Levand return 0; 6422832a81dSGeoff Levand } 6432832a81dSGeoff Levand 6449633ac8dSGeoff Levand static struct irq_host_ops ps3_host_ops = { 6459633ac8dSGeoff Levand .map = ps3_host_map, 6469633ac8dSGeoff Levand .unmap = ps3_host_unmap, 6472832a81dSGeoff Levand }; 6482832a81dSGeoff Levand 6492832a81dSGeoff Levand void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq) 6502832a81dSGeoff Levand { 6519633ac8dSGeoff Levand struct ps3_private *pd = &per_cpu(ps3_private, cpu); 6522832a81dSGeoff Levand 6532832a81dSGeoff Levand pd->bmp.ipi_debug_brk_mask = 0x8000000000000000UL >> virq; 6542832a81dSGeoff Levand 6552832a81dSGeoff Levand pr_debug("%s:%d: cpu %u, virq %u, mask %lxh\n", __func__, __LINE__, 6562832a81dSGeoff Levand cpu, virq, pd->bmp.ipi_debug_brk_mask); 6572832a81dSGeoff Levand } 6582832a81dSGeoff Levand 6599cf9e196SBenjamin Herrenschmidt unsigned int ps3_get_irq(void) 6602832a81dSGeoff Levand { 6619cf9e196SBenjamin Herrenschmidt struct ps3_private *pd = &__get_cpu_var(ps3_private); 662861be32cSGeoff Levand u64 x = (pd->bmp.status & pd->bmp.mask); 6639cf9e196SBenjamin Herrenschmidt unsigned int plug; 6642832a81dSGeoff Levand 6652832a81dSGeoff Levand /* check for ipi break first to stop this cpu ASAP */ 6662832a81dSGeoff Levand 6679cf9e196SBenjamin Herrenschmidt if (x & pd->bmp.ipi_debug_brk_mask) 6689cf9e196SBenjamin Herrenschmidt x &= pd->bmp.ipi_debug_brk_mask; 6692832a81dSGeoff Levand 6709cf9e196SBenjamin Herrenschmidt asm volatile("cntlzd %0,%1" : "=r" (plug) : "r" (x)); 6719cf9e196SBenjamin Herrenschmidt plug &= 0x3f; 6722832a81dSGeoff Levand 6739cf9e196SBenjamin Herrenschmidt if (unlikely(plug) == NO_IRQ) { 6742832a81dSGeoff Levand pr_debug("%s:%d: no plug found: cpu %u\n", __func__, __LINE__, 6752832a81dSGeoff Levand pd->cpu); 6769633ac8dSGeoff Levand dump_bmp(&per_cpu(ps3_private, 0)); 6779633ac8dSGeoff Levand dump_bmp(&per_cpu(ps3_private, 1)); 6782832a81dSGeoff Levand return NO_IRQ; 6792832a81dSGeoff Levand } 6802832a81dSGeoff Levand 6812832a81dSGeoff Levand #if defined(DEBUG) 6829cf9e196SBenjamin Herrenschmidt if (unlikely(plug < NUM_ISA_INTERRUPTS || plug > PS3_PLUG_MAX)) { 6839633ac8dSGeoff Levand dump_bmp(&per_cpu(ps3_private, 0)); 6849633ac8dSGeoff Levand dump_bmp(&per_cpu(ps3_private, 1)); 6852832a81dSGeoff Levand BUG(); 6862832a81dSGeoff Levand } 6872832a81dSGeoff Levand #endif 6882832a81dSGeoff Levand return plug; 6892832a81dSGeoff Levand } 6902832a81dSGeoff Levand 6912832a81dSGeoff Levand void __init ps3_init_IRQ(void) 6922832a81dSGeoff Levand { 6932832a81dSGeoff Levand int result; 6942832a81dSGeoff Levand unsigned cpu; 6952832a81dSGeoff Levand struct irq_host *host; 6962832a81dSGeoff Levand 6979633ac8dSGeoff Levand host = irq_alloc_host(IRQ_HOST_MAP_NOMAP, 0, &ps3_host_ops, 6982832a81dSGeoff Levand PS3_INVALID_OUTLET); 6992832a81dSGeoff Levand irq_set_default_host(host); 7002832a81dSGeoff Levand irq_set_virq_count(PS3_PLUG_MAX + 1); 7012832a81dSGeoff Levand 7022832a81dSGeoff Levand for_each_possible_cpu(cpu) { 7039633ac8dSGeoff Levand struct ps3_private *pd = &per_cpu(ps3_private, cpu); 7042832a81dSGeoff Levand 705407e24a0SGeoff Levand lv1_get_logical_ppe_id(&pd->node); 706407e24a0SGeoff Levand pd->cpu = get_hard_smp_processor_id(cpu); 7072832a81dSGeoff Levand spin_lock_init(&pd->bmp.lock); 7082832a81dSGeoff Levand 709407e24a0SGeoff Levand pr_debug("%s:%d: node %lu, cpu %d, bmp %lxh\n", __func__, 710407e24a0SGeoff Levand __LINE__, pd->node, pd->cpu, 711407e24a0SGeoff Levand ps3_mm_phys_to_lpar(__pa(&pd->bmp))); 712407e24a0SGeoff Levand 713407e24a0SGeoff Levand result = lv1_configure_irq_state_bitmap(pd->node, pd->cpu, 714407e24a0SGeoff Levand ps3_mm_phys_to_lpar(__pa(&pd->bmp))); 7152832a81dSGeoff Levand 7162832a81dSGeoff Levand if (result) 7172832a81dSGeoff Levand pr_debug("%s:%d: lv1_configure_irq_state_bitmap failed:" 7182832a81dSGeoff Levand " %s\n", __func__, __LINE__, 7192832a81dSGeoff Levand ps3_result(result)); 7202832a81dSGeoff Levand } 7212832a81dSGeoff Levand 7222832a81dSGeoff Levand ppc_md.get_irq = ps3_get_irq; 7232832a81dSGeoff Levand } 724