2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2013-06-20 09:20:52 +04:00
/*
* Derived from " arch/powerpc/platforms/pseries/pci_dlpar.c "
*
* Copyright ( C ) 2003 Linda Xie < lxie @ us . ibm . com >
* Copyright ( C ) 2005 International Business Machines
*
* Updates , 2005 , John Rose < johnrose @ austin . ibm . com >
* Updates , 2005 , Linas Vepstas < linas @ austin . ibm . com >
* Updates , 2013 , Gavin Shan < shangw @ linux . vnet . ibm . com >
*/
# include <linux/pci.h>
# include <linux/export.h>
# include <asm/pci-bridge.h>
# include <asm/ppc-pci.h>
# include <asm/firmware.h>
# include <asm/eeh.h>
2016-05-03 08:41:39 +03:00
static struct pci_bus * find_bus_among_children ( struct pci_bus * bus ,
struct device_node * dn )
{
struct pci_bus * child = NULL ;
struct pci_bus * tmp ;
if ( pci_bus_to_OF_node ( bus ) = = dn )
return bus ;
list_for_each_entry ( tmp , & bus - > children , node ) {
child = find_bus_among_children ( tmp , dn ) ;
if ( child )
break ;
}
return child ;
}
struct pci_bus * pci_find_bus_by_node ( struct device_node * dn )
{
struct pci_dn * pdn = PCI_DN ( dn ) ;
if ( ! pdn | | ! pdn - > phb | | ! pdn - > phb - > bus )
return NULL ;
return find_bus_among_children ( pdn - > phb - > bus , dn ) ;
}
EXPORT_SYMBOL_GPL ( pci_find_bus_by_node ) ;
2013-07-24 06:24:53 +04:00
/**
* pcibios_release_device - release PCI device
* @ dev : PCI device
*
* The function is called before releasing the indicated PCI device .
*/
void pcibios_release_device ( struct pci_dev * dev )
{
2015-05-27 09:06:57 +03:00
struct pci_controller * phb = pci_bus_to_host ( dev - > bus ) ;
2019-09-03 13:15:53 +03:00
struct pci_dn * pdn = pci_get_pdn ( dev ) ;
2015-05-27 09:06:57 +03:00
if ( phb - > controller_ops . release_device )
phb - > controller_ops . release_device ( dev ) ;
2019-09-03 13:15:53 +03:00
/* free()ing the pci_dn has been deferred to us, do it now */
if ( pdn & & ( pdn - > flags & PCI_DN_FLAG_DEAD ) ) {
pci_dbg ( dev , " freeing dead pdn \n " ) ;
kfree ( pdn ) ;
}
2013-07-24 06:24:53 +04:00
}
2013-06-20 09:20:52 +04:00
/**
2016-05-03 08:41:37 +03:00
* pci_hp_remove_devices - remove all devices under this bus
2013-06-20 09:20:52 +04:00
* @ bus : the indicated PCI bus
*
* Remove all of the PCI devices under this bus both from the
* linux pci device tree , and from the powerpc EEH address cache .
*/
2016-05-03 08:41:37 +03:00
void pci_hp_remove_devices ( struct pci_bus * bus )
2013-06-20 09:20:52 +04:00
{
struct pci_dev * dev , * tmp ;
struct pci_bus * child_bus ;
/* First go down child busses */
list_for_each_entry ( child_bus , & bus - > children , node )
2016-05-03 08:41:37 +03:00
pci_hp_remove_devices ( child_bus ) ;
2013-06-20 09:20:52 +04:00
pr_debug ( " PCI: Removing devices on bus %04x:%02x \n " ,
pci_domain_nr ( bus ) , bus - > number ) ;
2016-03-04 02:53:05 +03:00
list_for_each_entry_safe_reverse ( dev , tmp , & bus - > devices , bus_list ) {
2013-07-24 06:24:55 +04:00
pr_debug ( " Removing %s... \n " , pci_name ( dev ) ) ;
2013-06-20 09:20:52 +04:00
pci_stop_and_remove_bus_device ( dev ) ;
}
}
2016-05-03 08:41:37 +03:00
EXPORT_SYMBOL_GPL ( pci_hp_remove_devices ) ;
2013-06-20 09:20:52 +04:00
/**
2016-05-03 08:41:37 +03:00
* pci_hp_add_devices - adds new pci devices to bus
2013-06-20 09:20:52 +04:00
* @ bus : the indicated PCI bus
*
* This routine will find and fixup new pci devices under
* the indicated bus . This routine presumes that there
* might already be some devices under this bridge , so
* it carefully tries to add only new devices . ( And that
* is how this routine differs from other , similar pcibios
* routines . )
*/
2016-05-03 08:41:37 +03:00
void pci_hp_add_devices ( struct pci_bus * bus )
2013-06-20 09:20:52 +04:00
{
2017-11-10 20:52:30 +03:00
int slotno , mode , max ;
2013-06-20 09:20:52 +04:00
struct pci_dev * dev ;
2015-03-31 08:00:56 +03:00
struct pci_controller * phb ;
2013-06-20 09:20:52 +04:00
struct device_node * dn = pci_bus_to_OF_node ( bus ) ;
2015-03-31 08:00:56 +03:00
phb = pci_bus_to_host ( bus ) ;
2013-06-20 09:20:52 +04:00
mode = PCI_PROBE_NORMAL ;
2015-03-31 08:00:56 +03:00
if ( phb - > controller_ops . probe_mode )
mode = phb - > controller_ops . probe_mode ( bus ) ;
2013-06-20 09:20:52 +04:00
if ( mode = = PCI_PROBE_DEVTREE ) {
/* use ofdt-based probe */
of_rescan_bus ( dn , bus ) ;
2016-05-03 08:41:43 +03:00
} else if ( mode = = PCI_PROBE_NORMAL & &
dn - > child & & PCI_DN ( dn - > child ) ) {
2013-07-24 06:24:57 +04:00
/*
* Use legacy probe . In the partial hotplug case , we
* probably have grandchildren devices unplugged . So
* we don ' t check the return value from pci_scan_slot ( ) in
* order for fully rescan all the way down to pick them up .
* They can have been removed during partial hotplug .
*/
2013-06-20 09:20:52 +04:00
slotno = PCI_SLOT ( PCI_DN ( dn - > child ) - > devfn ) ;
2013-07-24 06:24:57 +04:00
pci_scan_slot ( bus , PCI_DEVFN ( slotno , 0 ) ) ;
2013-06-20 09:20:52 +04:00
max = bus - > busn_res . start ;
2017-11-10 20:52:30 +03:00
/*
* Scan bridges that are already configured . We don ' t touch
* them unless they are misconfigured ( which will be done in
* the second scan below ) .
*/
for_each_pci_bridge ( dev , bus )
max = pci_scan_bridge ( bus , dev , max , 0 ) ;
/* Scan bridges that need to be reconfigured */
for_each_pci_bridge ( dev , bus )
max = pci_scan_bridge ( bus , dev , max , 1 ) ;
2013-06-20 09:20:52 +04:00
}
pcibios_finish_adding_to_bus ( bus ) ;
}
2016-05-03 08:41:37 +03:00
EXPORT_SYMBOL_GPL ( pci_hp_add_devices ) ;