2006-06-21 02:23:28 +04:00
/* sbus.c: SBus support routines.
2005-04-17 02:20:36 +04:00
*
2006-06-21 02:23:28 +04:00
* Copyright ( C ) 1995 , 2006 David S . Miller ( davem @ davemloft . net )
2005-04-17 02:20:36 +04:00
*/
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/init.h>
2006-12-20 20:22:28 +03:00
# include <linux/device.h>
2005-04-17 02:20:36 +04:00
# include <asm/system.h>
# include <asm/sbus.h>
# include <asm/dma.h>
# include <asm/oplib.h>
2006-06-24 02:55:45 +04:00
# include <asm/prom.h>
# include <asm/of_device.h>
2005-04-17 02:20:36 +04:00
# include <asm/bpp.h>
# include <asm/irq.h>
2006-12-20 20:22:28 +03:00
static ssize_t
show_sbusobppath_attr ( struct device * dev , struct device_attribute * attr , char * buf )
{
struct sbus_dev * sbus ;
sbus = to_sbus_device ( dev ) ;
return snprintf ( buf , PAGE_SIZE , " %s \n " , sbus - > ofdev . node - > full_name ) ;
}
static DEVICE_ATTR ( obppath , S_IRUSR | S_IRGRP | S_IROTH , show_sbusobppath_attr , NULL ) ;
2006-06-21 02:23:28 +04:00
struct sbus_bus * sbus_root ;
2005-04-17 02:20:36 +04:00
2006-06-24 02:55:45 +04:00
static void __init fill_sbus_device ( struct device_node * dp , struct sbus_dev * sdev )
2005-04-17 02:20:36 +04:00
{
2007-07-19 09:03:25 +04:00
struct dev_archdata * sd ;
2006-06-24 02:55:45 +04:00
unsigned long base ;
2007-03-29 11:49:54 +04:00
const void * pval ;
2006-12-20 20:22:28 +03:00
int len , err ;
2005-04-17 02:20:36 +04:00
2006-06-24 02:55:45 +04:00
sdev - > prom_node = dp - > node ;
strcpy ( sdev - > prom_name , dp - > name ) ;
pval = of_get_property ( dp , " reg " , & len ) ;
2006-06-21 02:23:28 +04:00
sdev - > num_registers = 0 ;
2006-06-24 02:55:45 +04:00
if ( pval ) {
memcpy ( sdev - > reg_addrs , pval , len ) ;
2006-06-21 02:23:28 +04:00
sdev - > num_registers =
len / sizeof ( struct linux_prom_registers ) ;
2005-04-17 02:20:36 +04:00
2006-06-21 02:23:28 +04:00
base = ( unsigned long ) sdev - > reg_addrs [ 0 ] . phys_addr ;
2005-04-17 02:20:36 +04:00
2006-06-21 02:23:28 +04:00
/* Compute the slot number. */
if ( base > = SUN_SBUS_BVADDR & & sparc_cpu_model = = sun4m )
sdev - > slot = sbus_dev_slot ( base ) ;
else
sdev - > slot = sdev - > reg_addrs [ 0 ] . which_io ;
2005-04-17 02:20:36 +04:00
}
2006-06-24 02:55:45 +04:00
pval = of_get_property ( dp , " ranges " , & len ) ;
2006-06-21 02:23:28 +04:00
sdev - > num_device_ranges = 0 ;
2006-06-24 02:55:45 +04:00
if ( pval ) {
memcpy ( sdev - > device_ranges , pval , len ) ;
2006-06-21 02:23:28 +04:00
sdev - > num_device_ranges =
len / sizeof ( struct linux_prom_ranges ) ;
2006-06-24 02:55:45 +04:00
}
2005-04-17 02:20:36 +04:00
2006-06-21 02:23:28 +04:00
sbus_fill_device_irq ( sdev ) ;
2005-04-17 02:20:36 +04:00
2007-07-19 09:03:25 +04:00
sd = & sdev - > ofdev . dev . archdata ;
sd - > prom_node = dp ;
sd - > op = & sdev - > ofdev ;
2006-06-24 02:55:45 +04:00
sdev - > ofdev . node = dp ;
if ( sdev - > parent )
sdev - > ofdev . dev . parent = & sdev - > parent - > ofdev . dev ;
else
sdev - > ofdev . dev . parent = & sdev - > bus - > ofdev . dev ;
sdev - > ofdev . dev . bus = & sbus_bus_type ;
2006-10-27 12:03:31 +04:00
sprintf ( sdev - > ofdev . dev . bus_id , " sbus[%08x] " , dp - > node ) ;
2006-06-24 02:55:45 +04:00
if ( of_device_register ( & sdev - > ofdev ) ! = 0 )
printk ( KERN_DEBUG " sbus: device registration error for %s! \n " ,
2006-10-27 12:03:31 +04:00
dp - > path_component_name ) ;
2006-12-20 20:22:28 +03:00
/* WE HAVE BEEN INVADED BY ALIENS! */
err = sysfs_create_file ( & sdev - > ofdev . dev . kobj , & dev_attr_obppath . attr ) ;
2005-04-17 02:20:36 +04:00
}
2006-06-24 02:55:45 +04:00
static void __init sbus_bus_ranges_init ( struct device_node * dp , struct sbus_bus * sbus )
2005-04-17 02:20:36 +04:00
{
2007-03-29 11:49:54 +04:00
const void * pval ;
2005-04-17 02:20:36 +04:00
int len ;
2006-06-24 02:55:45 +04:00
pval = of_get_property ( dp , " ranges " , & len ) ;
sbus - > num_sbus_ranges = 0 ;
if ( pval ) {
memcpy ( sbus - > sbus_ranges , pval , len ) ;
sbus - > num_sbus_ranges =
len / sizeof ( struct linux_prom_ranges ) ;
sbus_arch_bus_ranges_init ( dp - > parent , sbus ) ;
2005-04-17 02:20:36 +04:00
}
}
static void __init __apply_ranges_to_regs ( struct linux_prom_ranges * ranges ,
int num_ranges ,
struct linux_prom_registers * regs ,
int num_regs )
{
if ( num_ranges ) {
int regnum ;
for ( regnum = 0 ; regnum < num_regs ; regnum + + ) {
int rngnum ;
for ( rngnum = 0 ; rngnum < num_ranges ; rngnum + + ) {
if ( regs [ regnum ] . which_io = = ranges [ rngnum ] . ot_child_space )
break ;
}
if ( rngnum = = num_ranges ) {
/* We used to flag this as an error. Actually
* some devices do not report the regs as we expect .
* For example , see SUNW , pln device . In that case
* the reg property is in a format internal to that
* node , ie . it is not in the SBUS register space
* per se . - DaveM
*/
return ;
}
regs [ regnum ] . which_io = ranges [ rngnum ] . ot_parent_space ;
regs [ regnum ] . phys_addr - = ranges [ rngnum ] . ot_child_base ;
regs [ regnum ] . phys_addr + = ranges [ rngnum ] . ot_parent_base ;
}
}
}
static void __init __fixup_regs_sdev ( struct sbus_dev * sdev )
{
if ( sdev - > num_registers ! = 0 ) {
struct sbus_dev * parent = sdev - > parent ;
int i ;
while ( parent ! = NULL ) {
__apply_ranges_to_regs ( parent - > device_ranges ,
parent - > num_device_ranges ,
sdev - > reg_addrs ,
sdev - > num_registers ) ;
parent = parent - > parent ;
}
__apply_ranges_to_regs ( sdev - > bus - > sbus_ranges ,
sdev - > bus - > num_sbus_ranges ,
sdev - > reg_addrs ,
sdev - > num_registers ) ;
for ( i = 0 ; i < sdev - > num_registers ; i + + ) {
struct resource * res = & sdev - > resource [ i ] ;
res - > start = sdev - > reg_addrs [ i ] . phys_addr ;
res - > end = ( res - > start +
( unsigned long ) sdev - > reg_addrs [ i ] . reg_size - 1UL ) ;
res - > flags = IORESOURCE_IO |
( sdev - > reg_addrs [ i ] . which_io & 0xff ) ;
}
}
}
static void __init sbus_fixup_all_regs ( struct sbus_dev * first_sdev )
{
struct sbus_dev * sdev ;
for ( sdev = first_sdev ; sdev ; sdev = sdev - > next ) {
if ( sdev - > child )
sbus_fixup_all_regs ( sdev - > child ) ;
__fixup_regs_sdev ( sdev ) ;
}
}
2006-06-24 02:55:45 +04:00
/* We preserve the "probe order" of these bus and device lists to give
* the same ordering as the old code .
*/
static void __init sbus_insert ( struct sbus_bus * sbus , struct sbus_bus * * root )
{
while ( * root )
root = & ( * root ) - > next ;
* root = sbus ;
sbus - > next = NULL ;
}
2005-04-17 02:20:36 +04:00
2006-06-24 02:55:45 +04:00
static void __init sdev_insert ( struct sbus_dev * sdev , struct sbus_dev * * root )
{
while ( * root )
root = & ( * root ) - > next ;
* root = sdev ;
sdev - > next = NULL ;
}
2005-04-17 02:20:36 +04:00
2006-06-24 02:55:45 +04:00
static void __init walk_children ( struct device_node * dp , struct sbus_dev * parent , struct sbus_bus * sbus )
2005-04-17 02:20:36 +04:00
{
2006-06-24 02:55:45 +04:00
dp = dp - > child ;
while ( dp ) {
struct sbus_dev * sdev ;
2005-04-17 02:20:36 +04:00
2006-06-24 02:55:45 +04:00
sdev = kzalloc ( sizeof ( struct sbus_dev ) , GFP_ATOMIC ) ;
if ( sdev ) {
sdev_insert ( sdev , & parent - > child ) ;
2005-04-17 02:20:36 +04:00
2006-06-24 02:55:45 +04:00
sdev - > bus = sbus ;
sdev - > parent = parent ;
2007-07-28 09:39:14 +04:00
sdev - > ofdev . dev . archdata . iommu =
sbus - > ofdev . dev . archdata . iommu ;
sdev - > ofdev . dev . archdata . stc =
sbus - > ofdev . dev . archdata . stc ;
2006-06-24 02:55:45 +04:00
fill_sbus_device ( dp , sdev ) ;
walk_children ( dp , sdev , sbus ) ;
2005-04-17 02:20:36 +04:00
}
2006-06-24 02:55:45 +04:00
dp = dp - > sibling ;
2005-04-17 02:20:36 +04:00
}
2006-06-24 02:55:45 +04:00
}
2005-04-17 02:20:36 +04:00
2006-06-24 02:55:45 +04:00
static void __init build_one_sbus ( struct device_node * dp , int num_sbus )
{
struct sbus_bus * sbus ;
unsigned int sbus_clock ;
struct device_node * dev_dp ;
2005-04-17 02:20:36 +04:00
2006-06-24 02:55:45 +04:00
sbus = kzalloc ( sizeof ( struct sbus_bus ) , GFP_ATOMIC ) ;
if ( ! sbus )
return ;
2005-04-17 02:20:36 +04:00
2006-06-24 02:55:45 +04:00
sbus_insert ( sbus , & sbus_root ) ;
sbus - > prom_node = dp - > node ;
2005-04-17 02:20:36 +04:00
2006-06-24 02:55:45 +04:00
sbus_setup_iommu ( sbus , dp ) ;
2005-04-17 02:20:36 +04:00
2006-06-24 02:55:45 +04:00
printk ( " sbus%d: " , num_sbus ) ;
2005-04-17 02:20:36 +04:00
2006-06-24 02:55:45 +04:00
sbus_clock = of_getintprop_default ( dp , " clock-frequency " ,
( 25 * 1000 * 1000 ) ) ;
sbus - > clock_freq = sbus_clock ;
printk ( " Clock %d.%d MHz \n " , ( int ) ( ( sbus_clock / 1000 ) / 1000 ) ,
( int ) ( ( ( sbus_clock / 1000 ) % 1000 ! = 0 ) ?
( ( ( sbus_clock / 1000 ) % 1000 ) + 1000 ) : 0 ) ) ;
strcpy ( sbus - > prom_name , dp - > name ) ;
sbus_setup_arch_props ( sbus , dp ) ;
sbus_bus_ranges_init ( dp , sbus ) ;
sbus - > ofdev . node = dp ;
sbus - > ofdev . dev . parent = NULL ;
sbus - > ofdev . dev . bus = & sbus_bus_type ;
2006-07-18 08:06:15 +04:00
sprintf ( sbus - > ofdev . dev . bus_id , " sbus%d " , num_sbus ) ;
2006-06-24 02:55:45 +04:00
if ( of_device_register ( & sbus - > ofdev ) ! = 0 )
printk ( KERN_DEBUG " sbus: device registration error for %s! \n " ,
sbus - > ofdev . dev . bus_id ) ;
dev_dp = dp - > child ;
while ( dev_dp ) {
struct sbus_dev * sdev ;
sdev = kzalloc ( sizeof ( struct sbus_dev ) , GFP_ATOMIC ) ;
if ( sdev ) {
sdev_insert ( sdev , & sbus - > devices ) ;
sdev - > bus = sbus ;
sdev - > parent = NULL ;
2007-07-28 09:39:14 +04:00
sdev - > ofdev . dev . archdata . iommu =
sbus - > ofdev . dev . archdata . iommu ;
sdev - > ofdev . dev . archdata . stc =
sbus - > ofdev . dev . archdata . stc ;
2006-06-24 02:55:45 +04:00
fill_sbus_device ( dev_dp , sdev ) ;
walk_children ( dev_dp , sdev , sbus ) ;
2005-04-17 02:20:36 +04:00
}
2006-06-24 02:55:45 +04:00
dev_dp = dev_dp - > sibling ;
}
sbus_fixup_all_regs ( sbus - > devices ) ;
dvma_init ( sbus ) ;
}
static int __init sbus_init ( void )
{
struct device_node * dp ;
const char * sbus_name = " sbus " ;
int num_sbus = 0 ;
2005-04-17 02:20:36 +04:00
2006-06-24 02:55:45 +04:00
if ( sbus_arch_preinit ( ) )
return 0 ;
2005-04-17 02:20:36 +04:00
2006-06-24 02:55:45 +04:00
if ( sparc_cpu_model = = sun4d )
sbus_name = " sbi " ;
for_each_node_by_name ( dp , sbus_name ) {
build_one_sbus ( dp , num_sbus ) ;
2005-04-17 02:20:36 +04:00
num_sbus + + ;
}
2006-06-24 02:55:45 +04:00
sbus_arch_postinit ( ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
subsys_initcall ( sbus_init ) ;