162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * PCI Dynamic LPAR, PCI Hot Plug and PCI EEH recovery code 462306a36Sopenharmony_ci * for RPA-compliant PPC64 platform. 562306a36Sopenharmony_ci * Copyright (C) 2003 Linda Xie <lxie@us.ibm.com> 662306a36Sopenharmony_ci * Copyright (C) 2005 International Business Machines 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Updates, 2005, John Rose <johnrose@austin.ibm.com> 962306a36Sopenharmony_ci * Updates, 2005, Linas Vepstas <linas@austin.ibm.com> 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/pci.h> 1362306a36Sopenharmony_ci#include <linux/export.h> 1462306a36Sopenharmony_ci#include <asm/pci-bridge.h> 1562306a36Sopenharmony_ci#include <asm/ppc-pci.h> 1662306a36Sopenharmony_ci#include <asm/firmware.h> 1762306a36Sopenharmony_ci#include <asm/eeh.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "pseries.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistruct pci_controller *init_phb_dynamic(struct device_node *dn) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci struct pci_controller *phb; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci pr_debug("PCI: Initializing new hotplug PHB %pOF\n", dn); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci phb = pcibios_alloc_controller(dn); 2862306a36Sopenharmony_ci if (!phb) 2962306a36Sopenharmony_ci return NULL; 3062306a36Sopenharmony_ci rtas_setup_phb(phb); 3162306a36Sopenharmony_ci pci_process_bridge_OF_ranges(phb, dn, 0); 3262306a36Sopenharmony_ci phb->controller_ops = pseries_pci_controller_ops; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci pci_devs_phb_init_dynamic(phb); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci pseries_msi_allocate_domains(phb); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci ppc_iommu_register_device(phb); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci /* Create EEH devices for the PHB */ 4162306a36Sopenharmony_ci eeh_phb_pe_create(phb); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci if (dn->child) 4462306a36Sopenharmony_ci pseries_eeh_init_edev_recursive(PCI_DN(dn)); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci pcibios_scan_phb(phb); 4762306a36Sopenharmony_ci pcibios_finish_adding_to_bus(phb->bus); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci return phb; 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(init_phb_dynamic); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* RPA-specific bits for removing PHBs */ 5462306a36Sopenharmony_ciint remove_phb_dynamic(struct pci_controller *phb) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci struct pci_bus *b = phb->bus; 5762306a36Sopenharmony_ci struct pci_host_bridge *host_bridge = to_pci_host_bridge(b->bridge); 5862306a36Sopenharmony_ci struct resource *res; 5962306a36Sopenharmony_ci int rc, i; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci pr_debug("PCI: Removing PHB %04x:%02x...\n", 6262306a36Sopenharmony_ci pci_domain_nr(b), b->number); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci /* We cannot to remove a root bus that has children */ 6562306a36Sopenharmony_ci if (!(list_empty(&b->children) && list_empty(&b->devices))) 6662306a36Sopenharmony_ci return -EBUSY; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci /* We -know- there aren't any child devices anymore at this stage 6962306a36Sopenharmony_ci * and thus, we can safely unmap the IO space as it's not in use 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_ci res = &phb->io_resource; 7262306a36Sopenharmony_ci if (res->flags & IORESOURCE_IO) { 7362306a36Sopenharmony_ci rc = pcibios_unmap_io_space(b); 7462306a36Sopenharmony_ci if (rc) { 7562306a36Sopenharmony_ci printk(KERN_ERR "%s: failed to unmap IO on bus %s\n", 7662306a36Sopenharmony_ci __func__, b->name); 7762306a36Sopenharmony_ci return 1; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci ppc_iommu_unregister_device(phb); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci pseries_msi_free_domains(phb); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* Keep a reference so phb isn't freed yet */ 8662306a36Sopenharmony_ci get_device(&host_bridge->dev); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci /* Remove the PCI bus and unregister the bridge device from sysfs */ 8962306a36Sopenharmony_ci phb->bus = NULL; 9062306a36Sopenharmony_ci pci_remove_bus(b); 9162306a36Sopenharmony_ci host_bridge->bus = NULL; 9262306a36Sopenharmony_ci device_unregister(&host_bridge->dev); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci /* Now release the IO resource */ 9562306a36Sopenharmony_ci if (res->flags & IORESOURCE_IO) 9662306a36Sopenharmony_ci release_resource(res); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci /* Release memory resources */ 9962306a36Sopenharmony_ci for (i = 0; i < 3; ++i) { 10062306a36Sopenharmony_ci res = &phb->mem_resources[i]; 10162306a36Sopenharmony_ci if (!(res->flags & IORESOURCE_MEM)) 10262306a36Sopenharmony_ci continue; 10362306a36Sopenharmony_ci release_resource(res); 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci /* 10762306a36Sopenharmony_ci * The pci_controller data structure is freed by 10862306a36Sopenharmony_ci * the pcibios_free_controller_deferred() callback; 10962306a36Sopenharmony_ci * see pseries_root_bridge_prepare(). 11062306a36Sopenharmony_ci */ 11162306a36Sopenharmony_ci put_device(&host_bridge->dev); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci return 0; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(remove_phb_dynamic); 116