2014-09-29 15:29:31 +01:00
/*
* Code borrowed from powerpc / kernel / pci - common . c
*
* Copyright ( C ) 2003 Anton Blanchard < anton @ au . ibm . com > , IBM
* Copyright ( C ) 2014 ARM Ltd .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation .
*
*/
2015-03-24 14:02:40 +00:00
# include <linux/acpi.h>
2014-09-29 15:29:31 +01:00
# include <linux/init.h>
# include <linux/io.h>
# include <linux/kernel.h>
# include <linux/mm.h>
# include <linux/of_pci.h>
# include <linux/of_platform.h>
2016-06-10 15:36:26 -05:00
# include <linux/pci.h>
2016-06-10 21:55:19 +02:00
# include <linux/pci-acpi.h>
# include <linux/pci-ecam.h>
2014-09-29 15:29:31 +01:00
# include <linux/slab.h>
/*
* Called after each bus is probed , but before its children are examined
*/
void pcibios_fixup_bus ( struct pci_bus * bus )
{
/* nothing to do, expected to be removed in the future */
}
/*
* We don ' t have to worry about legacy ISA devices , so nothing to do here
*/
resource_size_t pcibios_align_resource ( void * data , const struct resource * res ,
resource_size_t size , resource_size_t align )
{
return res - > start ;
}
/*
2016-06-10 21:55:17 +02:00
* Try to assign the IRQ number when probing a new device
2014-09-29 15:29:31 +01:00
*/
2016-06-10 21:55:17 +02:00
int pcibios_alloc_irq ( struct pci_dev * dev )
2014-09-29 15:29:31 +01:00
{
2016-06-10 21:55:17 +02:00
if ( acpi_disabled )
dev - > irq = of_irq_parse_and_map_pci ( dev , 0 , 0 ) ;
# ifdef CONFIG_ACPI
else
return acpi_pci_irq_enable ( dev ) ;
# endif
2014-09-29 15:29:31 +01:00
return 0 ;
}
2015-03-24 14:02:40 +00:00
/*
* raw_pci_read / write - Platform - specific PCI config space access .
*/
int raw_pci_read ( unsigned int domain , unsigned int bus ,
unsigned int devfn , int reg , int len , u32 * val )
{
2016-06-10 21:55:18 +02:00
struct pci_bus * b = pci_find_bus ( domain , bus ) ;
if ( ! b )
return PCIBIOS_DEVICE_NOT_FOUND ;
return b - > ops - > read ( b , devfn , reg , len , val ) ;
2015-03-24 14:02:40 +00:00
}
int raw_pci_write ( unsigned int domain , unsigned int bus ,
unsigned int devfn , int reg , int len , u32 val )
{
2016-06-10 21:55:18 +02:00
struct pci_bus * b = pci_find_bus ( domain , bus ) ;
if ( ! b )
return PCIBIOS_DEVICE_NOT_FOUND ;
return b - > ops - > write ( b , devfn , reg , len , val ) ;
2015-03-24 14:02:40 +00:00
}
2016-04-08 15:50:27 -07:00
# ifdef CONFIG_NUMA
int pcibus_to_node ( struct pci_bus * bus )
{
return dev_to_node ( & bus - > dev ) ;
}
EXPORT_SYMBOL ( pcibus_to_node ) ;
# endif
2015-03-24 14:02:40 +00:00
# ifdef CONFIG_ACPI
2016-06-10 15:36:26 -05:00
2016-06-10 21:55:19 +02:00
struct acpi_pci_generic_root_info {
struct acpi_pci_root_info common ;
struct pci_config_window * cfg ; /* config space mapping */
} ;
2016-06-10 15:36:26 -05:00
int acpi_pci_bus_find_domain_nr ( struct pci_bus * bus )
{
2016-06-10 21:55:19 +02:00
struct pci_config_window * cfg = bus - > sysdata ;
struct acpi_device * adev = to_acpi_device ( cfg - > parent ) ;
struct acpi_pci_root * root = acpi_driver_data ( adev ) ;
return root - > segment ;
}
int pcibios_root_bridge_prepare ( struct pci_host_bridge * bridge )
{
if ( ! acpi_disabled ) {
struct pci_config_window * cfg = bridge - > bus - > sysdata ;
struct acpi_device * adev = to_acpi_device ( cfg - > parent ) ;
ACPI_COMPANION_SET ( & bridge - > dev , adev ) ;
}
2016-06-10 15:36:26 -05:00
return 0 ;
}
2016-06-10 21:55:19 +02:00
/*
* Lookup the bus range for the domain in MCFG , and set up config space
* mapping .
*/
static struct pci_config_window *
pci_acpi_setup_ecam_mapping ( struct acpi_pci_root * root )
{
struct resource * bus_res = & root - > secondary ;
u16 seg = root - > segment ;
struct pci_config_window * cfg ;
struct resource cfgres ;
unsigned int bsz ;
/* Use address from _CBA if present, otherwise lookup MCFG */
if ( ! root - > mcfg_addr )
root - > mcfg_addr = pci_mcfg_lookup ( seg , bus_res ) ;
if ( ! root - > mcfg_addr ) {
dev_err ( & root - > device - > dev , " %04x:%pR ECAM region not found \n " ,
seg , bus_res ) ;
return NULL ;
}
bsz = 1 < < pci_generic_ecam_ops . bus_shift ;
cfgres . start = root - > mcfg_addr + bus_res - > start * bsz ;
cfgres . end = cfgres . start + resource_size ( bus_res ) * bsz - 1 ;
cfgres . flags = IORESOURCE_MEM ;
cfg = pci_ecam_create ( & root - > device - > dev , & cfgres , bus_res ,
& pci_generic_ecam_ops ) ;
if ( IS_ERR ( cfg ) ) {
dev_err ( & root - > device - > dev , " %04x:%pR error %ld mapping ECAM \n " ,
seg , bus_res , PTR_ERR ( cfg ) ) ;
return NULL ;
}
return cfg ;
}
/* release_info: free resources allocated by init_info */
static void pci_acpi_generic_release_info ( struct acpi_pci_root_info * ci )
{
struct acpi_pci_generic_root_info * ri ;
ri = container_of ( ci , struct acpi_pci_generic_root_info , common ) ;
pci_ecam_free ( ri - > cfg ) ;
kfree ( ri ) ;
}
static struct acpi_pci_root_ops acpi_pci_root_ops = {
. release_info = pci_acpi_generic_release_info ,
} ;
/* Interface called from ACPI code to setup PCI host controller */
2015-03-24 14:02:40 +00:00
struct pci_bus * pci_acpi_scan_root ( struct acpi_pci_root * root )
{
2016-06-10 21:55:19 +02:00
int node = acpi_get_node ( root - > device - > handle ) ;
struct acpi_pci_generic_root_info * ri ;
struct pci_bus * bus , * child ;
ri = kzalloc_node ( sizeof ( * ri ) , GFP_KERNEL , node ) ;
if ( ! ri )
return NULL ;
ri - > cfg = pci_acpi_setup_ecam_mapping ( root ) ;
if ( ! ri - > cfg ) {
kfree ( ri ) ;
return NULL ;
}
acpi_pci_root_ops . pci_ops = & ri - > cfg - > ops - > pci_ops ;
bus = acpi_pci_root_create ( root , & acpi_pci_root_ops , & ri - > common ,
ri - > cfg ) ;
if ( ! bus )
return NULL ;
pci_bus_size_bridges ( bus ) ;
pci_bus_assign_resources ( bus ) ;
list_for_each_entry ( child , & bus - > children , node )
pcie_bus_configure_settings ( child ) ;
return bus ;
}
void pcibios_add_bus ( struct pci_bus * bus )
{
acpi_pci_add_bus ( bus ) ;
2015-03-24 14:02:40 +00:00
}
2016-06-10 21:55:19 +02:00
void pcibios_remove_bus ( struct pci_bus * bus )
{
acpi_pci_remove_bus ( bus ) ;
}
2015-03-24 14:02:40 +00:00
# endif