2007-02-02 16:45:33 +09:00
/*
* spu management operations for of based platforms
*
* ( C ) Copyright IBM Deutschland Entwicklung GmbH 2005
* Copyright 2006 Sony Corp .
* ( C ) Copyright 2007 TOSHIBA CORPORATION
*
* 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 . ,
* 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA .
*/
# include <linux/interrupt.h>
# include <linux/list.h>
2011-07-22 18:24:23 -04:00
# include <linux/export.h>
2007-02-02 16:45:33 +09:00
# include <linux/ptrace.h>
# include <linux/wait.h>
# include <linux/mm.h>
# include <linux/io.h>
# include <linux/mutex.h>
# include <linux/device.h>
# include <asm/spu.h>
# include <asm/spu_priv1.h>
# include <asm/firmware.h>
# include <asm/prom.h>
2007-12-05 13:49:31 +11:00
# include "spufs/spufs.h"
2007-02-02 16:45:33 +09:00
# include "interrupt.h"
struct device_node * spu_devnode ( struct spu * spu )
{
return spu - > devnode ;
}
EXPORT_SYMBOL_GPL ( spu_devnode ) ;
static u64 __init find_spu_unit_number ( struct device_node * spe )
{
const unsigned int * prop ;
int proplen ;
2007-08-30 01:33:53 +02:00
/* new device trees should provide the physical-id attribute */
2007-08-23 03:01:25 +10:00
prop = of_get_property ( spe , " physical-id " , & proplen ) ;
2007-02-02 16:45:33 +09:00
if ( proplen = = 4 )
return ( u64 ) * prop ;
2007-08-30 01:33:53 +02:00
/* celleb device tree provides the unit-id */
prop = of_get_property ( spe , " unit-id " , & proplen ) ;
if ( proplen = = 4 )
return ( u64 ) * prop ;
/* legacy device trees provide the id in the reg attribute */
2007-04-03 22:26:41 +10:00
prop = of_get_property ( spe , " reg " , & proplen ) ;
2007-02-02 16:45:33 +09:00
if ( proplen = = 4 )
return ( u64 ) * prop ;
return 0 ;
}
static void spu_unmap ( struct spu * spu )
{
if ( ! firmware_has_feature ( FW_FEATURE_LPAR ) )
iounmap ( spu - > priv1 ) ;
iounmap ( spu - > priv2 ) ;
iounmap ( spu - > problem ) ;
iounmap ( ( __force u8 __iomem * ) spu - > local_store ) ;
}
static int __init spu_map_interrupts_old ( struct spu * spu ,
struct device_node * np )
{
unsigned int isrc ;
const u32 * tmp ;
int nid ;
/* Get the interrupt source unit from the device-tree */
2007-04-03 22:26:41 +10:00
tmp = of_get_property ( np , " isrc " , NULL ) ;
2007-02-02 16:45:33 +09:00
if ( ! tmp )
return - ENODEV ;
isrc = tmp [ 0 ] ;
2007-04-03 22:26:41 +10:00
tmp = of_get_property ( np - > parent - > parent , " node-id " , NULL ) ;
2007-02-02 16:45:33 +09:00
if ( ! tmp ) {
2008-03-29 08:21:07 +11:00
printk ( KERN_WARNING " %s: can't find node-id \n " , __func__ ) ;
2007-02-02 16:45:33 +09:00
nid = spu - > node ;
} else
nid = tmp [ 0 ] ;
/* Add the node number */
isrc | = nid < < IIC_IRQ_NODE_SHIFT ;
/* Now map interrupts of all 3 classes */
spu - > irqs [ 0 ] = irq_create_mapping ( NULL , IIC_IRQ_CLASS_0 | isrc ) ;
spu - > irqs [ 1 ] = irq_create_mapping ( NULL , IIC_IRQ_CLASS_1 | isrc ) ;
spu - > irqs [ 2 ] = irq_create_mapping ( NULL , IIC_IRQ_CLASS_2 | isrc ) ;
/* Right now, we only fail if class 2 failed */
2016-09-06 21:53:24 +10:00
if ( ! spu - > irqs [ 2 ] )
return - EINVAL ;
return 0 ;
2007-02-02 16:45:33 +09:00
}
2007-02-13 11:46:06 +11:00
static void __iomem * __init spu_map_prop_old ( struct spu * spu ,
struct device_node * n ,
const char * name )
{
const struct address_prop {
unsigned long address ;
unsigned int len ;
} __attribute__ ( ( packed ) ) * prop ;
int proplen ;
2007-04-03 22:26:41 +10:00
prop = of_get_property ( n , name , & proplen ) ;
2007-02-13 11:46:06 +11:00
if ( prop = = NULL | | proplen ! = sizeof ( struct address_prop ) )
return NULL ;
return ioremap ( prop - > address , prop - > len ) ;
}
2007-02-02 16:45:33 +09:00
static int __init spu_map_device_old ( struct spu * spu )
{
struct device_node * node = spu - > devnode ;
const char * prop ;
int ret ;
ret = - ENODEV ;
2007-04-03 22:26:41 +10:00
spu - > name = of_get_property ( node , " name " , NULL ) ;
2007-02-02 16:45:33 +09:00
if ( ! spu - > name )
goto out ;
2007-04-03 22:26:41 +10:00
prop = of_get_property ( node , " local-store " , NULL ) ;
2007-02-02 16:45:33 +09:00
if ( ! prop )
goto out ;
spu - > local_store_phys = * ( unsigned long * ) prop ;
/* we use local store as ram, not io memory */
spu - > local_store = ( void __force * )
2007-02-13 11:46:06 +11:00
spu_map_prop_old ( spu , node , " local-store " ) ;
2007-02-02 16:45:33 +09:00
if ( ! spu - > local_store )
goto out ;
2007-04-03 22:26:41 +10:00
prop = of_get_property ( node , " problem " , NULL ) ;
2007-02-02 16:45:33 +09:00
if ( ! prop )
goto out_unmap ;
spu - > problem_phys = * ( unsigned long * ) prop ;
2007-02-13 11:46:06 +11:00
spu - > problem = spu_map_prop_old ( spu , node , " problem " ) ;
2007-02-02 16:45:33 +09:00
if ( ! spu - > problem )
goto out_unmap ;
2007-02-13 11:46:06 +11:00
spu - > priv2 = spu_map_prop_old ( spu , node , " priv2 " ) ;
2007-02-02 16:45:33 +09:00
if ( ! spu - > priv2 )
goto out_unmap ;
if ( ! firmware_has_feature ( FW_FEATURE_LPAR ) ) {
2007-02-13 11:46:06 +11:00
spu - > priv1 = spu_map_prop_old ( spu , node , " priv1 " ) ;
2007-02-02 16:45:33 +09:00
if ( ! spu - > priv1 )
goto out_unmap ;
}
ret = 0 ;
goto out ;
out_unmap :
spu_unmap ( spu ) ;
out :
return ret ;
}
static int __init spu_map_interrupts ( struct spu * spu , struct device_node * np )
{
2013-09-15 16:39:11 +01:00
struct of_phandle_args oirq ;
2007-02-02 16:45:33 +09:00
int ret ;
int i ;
for ( i = 0 ; i < 3 ; i + + ) {
2013-09-19 11:22:36 -05:00
ret = of_irq_parse_one ( np , i , & oirq ) ;
2007-02-02 16:45:33 +09:00
if ( ret ) {
pr_debug ( " spu_new: failed to get irq %d \n " , i ) ;
goto err ;
}
ret = - EINVAL ;
2017-08-21 10:16:47 -05:00
pr_debug ( " irq %d no 0x%x on %pOF \n " , i , oirq . args [ 0 ] ,
oirq . np ) ;
2013-09-15 16:55:53 +01:00
spu - > irqs [ i ] = irq_create_of_mapping ( & oirq ) ;
2016-09-06 21:53:24 +10:00
if ( ! spu - > irqs [ i ] ) {
2007-02-02 16:45:33 +09:00
pr_debug ( " spu_new: failed to map it ! \n " ) ;
goto err ;
}
}
return 0 ;
err :
2013-09-15 16:39:11 +01:00
pr_debug ( " failed to map irq %x for spu %s \n " , * oirq . args ,
2007-02-02 16:45:33 +09:00
spu - > name ) ;
for ( ; i > = 0 ; i - - ) {
2016-09-06 21:53:24 +10:00
if ( spu - > irqs [ i ] )
2007-02-02 16:45:33 +09:00
irq_dispose_mapping ( spu - > irqs [ i ] ) ;
}
return ret ;
}
static int spu_map_resource ( struct spu * spu , int nr ,
void __iomem * * virt , unsigned long * phys )
{
struct device_node * np = spu - > devnode ;
struct resource resource = { } ;
unsigned long len ;
int ret ;
ret = of_address_to_resource ( np , nr , & resource ) ;
if ( ret )
2007-02-13 11:46:06 +11:00
return ret ;
2007-02-02 16:45:33 +09:00
if ( phys )
* phys = resource . start ;
2011-06-09 09:13:32 -07:00
len = resource_size ( & resource ) ;
2007-02-02 16:45:33 +09:00
* virt = ioremap ( resource . start , len ) ;
if ( ! * virt )
2007-02-13 11:46:06 +11:00
return - EINVAL ;
return 0 ;
2007-02-02 16:45:33 +09:00
}
static int __init spu_map_device ( struct spu * spu )
{
struct device_node * np = spu - > devnode ;
int ret = - ENODEV ;
2007-04-03 22:26:41 +10:00
spu - > name = of_get_property ( np , " name " , NULL ) ;
2007-02-02 16:45:33 +09:00
if ( ! spu - > name )
goto out ;
ret = spu_map_resource ( spu , 0 , ( void __iomem * * ) & spu - > local_store ,
& spu - > local_store_phys ) ;
if ( ret ) {
2017-08-21 10:16:47 -05:00
pr_debug ( " spu_new: failed to map %pOF resource 0 \n " ,
np ) ;
2007-02-02 16:45:33 +09:00
goto out ;
}
ret = spu_map_resource ( spu , 1 , ( void __iomem * * ) & spu - > problem ,
& spu - > problem_phys ) ;
if ( ret ) {
2017-08-21 10:16:47 -05:00
pr_debug ( " spu_new: failed to map %pOF resource 1 \n " ,
np ) ;
2007-02-02 16:45:33 +09:00
goto out_unmap ;
}
ret = spu_map_resource ( spu , 2 , ( void __iomem * * ) & spu - > priv2 , NULL ) ;
if ( ret ) {
2017-08-21 10:16:47 -05:00
pr_debug ( " spu_new: failed to map %pOF resource 2 \n " ,
np ) ;
2007-02-02 16:45:33 +09:00
goto out_unmap ;
}
if ( ! firmware_has_feature ( FW_FEATURE_LPAR ) )
ret = spu_map_resource ( spu , 3 ,
( void __iomem * * ) & spu - > priv1 , NULL ) ;
if ( ret ) {
2017-08-21 10:16:47 -05:00
pr_debug ( " spu_new: failed to map %pOF resource 3 \n " ,
np ) ;
2007-02-02 16:45:33 +09:00
goto out_unmap ;
}
2017-08-21 10:16:47 -05:00
pr_debug ( " spu_new: %pOF maps: \n " , np ) ;
2007-02-02 16:45:33 +09:00
pr_debug ( " local store : 0x%016lx -> 0x%p \n " ,
spu - > local_store_phys , spu - > local_store ) ;
pr_debug ( " problem state : 0x%016lx -> 0x%p \n " ,
spu - > problem_phys , spu - > problem ) ;
pr_debug ( " priv2 : 0x%p \n " , spu - > priv2 ) ;
pr_debug ( " priv1 : 0x%p \n " , spu - > priv1 ) ;
return 0 ;
out_unmap :
spu_unmap ( spu ) ;
out :
pr_debug ( " failed to map spe %s: %d \n " , spu - > name , ret ) ;
return ret ;
}
static int __init of_enumerate_spus ( int ( * fn ) ( void * data ) )
{
int ret ;
struct device_node * node ;
2007-07-17 04:05:52 -07:00
unsigned int n = 0 ;
2007-02-02 16:45:33 +09:00
ret = - ENODEV ;
2017-01-31 17:54:38 -08:00
for_each_node_by_type ( node , " spe " ) {
2007-02-02 16:45:33 +09:00
ret = fn ( node ) ;
if ( ret ) {
printk ( KERN_WARNING " %s: Error initializing %s \n " ,
2008-03-29 08:21:07 +11:00
__func__ , node - > name ) ;
2017-01-31 17:54:38 -08:00
of_node_put ( node ) ;
2007-02-02 16:45:33 +09:00
break ;
}
2007-07-17 04:05:52 -07:00
n + + ;
2007-02-02 16:45:33 +09:00
}
2007-07-17 04:05:52 -07:00
return ret ? ret : n ;
2007-02-02 16:45:33 +09:00
}
static int __init of_create_spu ( struct spu * spu , void * data )
{
int ret ;
struct device_node * spe = ( struct device_node * ) data ;
static int legacy_map = 0 , legacy_irq = 0 ;
spu - > devnode = of_node_get ( spe ) ;
spu - > spe_id = find_spu_unit_number ( spe ) ;
spu - > node = of_node_to_nid ( spe ) ;
if ( spu - > node > = MAX_NUMNODES ) {
2017-08-21 10:16:47 -05:00
printk ( KERN_WARNING " SPE %pOF on node %d ignored, "
" node number too big \n " , spe , spu - > node ) ;
2007-02-02 16:45:33 +09:00
printk ( KERN_WARNING " Check if CONFIG_NUMA is enabled. \n " ) ;
ret = - ENODEV ;
goto out ;
}
ret = spu_map_device ( spu ) ;
if ( ret ) {
if ( ! legacy_map ) {
legacy_map = 1 ;
printk ( KERN_WARNING " %s: Legacy device tree found, "
2008-03-29 08:21:07 +11:00
" trying to map old style \n " , __func__ ) ;
2007-02-02 16:45:33 +09:00
}
ret = spu_map_device_old ( spu ) ;
if ( ret ) {
printk ( KERN_ERR " Unable to map %s \n " ,
spu - > name ) ;
goto out ;
}
}
ret = spu_map_interrupts ( spu , spe ) ;
if ( ret ) {
if ( ! legacy_irq ) {
legacy_irq = 1 ;
printk ( KERN_WARNING " %s: Legacy device tree found, "
2008-03-29 08:21:07 +11:00
" trying old style irq \n " , __func__ ) ;
2007-02-02 16:45:33 +09:00
}
ret = spu_map_interrupts_old ( spu , spe ) ;
if ( ret ) {
2007-11-01 19:04:04 +09:00
printk ( KERN_ERR " %s: could not map interrupts \n " ,
2007-02-02 16:45:33 +09:00
spu - > name ) ;
goto out_unmap ;
}
}
pr_debug ( " Using SPE %s %p %p %p %p %d \n " , spu - > name ,
spu - > local_store , spu - > problem , spu - > priv1 ,
spu - > priv2 , spu - > number ) ;
goto out ;
out_unmap :
spu_unmap ( spu ) ;
out :
return ret ;
}
static int of_destroy_spu ( struct spu * spu )
{
spu_unmap ( spu ) ;
of_node_put ( spu - > devnode ) ;
return 0 ;
}
2007-12-05 13:49:31 +11:00
static void enable_spu_by_master_run ( struct spu_context * ctx )
{
ctx - > ops - > master_start ( ctx ) ;
}
static void disable_spu_by_master_run ( struct spu_context * ctx )
{
ctx - > ops - > master_stop ( ctx ) ;
}
2007-08-03 18:53:46 -07:00
/* Hardcoded affinity idxs for qs20 */
# define QS20_SPES_PER_BE 8
static int qs20_reg_idxs [ QS20_SPES_PER_BE ] = { 0 , 2 , 4 , 6 , 7 , 5 , 3 , 1 } ;
static int qs20_reg_memory [ QS20_SPES_PER_BE ] = { 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 } ;
static struct spu * spu_lookup_reg ( int node , u32 reg )
{
struct spu * spu ;
2007-09-14 15:46:40 +10:00
const u32 * spu_reg ;
2007-08-03 18:53:46 -07:00
list_for_each_entry ( spu , & cbe_spu_info [ node ] . spus , cbe_list ) {
2007-09-14 15:46:40 +10:00
spu_reg = of_get_property ( spu_devnode ( spu ) , " reg " , NULL ) ;
2007-08-03 18:53:46 -07:00
if ( * spu_reg = = reg )
return spu ;
}
return NULL ;
}
static void init_affinity_qs20_harcoded ( void )
{
int node , i ;
struct spu * last_spu , * spu ;
u32 reg ;
for ( node = 0 ; node < MAX_NUMNODES ; node + + ) {
last_spu = NULL ;
for ( i = 0 ; i < QS20_SPES_PER_BE ; i + + ) {
reg = qs20_reg_idxs [ i ] ;
spu = spu_lookup_reg ( node , reg ) ;
if ( ! spu )
continue ;
spu - > has_mem_affinity = qs20_reg_memory [ reg ] ;
if ( last_spu )
list_add_tail ( & spu - > aff_list ,
& last_spu - > aff_list ) ;
last_spu = spu ;
}
}
}
static int of_has_vicinity ( void )
{
2007-12-05 13:49:31 +11:00
struct device_node * dn ;
2007-08-03 18:53:46 -07:00
2007-12-05 13:49:31 +11:00
for_each_node_by_type ( dn , " spe " ) {
if ( of_find_property ( dn , " vicinity " , NULL ) ) {
of_node_put ( dn ) ;
return 1 ;
}
}
return 0 ;
2007-08-03 18:53:46 -07:00
}
static struct spu * devnode_spu ( int cbe , struct device_node * dn )
{
struct spu * spu ;
list_for_each_entry ( spu , & cbe_spu_info [ cbe ] . spus , cbe_list )
if ( spu_devnode ( spu ) = = dn )
return spu ;
return NULL ;
}
static struct spu *
neighbour_spu ( int cbe , struct device_node * target , struct device_node * avoid )
{
struct spu * spu ;
struct device_node * spu_dn ;
const phandle * vic_handles ;
int lenp , i ;
list_for_each_entry ( spu , & cbe_spu_info [ cbe ] . spus , cbe_list ) {
spu_dn = spu_devnode ( spu ) ;
if ( spu_dn = = avoid )
continue ;
vic_handles = of_get_property ( spu_dn , " vicinity " , & lenp ) ;
for ( i = 0 ; i < ( lenp / sizeof ( phandle ) ) ; i + + ) {
2010-01-28 14:06:53 -07:00
if ( vic_handles [ i ] = = target - > phandle )
2007-08-03 18:53:46 -07:00
return spu ;
}
}
return NULL ;
}
static void init_affinity_node ( int cbe )
{
struct spu * spu , * last_spu ;
struct device_node * vic_dn , * last_spu_dn ;
phandle avoid_ph ;
const phandle * vic_handles ;
const char * name ;
int lenp , i , added ;
last_spu = list_first_entry ( & cbe_spu_info [ cbe ] . spus , struct spu ,
cbe_list ) ;
avoid_ph = 0 ;
for ( added = 1 ; added < cbe_spu_info [ cbe ] . n_spus ; added + + ) {
last_spu_dn = spu_devnode ( last_spu ) ;
vic_handles = of_get_property ( last_spu_dn , " vicinity " , & lenp ) ;
/*
* Walk through each phandle in vicinity property of the spu
* ( tipically two vicinity phandles per spe node )
*/
for ( i = 0 ; i < ( lenp / sizeof ( phandle ) ) ; i + + ) {
if ( vic_handles [ i ] = = avoid_ph )
continue ;
vic_dn = of_find_node_by_phandle ( vic_handles [ i ] ) ;
if ( ! vic_dn )
continue ;
/* a neighbour might be spe, mic-tm, or bif0 */
name = of_get_property ( vic_dn , " name " , NULL ) ;
if ( ! name )
continue ;
if ( strcmp ( name , " spe " ) = = 0 ) {
spu = devnode_spu ( cbe , vic_dn ) ;
2010-01-28 14:06:53 -07:00
avoid_ph = last_spu_dn - > phandle ;
2007-08-03 18:53:46 -07:00
} else {
/*
* " mic-tm " and " bif0 " nodes do not have
* vicinity property . So we need to find the
* spe which has vic_dn as neighbour , but
* skipping the one we came from ( last_spu_dn )
*/
spu = neighbour_spu ( cbe , vic_dn , last_spu_dn ) ;
if ( ! spu )
continue ;
if ( ! strcmp ( name , " mic-tm " ) ) {
last_spu - > has_mem_affinity = 1 ;
spu - > has_mem_affinity = 1 ;
}
2010-01-28 14:06:53 -07:00
avoid_ph = vic_dn - > phandle ;
2007-08-03 18:53:46 -07:00
}
list_add_tail ( & spu - > aff_list , & last_spu - > aff_list ) ;
last_spu = spu ;
break ;
}
}
}
static void init_affinity_fw ( void )
{
int cbe ;
for ( cbe = 0 ; cbe < MAX_NUMNODES ; cbe + + )
init_affinity_node ( cbe ) ;
}
static int __init init_affinity ( void )
{
if ( of_has_vicinity ( ) ) {
init_affinity_fw ( ) ;
} else {
2016-07-05 15:04:01 +10:00
if ( of_machine_is_compatible ( " IBM,CPBW-1.0 " ) )
2007-08-03 18:53:46 -07:00
init_affinity_qs20_harcoded ( ) ;
else
2007-11-01 19:04:04 +09:00
printk ( " No affinity configuration found \n " ) ;
2007-08-03 18:53:46 -07:00
}
return 0 ;
}
2007-02-02 16:45:33 +09:00
const struct spu_management_ops spu_management_of_ops = {
. enumerate_spus = of_enumerate_spus ,
. create_spu = of_create_spu ,
. destroy_spu = of_destroy_spu ,
2007-12-05 13:49:31 +11:00
. enable_spu = enable_spu_by_master_run ,
. disable_spu = disable_spu_by_master_run ,
2007-08-03 18:53:46 -07:00
. init_affinity = init_affinity ,
2007-02-02 16:45:33 +09:00
} ;