1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Generic PCI host driver common code 4 * 5 * Copyright (C) 2014 ARM Limited 6 * 7 * Author: Will Deacon <will.deacon@arm.com> 8 */ 9 10 #include <linux/kernel.h> 11 #include <linux/of_address.h> 12 #include <linux/of_pci.h> 13 #include <linux/pci-ecam.h> 14 #include <linux/platform_device.h> 15 16 static void gen_pci_unmap_cfg(void *ptr) 17 { 18 pci_ecam_free((struct pci_config_window *)ptr); 19 } 20 21 static struct pci_config_window *gen_pci_init(struct device *dev, 22 struct list_head *resources, struct pci_ecam_ops *ops) 23 { 24 int err; 25 struct resource cfgres; 26 struct resource *bus_range = NULL; 27 struct pci_config_window *cfg; 28 29 /* Parse our PCI ranges and request their resources */ 30 err = pci_parse_request_of_pci_ranges(dev, resources, &bus_range); 31 if (err) 32 return ERR_PTR(err); 33 34 err = of_address_to_resource(dev->of_node, 0, &cfgres); 35 if (err) { 36 dev_err(dev, "missing \"reg\" property\n"); 37 goto err_out; 38 } 39 40 cfg = pci_ecam_create(dev, &cfgres, bus_range, ops); 41 if (IS_ERR(cfg)) { 42 err = PTR_ERR(cfg); 43 goto err_out; 44 } 45 46 err = devm_add_action_or_reset(dev, gen_pci_unmap_cfg, cfg); 47 if (err) { 48 goto err_out; 49 } 50 return cfg; 51 52 err_out: 53 pci_free_resource_list(resources); 54 return ERR_PTR(err); 55 } 56 57 int pci_host_common_probe(struct platform_device *pdev, 58 struct pci_ecam_ops *ops) 59 { 60 struct device *dev = &pdev->dev; 61 struct pci_host_bridge *bridge; 62 struct pci_config_window *cfg; 63 struct list_head resources; 64 int ret; 65 66 bridge = devm_pci_alloc_host_bridge(dev, 0); 67 if (!bridge) 68 return -ENOMEM; 69 70 of_pci_check_probe_only(); 71 72 /* Parse and map our Configuration Space windows */ 73 cfg = gen_pci_init(dev, &resources, ops); 74 if (IS_ERR(cfg)) 75 return PTR_ERR(cfg); 76 77 /* Do not reassign resources if probe only */ 78 if (!pci_has_flag(PCI_PROBE_ONLY)) 79 pci_add_flags(PCI_REASSIGN_ALL_BUS); 80 81 list_splice_init(&resources, &bridge->windows); 82 bridge->dev.parent = dev; 83 bridge->sysdata = cfg; 84 bridge->busnr = cfg->busr.start; 85 bridge->ops = &ops->pci_ops; 86 bridge->map_irq = of_irq_parse_and_map_pci; 87 bridge->swizzle_irq = pci_common_swizzle; 88 89 ret = pci_host_probe(bridge); 90 if (ret < 0) { 91 pci_free_resource_list(&resources); 92 return ret; 93 } 94 95 platform_set_drvdata(pdev, bridge->bus); 96 return 0; 97 } 98 99 int pci_host_common_remove(struct platform_device *pdev) 100 { 101 struct pci_bus *bus = platform_get_drvdata(pdev); 102 103 pci_lock_rescan_remove(); 104 pci_stop_root_bus(bus); 105 pci_remove_root_bus(bus); 106 pci_unlock_rescan_remove(); 107 108 return 0; 109 } 110