2006-11-23 02:46:56 +03:00
/*
* PS3 interrupt routines .
*
* Copyright ( C ) 2006 Sony Computer Entertainment Inc .
* Copyright 2006 Sony Corp .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; version 2 of the License .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include <linux/kernel.h>
2011-07-23 02:24:23 +04:00
# include <linux/export.h>
2006-11-23 02:46:56 +03:00
# include <linux/irq.h>
# include <asm/machdep.h>
# include <asm/udbg.h>
# include <asm/lv1call.h>
2007-05-28 04:19:08 +04:00
# include <asm/smp.h>
2006-11-23 02:46:56 +03:00
# include "platform.h"
# if defined(DEBUG)
2007-06-16 01:19:23 +04:00
# define DBG udbg_printf
2011-11-29 19:38:49 +04:00
# define FAIL udbg_printf
2006-11-23 02:46:56 +03:00
# else
2011-11-29 19:38:49 +04:00
# define DBG pr_devel
# define FAIL pr_debug
2006-11-23 02:46:56 +03:00
# endif
2007-01-27 06:08:08 +03:00
/**
* struct ps3_bmp - a per cpu irq status and mask bitmap structure
* @ status : 256 bit status bitmap indexed by plug
2011-11-29 19:38:49 +04:00
* @ unused_1 : Alignment
2007-01-27 06:08:08 +03:00
* @ mask : 256 bit mask bitmap indexed by plug
2011-11-29 19:38:49 +04:00
* @ unused_2 : Alignment
2007-01-27 06:08:08 +03:00
*
tree-wide: fix comment/printk typos
"gadget", "through", "command", "maintain", "maintain", "controller", "address",
"between", "initiali[zs]e", "instead", "function", "select", "already",
"equal", "access", "management", "hierarchy", "registration", "interest",
"relative", "memory", "offset", "already",
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2010-11-01 22:38:34 +03:00
* The HV maintains per SMT thread mappings of HV outlet to HV plug on
2007-01-27 06:08:08 +03:00
* behalf of the guest . These mappings are implemented as 256 bit guest
* supplied bitmaps indexed by plug number . The addresses of the bitmaps
* are registered with the HV through lv1_configure_irq_state_bitmap ( ) .
2007-01-27 06:08:16 +03:00
* The HV requires that the 512 bits of status + mask not cross a page
* boundary . PS3_BMP_MINALIGN is used to define this minimal 64 byte
* alignment .
2007-01-27 06:08:08 +03:00
*
* The HV supports 256 plugs per thread , assigned as { 0. .255 } , for a total
* of 512 plugs supported on a processor . To simplify the logic this
* implementation equates HV plug value to Linux virq value , constrains each
* interrupt to have a system wide unique plug number , and limits the range
* of the plug values to map into the first dword of the bitmaps . This
* gives a usable range of plug values of { NUM_ISA_INTERRUPTS . .63 } . Note
* that there is no constraint on how many in this set an individual thread
* can acquire .
2009-01-13 23:00:29 +03:00
*
* The mask is declared as unsigned long so we can use set / clear_bit on it .
2007-01-27 06:08:08 +03:00
*/
2007-01-27 06:08:16 +03:00
# define PS3_BMP_MINALIGN 64
2007-01-27 06:08:08 +03:00
struct ps3_bmp {
struct {
u64 status ;
u64 unused_1 [ 3 ] ;
2009-01-13 23:00:29 +03:00
unsigned long mask ;
2007-01-27 06:08:08 +03:00
u64 unused_2 [ 3 ] ;
} ;
} ;
/**
* struct ps3_private - a per cpu data structure
* @ bmp : ps3_bmp structure
2011-11-29 19:38:49 +04:00
* @ bmp_lock : Syncronize access to bmp .
* @ ipi_debug_brk_mask : Mask for debug break IPIs
2007-06-16 02:06:04 +04:00
* @ ppe_id : HV logical_ppe_id
* @ thread_id : HV thread_id
2011-11-29 19:38:49 +04:00
* @ ipi_mask : Mask of IPI virqs
2007-01-27 06:08:08 +03:00
*/
struct ps3_private {
2007-01-27 06:08:16 +03:00
struct ps3_bmp bmp __attribute__ ( ( aligned ( PS3_BMP_MINALIGN ) ) ) ;
2011-11-29 19:38:49 +04:00
spinlock_t bmp_lock ;
2007-06-16 02:06:04 +04:00
u64 ppe_id ;
u64 thread_id ;
2011-11-29 19:38:49 +04:00
unsigned long ipi_debug_brk_mask ;
2011-11-08 16:37:26 +04:00
unsigned long ipi_mask ;
2007-01-27 06:08:08 +03:00
} ;
static DEFINE_PER_CPU ( struct ps3_private , ps3_private ) ;
2007-06-16 01:19:18 +04:00
/**
* ps3_chip_mask - Set an interrupt mask bit in ps3_bmp .
* @ virq : The assigned Linux virq .
*
* Sets ps3_bmp . mask and calls lv1_did_update_interrupt_mask ( ) .
*/
2011-03-09 01:26:56 +03:00
static void ps3_chip_mask ( struct irq_data * d )
2007-06-16 01:19:18 +04:00
{
2011-03-09 01:26:56 +03:00
struct ps3_private * pd = irq_data_get_irq_chip_data ( d ) ;
2007-06-16 01:19:18 +04:00
unsigned long flags ;
2011-11-29 19:38:49 +04:00
DBG ( " %s:%d: thread_id %llu, virq %d \n " , __func__ , __LINE__ ,
2011-03-09 01:26:56 +03:00
pd - > thread_id , d - > irq ) ;
2007-06-16 01:19:18 +04:00
local_irq_save ( flags ) ;
2011-03-09 01:26:56 +03:00
clear_bit ( 63 - d - > irq , & pd - > bmp . mask ) ;
2007-06-16 02:06:04 +04:00
lv1_did_update_interrupt_mask ( pd - > ppe_id , pd - > thread_id ) ;
2007-06-16 01:19:18 +04:00
local_irq_restore ( flags ) ;
}
/**
* ps3_chip_unmask - Clear an interrupt mask bit in ps3_bmp .
* @ virq : The assigned Linux virq .
*
* Clears ps3_bmp . mask and calls lv1_did_update_interrupt_mask ( ) .
*/
2011-03-09 01:26:56 +03:00
static void ps3_chip_unmask ( struct irq_data * d )
2007-06-16 01:19:18 +04:00
{
2011-03-09 01:26:56 +03:00
struct ps3_private * pd = irq_data_get_irq_chip_data ( d ) ;
2007-06-16 01:19:18 +04:00
unsigned long flags ;
2011-11-29 19:38:49 +04:00
DBG ( " %s:%d: thread_id %llu, virq %d \n " , __func__ , __LINE__ ,
2011-03-09 01:26:56 +03:00
pd - > thread_id , d - > irq ) ;
2007-06-16 01:19:18 +04:00
local_irq_save ( flags ) ;
2011-03-09 01:26:56 +03:00
set_bit ( 63 - d - > irq , & pd - > bmp . mask ) ;
2007-06-16 02:06:04 +04:00
lv1_did_update_interrupt_mask ( pd - > ppe_id , pd - > thread_id ) ;
2007-06-16 01:19:18 +04:00
local_irq_restore ( flags ) ;
}
/**
* ps3_chip_eoi - HV end - of - interrupt .
* @ virq : The assigned Linux virq .
*
* Calls lv1_end_of_interrupt_ext ( ) .
*/
2011-03-09 01:26:56 +03:00
static void ps3_chip_eoi ( struct irq_data * d )
2007-06-16 01:19:18 +04:00
{
2011-03-09 01:26:56 +03:00
const struct ps3_private * pd = irq_data_get_irq_chip_data ( d ) ;
2011-11-08 16:37:26 +04:00
/* non-IPIs are EOIed here. */
if ( ! test_bit ( 63 - d - > irq , & pd - > ipi_mask ) )
lv1_end_of_interrupt_ext ( pd - > ppe_id , pd - > thread_id , d - > irq ) ;
2007-06-16 01:19:18 +04:00
}
/**
* ps3_irq_chip - Represents the ps3_bmp as a Linux struct irq_chip .
*/
static struct irq_chip ps3_irq_chip = {
2009-11-19 02:44:21 +03:00
. name = " ps3 " ,
2011-03-09 01:26:56 +03:00
. irq_mask = ps3_chip_mask ,
. irq_unmask = ps3_chip_unmask ,
. irq_eoi = ps3_chip_eoi ,
2007-06-16 01:19:18 +04:00
} ;
2007-05-01 01:01:01 +04:00
/**
* ps3_virq_setup - virq related setup .
* @ cpu : enum ps3_cpu_binding indicating the cpu the interrupt should be
* serviced on .
* @ outlet : The HV outlet from the various create outlet routines .
* @ virq : The assigned Linux virq .
*
* Calls irq_create_mapping ( ) to get a virq and sets the chip data to
* ps3_private data .
*/
2008-05-01 02:25:18 +04:00
static int ps3_virq_setup ( enum ps3_cpu_binding cpu , unsigned long outlet ,
unsigned int * virq )
2007-01-27 06:08:08 +03:00
{
int result ;
struct ps3_private * pd ;
/* This defines the default interrupt distribution policy. */
if ( cpu = = PS3_BINDING_CPU_ANY )
cpu = 0 ;
pd = & per_cpu ( ps3_private , cpu ) ;
* virq = irq_create_mapping ( NULL , outlet ) ;
if ( * virq = = NO_IRQ ) {
2011-11-29 19:38:49 +04:00
FAIL ( " %s:%d: irq_create_mapping failed: outlet %lu \n " ,
2007-01-27 06:08:08 +03:00
__func__ , __LINE__ , outlet ) ;
result = - ENOMEM ;
goto fail_create ;
}
2011-11-29 19:38:49 +04:00
DBG ( " %s:%d: outlet %lu => cpu %u, virq %u \n " , __func__ , __LINE__ ,
2007-01-27 06:08:08 +03:00
outlet , cpu , * virq ) ;
2011-03-25 18:45:20 +03:00
result = irq_set_chip_data ( * virq , pd ) ;
2007-01-27 06:08:08 +03:00
if ( result ) {
2011-11-29 19:38:49 +04:00
FAIL ( " %s:%d: irq_set_chip_data failed \n " ,
2007-01-27 06:08:08 +03:00
__func__ , __LINE__ ) ;
goto fail_set ;
}
2011-03-09 01:26:56 +03:00
ps3_chip_mask ( irq_get_irq_data ( * virq ) ) ;
2007-06-16 01:19:32 +04:00
2007-01-27 06:08:08 +03:00
return result ;
fail_set :
irq_dispose_mapping ( * virq ) ;
fail_create :
return result ;
}
2007-05-01 01:01:01 +04:00
/**
* ps3_virq_destroy - virq related teardown .
* @ virq : The assigned Linux virq .
*
* Clears chip data and calls irq_dispose_mapping ( ) for the virq .
*/
2008-05-01 02:25:18 +04:00
static int ps3_virq_destroy ( unsigned int virq )
2007-01-27 06:08:08 +03:00
{
2011-03-25 18:45:20 +03:00
const struct ps3_private * pd = irq_get_chip_data ( virq ) ;
2007-01-27 06:08:08 +03:00
2011-11-29 19:38:49 +04:00
DBG ( " %s:%d: ppe_id %llu, thread_id %llu, virq %u \n " , __func__ ,
2007-06-16 02:06:04 +04:00
__LINE__ , pd - > ppe_id , pd - > thread_id , virq ) ;
2007-01-27 06:08:08 +03:00
2011-03-25 18:45:20 +03:00
irq_set_chip_data ( virq , NULL ) ;
2007-01-27 06:08:08 +03:00
irq_dispose_mapping ( virq ) ;
2007-05-01 01:01:01 +04:00
2011-11-29 19:38:49 +04:00
DBG ( " %s:%d <- \n " , __func__ , __LINE__ ) ;
2007-05-01 01:01:01 +04:00
return 0 ;
2007-01-27 06:08:08 +03:00
}
2006-11-23 02:46:56 +03:00
/**
2007-05-01 01:01:01 +04:00
* ps3_irq_plug_setup - Generic outlet and virq related setup .
2007-01-27 06:08:08 +03:00
* @ cpu : enum ps3_cpu_binding indicating the cpu the interrupt should be
* serviced on .
2007-05-01 01:01:01 +04:00
* @ outlet : The HV outlet from the various create outlet routines .
2006-11-23 02:46:56 +03:00
* @ virq : The assigned Linux virq .
*
2007-05-01 01:01:01 +04:00
* Sets up virq and connects the irq plug .
2006-11-23 02:46:56 +03:00
*/
2007-05-01 01:01:01 +04:00
int ps3_irq_plug_setup ( enum ps3_cpu_binding cpu , unsigned long outlet ,
2007-01-27 06:08:08 +03:00
unsigned int * virq )
2006-11-23 02:46:56 +03:00
{
int result ;
2007-05-01 01:01:01 +04:00
struct ps3_private * pd ;
2006-11-23 02:46:56 +03:00
2007-05-01 01:01:01 +04:00
result = ps3_virq_setup ( cpu , outlet , virq ) ;
2006-11-23 02:46:56 +03:00
if ( result ) {
2011-11-29 19:38:49 +04:00
FAIL ( " %s:%d: ps3_virq_setup failed \n " , __func__ , __LINE__ ) ;
2007-05-01 01:01:01 +04:00
goto fail_setup ;
2006-11-23 02:46:56 +03:00
}
2011-03-25 18:45:20 +03:00
pd = irq_get_chip_data ( * virq ) ;
2007-05-01 01:01:01 +04:00
/* Binds outlet to cpu + virq. */
2007-06-16 02:06:04 +04:00
result = lv1_connect_irq_plug_ext ( pd - > ppe_id , pd - > thread_id , * virq ,
outlet , 0 ) ;
2006-11-23 02:46:56 +03:00
2007-05-01 01:01:01 +04:00
if ( result ) {
2011-11-29 19:38:49 +04:00
FAIL ( " %s:%d: lv1_connect_irq_plug_ext failed: %s \n " ,
2007-05-01 01:01:01 +04:00
__func__ , __LINE__ , ps3_result ( result ) ) ;
result = - EPERM ;
goto fail_connect ;
}
return result ;
fail_connect :
ps3_virq_destroy ( * virq ) ;
fail_setup :
2007-01-27 06:08:08 +03:00
return result ;
2006-11-23 02:46:56 +03:00
}
2007-05-01 01:01:01 +04:00
EXPORT_SYMBOL_GPL ( ps3_irq_plug_setup ) ;
/**
* ps3_irq_plug_destroy - Generic outlet and virq related teardown .
* @ virq : The assigned Linux virq .
*
* Disconnects the irq plug and tears down virq .
* Do not call for system bus event interrupts setup with
* ps3_sb_event_receive_port_setup ( ) .
*/
2006-11-23 02:46:56 +03:00
2007-05-01 01:01:01 +04:00
int ps3_irq_plug_destroy ( unsigned int virq )
2006-11-23 02:46:56 +03:00
{
int result ;
2011-03-25 18:45:20 +03:00
const struct ps3_private * pd = irq_get_chip_data ( virq ) ;
2006-11-23 02:46:56 +03:00
2011-11-29 19:38:49 +04:00
DBG ( " %s:%d: ppe_id %llu, thread_id %llu, virq %u \n " , __func__ ,
2007-06-16 02:06:04 +04:00
__LINE__ , pd - > ppe_id , pd - > thread_id , virq ) ;
2007-05-01 01:01:01 +04:00
2011-03-09 01:26:56 +03:00
ps3_chip_mask ( irq_get_irq_data ( virq ) ) ;
2007-06-16 01:19:32 +04:00
2007-06-16 02:06:04 +04:00
result = lv1_disconnect_irq_plug_ext ( pd - > ppe_id , pd - > thread_id , virq ) ;
2006-11-23 02:46:56 +03:00
2006-12-21 15:57:16 +03:00
if ( result )
2011-11-29 19:38:49 +04:00
FAIL ( " %s:%d: lv1_disconnect_irq_plug_ext failed: %s \n " ,
2007-05-01 01:01:01 +04:00
__func__ , __LINE__ , ps3_result ( result ) ) ;
2006-11-23 02:46:56 +03:00
2007-05-01 01:01:01 +04:00
ps3_virq_destroy ( virq ) ;
2006-11-23 02:46:56 +03:00
return result ;
}
2007-05-01 01:01:01 +04:00
EXPORT_SYMBOL_GPL ( ps3_irq_plug_destroy ) ;
2006-11-23 02:46:56 +03:00
/**
2007-05-01 01:01:01 +04:00
* ps3_event_receive_port_setup - Setup an event receive port .
2007-01-27 06:08:08 +03:00
* @ cpu : enum ps3_cpu_binding indicating the cpu the interrupt should be
* serviced on .
2006-11-23 02:46:56 +03:00
* @ virq : The assigned Linux virq .
*
* The virq can be used with lv1_connect_interrupt_event_receive_port ( ) to
2007-05-01 01:01:01 +04:00
* arrange to receive interrupts from system - bus devices , or with
* ps3_send_event_locally ( ) to signal events .
2006-11-23 02:46:56 +03:00
*/
2007-05-01 01:01:01 +04:00
int ps3_event_receive_port_setup ( enum ps3_cpu_binding cpu , unsigned int * virq )
2006-11-23 02:46:56 +03:00
{
int result ;
2009-01-13 22:59:41 +03:00
u64 outlet ;
2006-11-23 02:46:56 +03:00
result = lv1_construct_event_receive_port ( & outlet ) ;
if ( result ) {
2011-11-29 19:38:49 +04:00
FAIL ( " %s:%d: lv1_construct_event_receive_port failed: %s \n " ,
2006-11-23 02:46:56 +03:00
__func__ , __LINE__ , ps3_result ( result ) ) ;
* virq = NO_IRQ ;
return result ;
}
2007-05-01 01:01:01 +04:00
result = ps3_irq_plug_setup ( cpu , outlet , virq ) ;
2007-01-27 06:08:08 +03:00
BUG_ON ( result ) ;
2006-11-23 02:46:56 +03:00
2007-01-27 06:08:08 +03:00
return result ;
2006-11-23 02:46:56 +03:00
}
2007-05-01 01:01:01 +04:00
EXPORT_SYMBOL_GPL ( ps3_event_receive_port_setup ) ;
/**
* ps3_event_receive_port_destroy - Destroy an event receive port .
* @ virq : The assigned Linux virq .
*
* Since ps3_event_receive_port_destroy destroys the receive port outlet ,
* SB devices need to call disconnect_interrupt_event_receive_port ( ) before
* this .
*/
2006-11-23 02:46:56 +03:00
2007-05-01 01:01:01 +04:00
int ps3_event_receive_port_destroy ( unsigned int virq )
2006-11-23 02:46:56 +03:00
{
int result ;
2011-11-29 19:38:49 +04:00
DBG ( " -> %s:%d virq %u \n " , __func__ , __LINE__ , virq ) ;
2007-06-16 01:19:32 +04:00
2011-03-09 01:26:56 +03:00
ps3_chip_mask ( irq_get_irq_data ( virq ) ) ;
2006-11-23 02:46:56 +03:00
result = lv1_destruct_event_receive_port ( virq_to_hw ( virq ) ) ;
if ( result )
2011-11-29 19:38:49 +04:00
FAIL ( " %s:%d: lv1_destruct_event_receive_port failed: %s \n " ,
2006-11-23 02:46:56 +03:00
__func__ , __LINE__ , ps3_result ( result ) ) ;
2007-06-16 01:19:32 +04:00
/*
* Don ' t call ps3_virq_destroy ( ) here since ps3_smp_cleanup_cpu ( )
* calls from interrupt context ( smp_call_function ) when kexecing .
2007-05-01 01:01:01 +04:00
*/
2011-11-29 19:38:49 +04:00
DBG ( " <- %s:%d \n " , __func__ , __LINE__ ) ;
2006-11-23 02:46:56 +03:00
return result ;
}
int ps3_send_event_locally ( unsigned int virq )
{
return lv1_send_event_locally ( virq_to_hw ( virq ) ) ;
}
/**
2007-05-01 01:01:01 +04:00
* ps3_sb_event_receive_port_setup - Setup a system bus event receive port .
2007-01-27 06:08:08 +03:00
* @ cpu : enum ps3_cpu_binding indicating the cpu the interrupt should be
* serviced on .
2007-06-16 01:52:02 +04:00
* @ dev : The system bus device instance .
2006-11-23 02:46:56 +03:00
* @ virq : The assigned Linux virq .
*
* An event irq represents a virtual device interrupt . The interrupt_id
* coresponds to the software interrupt number .
*/
2007-06-16 01:52:02 +04:00
int ps3_sb_event_receive_port_setup ( struct ps3_system_bus_device * dev ,
enum ps3_cpu_binding cpu , unsigned int * virq )
2006-11-23 02:46:56 +03:00
{
2007-05-01 01:01:01 +04:00
/* this should go in system-bus.c */
2006-11-23 02:46:56 +03:00
int result ;
2007-05-01 01:01:01 +04:00
result = ps3_event_receive_port_setup ( cpu , virq ) ;
2006-11-23 02:46:56 +03:00
if ( result )
return result ;
2007-06-16 01:52:02 +04:00
result = lv1_connect_interrupt_event_receive_port ( dev - > bus_id ,
dev - > dev_id , virq_to_hw ( * virq ) , dev - > interrupt_id ) ;
2006-11-23 02:46:56 +03:00
if ( result ) {
2011-11-29 19:38:49 +04:00
FAIL ( " %s:%d: lv1_connect_interrupt_event_receive_port "
2006-11-23 02:46:56 +03:00
" failed: %s \n " , __func__ , __LINE__ ,
ps3_result ( result ) ) ;
2007-05-01 01:01:01 +04:00
ps3_event_receive_port_destroy ( * virq ) ;
2006-11-23 02:46:56 +03:00
* virq = NO_IRQ ;
return result ;
}
2011-11-29 19:38:49 +04:00
DBG ( " %s:%d: interrupt_id %u, virq %u \n " , __func__ , __LINE__ ,
2007-06-16 01:52:02 +04:00
dev - > interrupt_id , * virq ) ;
2006-11-23 02:46:56 +03:00
return 0 ;
}
2007-05-01 01:01:01 +04:00
EXPORT_SYMBOL ( ps3_sb_event_receive_port_setup ) ;
2006-11-23 02:46:56 +03:00
2007-06-16 01:52:02 +04:00
int ps3_sb_event_receive_port_destroy ( struct ps3_system_bus_device * dev ,
unsigned int virq )
2006-11-23 02:46:56 +03:00
{
2007-05-01 01:01:01 +04:00
/* this should go in system-bus.c */
2006-11-23 02:46:56 +03:00
int result ;
2011-11-29 19:38:49 +04:00
DBG ( " -> %s:%d: interrupt_id %u, virq %u \n " , __func__ , __LINE__ ,
2007-06-16 01:52:02 +04:00
dev - > interrupt_id , virq ) ;
2006-11-23 02:46:56 +03:00
2007-06-16 01:52:02 +04:00
result = lv1_disconnect_interrupt_event_receive_port ( dev - > bus_id ,
dev - > dev_id , virq_to_hw ( virq ) , dev - > interrupt_id ) ;
2006-11-23 02:46:56 +03:00
if ( result )
2011-11-29 19:38:49 +04:00
FAIL ( " %s:%d: lv1_disconnect_interrupt_event_receive_port "
2006-11-23 02:46:56 +03:00
" failed: %s \n " , __func__ , __LINE__ ,
ps3_result ( result ) ) ;
2007-05-01 01:01:01 +04:00
result = ps3_event_receive_port_destroy ( virq ) ;
BUG_ON ( result ) ;
2006-11-23 02:46:56 +03:00
2007-06-16 01:19:32 +04:00
/*
* ps3_event_receive_port_destroy ( ) destroys the IRQ plug ,
* so don ' t call ps3_irq_plug_destroy ( ) here .
*/
result = ps3_virq_destroy ( virq ) ;
BUG_ON ( result ) ;
2011-11-29 19:38:49 +04:00
DBG ( " <- %s:%d \n " , __func__ , __LINE__ ) ;
2006-11-23 02:46:56 +03:00
return result ;
}
2007-05-01 01:01:01 +04:00
EXPORT_SYMBOL ( ps3_sb_event_receive_port_destroy ) ;
2006-11-23 02:46:56 +03:00
/**
2007-05-01 01:01:01 +04:00
* ps3_io_irq_setup - Setup a system bus io irq .
* @ cpu : enum ps3_cpu_binding indicating the cpu the interrupt should be
* serviced on .
* @ interrupt_id : The device interrupt id read from the system repository .
* @ virq : The assigned Linux virq .
*
* An io irq represents a non - virtualized device interrupt . interrupt_id
* coresponds to the interrupt number of the interrupt controller .
*/
int ps3_io_irq_setup ( enum ps3_cpu_binding cpu , unsigned int interrupt_id ,
unsigned int * virq )
{
int result ;
2009-01-13 22:59:41 +03:00
u64 outlet ;
2007-05-01 01:01:01 +04:00
result = lv1_construct_io_irq_outlet ( interrupt_id , & outlet ) ;
if ( result ) {
2011-11-29 19:38:49 +04:00
FAIL ( " %s:%d: lv1_construct_io_irq_outlet failed: %s \n " ,
2007-05-01 01:01:01 +04:00
__func__ , __LINE__ , ps3_result ( result ) ) ;
return result ;
}
result = ps3_irq_plug_setup ( cpu , outlet , virq ) ;
BUG_ON ( result ) ;
return result ;
}
EXPORT_SYMBOL_GPL ( ps3_io_irq_setup ) ;
int ps3_io_irq_destroy ( unsigned int virq )
{
int result ;
2007-06-16 01:19:32 +04:00
unsigned long outlet = virq_to_hw ( virq ) ;
2007-05-01 01:01:01 +04:00
2011-03-09 01:26:56 +03:00
ps3_chip_mask ( irq_get_irq_data ( virq ) ) ;
2007-05-01 01:01:01 +04:00
2007-06-16 01:19:32 +04:00
/*
* lv1_destruct_io_irq_outlet ( ) will destroy the IRQ plug ,
* so call ps3_irq_plug_destroy ( ) first .
*/
2007-05-01 01:01:01 +04:00
result = ps3_irq_plug_destroy ( virq ) ;
BUG_ON ( result ) ;
2007-06-16 01:19:32 +04:00
result = lv1_destruct_io_irq_outlet ( outlet ) ;
if ( result )
2011-11-29 19:38:49 +04:00
FAIL ( " %s:%d: lv1_destruct_io_irq_outlet failed: %s \n " ,
2007-06-16 01:19:32 +04:00
__func__ , __LINE__ , ps3_result ( result ) ) ;
2007-05-01 01:01:01 +04:00
return result ;
}
EXPORT_SYMBOL_GPL ( ps3_io_irq_destroy ) ;
/**
* ps3_vuart_irq_setup - Setup the system virtual uart virq .
2007-01-27 06:08:08 +03:00
* @ cpu : enum ps3_cpu_binding indicating the cpu the interrupt should be
* serviced on .
2006-11-23 02:46:56 +03:00
* @ virt_addr_bmp : The caller supplied virtual uart interrupt bitmap .
* @ virq : The assigned Linux virq .
*
* The system supports only a single virtual uart , so multiple calls without
* freeing the interrupt will return a wrong state error .
*/
2007-05-01 01:01:01 +04:00
int ps3_vuart_irq_setup ( enum ps3_cpu_binding cpu , void * virt_addr_bmp ,
2007-01-27 06:08:08 +03:00
unsigned int * virq )
2006-11-23 02:46:56 +03:00
{
int result ;
2009-01-13 22:59:41 +03:00
u64 outlet ;
2007-01-27 06:08:08 +03:00
u64 lpar_addr ;
2006-11-23 02:46:56 +03:00
2007-01-27 06:08:08 +03:00
BUG_ON ( ! is_kernel_addr ( ( u64 ) virt_addr_bmp ) ) ;
2006-11-23 02:46:56 +03:00
lpar_addr = ps3_mm_phys_to_lpar ( __pa ( virt_addr_bmp ) ) ;
result = lv1_configure_virtual_uart_irq ( lpar_addr , & outlet ) ;
if ( result ) {
2011-11-29 19:38:49 +04:00
FAIL ( " %s:%d: lv1_configure_virtual_uart_irq failed: %s \n " ,
2006-11-23 02:46:56 +03:00
__func__ , __LINE__ , ps3_result ( result ) ) ;
return result ;
}
2007-05-01 01:01:01 +04:00
result = ps3_irq_plug_setup ( cpu , outlet , virq ) ;
2007-01-27 06:08:08 +03:00
BUG_ON ( result ) ;
2006-11-23 02:46:56 +03:00
2007-01-27 06:08:08 +03:00
return result ;
2006-11-23 02:46:56 +03:00
}
2007-06-16 02:01:06 +04:00
EXPORT_SYMBOL_GPL ( ps3_vuart_irq_setup ) ;
2006-11-23 02:46:56 +03:00
2007-05-01 01:01:01 +04:00
int ps3_vuart_irq_destroy ( unsigned int virq )
2006-11-23 02:46:56 +03:00
{
int result ;
2011-03-09 01:26:56 +03:00
ps3_chip_mask ( irq_get_irq_data ( virq ) ) ;
2006-11-23 02:46:56 +03:00
result = lv1_deconfigure_virtual_uart_irq ( ) ;
if ( result ) {
2011-11-29 19:38:49 +04:00
FAIL ( " %s:%d: lv1_configure_virtual_uart_irq failed: %s \n " ,
2006-11-23 02:46:56 +03:00
__func__ , __LINE__ , ps3_result ( result ) ) ;
return result ;
}
2007-05-01 01:01:01 +04:00
result = ps3_irq_plug_destroy ( virq ) ;
BUG_ON ( result ) ;
2006-11-23 02:46:56 +03:00
return result ;
}
2007-06-16 02:01:06 +04:00
EXPORT_SYMBOL_GPL ( ps3_vuart_irq_destroy ) ;
2006-11-23 02:46:56 +03:00
/**
2007-05-01 01:01:01 +04:00
* ps3_spe_irq_setup - Setup an spe virq .
2007-01-27 06:08:08 +03:00
* @ cpu : enum ps3_cpu_binding indicating the cpu the interrupt should be
* serviced on .
2006-11-23 02:46:56 +03:00
* @ spe_id : The spe_id returned from lv1_construct_logical_spe ( ) .
* @ class : The spe interrupt class { 0 , 1 , 2 } .
* @ virq : The assigned Linux virq .
*
*/
2007-05-01 01:01:01 +04:00
int ps3_spe_irq_setup ( enum ps3_cpu_binding cpu , unsigned long spe_id ,
2007-01-27 06:08:08 +03:00
unsigned int class , unsigned int * virq )
2006-11-23 02:46:56 +03:00
{
int result ;
2009-01-13 22:59:41 +03:00
u64 outlet ;
2006-11-23 02:46:56 +03:00
BUG_ON ( class > 2 ) ;
result = lv1_get_spe_irq_outlet ( spe_id , class , & outlet ) ;
if ( result ) {
2011-11-29 19:38:49 +04:00
FAIL ( " %s:%d: lv1_get_spe_irq_outlet failed: %s \n " ,
2006-11-23 02:46:56 +03:00
__func__ , __LINE__ , ps3_result ( result ) ) ;
return result ;
}
2007-05-01 01:01:01 +04:00
result = ps3_irq_plug_setup ( cpu , outlet , virq ) ;
2007-01-27 06:08:08 +03:00
BUG_ON ( result ) ;
2006-11-23 02:46:56 +03:00
2007-01-27 06:08:08 +03:00
return result ;
2006-11-23 02:46:56 +03:00
}
2007-05-01 01:01:01 +04:00
int ps3_spe_irq_destroy ( unsigned int virq )
2006-11-23 02:46:56 +03:00
{
2007-06-16 01:19:32 +04:00
int result ;
2011-03-09 01:26:56 +03:00
ps3_chip_mask ( irq_get_irq_data ( virq ) ) ;
2007-06-16 01:19:32 +04:00
result = ps3_irq_plug_destroy ( virq ) ;
2007-05-01 01:01:01 +04:00
BUG_ON ( result ) ;
2007-06-16 01:19:32 +04:00
return result ;
2006-11-23 02:46:56 +03:00
}
2007-01-27 06:08:12 +03:00
2006-11-23 02:46:56 +03:00
# define PS3_INVALID_OUTLET ((irq_hw_number_t)-1)
# define PS3_PLUG_MAX 63
# if defined(DEBUG)
2007-01-27 06:08:08 +03:00
static void _dump_64_bmp ( const char * header , const u64 * p , unsigned cpu ,
2006-11-23 02:46:56 +03:00
const char * func , int line )
{
2011-11-29 19:38:49 +04:00
pr_debug ( " %s:%d: %s %u {%04llx_%04llx_%04llx_%04llx} \n " ,
2006-11-23 02:46:56 +03:00
func , line , header , cpu ,
* p > > 48 , ( * p > > 32 ) & 0xffff , ( * p > > 16 ) & 0xffff ,
* p & 0xffff ) ;
}
2007-06-16 01:18:14 +04:00
static void __maybe_unused _dump_256_bmp ( const char * header ,
2007-01-27 06:08:08 +03:00
const u64 * p , unsigned cpu , const char * func , int line )
2006-11-23 02:46:56 +03:00
{
2011-11-29 19:38:49 +04:00
pr_debug ( " %s:%d: %s %u {%016llx:%016llx:%016llx:%016llx} \n " ,
2006-11-23 02:46:56 +03:00
func , line , header , cpu , p [ 0 ] , p [ 1 ] , p [ 2 ] , p [ 3 ] ) ;
}
# define dump_bmp(_x) _dump_bmp(_x, __func__, __LINE__)
2007-01-27 06:07:59 +03:00
static void _dump_bmp ( struct ps3_private * pd , const char * func , int line )
2006-11-23 02:46:56 +03:00
{
unsigned long flags ;
2011-11-29 19:38:49 +04:00
spin_lock_irqsave ( & pd - > bmp_lock , flags ) ;
2007-06-16 02:06:04 +04:00
_dump_64_bmp ( " stat " , & pd - > bmp . status , pd - > thread_id , func , line ) ;
2011-11-29 19:38:49 +04:00
_dump_64_bmp ( " mask " , ( u64 * ) & pd - > bmp . mask , pd - > thread_id , func , line ) ;
spin_unlock_irqrestore ( & pd - > bmp_lock , flags ) ;
2006-11-23 02:46:56 +03:00
}
# define dump_mask(_x) _dump_mask(_x, __func__, __LINE__)
2007-06-16 01:18:14 +04:00
static void __maybe_unused _dump_mask ( struct ps3_private * pd ,
2006-11-23 02:46:56 +03:00
const char * func , int line )
{
unsigned long flags ;
2011-11-29 19:38:49 +04:00
spin_lock_irqsave ( & pd - > bmp_lock , flags ) ;
_dump_64_bmp ( " mask " , ( u64 * ) & pd - > bmp . mask , pd - > thread_id , func , line ) ;
spin_unlock_irqrestore ( & pd - > bmp_lock , flags ) ;
2006-11-23 02:46:56 +03:00
}
# else
2007-01-27 06:07:59 +03:00
static void dump_bmp ( struct ps3_private * pd ) { } ;
2006-11-23 02:46:56 +03:00
# endif /* defined(DEBUG) */
2007-01-27 06:07:59 +03:00
static int ps3_host_map ( struct irq_host * h , unsigned int virq ,
2006-11-23 02:46:56 +03:00
irq_hw_number_t hwirq )
{
2011-11-29 19:38:49 +04:00
DBG ( " %s:%d: hwirq %lu, virq %u \n " , __func__ , __LINE__ , hwirq ,
2007-01-27 06:08:08 +03:00
virq ) ;
2006-11-23 02:46:56 +03:00
2011-03-25 18:45:20 +03:00
irq_set_chip_and_handler ( virq , & ps3_irq_chip , handle_fasteoi_irq ) ;
2006-11-23 02:46:56 +03:00
2007-01-27 06:08:08 +03:00
return 0 ;
2006-11-23 02:46:56 +03:00
}
2007-08-28 12:47:55 +04:00
static int ps3_host_match ( struct irq_host * h , struct device_node * np )
{
/* Match all */
return 1 ;
}
2007-01-27 06:07:59 +03:00
static struct irq_host_ops ps3_host_ops = {
. map = ps3_host_map ,
2007-08-28 12:47:55 +04:00
. match = ps3_host_match ,
2006-11-23 02:46:56 +03:00
} ;
void __init ps3_register_ipi_debug_brk ( unsigned int cpu , unsigned int virq )
{
2007-01-27 06:07:59 +03:00
struct ps3_private * pd = & per_cpu ( ps3_private , cpu ) ;
2006-11-23 02:46:56 +03:00
2011-11-29 19:38:49 +04:00
set_bit ( 63 - virq , & pd - > ipi_debug_brk_mask ) ;
2006-11-23 02:46:56 +03:00
2011-11-29 19:38:49 +04:00
DBG ( " %s:%d: cpu %u, virq %u, mask %lxh \n " , __func__ , __LINE__ ,
cpu , virq , pd - > ipi_debug_brk_mask ) ;
2006-11-23 02:46:56 +03:00
}
2011-11-08 16:37:26 +04:00
void __init ps3_register_ipi_irq ( unsigned int cpu , unsigned int virq )
{
struct ps3_private * pd = & per_cpu ( ps3_private , cpu ) ;
set_bit ( 63 - virq , & pd - > ipi_mask ) ;
DBG ( " %s:%d: cpu %u, virq %u, ipi_mask %lxh \n " , __func__ , __LINE__ ,
cpu , virq , pd - > ipi_mask ) ;
}
2007-06-16 01:19:32 +04:00
static unsigned int ps3_get_irq ( void )
2006-11-23 02:46:56 +03:00
{
2007-01-27 06:08:05 +03:00
struct ps3_private * pd = & __get_cpu_var ( ps3_private ) ;
2007-01-27 06:08:08 +03:00
u64 x = ( pd - > bmp . status & pd - > bmp . mask ) ;
2007-01-27 06:08:05 +03:00
unsigned int plug ;
2006-11-23 02:46:56 +03:00
/* check for ipi break first to stop this cpu ASAP */
2011-11-29 19:38:49 +04:00
if ( x & pd - > ipi_debug_brk_mask )
x & = pd - > ipi_debug_brk_mask ;
2006-11-23 02:46:56 +03:00
2007-01-27 06:08:05 +03:00
asm volatile ( " cntlzd %0,%1 " : " =r " ( plug ) : " r " ( x ) ) ;
plug & = 0x3f ;
2006-11-23 02:46:56 +03:00
2008-03-27 03:37:18 +03:00
if ( unlikely ( plug = = NO_IRQ ) ) {
2011-11-29 19:38:49 +04:00
DBG ( " %s:%d: no plug found: thread_id %llu \n " , __func__ ,
2007-06-16 02:06:04 +04:00
__LINE__ , pd - > thread_id ) ;
2007-01-27 06:07:59 +03:00
dump_bmp ( & per_cpu ( ps3_private , 0 ) ) ;
dump_bmp ( & per_cpu ( ps3_private , 1 ) ) ;
2006-11-23 02:46:56 +03:00
return NO_IRQ ;
}
# if defined(DEBUG)
2007-01-27 06:08:05 +03:00
if ( unlikely ( plug < NUM_ISA_INTERRUPTS | | plug > PS3_PLUG_MAX ) ) {
2007-01-27 06:07:59 +03:00
dump_bmp ( & per_cpu ( ps3_private , 0 ) ) ;
dump_bmp ( & per_cpu ( ps3_private , 1 ) ) ;
2006-11-23 02:46:56 +03:00
BUG ( ) ;
}
# endif
2011-11-08 16:37:26 +04:00
/* IPIs are EOIed here. */
if ( test_bit ( 63 - plug , & pd - > ipi_mask ) )
lv1_end_of_interrupt_ext ( pd - > ppe_id , pd - > thread_id , plug ) ;
2006-11-23 02:46:56 +03:00
return plug ;
}
void __init ps3_init_IRQ ( void )
{
int result ;
unsigned cpu ;
struct irq_host * host ;
2007-08-28 12:47:54 +04:00
host = irq_alloc_host ( NULL , IRQ_HOST_MAP_NOMAP , 0 , & ps3_host_ops ,
2006-11-23 02:46:56 +03:00
PS3_INVALID_OUTLET ) ;
irq_set_default_host ( host ) ;
irq_set_virq_count ( PS3_PLUG_MAX + 1 ) ;
for_each_possible_cpu ( cpu ) {
2007-01-27 06:07:59 +03:00
struct ps3_private * pd = & per_cpu ( ps3_private , cpu ) ;
2006-11-23 02:46:56 +03:00
2007-06-16 02:06:04 +04:00
lv1_get_logical_ppe_id ( & pd - > ppe_id ) ;
pd - > thread_id = get_hard_smp_processor_id ( cpu ) ;
2011-11-29 19:38:49 +04:00
spin_lock_init ( & pd - > bmp_lock ) ;
2006-11-23 02:46:56 +03:00
2011-11-29 19:38:49 +04:00
DBG ( " %s:%d: ppe_id %llu, thread_id %llu, bmp %lxh \n " ,
2007-06-16 02:06:04 +04:00
__func__ , __LINE__ , pd - > ppe_id , pd - > thread_id ,
2007-01-27 06:08:02 +03:00
ps3_mm_phys_to_lpar ( __pa ( & pd - > bmp ) ) ) ;
2007-06-16 02:06:04 +04:00
result = lv1_configure_irq_state_bitmap ( pd - > ppe_id ,
pd - > thread_id , ps3_mm_phys_to_lpar ( __pa ( & pd - > bmp ) ) ) ;
2006-11-23 02:46:56 +03:00
if ( result )
2011-11-29 19:38:49 +04:00
FAIL ( " %s:%d: lv1_configure_irq_state_bitmap failed: "
2006-11-23 02:46:56 +03:00
" %s \n " , __func__ , __LINE__ ,
ps3_result ( result ) ) ;
}
ppc_md . get_irq = ps3_get_irq ;
}
2007-06-16 01:19:32 +04:00
void ps3_shutdown_IRQ ( int cpu )
{
int result ;
u64 ppe_id ;
u64 thread_id = get_hard_smp_processor_id ( cpu ) ;
lv1_get_logical_ppe_id ( & ppe_id ) ;
result = lv1_configure_irq_state_bitmap ( ppe_id , thread_id , 0 ) ;
2009-01-13 23:02:39 +03:00
DBG ( " %s:%d: lv1_configure_irq_state_bitmap (%llu:%llu/%d) %s \n " , __func__ ,
2007-06-16 01:19:32 +04:00
__LINE__ , ppe_id , thread_id , cpu , ps3_result ( result ) ) ;
}