|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | /* | 
|  | * Generic PCI host driver common code | 
|  | * | 
|  | * Copyright (C) 2014 ARM Limited | 
|  | * | 
|  | * Author: Will Deacon <will.deacon@arm.com> | 
|  | */ | 
|  |  | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/module.h> | 
|  | #include <linux/of_address.h> | 
|  | #include <linux/of_device.h> | 
|  | #include <linux/of_pci.h> | 
|  | #include <linux/pci-ecam.h> | 
|  | #include <linux/platform_device.h> | 
|  |  | 
|  | static void gen_pci_unmap_cfg(void *ptr) | 
|  | { | 
|  | pci_ecam_free((struct pci_config_window *)ptr); | 
|  | } | 
|  |  | 
|  | static struct pci_config_window *gen_pci_init(struct device *dev, | 
|  | struct pci_host_bridge *bridge, const struct pci_ecam_ops *ops) | 
|  | { | 
|  | int err; | 
|  | struct resource cfgres; | 
|  | struct resource_entry *bus; | 
|  | struct pci_config_window *cfg; | 
|  |  | 
|  | err = of_address_to_resource(dev->of_node, 0, &cfgres); | 
|  | if (err) { | 
|  | dev_err(dev, "missing \"reg\" property\n"); | 
|  | return ERR_PTR(err); | 
|  | } | 
|  |  | 
|  | bus = resource_list_first_type(&bridge->windows, IORESOURCE_BUS); | 
|  | if (!bus) | 
|  | return ERR_PTR(-ENODEV); | 
|  |  | 
|  | cfg = pci_ecam_create(dev, &cfgres, bus->res, ops); | 
|  | if (IS_ERR(cfg)) | 
|  | return cfg; | 
|  |  | 
|  | err = devm_add_action_or_reset(dev, gen_pci_unmap_cfg, cfg); | 
|  | if (err) | 
|  | return ERR_PTR(err); | 
|  |  | 
|  | return cfg; | 
|  | } | 
|  |  | 
|  | int pci_host_common_probe(struct platform_device *pdev) | 
|  | { | 
|  | struct device *dev = &pdev->dev; | 
|  | struct pci_host_bridge *bridge; | 
|  | struct pci_config_window *cfg; | 
|  | const struct pci_ecam_ops *ops; | 
|  |  | 
|  | ops = of_device_get_match_data(&pdev->dev); | 
|  | if (!ops) | 
|  | return -ENODEV; | 
|  |  | 
|  | bridge = devm_pci_alloc_host_bridge(dev, 0); | 
|  | if (!bridge) | 
|  | return -ENOMEM; | 
|  |  | 
|  | platform_set_drvdata(pdev, bridge); | 
|  |  | 
|  | of_pci_check_probe_only(); | 
|  |  | 
|  | /* Parse and map our Configuration Space windows */ | 
|  | cfg = gen_pci_init(dev, bridge, ops); | 
|  | if (IS_ERR(cfg)) | 
|  | return PTR_ERR(cfg); | 
|  |  | 
|  | /* Do not reassign resources if probe only */ | 
|  | if (!pci_has_flag(PCI_PROBE_ONLY)) | 
|  | pci_add_flags(PCI_REASSIGN_ALL_BUS); | 
|  |  | 
|  | bridge->sysdata = cfg; | 
|  | bridge->ops = (struct pci_ops *)&ops->pci_ops; | 
|  | bridge->msi_domain = true; | 
|  |  | 
|  | return pci_host_probe(bridge); | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(pci_host_common_probe); | 
|  |  | 
|  | int pci_host_common_remove(struct platform_device *pdev) | 
|  | { | 
|  | struct pci_host_bridge *bridge = platform_get_drvdata(pdev); | 
|  |  | 
|  | pci_lock_rescan_remove(); | 
|  | pci_stop_root_bus(bridge->bus); | 
|  | pci_remove_root_bus(bridge->bus); | 
|  | pci_unlock_rescan_remove(); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(pci_host_common_remove); | 
|  |  | 
|  | MODULE_LICENSE("GPL v2"); |