2006-11-23 02:46:59 +03:00
/*
* PS3 Platform spu 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>
# include <linux/init.h>
# include <linux/mmzone.h>
# include <linux/io.h>
# include <linux/mm.h>
# include <asm/spu.h>
# include <asm/spu_priv1.h>
# include <asm/lv1call.h>
2008-03-27 03:37:38 +03:00
# include <asm/ps3.h>
2006-11-23 02:46:59 +03:00
2007-12-05 05:49:31 +03:00
# include "../cell/spufs/spufs.h"
2007-01-31 02:20:27 +03:00
# include "platform.h"
2006-11-23 02:46:59 +03:00
/* spu_management_ops */
/**
* enum spe_type - Type of spe to create .
* @ spe_type_logical : Standard logical spe .
*
* For use with lv1_construct_logical_spe ( ) . The current HV does not support
* any types other than those listed .
*/
enum spe_type {
SPE_TYPE_LOGICAL = 0 ,
} ;
/**
* struct spe_shadow - logical spe shadow register area .
*
* Read - only shadow of spe registers .
*/
struct spe_shadow {
2007-01-27 06:07:56 +03:00
u8 padding_0140 [ 0x0140 ] ;
2006-11-23 02:46:59 +03:00
u64 int_status_class0_RW ; /* 0x0140 */
u64 int_status_class1_RW ; /* 0x0148 */
u64 int_status_class2_RW ; /* 0x0150 */
u8 padding_0158 [ 0x0610 - 0x0158 ] ;
u64 mfc_dsisr_RW ; /* 0x0610 */
u8 padding_0618 [ 0x0620 - 0x0618 ] ;
u64 mfc_dar_RW ; /* 0x0620 */
u8 padding_0628 [ 0x0800 - 0x0628 ] ;
u64 mfc_dsipr_R ; /* 0x0800 */
u8 padding_0808 [ 0x0810 - 0x0808 ] ;
u64 mfc_lscrr_R ; /* 0x0810 */
u8 padding_0818 [ 0x0c00 - 0x0818 ] ;
u64 mfc_cer_R ; /* 0x0c00 */
u8 padding_0c08 [ 0x0f00 - 0x0c08 ] ;
u64 spe_execution_status ; /* 0x0f00 */
u8 padding_0f08 [ 0x1000 - 0x0f08 ] ;
2007-01-27 06:07:56 +03:00
} ;
2006-11-23 02:46:59 +03:00
/**
* enum spe_ex_state - Logical spe execution state .
* @ spe_ex_state_unexecutable : Uninitialized .
* @ spe_ex_state_executable : Enabled , not ready .
* @ spe_ex_state_executed : Ready for use .
*
* The execution state ( status ) of the logical spe as reported in
* struct spe_shadow : spe_execution_status .
*/
enum spe_ex_state {
SPE_EX_STATE_UNEXECUTABLE = 0 ,
SPE_EX_STATE_EXECUTABLE = 2 ,
SPE_EX_STATE_EXECUTED = 3 ,
} ;
/**
* struct priv1_cache - Cached values of priv1 registers .
* @ masks [ ] : Array of cached spe interrupt masks , indexed by class .
* @ sr1 : Cached mfc_sr1 register .
* @ tclass_id : Cached mfc_tclass_id register .
*/
struct priv1_cache {
u64 masks [ 3 ] ;
u64 sr1 ;
u64 tclass_id ;
} ;
/**
* struct spu_pdata - Platform state variables .
* @ spe_id : HV spe id returned by lv1_construct_logical_spe ( ) .
* @ resource_id : HV spe resource id returned by
* ps3_repository_read_spe_resource_id ( ) .
* @ priv2_addr : lpar address of spe priv2 area returned by
* lv1_construct_logical_spe ( ) .
* @ shadow_addr : lpar address of spe register shadow area returned by
* lv1_construct_logical_spe ( ) .
* @ shadow : Virtual ( ioremap ) address of spe register shadow area .
* @ cache : Cached values of priv1 registers .
*/
struct spu_pdata {
u64 spe_id ;
u64 resource_id ;
u64 priv2_addr ;
u64 shadow_addr ;
struct spe_shadow __iomem * shadow ;
struct priv1_cache cache ;
} ;
static struct spu_pdata * spu_pdata ( struct spu * spu )
{
return spu - > pdata ;
}
# define dump_areas(_a, _b, _c, _d, _e) \
_dump_areas ( _a , _b , _c , _d , _e , __func__ , __LINE__ )
static void _dump_areas ( unsigned int spe_id , unsigned long priv2 ,
unsigned long problem , unsigned long ls , unsigned long shadow ,
const char * func , int line )
{
pr_debug ( " %s:%d: spe_id: %xh (%u) \n " , func , line , spe_id , spe_id ) ;
pr_debug ( " %s:%d: priv2: %lxh \n " , func , line , priv2 ) ;
pr_debug ( " %s:%d: problem: %lxh \n " , func , line , problem ) ;
pr_debug ( " %s:%d: ls: %lxh \n " , func , line , ls ) ;
pr_debug ( " %s:%d: shadow: %lxh \n " , func , line , shadow ) ;
}
2008-03-27 03:37:38 +03:00
inline u64 ps3_get_spe_id ( void * arg )
{
return spu_pdata ( arg ) - > spe_id ;
}
EXPORT_SYMBOL_GPL ( ps3_get_spe_id ) ;
2006-11-23 02:46:59 +03:00
static unsigned long get_vas_id ( void )
{
2009-01-13 22:59:41 +03:00
u64 id ;
2006-11-23 02:46:59 +03:00
lv1_get_logical_ppe_id ( & id ) ;
lv1_get_virtual_address_space_id_of_ppe ( id , & id ) ;
return id ;
}
static int __init construct_spu ( struct spu * spu )
{
int result ;
2009-01-13 22:59:41 +03:00
u64 unused ;
u64 problem_phys ;
u64 local_store_phys ;
2006-11-23 02:46:59 +03:00
result = lv1_construct_logical_spe ( PAGE_SHIFT , PAGE_SHIFT , PAGE_SHIFT ,
PAGE_SHIFT , PAGE_SHIFT , get_vas_id ( ) , SPE_TYPE_LOGICAL ,
2009-01-13 22:59:41 +03:00
& spu_pdata ( spu ) - > priv2_addr , & problem_phys ,
& local_store_phys , & unused ,
2006-11-23 02:46:59 +03:00
& spu_pdata ( spu ) - > shadow_addr ,
& spu_pdata ( spu ) - > spe_id ) ;
2009-01-13 22:59:41 +03:00
spu - > problem_phys = problem_phys ;
spu - > local_store_phys = local_store_phys ;
2006-11-23 02:46:59 +03:00
if ( result ) {
pr_debug ( " %s:%d: lv1_construct_logical_spe failed: %s \n " ,
__func__ , __LINE__ , ps3_result ( result ) ) ;
return result ;
}
return result ;
}
static void spu_unmap ( struct spu * spu )
{
iounmap ( spu - > priv2 ) ;
iounmap ( spu - > problem ) ;
iounmap ( ( __force u8 __iomem * ) spu - > local_store ) ;
iounmap ( spu_pdata ( spu ) - > shadow ) ;
}
2008-08-21 00:18:56 +04:00
/**
* setup_areas - Map the spu regions into the address space .
*
* The current HV requires the spu shadow regs to be mapped with the
* PTE page protection bits set as read - only ( PP = 3 ) . This implementation
* uses the low level __ioremap ( ) to bypass the page protection settings
* inforced by ioremap_flags ( ) to get the needed PTE bits set for the
* shadow regs .
*/
2006-11-23 02:46:59 +03:00
static int __init setup_areas ( struct spu * spu )
{
struct table { char * name ; unsigned long addr ; unsigned long size ; } ;
2008-08-21 00:18:56 +04:00
static const unsigned long shadow_flags = _PAGE_NO_CACHE | 3 ;
2006-11-23 02:46:59 +03:00
2008-08-21 00:18:56 +04:00
spu_pdata ( spu ) - > shadow = __ioremap ( spu_pdata ( spu ) - > shadow_addr ,
sizeof ( struct spe_shadow ) ,
shadow_flags ) ;
2006-11-23 02:46:59 +03:00
if ( ! spu_pdata ( spu ) - > shadow ) {
pr_debug ( " %s:%d: ioremap shadow failed \n " , __func__ , __LINE__ ) ;
goto fail_ioremap ;
}
2007-06-16 01:18:56 +04:00
spu - > local_store = ( __force void * ) ioremap_flags ( spu - > local_store_phys ,
LS_SIZE , _PAGE_NO_CACHE ) ;
2006-11-23 02:46:59 +03:00
if ( ! spu - > local_store ) {
pr_debug ( " %s:%d: ioremap local_store failed \n " ,
__func__ , __LINE__ ) ;
goto fail_ioremap ;
}
spu - > problem = ioremap ( spu - > problem_phys ,
sizeof ( struct spu_problem ) ) ;
2007-06-16 01:18:56 +04:00
2006-11-23 02:46:59 +03:00
if ( ! spu - > problem ) {
pr_debug ( " %s:%d: ioremap problem failed \n " , __func__ , __LINE__ ) ;
goto fail_ioremap ;
}
spu - > priv2 = ioremap ( spu_pdata ( spu ) - > priv2_addr ,
sizeof ( struct spu_priv2 ) ) ;
2007-06-16 01:18:56 +04:00
2006-11-23 02:46:59 +03:00
if ( ! spu - > priv2 ) {
pr_debug ( " %s:%d: ioremap priv2 failed \n " , __func__ , __LINE__ ) ;
goto fail_ioremap ;
}
dump_areas ( spu_pdata ( spu ) - > spe_id , spu_pdata ( spu ) - > priv2_addr ,
spu - > problem_phys , spu - > local_store_phys ,
spu_pdata ( spu ) - > shadow_addr ) ;
dump_areas ( spu_pdata ( spu ) - > spe_id , ( unsigned long ) spu - > priv2 ,
( unsigned long ) spu - > problem , ( unsigned long ) spu - > local_store ,
( unsigned long ) spu_pdata ( spu ) - > shadow ) ;
return 0 ;
fail_ioremap :
spu_unmap ( spu ) ;
2007-02-13 03:46:07 +03:00
return - ENOMEM ;
2006-11-23 02:46:59 +03:00
}
static int __init setup_interrupts ( struct spu * spu )
{
int result ;
2007-05-01 01:01:01 +04:00
result = ps3_spe_irq_setup ( PS3_BINDING_CPU_ANY , spu_pdata ( spu ) - > spe_id ,
2007-01-27 06:08:08 +03:00
0 , & spu - > irqs [ 0 ] ) ;
2006-11-23 02:46:59 +03:00
if ( result )
goto fail_alloc_0 ;
2007-05-01 01:01:01 +04:00
result = ps3_spe_irq_setup ( PS3_BINDING_CPU_ANY , spu_pdata ( spu ) - > spe_id ,
2007-01-27 06:08:08 +03:00
1 , & spu - > irqs [ 1 ] ) ;
2006-11-23 02:46:59 +03:00
if ( result )
goto fail_alloc_1 ;
2007-05-01 01:01:01 +04:00
result = ps3_spe_irq_setup ( PS3_BINDING_CPU_ANY , spu_pdata ( spu ) - > spe_id ,
2007-01-27 06:08:08 +03:00
2 , & spu - > irqs [ 2 ] ) ;
2006-11-23 02:46:59 +03:00
if ( result )
goto fail_alloc_2 ;
return result ;
fail_alloc_2 :
2007-05-01 01:01:01 +04:00
ps3_spe_irq_destroy ( spu - > irqs [ 1 ] ) ;
2006-11-23 02:46:59 +03:00
fail_alloc_1 :
2007-05-01 01:01:01 +04:00
ps3_spe_irq_destroy ( spu - > irqs [ 0 ] ) ;
2006-11-23 02:46:59 +03:00
fail_alloc_0 :
spu - > irqs [ 0 ] = spu - > irqs [ 1 ] = spu - > irqs [ 2 ] = NO_IRQ ;
return result ;
}
static int __init enable_spu ( struct spu * spu )
{
int result ;
result = lv1_enable_logical_spe ( spu_pdata ( spu ) - > spe_id ,
spu_pdata ( spu ) - > resource_id ) ;
if ( result ) {
pr_debug ( " %s:%d: lv1_enable_logical_spe failed: %s \n " ,
__func__ , __LINE__ , ps3_result ( result ) ) ;
goto fail_enable ;
}
result = setup_areas ( spu ) ;
if ( result )
goto fail_areas ;
result = setup_interrupts ( spu ) ;
if ( result )
goto fail_interrupts ;
return 0 ;
fail_interrupts :
spu_unmap ( spu ) ;
fail_areas :
lv1_disable_logical_spe ( spu_pdata ( spu ) - > spe_id , 0 ) ;
fail_enable :
return result ;
}
static int ps3_destroy_spu ( struct spu * spu )
{
int result ;
pr_debug ( " %s:%d spu_%d \n " , __func__ , __LINE__ , spu - > number ) ;
result = lv1_disable_logical_spe ( spu_pdata ( spu ) - > spe_id , 0 ) ;
BUG_ON ( result ) ;
2007-05-01 01:01:01 +04:00
ps3_spe_irq_destroy ( spu - > irqs [ 2 ] ) ;
ps3_spe_irq_destroy ( spu - > irqs [ 1 ] ) ;
ps3_spe_irq_destroy ( spu - > irqs [ 0 ] ) ;
2006-11-23 02:46:59 +03:00
spu - > irqs [ 0 ] = spu - > irqs [ 1 ] = spu - > irqs [ 2 ] = NO_IRQ ;
spu_unmap ( spu ) ;
result = lv1_destruct_logical_spe ( spu_pdata ( spu ) - > spe_id ) ;
BUG_ON ( result ) ;
kfree ( spu - > pdata ) ;
spu - > pdata = NULL ;
return 0 ;
}
static int __init ps3_create_spu ( struct spu * spu , void * data )
{
int result ;
pr_debug ( " %s:%d spu_%d \n " , __func__ , __LINE__ , spu - > number ) ;
spu - > pdata = kzalloc ( sizeof ( struct spu_pdata ) ,
GFP_KERNEL ) ;
if ( ! spu - > pdata ) {
result = - ENOMEM ;
goto fail_malloc ;
}
spu_pdata ( spu ) - > resource_id = ( unsigned long ) data ;
/* Init cached reg values to HV defaults. */
spu_pdata ( spu ) - > cache . sr1 = 0x33 ;
result = construct_spu ( spu ) ;
if ( result )
goto fail_construct ;
/* For now, just go ahead and enable it. */
result = enable_spu ( spu ) ;
if ( result )
goto fail_enable ;
/* Make sure the spu is in SPE_EX_STATE_EXECUTED. */
/* need something better here!!! */
while ( in_be64 ( & spu_pdata ( spu ) - > shadow - > spe_execution_status )
! = SPE_EX_STATE_EXECUTED )
( void ) 0 ;
return result ;
fail_enable :
fail_construct :
ps3_destroy_spu ( spu ) ;
fail_malloc :
return result ;
}
static int __init ps3_enumerate_spus ( int ( * fn ) ( void * data ) )
{
int result ;
unsigned int num_resource_id ;
unsigned int i ;
result = ps3_repository_read_num_spu_resource_id ( & num_resource_id ) ;
pr_debug ( " %s:%d: num_resource_id %u \n " , __func__ , __LINE__ ,
num_resource_id ) ;
/*
* For now , just create logical spus equal to the number
* of physical spus reserved for the partition .
*/
for ( i = 0 ; i < num_resource_id ; i + + ) {
enum ps3_spu_resource_type resource_type ;
unsigned int resource_id ;
result = ps3_repository_read_spu_resource_id ( i ,
& resource_type , & resource_id ) ;
if ( result )
break ;
if ( resource_type = = PS3_SPU_RESOURCE_TYPE_EXCLUSIVE ) {
result = fn ( ( void * ) ( unsigned long ) resource_id ) ;
if ( result )
break ;
}
}
2007-07-17 15:05:52 +04:00
if ( result ) {
2006-11-23 02:46:59 +03:00
printk ( KERN_WARNING " %s:%d: Error initializing spus \n " ,
__func__ , __LINE__ ) ;
2007-07-17 15:05:52 +04:00
return result ;
}
2006-11-23 02:46:59 +03:00
2007-07-17 15:05:52 +04:00
return num_resource_id ;
2006-11-23 02:46:59 +03:00
}
2007-08-04 05:53:46 +04:00
static int ps3_init_affinity ( void )
{
return 0 ;
}
2007-12-05 05:49:31 +03:00
/**
* ps3_enable_spu - Enable SPU run control .
*
* An outstanding enhancement for the PS3 would be to add a guard to check
* for incorrect access to the spu problem state when the spu context is
* disabled . This check could be implemented with a flag added to the spu
* context that would inhibit mapping problem state pages , and a routine
* to unmap spu problem state pages . When the spu is enabled with
* ps3_enable_spu ( ) the flag would be set allowing pages to be mapped ,
* and when the spu is disabled with ps3_disable_spu ( ) the flag would be
* cleared and the mapped problem state pages would be unmapped .
*/
static void ps3_enable_spu ( struct spu_context * ctx )
{
}
static void ps3_disable_spu ( struct spu_context * ctx )
{
ctx - > ops - > runcntl_stop ( ctx ) ;
}
2006-11-23 02:46:59 +03:00
const struct spu_management_ops spu_management_ps3_ops = {
. enumerate_spus = ps3_enumerate_spus ,
. create_spu = ps3_create_spu ,
. destroy_spu = ps3_destroy_spu ,
2007-12-05 05:49:31 +03:00
. enable_spu = ps3_enable_spu ,
. disable_spu = ps3_disable_spu ,
2007-08-04 05:53:46 +04:00
. init_affinity = ps3_init_affinity ,
2006-11-23 02:46:59 +03:00
} ;
/* spu_priv1_ops */
static void int_mask_and ( struct spu * spu , int class , u64 mask )
{
u64 old_mask ;
/* are these serialized by caller??? */
old_mask = spu_int_mask_get ( spu , class ) ;
spu_int_mask_set ( spu , class , old_mask & mask ) ;
}
static void int_mask_or ( struct spu * spu , int class , u64 mask )
{
u64 old_mask ;
old_mask = spu_int_mask_get ( spu , class ) ;
spu_int_mask_set ( spu , class , old_mask | mask ) ;
}
static void int_mask_set ( struct spu * spu , int class , u64 mask )
{
spu_pdata ( spu ) - > cache . masks [ class ] = mask ;
lv1_set_spe_interrupt_mask ( spu_pdata ( spu ) - > spe_id , class ,
spu_pdata ( spu ) - > cache . masks [ class ] ) ;
}
static u64 int_mask_get ( struct spu * spu , int class )
{
return spu_pdata ( spu ) - > cache . masks [ class ] ;
}
static void int_stat_clear ( struct spu * spu , int class , u64 stat )
{
/* Note that MFC_DSISR will be cleared when class1[MF] is set. */
lv1_clear_spe_interrupt_status ( spu_pdata ( spu ) - > spe_id , class ,
stat , 0 ) ;
}
static u64 int_stat_get ( struct spu * spu , int class )
{
u64 stat ;
lv1_get_spe_interrupt_status ( spu_pdata ( spu ) - > spe_id , class , & stat ) ;
return stat ;
}
static void cpu_affinity_set ( struct spu * spu , int cpu )
{
/* No support. */
}
static u64 mfc_dar_get ( struct spu * spu )
{
return in_be64 ( & spu_pdata ( spu ) - > shadow - > mfc_dar_RW ) ;
}
static void mfc_dsisr_set ( struct spu * spu , u64 dsisr )
{
/* Nothing to do, cleared in int_stat_clear(). */
}
static u64 mfc_dsisr_get ( struct spu * spu )
{
return in_be64 ( & spu_pdata ( spu ) - > shadow - > mfc_dsisr_RW ) ;
}
static void mfc_sdr_setup ( struct spu * spu )
{
/* Nothing to do. */
}
static void mfc_sr1_set ( struct spu * spu , u64 sr1 )
{
/* Check bits allowed by HV. */
static const u64 allowed = ~ ( MFC_STATE1_LOCAL_STORAGE_DECODE_MASK
| MFC_STATE1_PROBLEM_STATE_MASK ) ;
BUG_ON ( ( sr1 & allowed ) ! = ( spu_pdata ( spu ) - > cache . sr1 & allowed ) ) ;
spu_pdata ( spu ) - > cache . sr1 = sr1 ;
lv1_set_spe_privilege_state_area_1_register (
spu_pdata ( spu ) - > spe_id ,
offsetof ( struct spu_priv1 , mfc_sr1_RW ) ,
spu_pdata ( spu ) - > cache . sr1 ) ;
}
static u64 mfc_sr1_get ( struct spu * spu )
{
return spu_pdata ( spu ) - > cache . sr1 ;
}
static void mfc_tclass_id_set ( struct spu * spu , u64 tclass_id )
{
spu_pdata ( spu ) - > cache . tclass_id = tclass_id ;
lv1_set_spe_privilege_state_area_1_register (
spu_pdata ( spu ) - > spe_id ,
offsetof ( struct spu_priv1 , mfc_tclass_id_RW ) ,
spu_pdata ( spu ) - > cache . tclass_id ) ;
}
static u64 mfc_tclass_id_get ( struct spu * spu )
{
return spu_pdata ( spu ) - > cache . tclass_id ;
}
static void tlb_invalidate ( struct spu * spu )
{
/* Nothing to do. */
}
static void resource_allocation_groupID_set ( struct spu * spu , u64 id )
{
/* No support. */
}
static u64 resource_allocation_groupID_get ( struct spu * spu )
{
return 0 ; /* No support. */
}
static void resource_allocation_enable_set ( struct spu * spu , u64 enable )
{
/* No support. */
}
static u64 resource_allocation_enable_get ( struct spu * spu )
{
return 0 ; /* No support. */
}
const struct spu_priv1_ops spu_priv1_ps3_ops = {
. int_mask_and = int_mask_and ,
. int_mask_or = int_mask_or ,
. int_mask_set = int_mask_set ,
. int_mask_get = int_mask_get ,
. int_stat_clear = int_stat_clear ,
. int_stat_get = int_stat_get ,
. cpu_affinity_set = cpu_affinity_set ,
. mfc_dar_get = mfc_dar_get ,
. mfc_dsisr_set = mfc_dsisr_set ,
. mfc_dsisr_get = mfc_dsisr_get ,
. mfc_sdr_setup = mfc_sdr_setup ,
. mfc_sr1_set = mfc_sr1_set ,
. mfc_sr1_get = mfc_sr1_get ,
. mfc_tclass_id_set = mfc_tclass_id_set ,
. mfc_tclass_id_get = mfc_tclass_id_get ,
. tlb_invalidate = tlb_invalidate ,
. resource_allocation_groupID_set = resource_allocation_groupID_set ,
. resource_allocation_groupID_get = resource_allocation_groupID_get ,
. resource_allocation_enable_set = resource_allocation_enable_set ,
. resource_allocation_enable_get = resource_allocation_enable_get ,
} ;
void ps3_spu_set_platform ( void )
{
spu_priv1_ops = & spu_priv1_ps3_ops ;
spu_management_ops = & spu_management_ps3_ops ;
}