162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * PS3 interrupt routines. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2006 Sony Computer Entertainment Inc. 662306a36Sopenharmony_ci * Copyright 2006 Sony Corp. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/export.h> 1162306a36Sopenharmony_ci#include <linux/irq.h> 1262306a36Sopenharmony_ci#include <linux/irqdomain.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <asm/machdep.h> 1562306a36Sopenharmony_ci#include <asm/udbg.h> 1662306a36Sopenharmony_ci#include <asm/lv1call.h> 1762306a36Sopenharmony_ci#include <asm/smp.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "platform.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#if defined(DEBUG) 2262306a36Sopenharmony_ci#define DBG udbg_printf 2362306a36Sopenharmony_ci#define FAIL udbg_printf 2462306a36Sopenharmony_ci#else 2562306a36Sopenharmony_ci#define DBG pr_devel 2662306a36Sopenharmony_ci#define FAIL pr_debug 2762306a36Sopenharmony_ci#endif 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/** 3062306a36Sopenharmony_ci * struct ps3_bmp - a per cpu irq status and mask bitmap structure 3162306a36Sopenharmony_ci * @status: 256 bit status bitmap indexed by plug 3262306a36Sopenharmony_ci * @unused_1: Alignment 3362306a36Sopenharmony_ci * @mask: 256 bit mask bitmap indexed by plug 3462306a36Sopenharmony_ci * @unused_2: Alignment 3562306a36Sopenharmony_ci * 3662306a36Sopenharmony_ci * The HV maintains per SMT thread mappings of HV outlet to HV plug on 3762306a36Sopenharmony_ci * behalf of the guest. These mappings are implemented as 256 bit guest 3862306a36Sopenharmony_ci * supplied bitmaps indexed by plug number. The addresses of the bitmaps 3962306a36Sopenharmony_ci * are registered with the HV through lv1_configure_irq_state_bitmap(). 4062306a36Sopenharmony_ci * The HV requires that the 512 bits of status + mask not cross a page 4162306a36Sopenharmony_ci * boundary. PS3_BMP_MINALIGN is used to define this minimal 64 byte 4262306a36Sopenharmony_ci * alignment. 4362306a36Sopenharmony_ci * 4462306a36Sopenharmony_ci * The HV supports 256 plugs per thread, assigned as {0..255}, for a total 4562306a36Sopenharmony_ci * of 512 plugs supported on a processor. To simplify the logic this 4662306a36Sopenharmony_ci * implementation equates HV plug value to Linux virq value, constrains each 4762306a36Sopenharmony_ci * interrupt to have a system wide unique plug number, and limits the range 4862306a36Sopenharmony_ci * of the plug values to map into the first dword of the bitmaps. This 4962306a36Sopenharmony_ci * gives a usable range of plug values of {NR_IRQS_LEGACY..63}. Note 5062306a36Sopenharmony_ci * that there is no constraint on how many in this set an individual thread 5162306a36Sopenharmony_ci * can acquire. 5262306a36Sopenharmony_ci * 5362306a36Sopenharmony_ci * The mask is declared as unsigned long so we can use set/clear_bit on it. 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#define PS3_BMP_MINALIGN 64 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistruct ps3_bmp { 5962306a36Sopenharmony_ci struct { 6062306a36Sopenharmony_ci u64 status; 6162306a36Sopenharmony_ci u64 unused_1[3]; 6262306a36Sopenharmony_ci unsigned long mask; 6362306a36Sopenharmony_ci u64 unused_2[3]; 6462306a36Sopenharmony_ci }; 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/** 6862306a36Sopenharmony_ci * struct ps3_private - a per cpu data structure 6962306a36Sopenharmony_ci * @bmp: ps3_bmp structure 7062306a36Sopenharmony_ci * @bmp_lock: Synchronize access to bmp. 7162306a36Sopenharmony_ci * @ipi_debug_brk_mask: Mask for debug break IPIs 7262306a36Sopenharmony_ci * @ppe_id: HV logical_ppe_id 7362306a36Sopenharmony_ci * @thread_id: HV thread_id 7462306a36Sopenharmony_ci * @ipi_mask: Mask of IPI virqs 7562306a36Sopenharmony_ci */ 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistruct ps3_private { 7862306a36Sopenharmony_ci struct ps3_bmp bmp __attribute__ ((aligned (PS3_BMP_MINALIGN))); 7962306a36Sopenharmony_ci spinlock_t bmp_lock; 8062306a36Sopenharmony_ci u64 ppe_id; 8162306a36Sopenharmony_ci u64 thread_id; 8262306a36Sopenharmony_ci unsigned long ipi_debug_brk_mask; 8362306a36Sopenharmony_ci unsigned long ipi_mask; 8462306a36Sopenharmony_ci}; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic DEFINE_PER_CPU(struct ps3_private, ps3_private); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/** 8962306a36Sopenharmony_ci * ps3_chip_mask - Set an interrupt mask bit in ps3_bmp. 9062306a36Sopenharmony_ci * @virq: The assigned Linux virq. 9162306a36Sopenharmony_ci * 9262306a36Sopenharmony_ci * Sets ps3_bmp.mask and calls lv1_did_update_interrupt_mask(). 9362306a36Sopenharmony_ci */ 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic void ps3_chip_mask(struct irq_data *d) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci struct ps3_private *pd = irq_data_get_irq_chip_data(d); 9862306a36Sopenharmony_ci unsigned long flags; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci DBG("%s:%d: thread_id %llu, virq %d\n", __func__, __LINE__, 10162306a36Sopenharmony_ci pd->thread_id, d->irq); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci local_irq_save(flags); 10462306a36Sopenharmony_ci clear_bit(63 - d->irq, &pd->bmp.mask); 10562306a36Sopenharmony_ci lv1_did_update_interrupt_mask(pd->ppe_id, pd->thread_id); 10662306a36Sopenharmony_ci local_irq_restore(flags); 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci/** 11062306a36Sopenharmony_ci * ps3_chip_unmask - Clear an interrupt mask bit in ps3_bmp. 11162306a36Sopenharmony_ci * @virq: The assigned Linux virq. 11262306a36Sopenharmony_ci * 11362306a36Sopenharmony_ci * Clears ps3_bmp.mask and calls lv1_did_update_interrupt_mask(). 11462306a36Sopenharmony_ci */ 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic void ps3_chip_unmask(struct irq_data *d) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct ps3_private *pd = irq_data_get_irq_chip_data(d); 11962306a36Sopenharmony_ci unsigned long flags; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci DBG("%s:%d: thread_id %llu, virq %d\n", __func__, __LINE__, 12262306a36Sopenharmony_ci pd->thread_id, d->irq); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci local_irq_save(flags); 12562306a36Sopenharmony_ci set_bit(63 - d->irq, &pd->bmp.mask); 12662306a36Sopenharmony_ci lv1_did_update_interrupt_mask(pd->ppe_id, pd->thread_id); 12762306a36Sopenharmony_ci local_irq_restore(flags); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci/** 13162306a36Sopenharmony_ci * ps3_chip_eoi - HV end-of-interrupt. 13262306a36Sopenharmony_ci * @virq: The assigned Linux virq. 13362306a36Sopenharmony_ci * 13462306a36Sopenharmony_ci * Calls lv1_end_of_interrupt_ext(). 13562306a36Sopenharmony_ci */ 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic void ps3_chip_eoi(struct irq_data *d) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci const struct ps3_private *pd = irq_data_get_irq_chip_data(d); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* non-IPIs are EOIed here. */ 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (!test_bit(63 - d->irq, &pd->ipi_mask)) 14462306a36Sopenharmony_ci lv1_end_of_interrupt_ext(pd->ppe_id, pd->thread_id, d->irq); 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci/** 14862306a36Sopenharmony_ci * ps3_irq_chip - Represents the ps3_bmp as a Linux struct irq_chip. 14962306a36Sopenharmony_ci */ 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic struct irq_chip ps3_irq_chip = { 15262306a36Sopenharmony_ci .name = "ps3", 15362306a36Sopenharmony_ci .irq_mask = ps3_chip_mask, 15462306a36Sopenharmony_ci .irq_unmask = ps3_chip_unmask, 15562306a36Sopenharmony_ci .irq_eoi = ps3_chip_eoi, 15662306a36Sopenharmony_ci}; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci/** 15962306a36Sopenharmony_ci * ps3_virq_setup - virq related setup. 16062306a36Sopenharmony_ci * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 16162306a36Sopenharmony_ci * serviced on. 16262306a36Sopenharmony_ci * @outlet: The HV outlet from the various create outlet routines. 16362306a36Sopenharmony_ci * @virq: The assigned Linux virq. 16462306a36Sopenharmony_ci * 16562306a36Sopenharmony_ci * Calls irq_create_mapping() to get a virq and sets the chip data to 16662306a36Sopenharmony_ci * ps3_private data. 16762306a36Sopenharmony_ci */ 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic int ps3_virq_setup(enum ps3_cpu_binding cpu, unsigned long outlet, 17062306a36Sopenharmony_ci unsigned int *virq) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci int result; 17362306a36Sopenharmony_ci struct ps3_private *pd; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci /* This defines the default interrupt distribution policy. */ 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (cpu == PS3_BINDING_CPU_ANY) 17862306a36Sopenharmony_ci cpu = 0; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci pd = &per_cpu(ps3_private, cpu); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci *virq = irq_create_mapping(NULL, outlet); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (!*virq) { 18562306a36Sopenharmony_ci FAIL("%s:%d: irq_create_mapping failed: outlet %lu\n", 18662306a36Sopenharmony_ci __func__, __LINE__, outlet); 18762306a36Sopenharmony_ci result = -ENOMEM; 18862306a36Sopenharmony_ci goto fail_create; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci DBG("%s:%d: outlet %lu => cpu %u, virq %u\n", __func__, __LINE__, 19262306a36Sopenharmony_ci outlet, cpu, *virq); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci result = irq_set_chip_data(*virq, pd); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (result) { 19762306a36Sopenharmony_ci FAIL("%s:%d: irq_set_chip_data failed\n", 19862306a36Sopenharmony_ci __func__, __LINE__); 19962306a36Sopenharmony_ci goto fail_set; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci ps3_chip_mask(irq_get_irq_data(*virq)); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci return result; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cifail_set: 20762306a36Sopenharmony_ci irq_dispose_mapping(*virq); 20862306a36Sopenharmony_cifail_create: 20962306a36Sopenharmony_ci return result; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci/** 21362306a36Sopenharmony_ci * ps3_virq_destroy - virq related teardown. 21462306a36Sopenharmony_ci * @virq: The assigned Linux virq. 21562306a36Sopenharmony_ci * 21662306a36Sopenharmony_ci * Clears chip data and calls irq_dispose_mapping() for the virq. 21762306a36Sopenharmony_ci */ 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic int ps3_virq_destroy(unsigned int virq) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci const struct ps3_private *pd = irq_get_chip_data(virq); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci DBG("%s:%d: ppe_id %llu, thread_id %llu, virq %u\n", __func__, 22462306a36Sopenharmony_ci __LINE__, pd->ppe_id, pd->thread_id, virq); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci irq_set_chip_data(virq, NULL); 22762306a36Sopenharmony_ci irq_dispose_mapping(virq); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci DBG("%s:%d <-\n", __func__, __LINE__); 23062306a36Sopenharmony_ci return 0; 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci/** 23462306a36Sopenharmony_ci * ps3_irq_plug_setup - Generic outlet and virq related setup. 23562306a36Sopenharmony_ci * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 23662306a36Sopenharmony_ci * serviced on. 23762306a36Sopenharmony_ci * @outlet: The HV outlet from the various create outlet routines. 23862306a36Sopenharmony_ci * @virq: The assigned Linux virq. 23962306a36Sopenharmony_ci * 24062306a36Sopenharmony_ci * Sets up virq and connects the irq plug. 24162306a36Sopenharmony_ci */ 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ciint ps3_irq_plug_setup(enum ps3_cpu_binding cpu, unsigned long outlet, 24462306a36Sopenharmony_ci unsigned int *virq) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci int result; 24762306a36Sopenharmony_ci struct ps3_private *pd; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci result = ps3_virq_setup(cpu, outlet, virq); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (result) { 25262306a36Sopenharmony_ci FAIL("%s:%d: ps3_virq_setup failed\n", __func__, __LINE__); 25362306a36Sopenharmony_ci goto fail_setup; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci pd = irq_get_chip_data(*virq); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* Binds outlet to cpu + virq. */ 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci result = lv1_connect_irq_plug_ext(pd->ppe_id, pd->thread_id, *virq, 26162306a36Sopenharmony_ci outlet, 0); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (result) { 26462306a36Sopenharmony_ci FAIL("%s:%d: lv1_connect_irq_plug_ext failed: %s\n", 26562306a36Sopenharmony_ci __func__, __LINE__, ps3_result(result)); 26662306a36Sopenharmony_ci result = -EPERM; 26762306a36Sopenharmony_ci goto fail_connect; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci return result; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cifail_connect: 27362306a36Sopenharmony_ci ps3_virq_destroy(*virq); 27462306a36Sopenharmony_cifail_setup: 27562306a36Sopenharmony_ci return result; 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_irq_plug_setup); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci/** 28062306a36Sopenharmony_ci * ps3_irq_plug_destroy - Generic outlet and virq related teardown. 28162306a36Sopenharmony_ci * @virq: The assigned Linux virq. 28262306a36Sopenharmony_ci * 28362306a36Sopenharmony_ci * Disconnects the irq plug and tears down virq. 28462306a36Sopenharmony_ci * Do not call for system bus event interrupts setup with 28562306a36Sopenharmony_ci * ps3_sb_event_receive_port_setup(). 28662306a36Sopenharmony_ci */ 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ciint ps3_irq_plug_destroy(unsigned int virq) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci int result; 29162306a36Sopenharmony_ci const struct ps3_private *pd = irq_get_chip_data(virq); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci DBG("%s:%d: ppe_id %llu, thread_id %llu, virq %u\n", __func__, 29462306a36Sopenharmony_ci __LINE__, pd->ppe_id, pd->thread_id, virq); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci ps3_chip_mask(irq_get_irq_data(virq)); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci result = lv1_disconnect_irq_plug_ext(pd->ppe_id, pd->thread_id, virq); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (result) 30162306a36Sopenharmony_ci FAIL("%s:%d: lv1_disconnect_irq_plug_ext failed: %s\n", 30262306a36Sopenharmony_ci __func__, __LINE__, ps3_result(result)); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci ps3_virq_destroy(virq); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci return result; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_irq_plug_destroy); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci/** 31162306a36Sopenharmony_ci * ps3_event_receive_port_setup - Setup an event receive port. 31262306a36Sopenharmony_ci * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 31362306a36Sopenharmony_ci * serviced on. 31462306a36Sopenharmony_ci * @virq: The assigned Linux virq. 31562306a36Sopenharmony_ci * 31662306a36Sopenharmony_ci * The virq can be used with lv1_connect_interrupt_event_receive_port() to 31762306a36Sopenharmony_ci * arrange to receive interrupts from system-bus devices, or with 31862306a36Sopenharmony_ci * ps3_send_event_locally() to signal events. 31962306a36Sopenharmony_ci */ 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ciint ps3_event_receive_port_setup(enum ps3_cpu_binding cpu, unsigned int *virq) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci int result; 32462306a36Sopenharmony_ci u64 outlet; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci result = lv1_construct_event_receive_port(&outlet); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (result) { 32962306a36Sopenharmony_ci FAIL("%s:%d: lv1_construct_event_receive_port failed: %s\n", 33062306a36Sopenharmony_ci __func__, __LINE__, ps3_result(result)); 33162306a36Sopenharmony_ci *virq = 0; 33262306a36Sopenharmony_ci return result; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci result = ps3_irq_plug_setup(cpu, outlet, virq); 33662306a36Sopenharmony_ci BUG_ON(result); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci return result; 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_event_receive_port_setup); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci/** 34362306a36Sopenharmony_ci * ps3_event_receive_port_destroy - Destroy an event receive port. 34462306a36Sopenharmony_ci * @virq: The assigned Linux virq. 34562306a36Sopenharmony_ci * 34662306a36Sopenharmony_ci * Since ps3_event_receive_port_destroy destroys the receive port outlet, 34762306a36Sopenharmony_ci * SB devices need to call disconnect_interrupt_event_receive_port() before 34862306a36Sopenharmony_ci * this. 34962306a36Sopenharmony_ci */ 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ciint ps3_event_receive_port_destroy(unsigned int virq) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci int result; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci DBG(" -> %s:%d virq %u\n", __func__, __LINE__, virq); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci ps3_chip_mask(irq_get_irq_data(virq)); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci result = lv1_destruct_event_receive_port(virq_to_hw(virq)); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (result) 36262306a36Sopenharmony_ci FAIL("%s:%d: lv1_destruct_event_receive_port failed: %s\n", 36362306a36Sopenharmony_ci __func__, __LINE__, ps3_result(result)); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci /* 36662306a36Sopenharmony_ci * Don't call ps3_virq_destroy() here since ps3_smp_cleanup_cpu() 36762306a36Sopenharmony_ci * calls from interrupt context (smp_call_function) when kexecing. 36862306a36Sopenharmony_ci */ 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci DBG(" <- %s:%d\n", __func__, __LINE__); 37162306a36Sopenharmony_ci return result; 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ciint ps3_send_event_locally(unsigned int virq) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci return lv1_send_event_locally(virq_to_hw(virq)); 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci/** 38062306a36Sopenharmony_ci * ps3_sb_event_receive_port_setup - Setup a system bus event receive port. 38162306a36Sopenharmony_ci * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 38262306a36Sopenharmony_ci * serviced on. 38362306a36Sopenharmony_ci * @dev: The system bus device instance. 38462306a36Sopenharmony_ci * @virq: The assigned Linux virq. 38562306a36Sopenharmony_ci * 38662306a36Sopenharmony_ci * An event irq represents a virtual device interrupt. The interrupt_id 38762306a36Sopenharmony_ci * coresponds to the software interrupt number. 38862306a36Sopenharmony_ci */ 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ciint ps3_sb_event_receive_port_setup(struct ps3_system_bus_device *dev, 39162306a36Sopenharmony_ci enum ps3_cpu_binding cpu, unsigned int *virq) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci /* this should go in system-bus.c */ 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci int result; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci result = ps3_event_receive_port_setup(cpu, virq); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (result) 40062306a36Sopenharmony_ci return result; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci result = lv1_connect_interrupt_event_receive_port(dev->bus_id, 40362306a36Sopenharmony_ci dev->dev_id, virq_to_hw(*virq), dev->interrupt_id); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (result) { 40662306a36Sopenharmony_ci FAIL("%s:%d: lv1_connect_interrupt_event_receive_port" 40762306a36Sopenharmony_ci " failed: %s\n", __func__, __LINE__, 40862306a36Sopenharmony_ci ps3_result(result)); 40962306a36Sopenharmony_ci ps3_event_receive_port_destroy(*virq); 41062306a36Sopenharmony_ci *virq = 0; 41162306a36Sopenharmony_ci return result; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci DBG("%s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__, 41562306a36Sopenharmony_ci dev->interrupt_id, *virq); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci return 0; 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ciEXPORT_SYMBOL(ps3_sb_event_receive_port_setup); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ciint ps3_sb_event_receive_port_destroy(struct ps3_system_bus_device *dev, 42262306a36Sopenharmony_ci unsigned int virq) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci /* this should go in system-bus.c */ 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci int result; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci DBG(" -> %s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__, 42962306a36Sopenharmony_ci dev->interrupt_id, virq); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci result = lv1_disconnect_interrupt_event_receive_port(dev->bus_id, 43262306a36Sopenharmony_ci dev->dev_id, virq_to_hw(virq), dev->interrupt_id); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (result) 43562306a36Sopenharmony_ci FAIL("%s:%d: lv1_disconnect_interrupt_event_receive_port" 43662306a36Sopenharmony_ci " failed: %s\n", __func__, __LINE__, 43762306a36Sopenharmony_ci ps3_result(result)); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci result = ps3_event_receive_port_destroy(virq); 44062306a36Sopenharmony_ci BUG_ON(result); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* 44362306a36Sopenharmony_ci * ps3_event_receive_port_destroy() destroys the IRQ plug, 44462306a36Sopenharmony_ci * so don't call ps3_irq_plug_destroy() here. 44562306a36Sopenharmony_ci */ 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci result = ps3_virq_destroy(virq); 44862306a36Sopenharmony_ci BUG_ON(result); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci DBG(" <- %s:%d\n", __func__, __LINE__); 45162306a36Sopenharmony_ci return result; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ciEXPORT_SYMBOL(ps3_sb_event_receive_port_destroy); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci/** 45662306a36Sopenharmony_ci * ps3_io_irq_setup - Setup a system bus io irq. 45762306a36Sopenharmony_ci * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 45862306a36Sopenharmony_ci * serviced on. 45962306a36Sopenharmony_ci * @interrupt_id: The device interrupt id read from the system repository. 46062306a36Sopenharmony_ci * @virq: The assigned Linux virq. 46162306a36Sopenharmony_ci * 46262306a36Sopenharmony_ci * An io irq represents a non-virtualized device interrupt. interrupt_id 46362306a36Sopenharmony_ci * coresponds to the interrupt number of the interrupt controller. 46462306a36Sopenharmony_ci */ 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ciint ps3_io_irq_setup(enum ps3_cpu_binding cpu, unsigned int interrupt_id, 46762306a36Sopenharmony_ci unsigned int *virq) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci int result; 47062306a36Sopenharmony_ci u64 outlet; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci result = lv1_construct_io_irq_outlet(interrupt_id, &outlet); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci if (result) { 47562306a36Sopenharmony_ci FAIL("%s:%d: lv1_construct_io_irq_outlet failed: %s\n", 47662306a36Sopenharmony_ci __func__, __LINE__, ps3_result(result)); 47762306a36Sopenharmony_ci return result; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci result = ps3_irq_plug_setup(cpu, outlet, virq); 48162306a36Sopenharmony_ci BUG_ON(result); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci return result; 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_io_irq_setup); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ciint ps3_io_irq_destroy(unsigned int virq) 48862306a36Sopenharmony_ci{ 48962306a36Sopenharmony_ci int result; 49062306a36Sopenharmony_ci unsigned long outlet = virq_to_hw(virq); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci ps3_chip_mask(irq_get_irq_data(virq)); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci /* 49562306a36Sopenharmony_ci * lv1_destruct_io_irq_outlet() will destroy the IRQ plug, 49662306a36Sopenharmony_ci * so call ps3_irq_plug_destroy() first. 49762306a36Sopenharmony_ci */ 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci result = ps3_irq_plug_destroy(virq); 50062306a36Sopenharmony_ci BUG_ON(result); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci result = lv1_destruct_io_irq_outlet(outlet); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (result) 50562306a36Sopenharmony_ci FAIL("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n", 50662306a36Sopenharmony_ci __func__, __LINE__, ps3_result(result)); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci return result; 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_io_irq_destroy); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci/** 51362306a36Sopenharmony_ci * ps3_vuart_irq_setup - Setup the system virtual uart virq. 51462306a36Sopenharmony_ci * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 51562306a36Sopenharmony_ci * serviced on. 51662306a36Sopenharmony_ci * @virt_addr_bmp: The caller supplied virtual uart interrupt bitmap. 51762306a36Sopenharmony_ci * @virq: The assigned Linux virq. 51862306a36Sopenharmony_ci * 51962306a36Sopenharmony_ci * The system supports only a single virtual uart, so multiple calls without 52062306a36Sopenharmony_ci * freeing the interrupt will return a wrong state error. 52162306a36Sopenharmony_ci */ 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ciint ps3_vuart_irq_setup(enum ps3_cpu_binding cpu, void* virt_addr_bmp, 52462306a36Sopenharmony_ci unsigned int *virq) 52562306a36Sopenharmony_ci{ 52662306a36Sopenharmony_ci int result; 52762306a36Sopenharmony_ci u64 outlet; 52862306a36Sopenharmony_ci u64 lpar_addr; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci BUG_ON(!is_kernel_addr((u64)virt_addr_bmp)); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci lpar_addr = ps3_mm_phys_to_lpar(__pa(virt_addr_bmp)); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci result = lv1_configure_virtual_uart_irq(lpar_addr, &outlet); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci if (result) { 53762306a36Sopenharmony_ci FAIL("%s:%d: lv1_configure_virtual_uart_irq failed: %s\n", 53862306a36Sopenharmony_ci __func__, __LINE__, ps3_result(result)); 53962306a36Sopenharmony_ci return result; 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci result = ps3_irq_plug_setup(cpu, outlet, virq); 54362306a36Sopenharmony_ci BUG_ON(result); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci return result; 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_vuart_irq_setup); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ciint ps3_vuart_irq_destroy(unsigned int virq) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci int result; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci ps3_chip_mask(irq_get_irq_data(virq)); 55462306a36Sopenharmony_ci result = lv1_deconfigure_virtual_uart_irq(); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci if (result) { 55762306a36Sopenharmony_ci FAIL("%s:%d: lv1_configure_virtual_uart_irq failed: %s\n", 55862306a36Sopenharmony_ci __func__, __LINE__, ps3_result(result)); 55962306a36Sopenharmony_ci return result; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci result = ps3_irq_plug_destroy(virq); 56362306a36Sopenharmony_ci BUG_ON(result); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci return result; 56662306a36Sopenharmony_ci} 56762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_vuart_irq_destroy); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci/** 57062306a36Sopenharmony_ci * ps3_spe_irq_setup - Setup an spe virq. 57162306a36Sopenharmony_ci * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 57262306a36Sopenharmony_ci * serviced on. 57362306a36Sopenharmony_ci * @spe_id: The spe_id returned from lv1_construct_logical_spe(). 57462306a36Sopenharmony_ci * @class: The spe interrupt class {0,1,2}. 57562306a36Sopenharmony_ci * @virq: The assigned Linux virq. 57662306a36Sopenharmony_ci * 57762306a36Sopenharmony_ci */ 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ciint ps3_spe_irq_setup(enum ps3_cpu_binding cpu, unsigned long spe_id, 58062306a36Sopenharmony_ci unsigned int class, unsigned int *virq) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci int result; 58362306a36Sopenharmony_ci u64 outlet; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci BUG_ON(class > 2); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci result = lv1_get_spe_irq_outlet(spe_id, class, &outlet); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci if (result) { 59062306a36Sopenharmony_ci FAIL("%s:%d: lv1_get_spe_irq_outlet failed: %s\n", 59162306a36Sopenharmony_ci __func__, __LINE__, ps3_result(result)); 59262306a36Sopenharmony_ci return result; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci result = ps3_irq_plug_setup(cpu, outlet, virq); 59662306a36Sopenharmony_ci BUG_ON(result); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci return result; 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ciint ps3_spe_irq_destroy(unsigned int virq) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci int result; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci ps3_chip_mask(irq_get_irq_data(virq)); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci result = ps3_irq_plug_destroy(virq); 60862306a36Sopenharmony_ci BUG_ON(result); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci return result; 61162306a36Sopenharmony_ci} 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci#define PS3_INVALID_OUTLET ((irq_hw_number_t)-1) 61562306a36Sopenharmony_ci#define PS3_PLUG_MAX 63 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci#if defined(DEBUG) 61862306a36Sopenharmony_cistatic void _dump_64_bmp(const char *header, const u64 *p, unsigned cpu, 61962306a36Sopenharmony_ci const char* func, int line) 62062306a36Sopenharmony_ci{ 62162306a36Sopenharmony_ci pr_debug("%s:%d: %s %u {%04llx_%04llx_%04llx_%04llx}\n", 62262306a36Sopenharmony_ci func, line, header, cpu, 62362306a36Sopenharmony_ci *p >> 48, (*p >> 32) & 0xffff, (*p >> 16) & 0xffff, 62462306a36Sopenharmony_ci *p & 0xffff); 62562306a36Sopenharmony_ci} 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_cistatic void __maybe_unused _dump_256_bmp(const char *header, 62862306a36Sopenharmony_ci const u64 *p, unsigned cpu, const char* func, int line) 62962306a36Sopenharmony_ci{ 63062306a36Sopenharmony_ci pr_debug("%s:%d: %s %u {%016llx:%016llx:%016llx:%016llx}\n", 63162306a36Sopenharmony_ci func, line, header, cpu, p[0], p[1], p[2], p[3]); 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci#define dump_bmp(_x) _dump_bmp(_x, __func__, __LINE__) 63562306a36Sopenharmony_cistatic void _dump_bmp(struct ps3_private* pd, const char* func, int line) 63662306a36Sopenharmony_ci{ 63762306a36Sopenharmony_ci unsigned long flags; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci spin_lock_irqsave(&pd->bmp_lock, flags); 64062306a36Sopenharmony_ci _dump_64_bmp("stat", &pd->bmp.status, pd->thread_id, func, line); 64162306a36Sopenharmony_ci _dump_64_bmp("mask", (u64*)&pd->bmp.mask, pd->thread_id, func, line); 64262306a36Sopenharmony_ci spin_unlock_irqrestore(&pd->bmp_lock, flags); 64362306a36Sopenharmony_ci} 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci#define dump_mask(_x) _dump_mask(_x, __func__, __LINE__) 64662306a36Sopenharmony_cistatic void __maybe_unused _dump_mask(struct ps3_private *pd, 64762306a36Sopenharmony_ci const char* func, int line) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci unsigned long flags; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci spin_lock_irqsave(&pd->bmp_lock, flags); 65262306a36Sopenharmony_ci _dump_64_bmp("mask", (u64*)&pd->bmp.mask, pd->thread_id, func, line); 65362306a36Sopenharmony_ci spin_unlock_irqrestore(&pd->bmp_lock, flags); 65462306a36Sopenharmony_ci} 65562306a36Sopenharmony_ci#else 65662306a36Sopenharmony_cistatic void dump_bmp(struct ps3_private* pd) {}; 65762306a36Sopenharmony_ci#endif /* defined(DEBUG) */ 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_cistatic int ps3_host_map(struct irq_domain *h, unsigned int virq, 66062306a36Sopenharmony_ci irq_hw_number_t hwirq) 66162306a36Sopenharmony_ci{ 66262306a36Sopenharmony_ci DBG("%s:%d: hwirq %lu, virq %u\n", __func__, __LINE__, hwirq, 66362306a36Sopenharmony_ci virq); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci irq_set_chip_and_handler(virq, &ps3_irq_chip, handle_fasteoi_irq); 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci return 0; 66862306a36Sopenharmony_ci} 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_cistatic int ps3_host_match(struct irq_domain *h, struct device_node *np, 67162306a36Sopenharmony_ci enum irq_domain_bus_token bus_token) 67262306a36Sopenharmony_ci{ 67362306a36Sopenharmony_ci /* Match all */ 67462306a36Sopenharmony_ci return 1; 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_cistatic const struct irq_domain_ops ps3_host_ops = { 67862306a36Sopenharmony_ci .map = ps3_host_map, 67962306a36Sopenharmony_ci .match = ps3_host_match, 68062306a36Sopenharmony_ci}; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_civoid __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci struct ps3_private *pd = &per_cpu(ps3_private, cpu); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci set_bit(63 - virq, &pd->ipi_debug_brk_mask); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci DBG("%s:%d: cpu %u, virq %u, mask %lxh\n", __func__, __LINE__, 68962306a36Sopenharmony_ci cpu, virq, pd->ipi_debug_brk_mask); 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_civoid __init ps3_register_ipi_irq(unsigned int cpu, unsigned int virq) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci struct ps3_private *pd = &per_cpu(ps3_private, cpu); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci set_bit(63 - virq, &pd->ipi_mask); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci DBG("%s:%d: cpu %u, virq %u, ipi_mask %lxh\n", __func__, __LINE__, 69962306a36Sopenharmony_ci cpu, virq, pd->ipi_mask); 70062306a36Sopenharmony_ci} 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_cistatic unsigned int ps3_get_irq(void) 70362306a36Sopenharmony_ci{ 70462306a36Sopenharmony_ci struct ps3_private *pd = this_cpu_ptr(&ps3_private); 70562306a36Sopenharmony_ci u64 x = (pd->bmp.status & pd->bmp.mask); 70662306a36Sopenharmony_ci unsigned int plug; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci /* check for ipi break first to stop this cpu ASAP */ 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci if (x & pd->ipi_debug_brk_mask) 71162306a36Sopenharmony_ci x &= pd->ipi_debug_brk_mask; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci asm volatile("cntlzd %0,%1" : "=r" (plug) : "r" (x)); 71462306a36Sopenharmony_ci plug &= 0x3f; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci if (unlikely(!plug)) { 71762306a36Sopenharmony_ci DBG("%s:%d: no plug found: thread_id %llu\n", __func__, 71862306a36Sopenharmony_ci __LINE__, pd->thread_id); 71962306a36Sopenharmony_ci dump_bmp(&per_cpu(ps3_private, 0)); 72062306a36Sopenharmony_ci dump_bmp(&per_cpu(ps3_private, 1)); 72162306a36Sopenharmony_ci return 0; 72262306a36Sopenharmony_ci } 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci#if defined(DEBUG) 72562306a36Sopenharmony_ci if (unlikely(plug < NR_IRQS_LEGACY || plug > PS3_PLUG_MAX)) { 72662306a36Sopenharmony_ci dump_bmp(&per_cpu(ps3_private, 0)); 72762306a36Sopenharmony_ci dump_bmp(&per_cpu(ps3_private, 1)); 72862306a36Sopenharmony_ci BUG(); 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci#endif 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci /* IPIs are EOIed here. */ 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci if (test_bit(63 - plug, &pd->ipi_mask)) 73562306a36Sopenharmony_ci lv1_end_of_interrupt_ext(pd->ppe_id, pd->thread_id, plug); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci return plug; 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_civoid __init ps3_init_IRQ(void) 74162306a36Sopenharmony_ci{ 74262306a36Sopenharmony_ci int result; 74362306a36Sopenharmony_ci unsigned cpu; 74462306a36Sopenharmony_ci struct irq_domain *host; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci host = irq_domain_add_nomap(NULL, PS3_PLUG_MAX + 1, &ps3_host_ops, NULL); 74762306a36Sopenharmony_ci irq_set_default_host(host); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci for_each_possible_cpu(cpu) { 75062306a36Sopenharmony_ci struct ps3_private *pd = &per_cpu(ps3_private, cpu); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci lv1_get_logical_ppe_id(&pd->ppe_id); 75362306a36Sopenharmony_ci pd->thread_id = get_hard_smp_processor_id(cpu); 75462306a36Sopenharmony_ci spin_lock_init(&pd->bmp_lock); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci DBG("%s:%d: ppe_id %llu, thread_id %llu, bmp %lxh\n", 75762306a36Sopenharmony_ci __func__, __LINE__, pd->ppe_id, pd->thread_id, 75862306a36Sopenharmony_ci ps3_mm_phys_to_lpar(__pa(&pd->bmp))); 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci result = lv1_configure_irq_state_bitmap(pd->ppe_id, 76162306a36Sopenharmony_ci pd->thread_id, ps3_mm_phys_to_lpar(__pa(&pd->bmp))); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci if (result) 76462306a36Sopenharmony_ci FAIL("%s:%d: lv1_configure_irq_state_bitmap failed:" 76562306a36Sopenharmony_ci " %s\n", __func__, __LINE__, 76662306a36Sopenharmony_ci ps3_result(result)); 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci ppc_md.get_irq = ps3_get_irq; 77062306a36Sopenharmony_ci} 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_civoid ps3_shutdown_IRQ(int cpu) 77362306a36Sopenharmony_ci{ 77462306a36Sopenharmony_ci int result; 77562306a36Sopenharmony_ci u64 ppe_id; 77662306a36Sopenharmony_ci u64 thread_id = get_hard_smp_processor_id(cpu); 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci lv1_get_logical_ppe_id(&ppe_id); 77962306a36Sopenharmony_ci result = lv1_configure_irq_state_bitmap(ppe_id, thread_id, 0); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci DBG("%s:%d: lv1_configure_irq_state_bitmap (%llu:%llu/%d) %s\n", __func__, 78262306a36Sopenharmony_ci __LINE__, ppe_id, thread_id, cpu, ps3_result(result)); 78362306a36Sopenharmony_ci} 784