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> 2842d284bcSStephen Rothwell #include <asm/smp.h> 292832a81dSGeoff Levand 302832a81dSGeoff Levand #include "platform.h" 312832a81dSGeoff Levand 322832a81dSGeoff Levand #if defined(DEBUG) 3383bb643dSGeert Uytterhoeven #define DBG udbg_printf 342832a81dSGeoff Levand #else 3583bb643dSGeert Uytterhoeven #define DBG pr_debug 362832a81dSGeoff Levand #endif 372832a81dSGeoff Levand 382832a81dSGeoff Levand /** 39861be32cSGeoff Levand * struct ps3_bmp - a per cpu irq status and mask bitmap structure 40861be32cSGeoff Levand * @status: 256 bit status bitmap indexed by plug 41861be32cSGeoff Levand * @unused_1: 42861be32cSGeoff Levand * @mask: 256 bit mask bitmap indexed by plug 43861be32cSGeoff Levand * @unused_2: 44861be32cSGeoff Levand * @lock: 45861be32cSGeoff Levand * @ipi_debug_brk_mask: 46861be32cSGeoff Levand * 47861be32cSGeoff Levand * The HV mantains per SMT thread mappings of HV outlet to HV plug on 48861be32cSGeoff Levand * behalf of the guest. These mappings are implemented as 256 bit guest 49861be32cSGeoff Levand * supplied bitmaps indexed by plug number. The addresses of the bitmaps 50861be32cSGeoff Levand * are registered with the HV through lv1_configure_irq_state_bitmap(). 5157715765SGeoff Levand * The HV requires that the 512 bits of status + mask not cross a page 5257715765SGeoff Levand * boundary. PS3_BMP_MINALIGN is used to define this minimal 64 byte 5357715765SGeoff Levand * alignment. 54861be32cSGeoff Levand * 55861be32cSGeoff Levand * The HV supports 256 plugs per thread, assigned as {0..255}, for a total 56861be32cSGeoff Levand * of 512 plugs supported on a processor. To simplify the logic this 57861be32cSGeoff Levand * implementation equates HV plug value to Linux virq value, constrains each 58861be32cSGeoff Levand * interrupt to have a system wide unique plug number, and limits the range 59861be32cSGeoff Levand * of the plug values to map into the first dword of the bitmaps. This 60861be32cSGeoff Levand * gives a usable range of plug values of {NUM_ISA_INTERRUPTS..63}. Note 61861be32cSGeoff Levand * that there is no constraint on how many in this set an individual thread 62861be32cSGeoff Levand * can acquire. 63861be32cSGeoff Levand */ 64861be32cSGeoff Levand 6557715765SGeoff Levand #define PS3_BMP_MINALIGN 64 6657715765SGeoff Levand 67861be32cSGeoff Levand struct ps3_bmp { 68861be32cSGeoff Levand struct { 69861be32cSGeoff Levand u64 status; 70861be32cSGeoff Levand u64 unused_1[3]; 71861be32cSGeoff Levand u64 mask; 72861be32cSGeoff Levand u64 unused_2[3]; 73861be32cSGeoff Levand }; 74861be32cSGeoff Levand u64 ipi_debug_brk_mask; 75861be32cSGeoff Levand spinlock_t lock; 76861be32cSGeoff Levand }; 77861be32cSGeoff Levand 78861be32cSGeoff Levand /** 79861be32cSGeoff Levand * struct ps3_private - a per cpu data structure 80861be32cSGeoff Levand * @bmp: ps3_bmp structure 81861be32cSGeoff Levand * @node: HV logical_ppe_id 82861be32cSGeoff Levand * @cpu: HV thread_id 83861be32cSGeoff Levand */ 84861be32cSGeoff Levand 85861be32cSGeoff Levand struct ps3_private { 8657715765SGeoff Levand struct ps3_bmp bmp __attribute__ ((aligned (PS3_BMP_MINALIGN))); 87861be32cSGeoff Levand u64 node; 88861be32cSGeoff Levand unsigned int cpu; 89861be32cSGeoff Levand }; 90861be32cSGeoff Levand 91861be32cSGeoff Levand static DEFINE_PER_CPU(struct ps3_private, ps3_private); 92861be32cSGeoff Levand 93dc4f60c2SGeoff Levand /** 94743c1bb0SGeoff Levand * ps3_chip_mask - Set an interrupt mask bit in ps3_bmp. 95743c1bb0SGeoff Levand * @virq: The assigned Linux virq. 96743c1bb0SGeoff Levand * 97743c1bb0SGeoff Levand * Sets ps3_bmp.mask and calls lv1_did_update_interrupt_mask(). 98743c1bb0SGeoff Levand */ 99743c1bb0SGeoff Levand 100743c1bb0SGeoff Levand static void ps3_chip_mask(unsigned int virq) 101743c1bb0SGeoff Levand { 102743c1bb0SGeoff Levand struct ps3_private *pd = get_irq_chip_data(virq); 103743c1bb0SGeoff Levand u64 bit = 0x8000000000000000UL >> virq; 104743c1bb0SGeoff Levand u64 *p = &pd->bmp.mask; 105743c1bb0SGeoff Levand u64 old; 106743c1bb0SGeoff Levand unsigned long flags; 107743c1bb0SGeoff Levand 108743c1bb0SGeoff Levand pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq); 109743c1bb0SGeoff Levand 110743c1bb0SGeoff Levand local_irq_save(flags); 111743c1bb0SGeoff Levand asm volatile( 112743c1bb0SGeoff Levand "1: ldarx %0,0,%3\n" 113743c1bb0SGeoff Levand "andc %0,%0,%2\n" 114743c1bb0SGeoff Levand "stdcx. %0,0,%3\n" 115743c1bb0SGeoff Levand "bne- 1b" 116743c1bb0SGeoff Levand : "=&r" (old), "+m" (*p) 117743c1bb0SGeoff Levand : "r" (bit), "r" (p) 118743c1bb0SGeoff Levand : "cc" ); 119743c1bb0SGeoff Levand 120743c1bb0SGeoff Levand lv1_did_update_interrupt_mask(pd->node, pd->cpu); 121743c1bb0SGeoff Levand local_irq_restore(flags); 122743c1bb0SGeoff Levand } 123743c1bb0SGeoff Levand 124743c1bb0SGeoff Levand /** 125743c1bb0SGeoff Levand * ps3_chip_unmask - Clear an interrupt mask bit in ps3_bmp. 126743c1bb0SGeoff Levand * @virq: The assigned Linux virq. 127743c1bb0SGeoff Levand * 128743c1bb0SGeoff Levand * Clears ps3_bmp.mask and calls lv1_did_update_interrupt_mask(). 129743c1bb0SGeoff Levand */ 130743c1bb0SGeoff Levand 131743c1bb0SGeoff Levand static void ps3_chip_unmask(unsigned int virq) 132743c1bb0SGeoff Levand { 133743c1bb0SGeoff Levand struct ps3_private *pd = get_irq_chip_data(virq); 134743c1bb0SGeoff Levand u64 bit = 0x8000000000000000UL >> virq; 135743c1bb0SGeoff Levand u64 *p = &pd->bmp.mask; 136743c1bb0SGeoff Levand u64 old; 137743c1bb0SGeoff Levand unsigned long flags; 138743c1bb0SGeoff Levand 139743c1bb0SGeoff Levand pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq); 140743c1bb0SGeoff Levand 141743c1bb0SGeoff Levand local_irq_save(flags); 142743c1bb0SGeoff Levand asm volatile( 143743c1bb0SGeoff Levand "1: ldarx %0,0,%3\n" 144743c1bb0SGeoff Levand "or %0,%0,%2\n" 145743c1bb0SGeoff Levand "stdcx. %0,0,%3\n" 146743c1bb0SGeoff Levand "bne- 1b" 147743c1bb0SGeoff Levand : "=&r" (old), "+m" (*p) 148743c1bb0SGeoff Levand : "r" (bit), "r" (p) 149743c1bb0SGeoff Levand : "cc" ); 150743c1bb0SGeoff Levand 151743c1bb0SGeoff Levand lv1_did_update_interrupt_mask(pd->node, pd->cpu); 152743c1bb0SGeoff Levand local_irq_restore(flags); 153743c1bb0SGeoff Levand } 154743c1bb0SGeoff Levand 155743c1bb0SGeoff Levand /** 156743c1bb0SGeoff Levand * ps3_chip_eoi - HV end-of-interrupt. 157743c1bb0SGeoff Levand * @virq: The assigned Linux virq. 158743c1bb0SGeoff Levand * 159743c1bb0SGeoff Levand * Calls lv1_end_of_interrupt_ext(). 160743c1bb0SGeoff Levand */ 161743c1bb0SGeoff Levand 162743c1bb0SGeoff Levand static void ps3_chip_eoi(unsigned int virq) 163743c1bb0SGeoff Levand { 164743c1bb0SGeoff Levand const struct ps3_private *pd = get_irq_chip_data(virq); 165743c1bb0SGeoff Levand lv1_end_of_interrupt_ext(pd->node, pd->cpu, virq); 166743c1bb0SGeoff Levand } 167743c1bb0SGeoff Levand 168743c1bb0SGeoff Levand /** 169743c1bb0SGeoff Levand * ps3_irq_chip - Represents the ps3_bmp as a Linux struct irq_chip. 170743c1bb0SGeoff Levand */ 171743c1bb0SGeoff Levand 172743c1bb0SGeoff Levand static struct irq_chip ps3_irq_chip = { 173743c1bb0SGeoff Levand .typename = "ps3", 174743c1bb0SGeoff Levand .mask = ps3_chip_mask, 175743c1bb0SGeoff Levand .unmask = ps3_chip_unmask, 176743c1bb0SGeoff Levand .eoi = ps3_chip_eoi, 177743c1bb0SGeoff Levand }; 178743c1bb0SGeoff Levand 179743c1bb0SGeoff Levand /** 180dc4f60c2SGeoff Levand * ps3_virq_setup - virq related setup. 181dc4f60c2SGeoff Levand * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 182dc4f60c2SGeoff Levand * serviced on. 183dc4f60c2SGeoff Levand * @outlet: The HV outlet from the various create outlet routines. 184dc4f60c2SGeoff Levand * @virq: The assigned Linux virq. 185dc4f60c2SGeoff Levand * 186dc4f60c2SGeoff Levand * Calls irq_create_mapping() to get a virq and sets the chip data to 187dc4f60c2SGeoff Levand * ps3_private data. 188dc4f60c2SGeoff Levand */ 189dc4f60c2SGeoff Levand 190dc4f60c2SGeoff Levand int ps3_virq_setup(enum ps3_cpu_binding cpu, unsigned long outlet, 191861be32cSGeoff Levand unsigned int *virq) 192861be32cSGeoff Levand { 193861be32cSGeoff Levand int result; 194861be32cSGeoff Levand struct ps3_private *pd; 195861be32cSGeoff Levand 196861be32cSGeoff Levand /* This defines the default interrupt distribution policy. */ 197861be32cSGeoff Levand 198861be32cSGeoff Levand if (cpu == PS3_BINDING_CPU_ANY) 199861be32cSGeoff Levand cpu = 0; 200861be32cSGeoff Levand 201861be32cSGeoff Levand pd = &per_cpu(ps3_private, cpu); 202861be32cSGeoff Levand 203861be32cSGeoff Levand *virq = irq_create_mapping(NULL, outlet); 204861be32cSGeoff Levand 205861be32cSGeoff Levand if (*virq == NO_IRQ) { 206861be32cSGeoff Levand pr_debug("%s:%d: irq_create_mapping failed: outlet %lu\n", 207861be32cSGeoff Levand __func__, __LINE__, outlet); 208861be32cSGeoff Levand result = -ENOMEM; 209861be32cSGeoff Levand goto fail_create; 210861be32cSGeoff Levand } 211861be32cSGeoff Levand 212861be32cSGeoff Levand pr_debug("%s:%d: outlet %lu => cpu %u, virq %u\n", __func__, __LINE__, 213861be32cSGeoff Levand outlet, cpu, *virq); 214861be32cSGeoff Levand 215861be32cSGeoff Levand result = set_irq_chip_data(*virq, pd); 216861be32cSGeoff Levand 217861be32cSGeoff Levand if (result) { 218861be32cSGeoff Levand pr_debug("%s:%d: set_irq_chip_data failed\n", 219861be32cSGeoff Levand __func__, __LINE__); 220861be32cSGeoff Levand goto fail_set; 221861be32cSGeoff Levand } 222861be32cSGeoff Levand 223*9263e85aSGeoff Levand ps3_chip_mask(*virq); 224*9263e85aSGeoff Levand 225861be32cSGeoff Levand return result; 226861be32cSGeoff Levand 227861be32cSGeoff Levand fail_set: 228861be32cSGeoff Levand irq_dispose_mapping(*virq); 229861be32cSGeoff Levand fail_create: 230861be32cSGeoff Levand return result; 231861be32cSGeoff Levand } 232861be32cSGeoff Levand 233dc4f60c2SGeoff Levand /** 234dc4f60c2SGeoff Levand * ps3_virq_destroy - virq related teardown. 235dc4f60c2SGeoff Levand * @virq: The assigned Linux virq. 236dc4f60c2SGeoff Levand * 237dc4f60c2SGeoff Levand * Clears chip data and calls irq_dispose_mapping() for the virq. 238dc4f60c2SGeoff Levand */ 239dc4f60c2SGeoff Levand 240dc4f60c2SGeoff Levand int ps3_virq_destroy(unsigned int virq) 241dc4f60c2SGeoff Levand { 242dc4f60c2SGeoff Levand const struct ps3_private *pd = get_irq_chip_data(virq); 243dc4f60c2SGeoff Levand 244dc4f60c2SGeoff Levand pr_debug("%s:%d: node %lu, cpu %d, virq %u\n", __func__, __LINE__, 245dc4f60c2SGeoff Levand pd->node, pd->cpu, virq); 246dc4f60c2SGeoff Levand 247dc4f60c2SGeoff Levand set_irq_chip_data(virq, NULL); 248dc4f60c2SGeoff Levand irq_dispose_mapping(virq); 249dc4f60c2SGeoff Levand 250dc4f60c2SGeoff Levand pr_debug("%s:%d <-\n", __func__, __LINE__); 251dc4f60c2SGeoff Levand return 0; 252dc4f60c2SGeoff Levand } 253dc4f60c2SGeoff Levand 254dc4f60c2SGeoff Levand /** 255dc4f60c2SGeoff Levand * ps3_irq_plug_setup - Generic outlet and virq related setup. 256dc4f60c2SGeoff Levand * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 257dc4f60c2SGeoff Levand * serviced on. 258dc4f60c2SGeoff Levand * @outlet: The HV outlet from the various create outlet routines. 259dc4f60c2SGeoff Levand * @virq: The assigned Linux virq. 260dc4f60c2SGeoff Levand * 261dc4f60c2SGeoff Levand * Sets up virq and connects the irq plug. 262dc4f60c2SGeoff Levand */ 263dc4f60c2SGeoff Levand 264dc4f60c2SGeoff Levand int ps3_irq_plug_setup(enum ps3_cpu_binding cpu, unsigned long outlet, 265dc4f60c2SGeoff Levand unsigned int *virq) 266dc4f60c2SGeoff Levand { 267dc4f60c2SGeoff Levand int result; 268dc4f60c2SGeoff Levand struct ps3_private *pd; 269dc4f60c2SGeoff Levand 270dc4f60c2SGeoff Levand result = ps3_virq_setup(cpu, outlet, virq); 271dc4f60c2SGeoff Levand 272dc4f60c2SGeoff Levand if (result) { 273dc4f60c2SGeoff Levand pr_debug("%s:%d: ps3_virq_setup failed\n", __func__, __LINE__); 274dc4f60c2SGeoff Levand goto fail_setup; 275dc4f60c2SGeoff Levand } 276dc4f60c2SGeoff Levand 277dc4f60c2SGeoff Levand pd = get_irq_chip_data(*virq); 278dc4f60c2SGeoff Levand 279dc4f60c2SGeoff Levand /* Binds outlet to cpu + virq. */ 280dc4f60c2SGeoff Levand 281dc4f60c2SGeoff Levand result = lv1_connect_irq_plug_ext(pd->node, pd->cpu, *virq, outlet, 0); 282dc4f60c2SGeoff Levand 283dc4f60c2SGeoff Levand if (result) { 284dc4f60c2SGeoff Levand pr_info("%s:%d: lv1_connect_irq_plug_ext failed: %s\n", 285dc4f60c2SGeoff Levand __func__, __LINE__, ps3_result(result)); 286dc4f60c2SGeoff Levand result = -EPERM; 287dc4f60c2SGeoff Levand goto fail_connect; 288dc4f60c2SGeoff Levand } 289dc4f60c2SGeoff Levand 290dc4f60c2SGeoff Levand return result; 291dc4f60c2SGeoff Levand 292dc4f60c2SGeoff Levand fail_connect: 293dc4f60c2SGeoff Levand ps3_virq_destroy(*virq); 294dc4f60c2SGeoff Levand fail_setup: 295dc4f60c2SGeoff Levand return result; 296dc4f60c2SGeoff Levand } 297dc4f60c2SGeoff Levand EXPORT_SYMBOL_GPL(ps3_irq_plug_setup); 298dc4f60c2SGeoff Levand 299dc4f60c2SGeoff Levand /** 300dc4f60c2SGeoff Levand * ps3_irq_plug_destroy - Generic outlet and virq related teardown. 301dc4f60c2SGeoff Levand * @virq: The assigned Linux virq. 302dc4f60c2SGeoff Levand * 303dc4f60c2SGeoff Levand * Disconnects the irq plug and tears down virq. 304dc4f60c2SGeoff Levand * Do not call for system bus event interrupts setup with 305dc4f60c2SGeoff Levand * ps3_sb_event_receive_port_setup(). 306dc4f60c2SGeoff Levand */ 307dc4f60c2SGeoff Levand 308dc4f60c2SGeoff Levand int ps3_irq_plug_destroy(unsigned int virq) 309861be32cSGeoff Levand { 310861be32cSGeoff Levand int result; 311861be32cSGeoff Levand const struct ps3_private *pd = get_irq_chip_data(virq); 312861be32cSGeoff Levand 313861be32cSGeoff Levand pr_debug("%s:%d: node %lu, cpu %d, virq %u\n", __func__, __LINE__, 314861be32cSGeoff Levand pd->node, pd->cpu, virq); 315861be32cSGeoff Levand 316*9263e85aSGeoff Levand ps3_chip_mask(virq); 317*9263e85aSGeoff Levand 318861be32cSGeoff Levand result = lv1_disconnect_irq_plug_ext(pd->node, pd->cpu, virq); 319861be32cSGeoff Levand 320861be32cSGeoff Levand if (result) 321861be32cSGeoff Levand pr_info("%s:%d: lv1_disconnect_irq_plug_ext failed: %s\n", 322861be32cSGeoff Levand __func__, __LINE__, ps3_result(result)); 323861be32cSGeoff Levand 324dc4f60c2SGeoff Levand ps3_virq_destroy(virq); 325dc4f60c2SGeoff Levand 326b1eeb38eSGeert Uytterhoeven return result; 327861be32cSGeoff Levand } 328dc4f60c2SGeoff Levand EXPORT_SYMBOL_GPL(ps3_irq_plug_destroy); 329861be32cSGeoff Levand 330861be32cSGeoff Levand /** 331dc4f60c2SGeoff Levand * ps3_event_receive_port_setup - Setup an event receive port. 332861be32cSGeoff Levand * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 333861be32cSGeoff Levand * serviced on. 3342832a81dSGeoff Levand * @virq: The assigned Linux virq. 3352832a81dSGeoff Levand * 3362832a81dSGeoff Levand * The virq can be used with lv1_connect_interrupt_event_receive_port() to 337dc4f60c2SGeoff Levand * arrange to receive interrupts from system-bus devices, or with 338dc4f60c2SGeoff Levand * ps3_send_event_locally() to signal events. 3392832a81dSGeoff Levand */ 3402832a81dSGeoff Levand 341dc4f60c2SGeoff Levand int ps3_event_receive_port_setup(enum ps3_cpu_binding cpu, unsigned int *virq) 3422832a81dSGeoff Levand { 3432832a81dSGeoff Levand int result; 3442832a81dSGeoff Levand unsigned long outlet; 3452832a81dSGeoff Levand 3462832a81dSGeoff Levand result = lv1_construct_event_receive_port(&outlet); 3472832a81dSGeoff Levand 3482832a81dSGeoff Levand if (result) { 3492832a81dSGeoff Levand pr_debug("%s:%d: lv1_construct_event_receive_port failed: %s\n", 3502832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 3512832a81dSGeoff Levand *virq = NO_IRQ; 3522832a81dSGeoff Levand return result; 3532832a81dSGeoff Levand } 3542832a81dSGeoff Levand 355dc4f60c2SGeoff Levand result = ps3_irq_plug_setup(cpu, outlet, virq); 356861be32cSGeoff Levand BUG_ON(result); 3572832a81dSGeoff Levand 358861be32cSGeoff Levand return result; 3592832a81dSGeoff Levand } 360dc4f60c2SGeoff Levand EXPORT_SYMBOL_GPL(ps3_event_receive_port_setup); 3612832a81dSGeoff Levand 362dc4f60c2SGeoff Levand /** 363dc4f60c2SGeoff Levand * ps3_event_receive_port_destroy - Destroy an event receive port. 364dc4f60c2SGeoff Levand * @virq: The assigned Linux virq. 365dc4f60c2SGeoff Levand * 366dc4f60c2SGeoff Levand * Since ps3_event_receive_port_destroy destroys the receive port outlet, 367dc4f60c2SGeoff Levand * SB devices need to call disconnect_interrupt_event_receive_port() before 368dc4f60c2SGeoff Levand * this. 369dc4f60c2SGeoff Levand */ 370dc4f60c2SGeoff Levand 371dc4f60c2SGeoff Levand int ps3_event_receive_port_destroy(unsigned int virq) 3722832a81dSGeoff Levand { 3732832a81dSGeoff Levand int result; 3742832a81dSGeoff Levand 375*9263e85aSGeoff Levand pr_debug(" -> %s:%d virq %u\n", __func__, __LINE__, virq); 376*9263e85aSGeoff Levand 377*9263e85aSGeoff Levand ps3_chip_mask(virq); 3782832a81dSGeoff Levand 3792832a81dSGeoff Levand result = lv1_destruct_event_receive_port(virq_to_hw(virq)); 3802832a81dSGeoff Levand 3812832a81dSGeoff Levand if (result) 3822832a81dSGeoff Levand pr_debug("%s:%d: lv1_destruct_event_receive_port failed: %s\n", 3832832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 3842832a81dSGeoff Levand 385*9263e85aSGeoff Levand /* 386*9263e85aSGeoff Levand * Don't call ps3_virq_destroy() here since ps3_smp_cleanup_cpu() 387*9263e85aSGeoff Levand * calls from interrupt context (smp_call_function) when kexecing. 388dc4f60c2SGeoff Levand */ 389dc4f60c2SGeoff Levand 3902832a81dSGeoff Levand pr_debug(" <- %s:%d\n", __func__, __LINE__); 3912832a81dSGeoff Levand return result; 3922832a81dSGeoff Levand } 3932832a81dSGeoff Levand 3942832a81dSGeoff Levand int ps3_send_event_locally(unsigned int virq) 3952832a81dSGeoff Levand { 3962832a81dSGeoff Levand return lv1_send_event_locally(virq_to_hw(virq)); 3972832a81dSGeoff Levand } 3982832a81dSGeoff Levand 3992832a81dSGeoff Levand /** 400dc4f60c2SGeoff Levand * ps3_sb_event_receive_port_setup - Setup a system bus event receive port. 401861be32cSGeoff Levand * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 402861be32cSGeoff Levand * serviced on. 4032832a81dSGeoff Levand * @did: The HV device identifier read from the system repository. 4042832a81dSGeoff Levand * @interrupt_id: The device interrupt id read from the system repository. 4052832a81dSGeoff Levand * @virq: The assigned Linux virq. 4062832a81dSGeoff Levand * 4072832a81dSGeoff Levand * An event irq represents a virtual device interrupt. The interrupt_id 4082832a81dSGeoff Levand * coresponds to the software interrupt number. 4092832a81dSGeoff Levand */ 4102832a81dSGeoff Levand 411dc4f60c2SGeoff Levand int ps3_sb_event_receive_port_setup(enum ps3_cpu_binding cpu, 412861be32cSGeoff Levand const struct ps3_device_id *did, unsigned int interrupt_id, 413861be32cSGeoff Levand unsigned int *virq) 4142832a81dSGeoff Levand { 415dc4f60c2SGeoff Levand /* this should go in system-bus.c */ 416dc4f60c2SGeoff Levand 4172832a81dSGeoff Levand int result; 4182832a81dSGeoff Levand 419dc4f60c2SGeoff Levand result = ps3_event_receive_port_setup(cpu, virq); 4202832a81dSGeoff Levand 4212832a81dSGeoff Levand if (result) 4222832a81dSGeoff Levand return result; 4232832a81dSGeoff Levand 4242832a81dSGeoff Levand result = lv1_connect_interrupt_event_receive_port(did->bus_id, 4252832a81dSGeoff Levand did->dev_id, virq_to_hw(*virq), interrupt_id); 4262832a81dSGeoff Levand 4272832a81dSGeoff Levand if (result) { 4282832a81dSGeoff Levand pr_debug("%s:%d: lv1_connect_interrupt_event_receive_port" 4292832a81dSGeoff Levand " failed: %s\n", __func__, __LINE__, 4302832a81dSGeoff Levand ps3_result(result)); 431dc4f60c2SGeoff Levand ps3_event_receive_port_destroy(*virq); 4322832a81dSGeoff Levand *virq = NO_IRQ; 4332832a81dSGeoff Levand return result; 4342832a81dSGeoff Levand } 4352832a81dSGeoff Levand 4362832a81dSGeoff Levand pr_debug("%s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__, 4372832a81dSGeoff Levand interrupt_id, *virq); 4382832a81dSGeoff Levand 4392832a81dSGeoff Levand return 0; 4402832a81dSGeoff Levand } 441dc4f60c2SGeoff Levand EXPORT_SYMBOL(ps3_sb_event_receive_port_setup); 4422832a81dSGeoff Levand 443dc4f60c2SGeoff Levand int ps3_sb_event_receive_port_destroy(const struct ps3_device_id *did, 4442832a81dSGeoff Levand unsigned int interrupt_id, unsigned int virq) 4452832a81dSGeoff Levand { 446dc4f60c2SGeoff Levand /* this should go in system-bus.c */ 447dc4f60c2SGeoff Levand 4482832a81dSGeoff Levand int result; 4492832a81dSGeoff Levand 4502832a81dSGeoff Levand pr_debug(" -> %s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__, 4512832a81dSGeoff Levand interrupt_id, virq); 4522832a81dSGeoff Levand 4532832a81dSGeoff Levand result = lv1_disconnect_interrupt_event_receive_port(did->bus_id, 4542832a81dSGeoff Levand did->dev_id, virq_to_hw(virq), interrupt_id); 4552832a81dSGeoff Levand 4562832a81dSGeoff Levand if (result) 4572832a81dSGeoff Levand pr_debug("%s:%d: lv1_disconnect_interrupt_event_receive_port" 4582832a81dSGeoff Levand " failed: %s\n", __func__, __LINE__, 4592832a81dSGeoff Levand ps3_result(result)); 4602832a81dSGeoff Levand 461dc4f60c2SGeoff Levand result = ps3_event_receive_port_destroy(virq); 462dc4f60c2SGeoff Levand BUG_ON(result); 4632832a81dSGeoff Levand 464*9263e85aSGeoff Levand /* 465*9263e85aSGeoff Levand * ps3_event_receive_port_destroy() destroys the IRQ plug, 466*9263e85aSGeoff Levand * so don't call ps3_irq_plug_destroy() here. 467*9263e85aSGeoff Levand */ 468*9263e85aSGeoff Levand 469*9263e85aSGeoff Levand result = ps3_virq_destroy(virq); 470*9263e85aSGeoff Levand BUG_ON(result); 471*9263e85aSGeoff Levand 4722832a81dSGeoff Levand pr_debug(" <- %s:%d\n", __func__, __LINE__); 4732832a81dSGeoff Levand return result; 4742832a81dSGeoff Levand } 475dc4f60c2SGeoff Levand EXPORT_SYMBOL(ps3_sb_event_receive_port_destroy); 4762832a81dSGeoff Levand 4772832a81dSGeoff Levand /** 478dc4f60c2SGeoff Levand * ps3_io_irq_setup - Setup a system bus io irq. 479dc4f60c2SGeoff Levand * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 480dc4f60c2SGeoff Levand * serviced on. 481dc4f60c2SGeoff Levand * @interrupt_id: The device interrupt id read from the system repository. 482dc4f60c2SGeoff Levand * @virq: The assigned Linux virq. 483dc4f60c2SGeoff Levand * 484dc4f60c2SGeoff Levand * An io irq represents a non-virtualized device interrupt. interrupt_id 485dc4f60c2SGeoff Levand * coresponds to the interrupt number of the interrupt controller. 486dc4f60c2SGeoff Levand */ 487dc4f60c2SGeoff Levand 488dc4f60c2SGeoff Levand int ps3_io_irq_setup(enum ps3_cpu_binding cpu, unsigned int interrupt_id, 489dc4f60c2SGeoff Levand unsigned int *virq) 490dc4f60c2SGeoff Levand { 491dc4f60c2SGeoff Levand int result; 492dc4f60c2SGeoff Levand unsigned long outlet; 493dc4f60c2SGeoff Levand 494dc4f60c2SGeoff Levand result = lv1_construct_io_irq_outlet(interrupt_id, &outlet); 495dc4f60c2SGeoff Levand 496dc4f60c2SGeoff Levand if (result) { 497dc4f60c2SGeoff Levand pr_debug("%s:%d: lv1_construct_io_irq_outlet failed: %s\n", 498dc4f60c2SGeoff Levand __func__, __LINE__, ps3_result(result)); 499dc4f60c2SGeoff Levand return result; 500dc4f60c2SGeoff Levand } 501dc4f60c2SGeoff Levand 502dc4f60c2SGeoff Levand result = ps3_irq_plug_setup(cpu, outlet, virq); 503dc4f60c2SGeoff Levand BUG_ON(result); 504dc4f60c2SGeoff Levand 505dc4f60c2SGeoff Levand return result; 506dc4f60c2SGeoff Levand } 507dc4f60c2SGeoff Levand EXPORT_SYMBOL_GPL(ps3_io_irq_setup); 508dc4f60c2SGeoff Levand 509dc4f60c2SGeoff Levand int ps3_io_irq_destroy(unsigned int virq) 510dc4f60c2SGeoff Levand { 511dc4f60c2SGeoff Levand int result; 512*9263e85aSGeoff Levand unsigned long outlet = virq_to_hw(virq); 513dc4f60c2SGeoff Levand 514*9263e85aSGeoff Levand ps3_chip_mask(virq); 515*9263e85aSGeoff Levand 516*9263e85aSGeoff Levand /* 517*9263e85aSGeoff Levand * lv1_destruct_io_irq_outlet() will destroy the IRQ plug, 518*9263e85aSGeoff Levand * so call ps3_irq_plug_destroy() first. 519*9263e85aSGeoff Levand */ 520*9263e85aSGeoff Levand 521*9263e85aSGeoff Levand result = ps3_irq_plug_destroy(virq); 522*9263e85aSGeoff Levand BUG_ON(result); 523*9263e85aSGeoff Levand 524*9263e85aSGeoff Levand result = lv1_destruct_io_irq_outlet(outlet); 525dc4f60c2SGeoff Levand 526dc4f60c2SGeoff Levand if (result) 527dc4f60c2SGeoff Levand pr_debug("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n", 528dc4f60c2SGeoff Levand __func__, __LINE__, ps3_result(result)); 529dc4f60c2SGeoff Levand 530dc4f60c2SGeoff Levand return result; 531dc4f60c2SGeoff Levand } 532dc4f60c2SGeoff Levand EXPORT_SYMBOL_GPL(ps3_io_irq_destroy); 533dc4f60c2SGeoff Levand 534dc4f60c2SGeoff Levand /** 535dc4f60c2SGeoff Levand * ps3_vuart_irq_setup - Setup the system virtual uart virq. 536861be32cSGeoff Levand * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 537861be32cSGeoff Levand * serviced on. 5382832a81dSGeoff Levand * @virt_addr_bmp: The caller supplied virtual uart interrupt bitmap. 5392832a81dSGeoff Levand * @virq: The assigned Linux virq. 5402832a81dSGeoff Levand * 5412832a81dSGeoff Levand * The system supports only a single virtual uart, so multiple calls without 5422832a81dSGeoff Levand * freeing the interrupt will return a wrong state error. 5432832a81dSGeoff Levand */ 5442832a81dSGeoff Levand 545dc4f60c2SGeoff Levand int ps3_vuart_irq_setup(enum ps3_cpu_binding cpu, void* virt_addr_bmp, 546861be32cSGeoff Levand unsigned int *virq) 5472832a81dSGeoff Levand { 5482832a81dSGeoff Levand int result; 5492832a81dSGeoff Levand unsigned long outlet; 550861be32cSGeoff Levand u64 lpar_addr; 5512832a81dSGeoff Levand 552861be32cSGeoff Levand BUG_ON(!is_kernel_addr((u64)virt_addr_bmp)); 5532832a81dSGeoff Levand 5542832a81dSGeoff Levand lpar_addr = ps3_mm_phys_to_lpar(__pa(virt_addr_bmp)); 5552832a81dSGeoff Levand 5562832a81dSGeoff Levand result = lv1_configure_virtual_uart_irq(lpar_addr, &outlet); 5572832a81dSGeoff Levand 5582832a81dSGeoff Levand if (result) { 5592832a81dSGeoff Levand pr_debug("%s:%d: lv1_configure_virtual_uart_irq failed: %s\n", 5602832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 5612832a81dSGeoff Levand return result; 5622832a81dSGeoff Levand } 5632832a81dSGeoff Levand 564dc4f60c2SGeoff Levand result = ps3_irq_plug_setup(cpu, outlet, virq); 565861be32cSGeoff Levand BUG_ON(result); 5662832a81dSGeoff Levand 567861be32cSGeoff Levand return result; 5682832a81dSGeoff Levand } 5692832a81dSGeoff Levand 570dc4f60c2SGeoff Levand int ps3_vuart_irq_destroy(unsigned int virq) 5712832a81dSGeoff Levand { 5722832a81dSGeoff Levand int result; 5732832a81dSGeoff Levand 574*9263e85aSGeoff Levand ps3_chip_mask(virq); 5752832a81dSGeoff Levand result = lv1_deconfigure_virtual_uart_irq(); 5762832a81dSGeoff Levand 5772832a81dSGeoff Levand if (result) { 5782832a81dSGeoff Levand pr_debug("%s:%d: lv1_configure_virtual_uart_irq failed: %s\n", 5792832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 5802832a81dSGeoff Levand return result; 5812832a81dSGeoff Levand } 5822832a81dSGeoff Levand 583dc4f60c2SGeoff Levand result = ps3_irq_plug_destroy(virq); 584dc4f60c2SGeoff Levand BUG_ON(result); 5852832a81dSGeoff Levand 5862832a81dSGeoff Levand return result; 5872832a81dSGeoff Levand } 5882832a81dSGeoff Levand 5892832a81dSGeoff Levand /** 590dc4f60c2SGeoff Levand * ps3_spe_irq_setup - Setup an spe virq. 591861be32cSGeoff Levand * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 592861be32cSGeoff Levand * serviced on. 5932832a81dSGeoff Levand * @spe_id: The spe_id returned from lv1_construct_logical_spe(). 5942832a81dSGeoff Levand * @class: The spe interrupt class {0,1,2}. 5952832a81dSGeoff Levand * @virq: The assigned Linux virq. 5962832a81dSGeoff Levand * 5972832a81dSGeoff Levand */ 5982832a81dSGeoff Levand 599dc4f60c2SGeoff Levand int ps3_spe_irq_setup(enum ps3_cpu_binding cpu, unsigned long spe_id, 600861be32cSGeoff Levand unsigned int class, unsigned int *virq) 6012832a81dSGeoff Levand { 6022832a81dSGeoff Levand int result; 6032832a81dSGeoff Levand unsigned long outlet; 6042832a81dSGeoff Levand 6052832a81dSGeoff Levand BUG_ON(class > 2); 6062832a81dSGeoff Levand 6072832a81dSGeoff Levand result = lv1_get_spe_irq_outlet(spe_id, class, &outlet); 6082832a81dSGeoff Levand 6092832a81dSGeoff Levand if (result) { 6102832a81dSGeoff Levand pr_debug("%s:%d: lv1_get_spe_irq_outlet failed: %s\n", 6112832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 6122832a81dSGeoff Levand return result; 6132832a81dSGeoff Levand } 6142832a81dSGeoff Levand 615dc4f60c2SGeoff Levand result = ps3_irq_plug_setup(cpu, outlet, virq); 616861be32cSGeoff Levand BUG_ON(result); 6172832a81dSGeoff Levand 618861be32cSGeoff Levand return result; 6192832a81dSGeoff Levand } 6202832a81dSGeoff Levand 621dc4f60c2SGeoff Levand int ps3_spe_irq_destroy(unsigned int virq) 6222832a81dSGeoff Levand { 623*9263e85aSGeoff Levand int result; 624*9263e85aSGeoff Levand 625*9263e85aSGeoff Levand ps3_chip_mask(virq); 626*9263e85aSGeoff Levand 627*9263e85aSGeoff Levand result = ps3_irq_plug_destroy(virq); 628dc4f60c2SGeoff Levand BUG_ON(result); 629*9263e85aSGeoff Levand 630*9263e85aSGeoff Levand return result; 6312832a81dSGeoff Levand } 6322832a81dSGeoff Levand 633b1eeb38eSGeert Uytterhoeven 6342832a81dSGeoff Levand #define PS3_INVALID_OUTLET ((irq_hw_number_t)-1) 6352832a81dSGeoff Levand #define PS3_PLUG_MAX 63 6362832a81dSGeoff Levand 6372832a81dSGeoff Levand #if defined(DEBUG) 638861be32cSGeoff Levand static void _dump_64_bmp(const char *header, const u64 *p, unsigned cpu, 6392832a81dSGeoff Levand const char* func, int line) 6402832a81dSGeoff Levand { 6412832a81dSGeoff Levand pr_debug("%s:%d: %s %u {%04lx_%04lx_%04lx_%04lx}\n", 6422832a81dSGeoff Levand func, line, header, cpu, 6432832a81dSGeoff Levand *p >> 48, (*p >> 32) & 0xffff, (*p >> 16) & 0xffff, 6442832a81dSGeoff Levand *p & 0xffff); 6452832a81dSGeoff Levand } 6462832a81dSGeoff Levand 647848cfdc5SGeoff Levand static void __maybe_unused _dump_256_bmp(const char *header, 648861be32cSGeoff Levand const u64 *p, unsigned cpu, const char* func, int line) 6492832a81dSGeoff Levand { 6502832a81dSGeoff Levand pr_debug("%s:%d: %s %u {%016lx:%016lx:%016lx:%016lx}\n", 6512832a81dSGeoff Levand func, line, header, cpu, p[0], p[1], p[2], p[3]); 6522832a81dSGeoff Levand } 6532832a81dSGeoff Levand 6542832a81dSGeoff Levand #define dump_bmp(_x) _dump_bmp(_x, __func__, __LINE__) 6559633ac8dSGeoff Levand static void _dump_bmp(struct ps3_private* pd, const char* func, int line) 6562832a81dSGeoff Levand { 6572832a81dSGeoff Levand unsigned long flags; 6582832a81dSGeoff Levand 6592832a81dSGeoff Levand spin_lock_irqsave(&pd->bmp.lock, flags); 6602832a81dSGeoff Levand _dump_64_bmp("stat", &pd->bmp.status, pd->cpu, func, line); 6612832a81dSGeoff Levand _dump_64_bmp("mask", &pd->bmp.mask, pd->cpu, func, line); 6622832a81dSGeoff Levand spin_unlock_irqrestore(&pd->bmp.lock, flags); 6632832a81dSGeoff Levand } 6642832a81dSGeoff Levand 6652832a81dSGeoff Levand #define dump_mask(_x) _dump_mask(_x, __func__, __LINE__) 666848cfdc5SGeoff Levand static void __maybe_unused _dump_mask(struct ps3_private *pd, 6672832a81dSGeoff Levand const char* func, int line) 6682832a81dSGeoff Levand { 6692832a81dSGeoff Levand unsigned long flags; 6702832a81dSGeoff Levand 6712832a81dSGeoff Levand spin_lock_irqsave(&pd->bmp.lock, flags); 6722832a81dSGeoff Levand _dump_64_bmp("mask", &pd->bmp.mask, pd->cpu, func, line); 6732832a81dSGeoff Levand spin_unlock_irqrestore(&pd->bmp.lock, flags); 6742832a81dSGeoff Levand } 6752832a81dSGeoff Levand #else 6769633ac8dSGeoff Levand static void dump_bmp(struct ps3_private* pd) {}; 6772832a81dSGeoff Levand #endif /* defined(DEBUG) */ 6782832a81dSGeoff Levand 6799633ac8dSGeoff Levand static void ps3_host_unmap(struct irq_host *h, unsigned int virq) 6802832a81dSGeoff Levand { 681861be32cSGeoff Levand set_irq_chip_data(virq, NULL); 6822832a81dSGeoff Levand } 6832832a81dSGeoff Levand 6849633ac8dSGeoff Levand static int ps3_host_map(struct irq_host *h, unsigned int virq, 6852832a81dSGeoff Levand irq_hw_number_t hwirq) 6862832a81dSGeoff Levand { 687861be32cSGeoff Levand pr_debug("%s:%d: hwirq %lu, virq %u\n", __func__, __LINE__, hwirq, 688861be32cSGeoff Levand virq); 6892832a81dSGeoff Levand 690*9263e85aSGeoff Levand set_irq_chip_and_handler(virq, &ps3_irq_chip, handle_fasteoi_irq); 6912832a81dSGeoff Levand 692861be32cSGeoff Levand return 0; 6932832a81dSGeoff Levand } 6942832a81dSGeoff Levand 6959633ac8dSGeoff Levand static struct irq_host_ops ps3_host_ops = { 6969633ac8dSGeoff Levand .map = ps3_host_map, 6979633ac8dSGeoff Levand .unmap = ps3_host_unmap, 6982832a81dSGeoff Levand }; 6992832a81dSGeoff Levand 7002832a81dSGeoff Levand void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq) 7012832a81dSGeoff Levand { 7029633ac8dSGeoff Levand struct ps3_private *pd = &per_cpu(ps3_private, cpu); 7032832a81dSGeoff Levand 7042832a81dSGeoff Levand pd->bmp.ipi_debug_brk_mask = 0x8000000000000000UL >> virq; 7052832a81dSGeoff Levand 7062832a81dSGeoff Levand pr_debug("%s:%d: cpu %u, virq %u, mask %lxh\n", __func__, __LINE__, 7072832a81dSGeoff Levand cpu, virq, pd->bmp.ipi_debug_brk_mask); 7082832a81dSGeoff Levand } 7092832a81dSGeoff Levand 710*9263e85aSGeoff Levand static unsigned int ps3_get_irq(void) 7112832a81dSGeoff Levand { 7129cf9e196SBenjamin Herrenschmidt struct ps3_private *pd = &__get_cpu_var(ps3_private); 713861be32cSGeoff Levand u64 x = (pd->bmp.status & pd->bmp.mask); 7149cf9e196SBenjamin Herrenschmidt unsigned int plug; 7152832a81dSGeoff Levand 7162832a81dSGeoff Levand /* check for ipi break first to stop this cpu ASAP */ 7172832a81dSGeoff Levand 7189cf9e196SBenjamin Herrenschmidt if (x & pd->bmp.ipi_debug_brk_mask) 7199cf9e196SBenjamin Herrenschmidt x &= pd->bmp.ipi_debug_brk_mask; 7202832a81dSGeoff Levand 7219cf9e196SBenjamin Herrenschmidt asm volatile("cntlzd %0,%1" : "=r" (plug) : "r" (x)); 7229cf9e196SBenjamin Herrenschmidt plug &= 0x3f; 7232832a81dSGeoff Levand 7249cf9e196SBenjamin Herrenschmidt if (unlikely(plug) == NO_IRQ) { 7252832a81dSGeoff Levand pr_debug("%s:%d: no plug found: cpu %u\n", __func__, __LINE__, 7262832a81dSGeoff Levand pd->cpu); 7279633ac8dSGeoff Levand dump_bmp(&per_cpu(ps3_private, 0)); 7289633ac8dSGeoff Levand dump_bmp(&per_cpu(ps3_private, 1)); 7292832a81dSGeoff Levand return NO_IRQ; 7302832a81dSGeoff Levand } 7312832a81dSGeoff Levand 7322832a81dSGeoff Levand #if defined(DEBUG) 7339cf9e196SBenjamin Herrenschmidt if (unlikely(plug < NUM_ISA_INTERRUPTS || plug > PS3_PLUG_MAX)) { 7349633ac8dSGeoff Levand dump_bmp(&per_cpu(ps3_private, 0)); 7359633ac8dSGeoff Levand dump_bmp(&per_cpu(ps3_private, 1)); 7362832a81dSGeoff Levand BUG(); 7372832a81dSGeoff Levand } 7382832a81dSGeoff Levand #endif 7392832a81dSGeoff Levand return plug; 7402832a81dSGeoff Levand } 7412832a81dSGeoff Levand 7422832a81dSGeoff Levand void __init ps3_init_IRQ(void) 7432832a81dSGeoff Levand { 7442832a81dSGeoff Levand int result; 7452832a81dSGeoff Levand unsigned cpu; 7462832a81dSGeoff Levand struct irq_host *host; 7472832a81dSGeoff Levand 7489633ac8dSGeoff Levand host = irq_alloc_host(IRQ_HOST_MAP_NOMAP, 0, &ps3_host_ops, 7492832a81dSGeoff Levand PS3_INVALID_OUTLET); 7502832a81dSGeoff Levand irq_set_default_host(host); 7512832a81dSGeoff Levand irq_set_virq_count(PS3_PLUG_MAX + 1); 7522832a81dSGeoff Levand 7532832a81dSGeoff Levand for_each_possible_cpu(cpu) { 7549633ac8dSGeoff Levand struct ps3_private *pd = &per_cpu(ps3_private, cpu); 7552832a81dSGeoff Levand 756407e24a0SGeoff Levand lv1_get_logical_ppe_id(&pd->node); 757407e24a0SGeoff Levand pd->cpu = get_hard_smp_processor_id(cpu); 7582832a81dSGeoff Levand spin_lock_init(&pd->bmp.lock); 7592832a81dSGeoff Levand 760407e24a0SGeoff Levand pr_debug("%s:%d: node %lu, cpu %d, bmp %lxh\n", __func__, 761407e24a0SGeoff Levand __LINE__, pd->node, pd->cpu, 762407e24a0SGeoff Levand ps3_mm_phys_to_lpar(__pa(&pd->bmp))); 763407e24a0SGeoff Levand 764407e24a0SGeoff Levand result = lv1_configure_irq_state_bitmap(pd->node, pd->cpu, 765407e24a0SGeoff Levand ps3_mm_phys_to_lpar(__pa(&pd->bmp))); 7662832a81dSGeoff Levand 7672832a81dSGeoff Levand if (result) 7682832a81dSGeoff Levand pr_debug("%s:%d: lv1_configure_irq_state_bitmap failed:" 7692832a81dSGeoff Levand " %s\n", __func__, __LINE__, 7702832a81dSGeoff Levand ps3_result(result)); 7712832a81dSGeoff Levand } 7722832a81dSGeoff Levand 7732832a81dSGeoff Levand ppc_md.get_irq = ps3_get_irq; 7742832a81dSGeoff Levand } 775*9263e85aSGeoff Levand 776*9263e85aSGeoff Levand void ps3_shutdown_IRQ(int cpu) 777*9263e85aSGeoff Levand { 778*9263e85aSGeoff Levand int result; 779*9263e85aSGeoff Levand u64 ppe_id; 780*9263e85aSGeoff Levand u64 thread_id = get_hard_smp_processor_id(cpu); 781*9263e85aSGeoff Levand 782*9263e85aSGeoff Levand lv1_get_logical_ppe_id(&ppe_id); 783*9263e85aSGeoff Levand result = lv1_configure_irq_state_bitmap(ppe_id, thread_id, 0); 784*9263e85aSGeoff Levand 785*9263e85aSGeoff Levand DBG("%s:%d: lv1_configure_irq_state_bitmap (%lu:%lu/%d) %s\n", __func__, 786*9263e85aSGeoff Levand __LINE__, ppe_id, thread_id, cpu, ps3_result(result)); 787*9263e85aSGeoff Levand } 788