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> 224b16f8e2SPaul Gortmaker #include <linux/export.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 3432b9074bSGeoff Levand #define FAIL udbg_printf 352832a81dSGeoff Levand #else 3632b9074bSGeoff Levand #define DBG pr_devel 3732b9074bSGeoff Levand #define FAIL pr_debug 382832a81dSGeoff Levand #endif 392832a81dSGeoff Levand 402832a81dSGeoff Levand /** 41861be32cSGeoff Levand * struct ps3_bmp - a per cpu irq status and mask bitmap structure 42861be32cSGeoff Levand * @status: 256 bit status bitmap indexed by plug 4332b9074bSGeoff Levand * @unused_1: Alignment 44861be32cSGeoff Levand * @mask: 256 bit mask bitmap indexed by plug 4532b9074bSGeoff Levand * @unused_2: Alignment 46861be32cSGeoff Levand * 47b595076aSUwe Kleine-König * The HV maintains 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. 6346ca0d15SStephen Rothwell * 6446ca0d15SStephen Rothwell * The mask is declared as unsigned long so we can use set/clear_bit on it. 65861be32cSGeoff Levand */ 66861be32cSGeoff Levand 6757715765SGeoff Levand #define PS3_BMP_MINALIGN 64 6857715765SGeoff Levand 69861be32cSGeoff Levand struct ps3_bmp { 70861be32cSGeoff Levand struct { 71861be32cSGeoff Levand u64 status; 72861be32cSGeoff Levand u64 unused_1[3]; 7346ca0d15SStephen Rothwell unsigned long mask; 74861be32cSGeoff Levand u64 unused_2[3]; 75861be32cSGeoff Levand }; 76861be32cSGeoff Levand }; 77861be32cSGeoff Levand 78861be32cSGeoff Levand /** 79861be32cSGeoff Levand * struct ps3_private - a per cpu data structure 80861be32cSGeoff Levand * @bmp: ps3_bmp structure 8132b9074bSGeoff Levand * @bmp_lock: Syncronize access to bmp. 8232b9074bSGeoff Levand * @ipi_debug_brk_mask: Mask for debug break IPIs 83aab83500SGeoff Levand * @ppe_id: HV logical_ppe_id 84aab83500SGeoff Levand * @thread_id: HV thread_id 8532b9074bSGeoff Levand * @ipi_mask: Mask of IPI virqs 86861be32cSGeoff Levand */ 87861be32cSGeoff Levand 88861be32cSGeoff Levand struct ps3_private { 8957715765SGeoff Levand struct ps3_bmp bmp __attribute__ ((aligned (PS3_BMP_MINALIGN))); 9032b9074bSGeoff Levand spinlock_t bmp_lock; 91aab83500SGeoff Levand u64 ppe_id; 92aab83500SGeoff Levand u64 thread_id; 9332b9074bSGeoff Levand unsigned long ipi_debug_brk_mask; 9472f3bea0SGeoff Levand unsigned long ipi_mask; 95861be32cSGeoff Levand }; 96861be32cSGeoff Levand 97861be32cSGeoff Levand static DEFINE_PER_CPU(struct ps3_private, ps3_private); 98861be32cSGeoff Levand 99dc4f60c2SGeoff Levand /** 100743c1bb0SGeoff Levand * ps3_chip_mask - Set an interrupt mask bit in ps3_bmp. 101743c1bb0SGeoff Levand * @virq: The assigned Linux virq. 102743c1bb0SGeoff Levand * 103743c1bb0SGeoff Levand * Sets ps3_bmp.mask and calls lv1_did_update_interrupt_mask(). 104743c1bb0SGeoff Levand */ 105743c1bb0SGeoff Levand 1068126708aSLennert Buytenhek static void ps3_chip_mask(struct irq_data *d) 107743c1bb0SGeoff Levand { 1088126708aSLennert Buytenhek struct ps3_private *pd = irq_data_get_irq_chip_data(d); 109743c1bb0SGeoff Levand unsigned long flags; 110743c1bb0SGeoff Levand 11132b9074bSGeoff Levand DBG("%s:%d: thread_id %llu, virq %d\n", __func__, __LINE__, 1128126708aSLennert Buytenhek pd->thread_id, d->irq); 113743c1bb0SGeoff Levand 114743c1bb0SGeoff Levand local_irq_save(flags); 1158126708aSLennert Buytenhek clear_bit(63 - d->irq, &pd->bmp.mask); 116aab83500SGeoff Levand lv1_did_update_interrupt_mask(pd->ppe_id, pd->thread_id); 117743c1bb0SGeoff Levand local_irq_restore(flags); 118743c1bb0SGeoff Levand } 119743c1bb0SGeoff Levand 120743c1bb0SGeoff Levand /** 121743c1bb0SGeoff Levand * ps3_chip_unmask - Clear an interrupt mask bit in ps3_bmp. 122743c1bb0SGeoff Levand * @virq: The assigned Linux virq. 123743c1bb0SGeoff Levand * 124743c1bb0SGeoff Levand * Clears ps3_bmp.mask and calls lv1_did_update_interrupt_mask(). 125743c1bb0SGeoff Levand */ 126743c1bb0SGeoff Levand 1278126708aSLennert Buytenhek static void ps3_chip_unmask(struct irq_data *d) 128743c1bb0SGeoff Levand { 1298126708aSLennert Buytenhek struct ps3_private *pd = irq_data_get_irq_chip_data(d); 130743c1bb0SGeoff Levand unsigned long flags; 131743c1bb0SGeoff Levand 13232b9074bSGeoff Levand DBG("%s:%d: thread_id %llu, virq %d\n", __func__, __LINE__, 1338126708aSLennert Buytenhek pd->thread_id, d->irq); 134743c1bb0SGeoff Levand 135743c1bb0SGeoff Levand local_irq_save(flags); 1368126708aSLennert Buytenhek set_bit(63 - d->irq, &pd->bmp.mask); 137aab83500SGeoff Levand lv1_did_update_interrupt_mask(pd->ppe_id, pd->thread_id); 138743c1bb0SGeoff Levand local_irq_restore(flags); 139743c1bb0SGeoff Levand } 140743c1bb0SGeoff Levand 141743c1bb0SGeoff Levand /** 142743c1bb0SGeoff Levand * ps3_chip_eoi - HV end-of-interrupt. 143743c1bb0SGeoff Levand * @virq: The assigned Linux virq. 144743c1bb0SGeoff Levand * 145743c1bb0SGeoff Levand * Calls lv1_end_of_interrupt_ext(). 146743c1bb0SGeoff Levand */ 147743c1bb0SGeoff Levand 1488126708aSLennert Buytenhek static void ps3_chip_eoi(struct irq_data *d) 149743c1bb0SGeoff Levand { 1508126708aSLennert Buytenhek const struct ps3_private *pd = irq_data_get_irq_chip_data(d); 15172f3bea0SGeoff Levand 15272f3bea0SGeoff Levand /* non-IPIs are EOIed here. */ 15372f3bea0SGeoff Levand 15472f3bea0SGeoff Levand if (!test_bit(63 - d->irq, &pd->ipi_mask)) 1558126708aSLennert Buytenhek lv1_end_of_interrupt_ext(pd->ppe_id, pd->thread_id, d->irq); 156743c1bb0SGeoff Levand } 157743c1bb0SGeoff Levand 158743c1bb0SGeoff Levand /** 159743c1bb0SGeoff Levand * ps3_irq_chip - Represents the ps3_bmp as a Linux struct irq_chip. 160743c1bb0SGeoff Levand */ 161743c1bb0SGeoff Levand 162743c1bb0SGeoff Levand static struct irq_chip ps3_irq_chip = { 163b27df672SThomas Gleixner .name = "ps3", 1648126708aSLennert Buytenhek .irq_mask = ps3_chip_mask, 1658126708aSLennert Buytenhek .irq_unmask = ps3_chip_unmask, 1668126708aSLennert Buytenhek .irq_eoi = ps3_chip_eoi, 167743c1bb0SGeoff Levand }; 168743c1bb0SGeoff Levand 169743c1bb0SGeoff Levand /** 170dc4f60c2SGeoff Levand * ps3_virq_setup - virq related setup. 171dc4f60c2SGeoff Levand * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 172dc4f60c2SGeoff Levand * serviced on. 173dc4f60c2SGeoff Levand * @outlet: The HV outlet from the various create outlet routines. 174dc4f60c2SGeoff Levand * @virq: The assigned Linux virq. 175dc4f60c2SGeoff Levand * 176dc4f60c2SGeoff Levand * Calls irq_create_mapping() to get a virq and sets the chip data to 177dc4f60c2SGeoff Levand * ps3_private data. 178dc4f60c2SGeoff Levand */ 179dc4f60c2SGeoff Levand 180fdedb4caSGeert Uytterhoeven static int ps3_virq_setup(enum ps3_cpu_binding cpu, unsigned long outlet, 181861be32cSGeoff Levand unsigned int *virq) 182861be32cSGeoff Levand { 183861be32cSGeoff Levand int result; 184861be32cSGeoff Levand struct ps3_private *pd; 185861be32cSGeoff Levand 186861be32cSGeoff Levand /* This defines the default interrupt distribution policy. */ 187861be32cSGeoff Levand 188861be32cSGeoff Levand if (cpu == PS3_BINDING_CPU_ANY) 189861be32cSGeoff Levand cpu = 0; 190861be32cSGeoff Levand 191861be32cSGeoff Levand pd = &per_cpu(ps3_private, cpu); 192861be32cSGeoff Levand 193861be32cSGeoff Levand *virq = irq_create_mapping(NULL, outlet); 194861be32cSGeoff Levand 195861be32cSGeoff Levand if (*virq == NO_IRQ) { 19632b9074bSGeoff Levand FAIL("%s:%d: irq_create_mapping failed: outlet %lu\n", 197861be32cSGeoff Levand __func__, __LINE__, outlet); 198861be32cSGeoff Levand result = -ENOMEM; 199861be32cSGeoff Levand goto fail_create; 200861be32cSGeoff Levand } 201861be32cSGeoff Levand 20232b9074bSGeoff Levand DBG("%s:%d: outlet %lu => cpu %u, virq %u\n", __func__, __LINE__, 203861be32cSGeoff Levand outlet, cpu, *virq); 204861be32cSGeoff Levand 205ec775d0eSThomas Gleixner result = irq_set_chip_data(*virq, pd); 206861be32cSGeoff Levand 207861be32cSGeoff Levand if (result) { 20832b9074bSGeoff Levand FAIL("%s:%d: irq_set_chip_data failed\n", 209861be32cSGeoff Levand __func__, __LINE__); 210861be32cSGeoff Levand goto fail_set; 211861be32cSGeoff Levand } 212861be32cSGeoff Levand 2138126708aSLennert Buytenhek ps3_chip_mask(irq_get_irq_data(*virq)); 2149263e85aSGeoff Levand 215861be32cSGeoff Levand return result; 216861be32cSGeoff Levand 217861be32cSGeoff Levand fail_set: 218861be32cSGeoff Levand irq_dispose_mapping(*virq); 219861be32cSGeoff Levand fail_create: 220861be32cSGeoff Levand return result; 221861be32cSGeoff Levand } 222861be32cSGeoff Levand 223dc4f60c2SGeoff Levand /** 224dc4f60c2SGeoff Levand * ps3_virq_destroy - virq related teardown. 225dc4f60c2SGeoff Levand * @virq: The assigned Linux virq. 226dc4f60c2SGeoff Levand * 227dc4f60c2SGeoff Levand * Clears chip data and calls irq_dispose_mapping() for the virq. 228dc4f60c2SGeoff Levand */ 229dc4f60c2SGeoff Levand 230fdedb4caSGeert Uytterhoeven static int ps3_virq_destroy(unsigned int virq) 231dc4f60c2SGeoff Levand { 232ec775d0eSThomas Gleixner const struct ps3_private *pd = irq_get_chip_data(virq); 233dc4f60c2SGeoff Levand 23432b9074bSGeoff Levand DBG("%s:%d: ppe_id %llu, thread_id %llu, virq %u\n", __func__, 235aab83500SGeoff Levand __LINE__, pd->ppe_id, pd->thread_id, virq); 236dc4f60c2SGeoff Levand 237ec775d0eSThomas Gleixner irq_set_chip_data(virq, NULL); 238dc4f60c2SGeoff Levand irq_dispose_mapping(virq); 239dc4f60c2SGeoff Levand 24032b9074bSGeoff Levand DBG("%s:%d <-\n", __func__, __LINE__); 241dc4f60c2SGeoff Levand return 0; 242dc4f60c2SGeoff Levand } 243dc4f60c2SGeoff Levand 244dc4f60c2SGeoff Levand /** 245dc4f60c2SGeoff Levand * ps3_irq_plug_setup - Generic outlet and virq related setup. 246dc4f60c2SGeoff Levand * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 247dc4f60c2SGeoff Levand * serviced on. 248dc4f60c2SGeoff Levand * @outlet: The HV outlet from the various create outlet routines. 249dc4f60c2SGeoff Levand * @virq: The assigned Linux virq. 250dc4f60c2SGeoff Levand * 251dc4f60c2SGeoff Levand * Sets up virq and connects the irq plug. 252dc4f60c2SGeoff Levand */ 253dc4f60c2SGeoff Levand 254dc4f60c2SGeoff Levand int ps3_irq_plug_setup(enum ps3_cpu_binding cpu, unsigned long outlet, 255dc4f60c2SGeoff Levand unsigned int *virq) 256dc4f60c2SGeoff Levand { 257dc4f60c2SGeoff Levand int result; 258dc4f60c2SGeoff Levand struct ps3_private *pd; 259dc4f60c2SGeoff Levand 260dc4f60c2SGeoff Levand result = ps3_virq_setup(cpu, outlet, virq); 261dc4f60c2SGeoff Levand 262dc4f60c2SGeoff Levand if (result) { 26332b9074bSGeoff Levand FAIL("%s:%d: ps3_virq_setup failed\n", __func__, __LINE__); 264dc4f60c2SGeoff Levand goto fail_setup; 265dc4f60c2SGeoff Levand } 266dc4f60c2SGeoff Levand 267ec775d0eSThomas Gleixner pd = irq_get_chip_data(*virq); 268dc4f60c2SGeoff Levand 269dc4f60c2SGeoff Levand /* Binds outlet to cpu + virq. */ 270dc4f60c2SGeoff Levand 271aab83500SGeoff Levand result = lv1_connect_irq_plug_ext(pd->ppe_id, pd->thread_id, *virq, 272aab83500SGeoff Levand outlet, 0); 273dc4f60c2SGeoff Levand 274dc4f60c2SGeoff Levand if (result) { 27532b9074bSGeoff Levand FAIL("%s:%d: lv1_connect_irq_plug_ext failed: %s\n", 276dc4f60c2SGeoff Levand __func__, __LINE__, ps3_result(result)); 277dc4f60c2SGeoff Levand result = -EPERM; 278dc4f60c2SGeoff Levand goto fail_connect; 279dc4f60c2SGeoff Levand } 280dc4f60c2SGeoff Levand 281dc4f60c2SGeoff Levand return result; 282dc4f60c2SGeoff Levand 283dc4f60c2SGeoff Levand fail_connect: 284dc4f60c2SGeoff Levand ps3_virq_destroy(*virq); 285dc4f60c2SGeoff Levand fail_setup: 286dc4f60c2SGeoff Levand return result; 287dc4f60c2SGeoff Levand } 288dc4f60c2SGeoff Levand EXPORT_SYMBOL_GPL(ps3_irq_plug_setup); 289dc4f60c2SGeoff Levand 290dc4f60c2SGeoff Levand /** 291dc4f60c2SGeoff Levand * ps3_irq_plug_destroy - Generic outlet and virq related teardown. 292dc4f60c2SGeoff Levand * @virq: The assigned Linux virq. 293dc4f60c2SGeoff Levand * 294dc4f60c2SGeoff Levand * Disconnects the irq plug and tears down virq. 295dc4f60c2SGeoff Levand * Do not call for system bus event interrupts setup with 296dc4f60c2SGeoff Levand * ps3_sb_event_receive_port_setup(). 297dc4f60c2SGeoff Levand */ 298dc4f60c2SGeoff Levand 299dc4f60c2SGeoff Levand int ps3_irq_plug_destroy(unsigned int virq) 300861be32cSGeoff Levand { 301861be32cSGeoff Levand int result; 302ec775d0eSThomas Gleixner const struct ps3_private *pd = irq_get_chip_data(virq); 303861be32cSGeoff Levand 30432b9074bSGeoff Levand DBG("%s:%d: ppe_id %llu, thread_id %llu, virq %u\n", __func__, 305aab83500SGeoff Levand __LINE__, pd->ppe_id, pd->thread_id, virq); 306861be32cSGeoff Levand 3078126708aSLennert Buytenhek ps3_chip_mask(irq_get_irq_data(virq)); 3089263e85aSGeoff Levand 309aab83500SGeoff Levand result = lv1_disconnect_irq_plug_ext(pd->ppe_id, pd->thread_id, virq); 310861be32cSGeoff Levand 311861be32cSGeoff Levand if (result) 31232b9074bSGeoff Levand FAIL("%s:%d: lv1_disconnect_irq_plug_ext failed: %s\n", 313861be32cSGeoff Levand __func__, __LINE__, ps3_result(result)); 314861be32cSGeoff Levand 315dc4f60c2SGeoff Levand ps3_virq_destroy(virq); 316dc4f60c2SGeoff Levand 317b1eeb38eSGeert Uytterhoeven return result; 318861be32cSGeoff Levand } 319dc4f60c2SGeoff Levand EXPORT_SYMBOL_GPL(ps3_irq_plug_destroy); 320861be32cSGeoff Levand 321861be32cSGeoff Levand /** 322dc4f60c2SGeoff Levand * ps3_event_receive_port_setup - Setup an event receive port. 323861be32cSGeoff Levand * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 324861be32cSGeoff Levand * serviced on. 3252832a81dSGeoff Levand * @virq: The assigned Linux virq. 3262832a81dSGeoff Levand * 3272832a81dSGeoff Levand * The virq can be used with lv1_connect_interrupt_event_receive_port() to 328dc4f60c2SGeoff Levand * arrange to receive interrupts from system-bus devices, or with 329dc4f60c2SGeoff Levand * ps3_send_event_locally() to signal events. 3302832a81dSGeoff Levand */ 3312832a81dSGeoff Levand 332dc4f60c2SGeoff Levand int ps3_event_receive_port_setup(enum ps3_cpu_binding cpu, unsigned int *virq) 3332832a81dSGeoff Levand { 3342832a81dSGeoff Levand int result; 335b17b3df1SStephen Rothwell u64 outlet; 3362832a81dSGeoff Levand 3372832a81dSGeoff Levand result = lv1_construct_event_receive_port(&outlet); 3382832a81dSGeoff Levand 3392832a81dSGeoff Levand if (result) { 34032b9074bSGeoff Levand FAIL("%s:%d: lv1_construct_event_receive_port failed: %s\n", 3412832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 3422832a81dSGeoff Levand *virq = NO_IRQ; 3432832a81dSGeoff Levand return result; 3442832a81dSGeoff Levand } 3452832a81dSGeoff Levand 346dc4f60c2SGeoff Levand result = ps3_irq_plug_setup(cpu, outlet, virq); 347861be32cSGeoff Levand BUG_ON(result); 3482832a81dSGeoff Levand 349861be32cSGeoff Levand return result; 3502832a81dSGeoff Levand } 351dc4f60c2SGeoff Levand EXPORT_SYMBOL_GPL(ps3_event_receive_port_setup); 3522832a81dSGeoff Levand 353dc4f60c2SGeoff Levand /** 354dc4f60c2SGeoff Levand * ps3_event_receive_port_destroy - Destroy an event receive port. 355dc4f60c2SGeoff Levand * @virq: The assigned Linux virq. 356dc4f60c2SGeoff Levand * 357dc4f60c2SGeoff Levand * Since ps3_event_receive_port_destroy destroys the receive port outlet, 358dc4f60c2SGeoff Levand * SB devices need to call disconnect_interrupt_event_receive_port() before 359dc4f60c2SGeoff Levand * this. 360dc4f60c2SGeoff Levand */ 361dc4f60c2SGeoff Levand 362dc4f60c2SGeoff Levand int ps3_event_receive_port_destroy(unsigned int virq) 3632832a81dSGeoff Levand { 3642832a81dSGeoff Levand int result; 3652832a81dSGeoff Levand 36632b9074bSGeoff Levand DBG(" -> %s:%d virq %u\n", __func__, __LINE__, virq); 3679263e85aSGeoff Levand 3688126708aSLennert Buytenhek ps3_chip_mask(irq_get_irq_data(virq)); 3692832a81dSGeoff Levand 3702832a81dSGeoff Levand result = lv1_destruct_event_receive_port(virq_to_hw(virq)); 3712832a81dSGeoff Levand 3722832a81dSGeoff Levand if (result) 37332b9074bSGeoff Levand FAIL("%s:%d: lv1_destruct_event_receive_port failed: %s\n", 3742832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 3752832a81dSGeoff Levand 3769263e85aSGeoff Levand /* 3779263e85aSGeoff Levand * Don't call ps3_virq_destroy() here since ps3_smp_cleanup_cpu() 3789263e85aSGeoff Levand * calls from interrupt context (smp_call_function) when kexecing. 379dc4f60c2SGeoff Levand */ 380dc4f60c2SGeoff Levand 38132b9074bSGeoff Levand DBG(" <- %s:%d\n", __func__, __LINE__); 3822832a81dSGeoff Levand return result; 3832832a81dSGeoff Levand } 3842832a81dSGeoff Levand 3852832a81dSGeoff Levand int ps3_send_event_locally(unsigned int virq) 3862832a81dSGeoff Levand { 3872832a81dSGeoff Levand return lv1_send_event_locally(virq_to_hw(virq)); 3882832a81dSGeoff Levand } 3892832a81dSGeoff Levand 3902832a81dSGeoff Levand /** 391dc4f60c2SGeoff Levand * ps3_sb_event_receive_port_setup - Setup a system bus event receive port. 392861be32cSGeoff Levand * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 393861be32cSGeoff Levand * serviced on. 3946bb5cf10SGeoff Levand * @dev: The system bus device instance. 3952832a81dSGeoff Levand * @virq: The assigned Linux virq. 3962832a81dSGeoff Levand * 3972832a81dSGeoff Levand * An event irq represents a virtual device interrupt. The interrupt_id 3982832a81dSGeoff Levand * coresponds to the software interrupt number. 3992832a81dSGeoff Levand */ 4002832a81dSGeoff Levand 4016bb5cf10SGeoff Levand int ps3_sb_event_receive_port_setup(struct ps3_system_bus_device *dev, 4026bb5cf10SGeoff Levand enum ps3_cpu_binding cpu, unsigned int *virq) 4032832a81dSGeoff Levand { 404dc4f60c2SGeoff Levand /* this should go in system-bus.c */ 405dc4f60c2SGeoff Levand 4062832a81dSGeoff Levand int result; 4072832a81dSGeoff Levand 408dc4f60c2SGeoff Levand result = ps3_event_receive_port_setup(cpu, virq); 4092832a81dSGeoff Levand 4102832a81dSGeoff Levand if (result) 4112832a81dSGeoff Levand return result; 4122832a81dSGeoff Levand 4136bb5cf10SGeoff Levand result = lv1_connect_interrupt_event_receive_port(dev->bus_id, 4146bb5cf10SGeoff Levand dev->dev_id, virq_to_hw(*virq), dev->interrupt_id); 4152832a81dSGeoff Levand 4162832a81dSGeoff Levand if (result) { 41732b9074bSGeoff Levand FAIL("%s:%d: lv1_connect_interrupt_event_receive_port" 4182832a81dSGeoff Levand " failed: %s\n", __func__, __LINE__, 4192832a81dSGeoff Levand ps3_result(result)); 420dc4f60c2SGeoff Levand ps3_event_receive_port_destroy(*virq); 4212832a81dSGeoff Levand *virq = NO_IRQ; 4222832a81dSGeoff Levand return result; 4232832a81dSGeoff Levand } 4242832a81dSGeoff Levand 42532b9074bSGeoff Levand DBG("%s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__, 4266bb5cf10SGeoff Levand dev->interrupt_id, *virq); 4272832a81dSGeoff Levand 4282832a81dSGeoff Levand return 0; 4292832a81dSGeoff Levand } 430dc4f60c2SGeoff Levand EXPORT_SYMBOL(ps3_sb_event_receive_port_setup); 4312832a81dSGeoff Levand 4326bb5cf10SGeoff Levand int ps3_sb_event_receive_port_destroy(struct ps3_system_bus_device *dev, 4336bb5cf10SGeoff Levand unsigned int virq) 4342832a81dSGeoff Levand { 435dc4f60c2SGeoff Levand /* this should go in system-bus.c */ 436dc4f60c2SGeoff Levand 4372832a81dSGeoff Levand int result; 4382832a81dSGeoff Levand 43932b9074bSGeoff Levand DBG(" -> %s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__, 4406bb5cf10SGeoff Levand dev->interrupt_id, virq); 4412832a81dSGeoff Levand 4426bb5cf10SGeoff Levand result = lv1_disconnect_interrupt_event_receive_port(dev->bus_id, 4436bb5cf10SGeoff Levand dev->dev_id, virq_to_hw(virq), dev->interrupt_id); 4442832a81dSGeoff Levand 4452832a81dSGeoff Levand if (result) 44632b9074bSGeoff Levand FAIL("%s:%d: lv1_disconnect_interrupt_event_receive_port" 4472832a81dSGeoff Levand " failed: %s\n", __func__, __LINE__, 4482832a81dSGeoff Levand ps3_result(result)); 4492832a81dSGeoff Levand 450dc4f60c2SGeoff Levand result = ps3_event_receive_port_destroy(virq); 451dc4f60c2SGeoff Levand BUG_ON(result); 4522832a81dSGeoff Levand 4539263e85aSGeoff Levand /* 4549263e85aSGeoff Levand * ps3_event_receive_port_destroy() destroys the IRQ plug, 4559263e85aSGeoff Levand * so don't call ps3_irq_plug_destroy() here. 4569263e85aSGeoff Levand */ 4579263e85aSGeoff Levand 4589263e85aSGeoff Levand result = ps3_virq_destroy(virq); 4599263e85aSGeoff Levand BUG_ON(result); 4609263e85aSGeoff Levand 46132b9074bSGeoff Levand DBG(" <- %s:%d\n", __func__, __LINE__); 4622832a81dSGeoff Levand return result; 4632832a81dSGeoff Levand } 464dc4f60c2SGeoff Levand EXPORT_SYMBOL(ps3_sb_event_receive_port_destroy); 4652832a81dSGeoff Levand 4662832a81dSGeoff Levand /** 467dc4f60c2SGeoff Levand * ps3_io_irq_setup - Setup a system bus io irq. 468dc4f60c2SGeoff Levand * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 469dc4f60c2SGeoff Levand * serviced on. 470dc4f60c2SGeoff Levand * @interrupt_id: The device interrupt id read from the system repository. 471dc4f60c2SGeoff Levand * @virq: The assigned Linux virq. 472dc4f60c2SGeoff Levand * 473dc4f60c2SGeoff Levand * An io irq represents a non-virtualized device interrupt. interrupt_id 474dc4f60c2SGeoff Levand * coresponds to the interrupt number of the interrupt controller. 475dc4f60c2SGeoff Levand */ 476dc4f60c2SGeoff Levand 477dc4f60c2SGeoff Levand int ps3_io_irq_setup(enum ps3_cpu_binding cpu, unsigned int interrupt_id, 478dc4f60c2SGeoff Levand unsigned int *virq) 479dc4f60c2SGeoff Levand { 480dc4f60c2SGeoff Levand int result; 481b17b3df1SStephen Rothwell u64 outlet; 482dc4f60c2SGeoff Levand 483dc4f60c2SGeoff Levand result = lv1_construct_io_irq_outlet(interrupt_id, &outlet); 484dc4f60c2SGeoff Levand 485dc4f60c2SGeoff Levand if (result) { 48632b9074bSGeoff Levand FAIL("%s:%d: lv1_construct_io_irq_outlet failed: %s\n", 487dc4f60c2SGeoff Levand __func__, __LINE__, ps3_result(result)); 488dc4f60c2SGeoff Levand return result; 489dc4f60c2SGeoff Levand } 490dc4f60c2SGeoff Levand 491dc4f60c2SGeoff Levand result = ps3_irq_plug_setup(cpu, outlet, virq); 492dc4f60c2SGeoff Levand BUG_ON(result); 493dc4f60c2SGeoff Levand 494dc4f60c2SGeoff Levand return result; 495dc4f60c2SGeoff Levand } 496dc4f60c2SGeoff Levand EXPORT_SYMBOL_GPL(ps3_io_irq_setup); 497dc4f60c2SGeoff Levand 498dc4f60c2SGeoff Levand int ps3_io_irq_destroy(unsigned int virq) 499dc4f60c2SGeoff Levand { 500dc4f60c2SGeoff Levand int result; 5019263e85aSGeoff Levand unsigned long outlet = virq_to_hw(virq); 502dc4f60c2SGeoff Levand 5038126708aSLennert Buytenhek ps3_chip_mask(irq_get_irq_data(virq)); 5049263e85aSGeoff Levand 5059263e85aSGeoff Levand /* 5069263e85aSGeoff Levand * lv1_destruct_io_irq_outlet() will destroy the IRQ plug, 5079263e85aSGeoff Levand * so call ps3_irq_plug_destroy() first. 5089263e85aSGeoff Levand */ 5099263e85aSGeoff Levand 5109263e85aSGeoff Levand result = ps3_irq_plug_destroy(virq); 5119263e85aSGeoff Levand BUG_ON(result); 5129263e85aSGeoff Levand 5139263e85aSGeoff Levand result = lv1_destruct_io_irq_outlet(outlet); 514dc4f60c2SGeoff Levand 515dc4f60c2SGeoff Levand if (result) 51632b9074bSGeoff Levand FAIL("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n", 517dc4f60c2SGeoff Levand __func__, __LINE__, ps3_result(result)); 518dc4f60c2SGeoff Levand 519dc4f60c2SGeoff Levand return result; 520dc4f60c2SGeoff Levand } 521dc4f60c2SGeoff Levand EXPORT_SYMBOL_GPL(ps3_io_irq_destroy); 522dc4f60c2SGeoff Levand 523dc4f60c2SGeoff Levand /** 524dc4f60c2SGeoff Levand * ps3_vuart_irq_setup - Setup the system virtual uart virq. 525861be32cSGeoff Levand * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 526861be32cSGeoff Levand * serviced on. 5272832a81dSGeoff Levand * @virt_addr_bmp: The caller supplied virtual uart interrupt bitmap. 5282832a81dSGeoff Levand * @virq: The assigned Linux virq. 5292832a81dSGeoff Levand * 5302832a81dSGeoff Levand * The system supports only a single virtual uart, so multiple calls without 5312832a81dSGeoff Levand * freeing the interrupt will return a wrong state error. 5322832a81dSGeoff Levand */ 5332832a81dSGeoff Levand 534dc4f60c2SGeoff Levand int ps3_vuart_irq_setup(enum ps3_cpu_binding cpu, void* virt_addr_bmp, 535861be32cSGeoff Levand unsigned int *virq) 5362832a81dSGeoff Levand { 5372832a81dSGeoff Levand int result; 538b17b3df1SStephen Rothwell u64 outlet; 539861be32cSGeoff Levand u64 lpar_addr; 5402832a81dSGeoff Levand 541861be32cSGeoff Levand BUG_ON(!is_kernel_addr((u64)virt_addr_bmp)); 5422832a81dSGeoff Levand 5432832a81dSGeoff Levand lpar_addr = ps3_mm_phys_to_lpar(__pa(virt_addr_bmp)); 5442832a81dSGeoff Levand 5452832a81dSGeoff Levand result = lv1_configure_virtual_uart_irq(lpar_addr, &outlet); 5462832a81dSGeoff Levand 5472832a81dSGeoff Levand if (result) { 54832b9074bSGeoff Levand FAIL("%s:%d: lv1_configure_virtual_uart_irq failed: %s\n", 5492832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 5502832a81dSGeoff Levand return result; 5512832a81dSGeoff Levand } 5522832a81dSGeoff Levand 553dc4f60c2SGeoff Levand result = ps3_irq_plug_setup(cpu, outlet, virq); 554861be32cSGeoff Levand BUG_ON(result); 5552832a81dSGeoff Levand 556861be32cSGeoff Levand return result; 5572832a81dSGeoff Levand } 5587626e78dSGeoff Levand EXPORT_SYMBOL_GPL(ps3_vuart_irq_setup); 5592832a81dSGeoff Levand 560dc4f60c2SGeoff Levand int ps3_vuart_irq_destroy(unsigned int virq) 5612832a81dSGeoff Levand { 5622832a81dSGeoff Levand int result; 5632832a81dSGeoff Levand 5648126708aSLennert Buytenhek ps3_chip_mask(irq_get_irq_data(virq)); 5652832a81dSGeoff Levand result = lv1_deconfigure_virtual_uart_irq(); 5662832a81dSGeoff Levand 5672832a81dSGeoff Levand if (result) { 56832b9074bSGeoff Levand FAIL("%s:%d: lv1_configure_virtual_uart_irq failed: %s\n", 5692832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 5702832a81dSGeoff Levand return result; 5712832a81dSGeoff Levand } 5722832a81dSGeoff Levand 573dc4f60c2SGeoff Levand result = ps3_irq_plug_destroy(virq); 574dc4f60c2SGeoff Levand BUG_ON(result); 5752832a81dSGeoff Levand 5762832a81dSGeoff Levand return result; 5772832a81dSGeoff Levand } 5787626e78dSGeoff Levand EXPORT_SYMBOL_GPL(ps3_vuart_irq_destroy); 5792832a81dSGeoff Levand 5802832a81dSGeoff Levand /** 581dc4f60c2SGeoff Levand * ps3_spe_irq_setup - Setup an spe virq. 582861be32cSGeoff Levand * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 583861be32cSGeoff Levand * serviced on. 5842832a81dSGeoff Levand * @spe_id: The spe_id returned from lv1_construct_logical_spe(). 5852832a81dSGeoff Levand * @class: The spe interrupt class {0,1,2}. 5862832a81dSGeoff Levand * @virq: The assigned Linux virq. 5872832a81dSGeoff Levand * 5882832a81dSGeoff Levand */ 5892832a81dSGeoff Levand 590dc4f60c2SGeoff Levand int ps3_spe_irq_setup(enum ps3_cpu_binding cpu, unsigned long spe_id, 591861be32cSGeoff Levand unsigned int class, unsigned int *virq) 5922832a81dSGeoff Levand { 5932832a81dSGeoff Levand int result; 594b17b3df1SStephen Rothwell u64 outlet; 5952832a81dSGeoff Levand 5962832a81dSGeoff Levand BUG_ON(class > 2); 5972832a81dSGeoff Levand 5982832a81dSGeoff Levand result = lv1_get_spe_irq_outlet(spe_id, class, &outlet); 5992832a81dSGeoff Levand 6002832a81dSGeoff Levand if (result) { 60132b9074bSGeoff Levand FAIL("%s:%d: lv1_get_spe_irq_outlet failed: %s\n", 6022832a81dSGeoff Levand __func__, __LINE__, ps3_result(result)); 6032832a81dSGeoff Levand return result; 6042832a81dSGeoff Levand } 6052832a81dSGeoff Levand 606dc4f60c2SGeoff Levand result = ps3_irq_plug_setup(cpu, outlet, virq); 607861be32cSGeoff Levand BUG_ON(result); 6082832a81dSGeoff Levand 609861be32cSGeoff Levand return result; 6102832a81dSGeoff Levand } 6112832a81dSGeoff Levand 612dc4f60c2SGeoff Levand int ps3_spe_irq_destroy(unsigned int virq) 6132832a81dSGeoff Levand { 6149263e85aSGeoff Levand int result; 6159263e85aSGeoff Levand 6168126708aSLennert Buytenhek ps3_chip_mask(irq_get_irq_data(virq)); 6179263e85aSGeoff Levand 6189263e85aSGeoff Levand result = ps3_irq_plug_destroy(virq); 619dc4f60c2SGeoff Levand BUG_ON(result); 6209263e85aSGeoff Levand 6219263e85aSGeoff Levand return result; 6222832a81dSGeoff Levand } 6232832a81dSGeoff Levand 624b1eeb38eSGeert Uytterhoeven 6252832a81dSGeoff Levand #define PS3_INVALID_OUTLET ((irq_hw_number_t)-1) 6262832a81dSGeoff Levand #define PS3_PLUG_MAX 63 6272832a81dSGeoff Levand 6282832a81dSGeoff Levand #if defined(DEBUG) 629861be32cSGeoff Levand static void _dump_64_bmp(const char *header, const u64 *p, unsigned cpu, 6302832a81dSGeoff Levand const char* func, int line) 6312832a81dSGeoff Levand { 63232b9074bSGeoff Levand pr_debug("%s:%d: %s %u {%04llx_%04llx_%04llx_%04llx}\n", 6332832a81dSGeoff Levand func, line, header, cpu, 6342832a81dSGeoff Levand *p >> 48, (*p >> 32) & 0xffff, (*p >> 16) & 0xffff, 6352832a81dSGeoff Levand *p & 0xffff); 6362832a81dSGeoff Levand } 6372832a81dSGeoff Levand 638848cfdc5SGeoff Levand static void __maybe_unused _dump_256_bmp(const char *header, 639861be32cSGeoff Levand const u64 *p, unsigned cpu, const char* func, int line) 6402832a81dSGeoff Levand { 64132b9074bSGeoff Levand pr_debug("%s:%d: %s %u {%016llx:%016llx:%016llx:%016llx}\n", 6422832a81dSGeoff Levand func, line, header, cpu, p[0], p[1], p[2], p[3]); 6432832a81dSGeoff Levand } 6442832a81dSGeoff Levand 6452832a81dSGeoff Levand #define dump_bmp(_x) _dump_bmp(_x, __func__, __LINE__) 6469633ac8dSGeoff Levand static void _dump_bmp(struct ps3_private* pd, const char* func, int line) 6472832a81dSGeoff Levand { 6482832a81dSGeoff Levand unsigned long flags; 6492832a81dSGeoff Levand 65032b9074bSGeoff Levand spin_lock_irqsave(&pd->bmp_lock, flags); 651aab83500SGeoff Levand _dump_64_bmp("stat", &pd->bmp.status, pd->thread_id, func, line); 65232b9074bSGeoff Levand _dump_64_bmp("mask", (u64*)&pd->bmp.mask, pd->thread_id, func, line); 65332b9074bSGeoff Levand spin_unlock_irqrestore(&pd->bmp_lock, flags); 6542832a81dSGeoff Levand } 6552832a81dSGeoff Levand 6562832a81dSGeoff Levand #define dump_mask(_x) _dump_mask(_x, __func__, __LINE__) 657848cfdc5SGeoff Levand static void __maybe_unused _dump_mask(struct ps3_private *pd, 6582832a81dSGeoff Levand const char* func, int line) 6592832a81dSGeoff Levand { 6602832a81dSGeoff Levand unsigned long flags; 6612832a81dSGeoff Levand 66232b9074bSGeoff Levand spin_lock_irqsave(&pd->bmp_lock, flags); 66332b9074bSGeoff Levand _dump_64_bmp("mask", (u64*)&pd->bmp.mask, pd->thread_id, func, line); 66432b9074bSGeoff Levand spin_unlock_irqrestore(&pd->bmp_lock, flags); 6652832a81dSGeoff Levand } 6662832a81dSGeoff Levand #else 6679633ac8dSGeoff Levand static void dump_bmp(struct ps3_private* pd) {}; 6682832a81dSGeoff Levand #endif /* defined(DEBUG) */ 6692832a81dSGeoff Levand 670bae1d8f1SGrant Likely static int ps3_host_map(struct irq_domain *h, unsigned int virq, 6712832a81dSGeoff Levand irq_hw_number_t hwirq) 6722832a81dSGeoff Levand { 67332b9074bSGeoff Levand DBG("%s:%d: hwirq %lu, virq %u\n", __func__, __LINE__, hwirq, 674861be32cSGeoff Levand virq); 6752832a81dSGeoff Levand 676ec775d0eSThomas Gleixner irq_set_chip_and_handler(virq, &ps3_irq_chip, handle_fasteoi_irq); 6772832a81dSGeoff Levand 678861be32cSGeoff Levand return 0; 6792832a81dSGeoff Levand } 6802832a81dSGeoff Levand 681bae1d8f1SGrant Likely static int ps3_host_match(struct irq_domain *h, struct device_node *np) 6828528ab84SMichael Ellerman { 6838528ab84SMichael Ellerman /* Match all */ 6848528ab84SMichael Ellerman return 1; 6858528ab84SMichael Ellerman } 6868528ab84SMichael Ellerman 6879f70b8ebSGrant Likely static const struct irq_domain_ops ps3_host_ops = { 6889633ac8dSGeoff Levand .map = ps3_host_map, 6898528ab84SMichael Ellerman .match = ps3_host_match, 6902832a81dSGeoff Levand }; 6912832a81dSGeoff Levand 6922832a81dSGeoff Levand void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq) 6932832a81dSGeoff Levand { 6949633ac8dSGeoff Levand struct ps3_private *pd = &per_cpu(ps3_private, cpu); 6952832a81dSGeoff Levand 69632b9074bSGeoff Levand set_bit(63 - virq, &pd->ipi_debug_brk_mask); 6972832a81dSGeoff Levand 69832b9074bSGeoff Levand DBG("%s:%d: cpu %u, virq %u, mask %lxh\n", __func__, __LINE__, 69932b9074bSGeoff Levand cpu, virq, pd->ipi_debug_brk_mask); 7002832a81dSGeoff Levand } 7012832a81dSGeoff Levand 70272f3bea0SGeoff Levand void __init ps3_register_ipi_irq(unsigned int cpu, unsigned int virq) 70372f3bea0SGeoff Levand { 70472f3bea0SGeoff Levand struct ps3_private *pd = &per_cpu(ps3_private, cpu); 70572f3bea0SGeoff Levand 70672f3bea0SGeoff Levand set_bit(63 - virq, &pd->ipi_mask); 70772f3bea0SGeoff Levand 70872f3bea0SGeoff Levand DBG("%s:%d: cpu %u, virq %u, ipi_mask %lxh\n", __func__, __LINE__, 70972f3bea0SGeoff Levand cpu, virq, pd->ipi_mask); 71072f3bea0SGeoff Levand } 71172f3bea0SGeoff Levand 7129263e85aSGeoff Levand static unsigned int ps3_get_irq(void) 7132832a81dSGeoff Levand { 714*69111bacSChristoph Lameter struct ps3_private *pd = this_cpu_ptr(&ps3_private); 715861be32cSGeoff Levand u64 x = (pd->bmp.status & pd->bmp.mask); 7169cf9e196SBenjamin Herrenschmidt unsigned int plug; 7172832a81dSGeoff Levand 7182832a81dSGeoff Levand /* check for ipi break first to stop this cpu ASAP */ 7192832a81dSGeoff Levand 72032b9074bSGeoff Levand if (x & pd->ipi_debug_brk_mask) 72132b9074bSGeoff Levand x &= pd->ipi_debug_brk_mask; 7222832a81dSGeoff Levand 7239cf9e196SBenjamin Herrenschmidt asm volatile("cntlzd %0,%1" : "=r" (plug) : "r" (x)); 7249cf9e196SBenjamin Herrenschmidt plug &= 0x3f; 7252832a81dSGeoff Levand 726ad18c3dbSRoel Kluin if (unlikely(plug == NO_IRQ)) { 72732b9074bSGeoff Levand DBG("%s:%d: no plug found: thread_id %llu\n", __func__, 728aab83500SGeoff Levand __LINE__, pd->thread_id); 7299633ac8dSGeoff Levand dump_bmp(&per_cpu(ps3_private, 0)); 7309633ac8dSGeoff Levand dump_bmp(&per_cpu(ps3_private, 1)); 7312832a81dSGeoff Levand return NO_IRQ; 7322832a81dSGeoff Levand } 7332832a81dSGeoff Levand 7342832a81dSGeoff Levand #if defined(DEBUG) 7359cf9e196SBenjamin Herrenschmidt if (unlikely(plug < NUM_ISA_INTERRUPTS || plug > PS3_PLUG_MAX)) { 7369633ac8dSGeoff Levand dump_bmp(&per_cpu(ps3_private, 0)); 7379633ac8dSGeoff Levand dump_bmp(&per_cpu(ps3_private, 1)); 7382832a81dSGeoff Levand BUG(); 7392832a81dSGeoff Levand } 7402832a81dSGeoff Levand #endif 74172f3bea0SGeoff Levand 74272f3bea0SGeoff Levand /* IPIs are EOIed here. */ 74372f3bea0SGeoff Levand 74472f3bea0SGeoff Levand if (test_bit(63 - plug, &pd->ipi_mask)) 74572f3bea0SGeoff Levand lv1_end_of_interrupt_ext(pd->ppe_id, pd->thread_id, plug); 74672f3bea0SGeoff Levand 7472832a81dSGeoff Levand return plug; 7482832a81dSGeoff Levand } 7492832a81dSGeoff Levand 7502832a81dSGeoff Levand void __init ps3_init_IRQ(void) 7512832a81dSGeoff Levand { 7522832a81dSGeoff Levand int result; 7532832a81dSGeoff Levand unsigned cpu; 754bae1d8f1SGrant Likely struct irq_domain *host; 7552832a81dSGeoff Levand 7566fa6c8e2SGrant Likely host = irq_domain_add_nomap(NULL, PS3_PLUG_MAX + 1, &ps3_host_ops, NULL); 7572832a81dSGeoff Levand irq_set_default_host(host); 7582832a81dSGeoff Levand 7592832a81dSGeoff Levand for_each_possible_cpu(cpu) { 7609633ac8dSGeoff Levand struct ps3_private *pd = &per_cpu(ps3_private, cpu); 7612832a81dSGeoff Levand 762aab83500SGeoff Levand lv1_get_logical_ppe_id(&pd->ppe_id); 763aab83500SGeoff Levand pd->thread_id = get_hard_smp_processor_id(cpu); 76432b9074bSGeoff Levand spin_lock_init(&pd->bmp_lock); 7652832a81dSGeoff Levand 76632b9074bSGeoff Levand DBG("%s:%d: ppe_id %llu, thread_id %llu, bmp %lxh\n", 767aab83500SGeoff Levand __func__, __LINE__, pd->ppe_id, pd->thread_id, 768407e24a0SGeoff Levand ps3_mm_phys_to_lpar(__pa(&pd->bmp))); 769407e24a0SGeoff Levand 770aab83500SGeoff Levand result = lv1_configure_irq_state_bitmap(pd->ppe_id, 771aab83500SGeoff Levand pd->thread_id, ps3_mm_phys_to_lpar(__pa(&pd->bmp))); 7722832a81dSGeoff Levand 7732832a81dSGeoff Levand if (result) 77432b9074bSGeoff Levand FAIL("%s:%d: lv1_configure_irq_state_bitmap failed:" 7752832a81dSGeoff Levand " %s\n", __func__, __LINE__, 7762832a81dSGeoff Levand ps3_result(result)); 7772832a81dSGeoff Levand } 7782832a81dSGeoff Levand 7792832a81dSGeoff Levand ppc_md.get_irq = ps3_get_irq; 7802832a81dSGeoff Levand } 7819263e85aSGeoff Levand 7829263e85aSGeoff Levand void ps3_shutdown_IRQ(int cpu) 7839263e85aSGeoff Levand { 7849263e85aSGeoff Levand int result; 7859263e85aSGeoff Levand u64 ppe_id; 7869263e85aSGeoff Levand u64 thread_id = get_hard_smp_processor_id(cpu); 7879263e85aSGeoff Levand 7889263e85aSGeoff Levand lv1_get_logical_ppe_id(&ppe_id); 7899263e85aSGeoff Levand result = lv1_configure_irq_state_bitmap(ppe_id, thread_id, 0); 7909263e85aSGeoff Levand 7915c949070SStephen Rothwell DBG("%s:%d: lv1_configure_irq_state_bitmap (%llu:%llu/%d) %s\n", __func__, 7929263e85aSGeoff Levand __LINE__, ppe_id, thread_id, cpu, ps3_result(result)); 7939263e85aSGeoff Levand } 794