2005-04-17 02:20:36 +04:00
/* $Id: ebus.c,v 1.20 2002/01/05 01:13:43 davem Exp $
* ebus . c : PCI to EBus bridge device .
*
* Copyright ( C ) 1997 Eddie C . Dost ( ecd @ skynet . be )
*
* Adopted for sparc by V . Roganov and G . Raiko .
* Fixes for different platforms by Pete Zaitcev .
*/
# include <linux/kernel.h>
# include <linux/types.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/string.h>
# include <asm/system.h>
# include <asm/page.h>
# include <asm/pbm.h>
# include <asm/ebus.h>
# include <asm/io.h>
# include <asm/oplib.h>
2006-06-24 02:53:31 +04:00
# include <asm/prom.h>
2005-04-17 02:20:36 +04:00
# include <asm/bpp.h>
2005-12-05 02:48:45 +03:00
struct linux_ebus * ebus_chain = NULL ;
2005-04-17 02:20:36 +04:00
/* We are together with pcic.c under CONFIG_PCI. */
2007-04-26 11:03:53 +04:00
extern unsigned int pcic_pin_to_irq ( unsigned int , const char * name ) ;
2005-04-17 02:20:36 +04:00
/*
* IRQ Blacklist
* Here we list PROMs and systems that are known to supply crap as IRQ numbers .
*/
struct ebus_device_irq {
char * name ;
unsigned int pin ;
} ;
struct ebus_system_entry {
char * esname ;
struct ebus_device_irq * ipt ;
} ;
static struct ebus_device_irq je1_1 [ ] = {
{ " 8042 " , 3 } ,
{ " SUNW,CS4231 " , 0 } ,
{ " parallel " , 0 } ,
{ " se " , 2 } ,
2005-12-05 02:48:45 +03:00
{ NULL , 0 }
2005-04-17 02:20:36 +04:00
} ;
/*
* Gleb ' s JE1 supplied reasonable pin numbers , but mine did not ( OBP 2.32 ) .
* Blacklist the sucker . . . Note that Gleb ' s system will work .
*/
static struct ebus_system_entry ebus_blacklist [ ] = {
{ " SUNW,JavaEngine1 " , je1_1 } ,
2005-12-05 02:48:45 +03:00
{ NULL , NULL }
2005-04-17 02:20:36 +04:00
} ;
static struct ebus_device_irq * ebus_blackp = NULL ;
/*
*/
static inline unsigned long ebus_alloc ( size_t size )
{
return ( unsigned long ) kmalloc ( size , GFP_ATOMIC ) ;
}
/*
*/
2007-04-26 11:03:53 +04:00
int __init ebus_blacklist_irq ( const char * name )
2005-04-17 02:20:36 +04:00
{
struct ebus_device_irq * dp ;
if ( ( dp = ebus_blackp ) ! = NULL ) {
for ( ; dp - > name ! = NULL ; dp + + ) {
if ( strcmp ( name , dp - > name ) = = 0 ) {
return pcic_pin_to_irq ( dp - > pin , name ) ;
}
}
}
return 0 ;
}
2006-06-24 02:53:31 +04:00
void __init fill_ebus_child ( struct device_node * dp ,
struct linux_ebus_child * dev )
2005-04-17 02:20:36 +04:00
{
2007-03-29 11:47:23 +04:00
const int * regs ;
const int * irqs ;
2005-04-17 02:20:36 +04:00
int i , len ;
2006-06-24 02:53:31 +04:00
dev - > prom_node = dp ;
regs = of_get_property ( dp , " reg " , & len ) ;
if ( ! regs )
len = 0 ;
2005-04-17 02:20:36 +04:00
dev - > num_addrs = len / sizeof ( regs [ 0 ] ) ;
for ( i = 0 ; i < dev - > num_addrs ; i + + ) {
if ( regs [ i ] > = dev - > parent - > num_addrs ) {
prom_printf ( " UGH: property for %s was %d, need < %d \n " ,
2006-06-24 02:53:31 +04:00
dev - > prom_node - > name , len ,
dev - > parent - > num_addrs ) ;
2005-04-17 02:20:36 +04:00
panic ( __FUNCTION__ ) ;
}
2006-06-24 02:53:31 +04:00
/* XXX resource */
dev - > resource [ i ] . start =
dev - > parent - > resource [ regs [ i ] ] . start ;
2005-04-17 02:20:36 +04:00
}
for ( i = 0 ; i < PROMINTR_MAX ; i + + )
dev - > irqs [ i ] = PCI_IRQ_NONE ;
2006-06-24 02:53:31 +04:00
if ( ( dev - > irqs [ 0 ] = ebus_blacklist_irq ( dev - > prom_node - > name ) ) ! = 0 ) {
2005-04-17 02:20:36 +04:00
dev - > num_irqs = 1 ;
} else {
2006-06-24 02:53:31 +04:00
irqs = of_get_property ( dp , " interrupts " , & len ) ;
if ( ! irqs ) {
2005-04-17 02:20:36 +04:00
dev - > num_irqs = 0 ;
dev - > irqs [ 0 ] = 0 ;
2006-06-24 02:53:31 +04:00
if ( dev - > parent - > num_irqs ! = 0 ) {
dev - > num_irqs = 1 ;
dev - > irqs [ 0 ] = dev - > parent - > irqs [ 0 ] ;
}
2005-04-17 02:20:36 +04:00
} else {
2006-06-24 02:53:31 +04:00
dev - > num_irqs = len / sizeof ( irqs [ 0 ] ) ;
if ( irqs [ 0 ] = = 0 | | irqs [ 0 ] > = 8 ) {
/*
* XXX Zero is a valid pin number . . .
* This works as long as Ebus is not wired
* to INTA # .
*/
printk ( " EBUS: %s got bad irq %d from PROM \n " ,
dev - > prom_node - > name , irqs [ 0 ] ) ;
dev - > num_irqs = 0 ;
dev - > irqs [ 0 ] = 0 ;
} else {
dev - > irqs [ 0 ] =
pcic_pin_to_irq ( irqs [ 0 ] ,
dev - > prom_node - > name ) ;
}
2005-04-17 02:20:36 +04:00
}
}
}
2006-06-24 02:53:31 +04:00
void __init fill_ebus_device ( struct device_node * dp , struct linux_ebus_device * dev )
2005-04-17 02:20:36 +04:00
{
2007-03-29 11:47:23 +04:00
const struct linux_prom_registers * regs ;
2005-04-17 02:20:36 +04:00
struct linux_ebus_child * child ;
2007-03-29 11:47:23 +04:00
const int * irqs ;
2005-04-17 02:20:36 +04:00
int i , n , len ;
unsigned long baseaddr ;
2006-06-24 02:53:31 +04:00
dev - > prom_node = dp ;
2005-04-17 02:20:36 +04:00
2006-06-24 02:53:31 +04:00
regs = of_get_property ( dp , " reg " , & len ) ;
2005-04-17 02:20:36 +04:00
if ( len % sizeof ( struct linux_prom_registers ) ) {
prom_printf ( " UGH: proplen for %s was %d, need multiple of %d \n " ,
2006-06-24 02:53:31 +04:00
dev - > prom_node - > name , len ,
2005-04-17 02:20:36 +04:00
( int ) sizeof ( struct linux_prom_registers ) ) ;
panic ( __FUNCTION__ ) ;
}
dev - > num_addrs = len / sizeof ( struct linux_prom_registers ) ;
for ( i = 0 ; i < dev - > num_addrs ; i + + ) {
/*
* XXX Collect JE - 1 PROM
*
* Example - JS - E with 3.11 :
* / ebus
* regs
* 0x00000000 , 0x0 , 0x00000000 , 0x0 , 0x00000000 ,
* 0x82000010 , 0x0 , 0xf0000000 , 0x0 , 0x01000000 ,
* 0x82000014 , 0x0 , 0x38800000 , 0x0 , 0x00800000 ,
* ranges
* 0x00 , 0x00000000 , 0x02000010 , 0x0 , 0x0 , 0x01000000 ,
* 0x01 , 0x01000000 , 0x02000014 , 0x0 , 0x0 , 0x00800000 ,
* / ebus / 8042
* regs
* 0x00000001 , 0x00300060 , 0x00000008 ,
* 0x00000001 , 0x00300060 , 0x00000008 ,
*/
n = regs [ i ] . which_io ;
if ( n > = 4 ) {
/* XXX This is copied from old JE-1 by Gleb. */
n = ( regs [ i ] . which_io - 0x10 ) > > 2 ;
} else {
;
}
/*
* XXX Now as we have regions , why don ' t we make an on - demand allocation . . .
*/
dev - > resource [ i ] . start = 0 ;
if ( ( baseaddr = dev - > bus - > self - > resource [ n ] . start +
regs [ i ] . phys_addr ) ! = 0 ) {
/* dev->resource[i].name = dev->prom_name; */
if ( ( baseaddr = ( unsigned long ) ioremap ( baseaddr ,
regs [ i ] . reg_size ) ) = = 0 ) {
panic ( " ebus: unable to remap dev %s " ,
2006-06-24 02:53:31 +04:00
dev - > prom_node - > name ) ;
2005-04-17 02:20:36 +04:00
}
}
dev - > resource [ i ] . start = baseaddr ; /* XXX Unaligned */
}
for ( i = 0 ; i < PROMINTR_MAX ; i + + )
dev - > irqs [ i ] = PCI_IRQ_NONE ;
2006-06-24 02:53:31 +04:00
if ( ( dev - > irqs [ 0 ] = ebus_blacklist_irq ( dev - > prom_node - > name ) ) ! = 0 ) {
2005-04-17 02:20:36 +04:00
dev - > num_irqs = 1 ;
} else {
2006-06-24 02:53:31 +04:00
irqs = of_get_property ( dp , " interrupts " , & len ) ;
if ( ! irqs ) {
2005-04-17 02:20:36 +04:00
dev - > num_irqs = 0 ;
2006-06-24 02:53:31 +04:00
if ( ( dev - > irqs [ 0 ] = dev - > bus - > self - > irq ) ! = 0 ) {
dev - > num_irqs = 1 ;
/* P3 */ /* printk("EBUS: child %s irq %d from parent\n", dev->prom_name, dev->irqs[0]); */
}
2005-04-17 02:20:36 +04:00
} else {
2006-06-24 02:53:31 +04:00
dev - > num_irqs = 1 ; /* dev->num_irqs = len / sizeof(irqs[0]); */
if ( irqs [ 0 ] = = 0 | | irqs [ 0 ] > = 8 ) {
/* See above for the parent. XXX */
printk ( " EBUS: %s got bad irq %d from PROM \n " ,
dev - > prom_node - > name , irqs [ 0 ] ) ;
dev - > num_irqs = 0 ;
dev - > irqs [ 0 ] = 0 ;
} else {
dev - > irqs [ 0 ] =
pcic_pin_to_irq ( irqs [ 0 ] ,
dev - > prom_node - > name ) ;
}
2005-04-17 02:20:36 +04:00
}
}
2006-06-24 02:55:17 +04:00
dev - > ofdev . node = dp ;
dev - > ofdev . dev . parent = & dev - > bus - > ofdev . dev ;
dev - > ofdev . dev . bus = & ebus_bus_type ;
2006-10-27 12:03:31 +04:00
sprintf ( dev - > ofdev . dev . bus_id , " ebus[%08x] " , dp - > node ) ;
2006-06-24 02:55:17 +04:00
/* Register with core */
if ( of_device_register ( & dev - > ofdev ) ! = 0 )
printk ( KERN_DEBUG " ebus: device registration error for %s! \n " ,
2006-10-27 12:03:31 +04:00
dp - > path_component_name ) ;
2006-06-24 02:55:17 +04:00
2006-06-24 02:53:31 +04:00
if ( ( dp = dp - > child ) ! = NULL ) {
2005-04-17 02:20:36 +04:00
dev - > children = ( struct linux_ebus_child * )
ebus_alloc ( sizeof ( struct linux_ebus_child ) ) ;
child = dev - > children ;
2005-12-05 02:48:45 +03:00
child - > next = NULL ;
2005-04-17 02:20:36 +04:00
child - > parent = dev ;
child - > bus = dev - > bus ;
2006-06-24 02:53:31 +04:00
fill_ebus_child ( dp , child ) ;
2005-04-17 02:20:36 +04:00
2006-06-24 02:53:31 +04:00
while ( ( dp = dp - > sibling ) ! = NULL ) {
2005-04-17 02:20:36 +04:00
child - > next = ( struct linux_ebus_child * )
ebus_alloc ( sizeof ( struct linux_ebus_child ) ) ;
child = child - > next ;
2005-12-05 02:48:45 +03:00
child - > next = NULL ;
2005-04-17 02:20:36 +04:00
child - > parent = dev ;
child - > bus = dev - > bus ;
2006-06-24 02:53:31 +04:00
fill_ebus_child ( dp , child ) ;
2005-04-17 02:20:36 +04:00
}
}
}
void __init ebus_init ( void )
{
2007-03-29 11:47:23 +04:00
const struct linux_prom_pci_registers * regs ;
2005-04-17 02:20:36 +04:00
struct linux_pbm_info * pbm ;
struct linux_ebus_device * dev ;
struct linux_ebus * ebus ;
struct ebus_system_entry * sp ;
struct pci_dev * pdev ;
struct pcidev_cookie * cookie ;
2006-06-24 02:53:31 +04:00
struct device_node * dp ;
2006-09-23 04:18:41 +04:00
struct resource * p ;
2005-04-17 02:20:36 +04:00
unsigned short pci_command ;
2006-06-24 02:53:31 +04:00
int len , reg , nreg ;
2005-04-17 02:20:36 +04:00
int num_ebus = 0 ;
2006-06-24 02:53:31 +04:00
dp = of_find_node_by_path ( " / " ) ;
2005-04-17 02:20:36 +04:00
for ( sp = ebus_blacklist ; sp - > esname ! = NULL ; sp + + ) {
2006-06-24 02:53:31 +04:00
if ( strcmp ( dp - > name , sp - > esname ) = = 0 ) {
2005-04-17 02:20:36 +04:00
ebus_blackp = sp - > ipt ;
break ;
}
}
2005-12-05 02:48:45 +03:00
pdev = pci_get_device ( PCI_VENDOR_ID_SUN , PCI_DEVICE_ID_SUN_EBUS , NULL ) ;
2006-06-24 02:53:31 +04:00
if ( ! pdev )
2005-04-17 02:20:36 +04:00
return ;
2006-06-24 02:53:31 +04:00
2005-04-17 02:20:36 +04:00
cookie = pdev - > sysdata ;
2006-06-24 02:53:31 +04:00
dp = cookie - > prom_node ;
2005-04-17 02:20:36 +04:00
ebus_chain = ebus = ( struct linux_ebus * )
ebus_alloc ( sizeof ( struct linux_ebus ) ) ;
2005-12-05 02:48:45 +03:00
ebus - > next = NULL ;
2005-04-17 02:20:36 +04:00
2006-06-24 02:53:31 +04:00
while ( dp ) {
struct device_node * nd ;
2005-04-17 02:20:36 +04:00
2006-06-24 02:53:31 +04:00
ebus - > prom_node = dp ;
2005-04-17 02:20:36 +04:00
ebus - > self = pdev ;
ebus - > parent = pbm = cookie - > pbm ;
/* Enable BUS Master. */
pci_read_config_word ( pdev , PCI_COMMAND , & pci_command ) ;
pci_command | = PCI_COMMAND_MASTER ;
pci_write_config_word ( pdev , PCI_COMMAND , pci_command ) ;
2006-06-24 02:53:31 +04:00
regs = of_get_property ( dp , " reg " , & len ) ;
if ( ! regs ) {
2005-04-17 02:20:36 +04:00
prom_printf ( " %s: can't find reg property \n " ,
__FUNCTION__ ) ;
prom_halt ( ) ;
}
nreg = len / sizeof ( struct linux_prom_pci_registers ) ;
2006-09-23 04:18:41 +04:00
p = & ebus - > self - > resource [ 0 ] ;
2005-04-17 02:20:36 +04:00
for ( reg = 0 ; reg < nreg ; reg + + ) {
if ( ! ( regs [ reg ] . which_io & 0x03000000 ) )
continue ;
2006-09-23 04:18:41 +04:00
( p + + ) - > start = regs [ reg ] . phys_lo ;
2005-04-17 02:20:36 +04:00
}
2006-06-24 02:55:17 +04:00
ebus - > ofdev . node = dp ;
ebus - > ofdev . dev . parent = & pdev - > dev ;
ebus - > ofdev . dev . bus = & ebus_bus_type ;
2006-10-27 12:03:31 +04:00
sprintf ( ebus - > ofdev . dev . bus_id , " ebus%d " , num_ebus ) ;
2006-06-24 02:55:17 +04:00
/* Register with core */
if ( of_device_register ( & ebus - > ofdev ) ! = 0 )
printk ( KERN_DEBUG " ebus: device registration error for %s! \n " ,
2006-10-27 12:03:31 +04:00
dp - > path_component_name ) ;
2006-06-24 02:55:17 +04:00
2006-06-24 02:53:31 +04:00
nd = dp - > child ;
2005-04-17 02:20:36 +04:00
if ( ! nd )
goto next_ebus ;
ebus - > devices = ( struct linux_ebus_device * )
ebus_alloc ( sizeof ( struct linux_ebus_device ) ) ;
dev = ebus - > devices ;
2005-12-05 02:48:45 +03:00
dev - > next = NULL ;
dev - > children = NULL ;
2005-04-17 02:20:36 +04:00
dev - > bus = ebus ;
fill_ebus_device ( nd , dev ) ;
2006-06-24 02:53:31 +04:00
while ( ( nd = nd - > sibling ) ! = NULL ) {
2005-04-17 02:20:36 +04:00
dev - > next = ( struct linux_ebus_device * )
ebus_alloc ( sizeof ( struct linux_ebus_device ) ) ;
dev = dev - > next ;
2005-12-05 02:48:45 +03:00
dev - > next = NULL ;
dev - > children = NULL ;
2005-04-17 02:20:36 +04:00
dev - > bus = ebus ;
fill_ebus_device ( nd , dev ) ;
}
next_ebus :
pdev = pci_get_device ( PCI_VENDOR_ID_SUN ,
PCI_DEVICE_ID_SUN_EBUS , pdev ) ;
if ( ! pdev )
break ;
cookie = pdev - > sysdata ;
2006-06-24 02:53:31 +04:00
dp = cookie - > prom_node ;
2005-04-17 02:20:36 +04:00
ebus - > next = ( struct linux_ebus * )
ebus_alloc ( sizeof ( struct linux_ebus ) ) ;
ebus = ebus - > next ;
2005-12-05 02:48:45 +03:00
ebus - > next = NULL ;
2005-04-17 02:20:36 +04:00
+ + num_ebus ;
}
if ( pdev )
pci_dev_put ( pdev ) ;
}