1bbbd7f11SThomas Huth // SPDX-License-Identifier: GPL-2.0-or-later
22bf6a8faSLinas Vepstas /*
32bf6a8faSLinas Vepstas * PCI Dynamic LPAR, PCI Hot Plug and PCI EEH recovery code
42bf6a8faSLinas Vepstas * for RPA-compliant PPC64 platform.
52bf6a8faSLinas Vepstas * Copyright (C) 2003 Linda Xie <lxie@us.ibm.com>
62bf6a8faSLinas Vepstas * Copyright (C) 2005 International Business Machines
72bf6a8faSLinas Vepstas *
82bf6a8faSLinas Vepstas * Updates, 2005, John Rose <johnrose@austin.ibm.com>
92bf6a8faSLinas Vepstas * Updates, 2005, Linas Vepstas <linas@austin.ibm.com>
102bf6a8faSLinas Vepstas */
112bf6a8faSLinas Vepstas
122bf6a8faSLinas Vepstas #include <linux/pci.h>
1393087948SPaul Gortmaker #include <linux/export.h>
142bf6a8faSLinas Vepstas #include <asm/pci-bridge.h>
1592eb4602SJohn Rose #include <asm/ppc-pci.h>
16e8222502SBenjamin Herrenschmidt #include <asm/firmware.h>
17c3b9d9abSOlaf Hering #include <asm/eeh.h>
182bf6a8faSLinas Vepstas
1938ae9ec4SDaniel Axtens #include "pseries.h"
2038ae9ec4SDaniel Axtens
init_phb_dynamic(struct device_node * dn)21cad5cef6SGreg Kroah-Hartman struct pci_controller *init_phb_dynamic(struct device_node *dn)
2292eb4602SJohn Rose {
2392eb4602SJohn Rose struct pci_controller *phb;
2492eb4602SJohn Rose
25b7c670d6SRob Herring pr_debug("PCI: Initializing new hotplug PHB %pOF\n", dn);
26fd6852c8SBenjamin Herrenschmidt
2792eb4602SJohn Rose phb = pcibios_alloc_controller(dn);
2892eb4602SJohn Rose if (!phb)
2992eb4602SJohn Rose return NULL;
304c9d2800SBenjamin Herrenschmidt rtas_setup_phb(phb);
3192eb4602SJohn Rose pci_process_bridge_OF_ranges(phb, dn, 0);
3238ae9ec4SDaniel Axtens phb->controller_ops = pseries_pci_controller_ops;
3392eb4602SJohn Rose
3492eb4602SJohn Rose pci_devs_phb_init_dynamic(phb);
3592eb4602SJohn Rose
36174db9e7SCédric Le Goater pseries_msi_allocate_domains(phb);
37174db9e7SCédric Le Goater
38*b8315b2eSGaurav Batra ppc_iommu_register_device(phb);
39*b8315b2eSGaurav Batra
40eb740b5fSGavin Shan /* Create EEH devices for the PHB */
41475028efSOliver O'Halloran eeh_phb_pe_create(phb);
42eb740b5fSGavin Shan
4392eb4602SJohn Rose if (dn->child)
44b6eebb09SOliver O'Halloran pseries_eeh_init_edev_recursive(PCI_DN(dn));
4592eb4602SJohn Rose
46b5d937deSGrant Likely pcibios_scan_phb(phb);
47fd6852c8SBenjamin Herrenschmidt pcibios_finish_adding_to_bus(phb->bus);
4892eb4602SJohn Rose
4992eb4602SJohn Rose return phb;
5092eb4602SJohn Rose }
5192eb4602SJohn Rose EXPORT_SYMBOL_GPL(init_phb_dynamic);
52fd6852c8SBenjamin Herrenschmidt
53fd6852c8SBenjamin Herrenschmidt /* RPA-specific bits for removing PHBs */
remove_phb_dynamic(struct pci_controller * phb)54fd6852c8SBenjamin Herrenschmidt int remove_phb_dynamic(struct pci_controller *phb)
55fd6852c8SBenjamin Herrenschmidt {
56fd6852c8SBenjamin Herrenschmidt struct pci_bus *b = phb->bus;
5738d0b1c9STyrel Datwyler struct pci_host_bridge *host_bridge = to_pci_host_bridge(b->bridge);
58fd6852c8SBenjamin Herrenschmidt struct resource *res;
59fd6852c8SBenjamin Herrenschmidt int rc, i;
60fd6852c8SBenjamin Herrenschmidt
61fd6852c8SBenjamin Herrenschmidt pr_debug("PCI: Removing PHB %04x:%02x...\n",
62fd6852c8SBenjamin Herrenschmidt pci_domain_nr(b), b->number);
63fd6852c8SBenjamin Herrenschmidt
64fd6852c8SBenjamin Herrenschmidt /* We cannot to remove a root bus that has children */
65fd6852c8SBenjamin Herrenschmidt if (!(list_empty(&b->children) && list_empty(&b->devices)))
66fd6852c8SBenjamin Herrenschmidt return -EBUSY;
67fd6852c8SBenjamin Herrenschmidt
68fd6852c8SBenjamin Herrenschmidt /* We -know- there aren't any child devices anymore at this stage
69fd6852c8SBenjamin Herrenschmidt * and thus, we can safely unmap the IO space as it's not in use
70fd6852c8SBenjamin Herrenschmidt */
71fd6852c8SBenjamin Herrenschmidt res = &phb->io_resource;
72fd6852c8SBenjamin Herrenschmidt if (res->flags & IORESOURCE_IO) {
73fd6852c8SBenjamin Herrenschmidt rc = pcibios_unmap_io_space(b);
74fd6852c8SBenjamin Herrenschmidt if (rc) {
75fd6852c8SBenjamin Herrenschmidt printk(KERN_ERR "%s: failed to unmap IO on bus %s\n",
76fd6852c8SBenjamin Herrenschmidt __func__, b->name);
77fd6852c8SBenjamin Herrenschmidt return 1;
78fd6852c8SBenjamin Herrenschmidt }
79fd6852c8SBenjamin Herrenschmidt }
80fd6852c8SBenjamin Herrenschmidt
81*b8315b2eSGaurav Batra ppc_iommu_unregister_device(phb);
82*b8315b2eSGaurav Batra
83174db9e7SCédric Le Goater pseries_msi_free_domains(phb);
84174db9e7SCédric Le Goater
85fe2640bdSMichael Ellerman /* Keep a reference so phb isn't freed yet */
86fe2640bdSMichael Ellerman get_device(&host_bridge->dev);
87fe2640bdSMichael Ellerman
8873400565STyrel Datwyler /* Remove the PCI bus and unregister the bridge device from sysfs */
89fd6852c8SBenjamin Herrenschmidt phb->bus = NULL;
90fd6852c8SBenjamin Herrenschmidt pci_remove_bus(b);
9138d0b1c9STyrel Datwyler host_bridge->bus = NULL;
9238d0b1c9STyrel Datwyler device_unregister(&host_bridge->dev);
93fd6852c8SBenjamin Herrenschmidt
94fd6852c8SBenjamin Herrenschmidt /* Now release the IO resource */
95fd6852c8SBenjamin Herrenschmidt if (res->flags & IORESOURCE_IO)
96fd6852c8SBenjamin Herrenschmidt release_resource(res);
97fd6852c8SBenjamin Herrenschmidt
98fd6852c8SBenjamin Herrenschmidt /* Release memory resources */
99fd6852c8SBenjamin Herrenschmidt for (i = 0; i < 3; ++i) {
100fd6852c8SBenjamin Herrenschmidt res = &phb->mem_resources[i];
101fd6852c8SBenjamin Herrenschmidt if (!(res->flags & IORESOURCE_MEM))
102fd6852c8SBenjamin Herrenschmidt continue;
103fd6852c8SBenjamin Herrenschmidt release_resource(res);
104fd6852c8SBenjamin Herrenschmidt }
105fd6852c8SBenjamin Herrenschmidt
1062dd9c11bSMauricio Faria de Oliveira /*
1072dd9c11bSMauricio Faria de Oliveira * The pci_controller data structure is freed by
1082dd9c11bSMauricio Faria de Oliveira * the pcibios_free_controller_deferred() callback;
1092dd9c11bSMauricio Faria de Oliveira * see pseries_root_bridge_prepare().
1102dd9c11bSMauricio Faria de Oliveira */
111fe2640bdSMichael Ellerman put_device(&host_bridge->dev);
112fd6852c8SBenjamin Herrenschmidt
113fd6852c8SBenjamin Herrenschmidt return 0;
114fd6852c8SBenjamin Herrenschmidt }
115fd6852c8SBenjamin Herrenschmidt EXPORT_SYMBOL_GPL(remove_phb_dynamic);
116