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(dev, gen_pci_unmap_cfg, cfg); 47 if (err) { 48 gen_pci_unmap_cfg(cfg); 49 goto err_out; 50 } 51 return cfg; 52 53 err_out: 54 pci_free_resource_list(resources); 55 return ERR_PTR(err); 56 } 57 58 int pci_host_common_probe(struct platform_device *pdev, 59 struct pci_ecam_ops *ops) 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 int ret; 66 67 bridge = devm_pci_alloc_host_bridge(dev, 0); 68 if (!bridge) 69 return -ENOMEM; 70 71 of_pci_check_probe_only(); 72 73 /* Parse and map our Configuration Space windows */ 74 cfg = gen_pci_init(dev, &resources, ops); 75 if (IS_ERR(cfg)) 76 return PTR_ERR(cfg); 77 78 /* Do not reassign resources if probe only */ 79 if (!pci_has_flag(PCI_PROBE_ONLY)) 80 pci_add_flags(PCI_REASSIGN_ALL_BUS); 81 82 list_splice_init(&resources, &bridge->windows); 83 bridge->dev.parent = dev; 84 bridge->sysdata = cfg; 85 bridge->busnr = cfg->busr.start; 86 bridge->ops = &ops->pci_ops; 87 bridge->map_irq = of_irq_parse_and_map_pci; 88 bridge->swizzle_irq = pci_common_swizzle; 89 90 ret = pci_host_probe(bridge); 91 if (ret < 0) { 92 pci_free_resource_list(&resources); 93 return ret; 94 } 95 96 platform_set_drvdata(pdev, bridge->bus); 97 return 0; 98 } 99 100 int pci_host_common_remove(struct platform_device *pdev) 101 { 102 struct pci_bus *bus = platform_get_drvdata(pdev); 103 104 pci_lock_rescan_remove(); 105 pci_stop_root_bus(bus); 106 pci_remove_root_bus(bus); 107 pci_unlock_rescan_remove(); 108 109 return 0; 110 } 111