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/module.h> 12 #include <linux/of_address.h> 13 #include <linux/of_device.h> 14 #include <linux/of_pci.h> 15 #include <linux/pci-ecam.h> 16 #include <linux/platform_device.h> 17 18 static void gen_pci_unmap_cfg(void *ptr) 19 { 20 pci_ecam_free((struct pci_config_window *)ptr); 21 } 22 23 static struct pci_config_window *gen_pci_init(struct device *dev, 24 struct list_head *resources, const struct pci_ecam_ops *ops) 25 { 26 int err; 27 struct resource cfgres; 28 struct resource *bus_range = NULL; 29 struct pci_config_window *cfg; 30 31 /* Parse our PCI ranges and request their resources */ 32 err = pci_parse_request_of_pci_ranges(dev, resources, NULL, &bus_range); 33 if (err) 34 return ERR_PTR(err); 35 36 err = of_address_to_resource(dev->of_node, 0, &cfgres); 37 if (err) { 38 dev_err(dev, "missing \"reg\" property\n"); 39 goto err_out; 40 } 41 42 cfg = pci_ecam_create(dev, &cfgres, bus_range, ops); 43 if (IS_ERR(cfg)) { 44 err = PTR_ERR(cfg); 45 goto err_out; 46 } 47 48 err = devm_add_action_or_reset(dev, gen_pci_unmap_cfg, cfg); 49 if (err) { 50 goto err_out; 51 } 52 return cfg; 53 54 err_out: 55 pci_free_resource_list(resources); 56 return ERR_PTR(err); 57 } 58 59 int pci_host_common_probe(struct platform_device *pdev) 60 { 61 struct device *dev = &pdev->dev; 62 struct pci_host_bridge *bridge; 63 struct pci_config_window *cfg; 64 struct list_head resources; 65 const struct pci_ecam_ops *ops; 66 int ret; 67 68 ops = of_device_get_match_data(&pdev->dev); 69 if (!ops) 70 return -ENODEV; 71 72 bridge = devm_pci_alloc_host_bridge(dev, 0); 73 if (!bridge) 74 return -ENOMEM; 75 76 of_pci_check_probe_only(); 77 78 /* Parse and map our Configuration Space windows */ 79 cfg = gen_pci_init(dev, &resources, ops); 80 if (IS_ERR(cfg)) 81 return PTR_ERR(cfg); 82 83 /* Do not reassign resources if probe only */ 84 if (!pci_has_flag(PCI_PROBE_ONLY)) 85 pci_add_flags(PCI_REASSIGN_ALL_BUS); 86 87 list_splice_init(&resources, &bridge->windows); 88 bridge->dev.parent = dev; 89 bridge->sysdata = cfg; 90 bridge->busnr = cfg->busr.start; 91 bridge->ops = (struct pci_ops *)&ops->pci_ops; 92 bridge->map_irq = of_irq_parse_and_map_pci; 93 bridge->swizzle_irq = pci_common_swizzle; 94 95 ret = pci_host_probe(bridge); 96 if (ret < 0) { 97 pci_free_resource_list(&resources); 98 return ret; 99 } 100 101 platform_set_drvdata(pdev, bridge->bus); 102 return 0; 103 } 104 EXPORT_SYMBOL_GPL(pci_host_common_probe); 105 106 int pci_host_common_remove(struct platform_device *pdev) 107 { 108 struct pci_bus *bus = platform_get_drvdata(pdev); 109 110 pci_lock_rescan_remove(); 111 pci_stop_root_bus(bus); 112 pci_remove_root_bus(bus); 113 pci_unlock_rescan_remove(); 114 115 return 0; 116 } 117 EXPORT_SYMBOL_GPL(pci_host_common_remove); 118 119 MODULE_LICENSE("GPL v2"); 120