2007-05-12 04:57:12 +04:00
/*
* PCI bus setup for Marvell mv64360 / mv64460 host bridges ( Discovery )
*
* Author : Dale Farnsworth < dale @ farnsworth . org >
*
* 2007 ( c ) MontaVista , Software , Inc . This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed " as is " without any warranty of any kind , whether express
* or implied .
*/
# include <linux/stddef.h>
# include <linux/kernel.h>
# include <linux/init.h>
2011-05-27 21:27:45 +04:00
# include <linux/stat.h>
2007-05-12 04:57:12 +04:00
# include <linux/pci.h>
# include <asm/prom.h>
# include <asm/pci-bridge.h>
# define PCI_HEADER_TYPE_INVALID 0x7f /* Invalid PCI header type */
# ifdef CONFIG_SYSFS
/* 32-bit hex or dec stringified number + '\n' */
# define MV64X60_VAL_LEN_MAX 11
# define MV64X60_PCICFG_CPCI_HOTSWAP 0x68
2010-05-13 05:28:57 +04:00
static ssize_t mv64x60_hs_reg_read ( struct file * filp , struct kobject * kobj ,
2007-10-23 23:20:32 +04:00
struct bin_attribute * attr , char * buf ,
loff_t off , size_t count )
2007-05-12 04:57:12 +04:00
{
struct pci_dev * phb ;
u32 v ;
if ( off > 0 )
return 0 ;
if ( count < MV64X60_VAL_LEN_MAX )
return - EINVAL ;
phb = pci_get_bus_and_slot ( 0 , PCI_DEVFN ( 0 , 0 ) ) ;
if ( ! phb )
return - ENODEV ;
pci_read_config_dword ( phb , MV64X60_PCICFG_CPCI_HOTSWAP , & v ) ;
pci_dev_put ( phb ) ;
return sprintf ( buf , " 0x%08x \n " , v ) ;
}
2010-05-13 05:28:57 +04:00
static ssize_t mv64x60_hs_reg_write ( struct file * filp , struct kobject * kobj ,
2007-10-23 23:20:32 +04:00
struct bin_attribute * attr , char * buf ,
loff_t off , size_t count )
2007-05-12 04:57:12 +04:00
{
struct pci_dev * phb ;
u32 v ;
if ( off > 0 )
return 0 ;
if ( count < = 0 )
return - EINVAL ;
if ( sscanf ( buf , " %i " , & v ) ! = 1 )
return - EINVAL ;
phb = pci_get_bus_and_slot ( 0 , PCI_DEVFN ( 0 , 0 ) ) ;
if ( ! phb )
return - ENODEV ;
pci_write_config_dword ( phb , MV64X60_PCICFG_CPCI_HOTSWAP , v ) ;
pci_dev_put ( phb ) ;
return count ;
}
static struct bin_attribute mv64x60_hs_reg_attr = { /* Hotswap register */
. attr = {
. name = " hs_reg " ,
. mode = S_IRUGO | S_IWUSR ,
} ,
. size = MV64X60_VAL_LEN_MAX ,
. read = mv64x60_hs_reg_read ,
. write = mv64x60_hs_reg_write ,
} ;
static int __init mv64x60_sysfs_init ( void )
{
struct device_node * np ;
struct platform_device * pdev ;
const unsigned int * prop ;
2008-04-08 02:09:03 +04:00
np = of_find_compatible_node ( NULL , NULL , " marvell,mv64360 " ) ;
2007-05-12 04:57:12 +04:00
if ( ! np )
return 0 ;
prop = of_get_property ( np , " hs_reg_valid " , NULL ) ;
of_node_put ( np ) ;
2008-04-08 02:09:03 +04:00
pdev = platform_device_register_simple ( " marvell,mv64360 " , 0 , NULL , 0 ) ;
2007-05-12 04:57:12 +04:00
if ( IS_ERR ( pdev ) )
return PTR_ERR ( pdev ) ;
return sysfs_create_bin_file ( & pdev - > dev . kobj , & mv64x60_hs_reg_attr ) ;
}
subsys_initcall ( mv64x60_sysfs_init ) ;
# endif /* CONFIG_SYSFS */
static void __init mv64x60_pci_fixup_early ( struct pci_dev * dev )
{
/*
* Set the host bridge hdr_type to an invalid value so that
* pci_setup_device ( ) will ignore the host bridge .
*/
dev - > hdr_type = PCI_HEADER_TYPE_INVALID ;
}
DECLARE_PCI_FIXUP_EARLY ( PCI_VENDOR_ID_MARVELL , PCI_DEVICE_ID_MARVELL_MV64360 ,
mv64x60_pci_fixup_early ) ;
DECLARE_PCI_FIXUP_EARLY ( PCI_VENDOR_ID_MARVELL , PCI_DEVICE_ID_MARVELL_MV64460 ,
mv64x60_pci_fixup_early ) ;
static int __init mv64x60_add_bridge ( struct device_node * dev )
{
int len ;
struct pci_controller * hose ;
struct resource rsrc ;
const int * bus_range ;
int primary ;
memset ( & rsrc , 0 , sizeof ( rsrc ) ) ;
/* Fetch host bridge registers address */
if ( of_address_to_resource ( dev , 0 , & rsrc ) ) {
printk ( KERN_ERR " No PCI reg property in device tree \n " ) ;
return - ENODEV ;
}
/* Get bus range if any */
bus_range = of_get_property ( dev , " bus-range " , & len ) ;
if ( bus_range = = NULL | | len < 2 * sizeof ( int ) )
printk ( KERN_WARNING " Can't get bus-range for %s, assume "
" bus 0 \n " , dev - > full_name ) ;
2007-06-27 10:56:50 +04:00
hose = pcibios_alloc_controller ( dev ) ;
2007-05-12 04:57:12 +04:00
if ( ! hose )
return - ENOMEM ;
hose - > first_busno = bus_range ? bus_range [ 0 ] : 0 ;
hose - > last_busno = bus_range ? bus_range [ 1 ] : 0xff ;
2007-07-20 01:07:35 +04:00
setup_indirect_pci ( hose , rsrc . start , rsrc . start + 4 , 0 ) ;
2007-06-25 22:32:48 +04:00
hose - > self_busno = hose - > first_busno ;
2007-05-12 04:57:12 +04:00
printk ( KERN_INFO " Found MV64x60 PCI host bridge at 0x%016llx. "
" Firmware bus number: %d->%d \n " ,
( unsigned long long ) rsrc . start , hose - > first_busno ,
hose - > last_busno ) ;
/* Interpret the "ranges" property */
/* This also maps the I/O region and sets isa_io/mem_base */
primary = ( hose - > first_busno = = 0 ) ;
pci_process_bridge_OF_ranges ( hose , dev , primary ) ;
return 0 ;
}
void __init mv64x60_pci_init ( void )
{
2007-11-29 22:44:36 +03:00
struct device_node * np ;
2007-05-12 04:57:12 +04:00
2008-04-08 02:09:03 +04:00
for_each_compatible_node ( np , " pci " , " marvell,mv64360-pci " )
2007-05-12 04:57:12 +04:00
mv64x60_add_bridge ( np ) ;
}