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 const char *type; 62 struct device *dev = &pdev->dev; 63 struct device_node *np = dev->of_node; 64 struct pci_host_bridge *bridge; 65 struct pci_config_window *cfg; 66 struct list_head resources; 67 int ret; 68 69 bridge = devm_pci_alloc_host_bridge(dev, 0); 70 if (!bridge) 71 return -ENOMEM; 72 73 type = of_get_property(np, "device_type", NULL); 74 if (!type || strcmp(type, "pci")) { 75 dev_err(dev, "invalid \"device_type\" %s\n", type); 76 return -EINVAL; 77 } 78 79 of_pci_check_probe_only(); 80 81 /* Parse and map our Configuration Space windows */ 82 cfg = gen_pci_init(dev, &resources, ops); 83 if (IS_ERR(cfg)) 84 return PTR_ERR(cfg); 85 86 /* Do not reassign resources if probe only */ 87 if (!pci_has_flag(PCI_PROBE_ONLY)) 88 pci_add_flags(PCI_REASSIGN_ALL_BUS); 89 90 list_splice_init(&resources, &bridge->windows); 91 bridge->dev.parent = dev; 92 bridge->sysdata = cfg; 93 bridge->busnr = cfg->busr.start; 94 bridge->ops = &ops->pci_ops; 95 bridge->map_irq = of_irq_parse_and_map_pci; 96 bridge->swizzle_irq = pci_common_swizzle; 97 98 ret = pci_host_probe(bridge); 99 if (ret < 0) { 100 pci_free_resource_list(&resources); 101 return ret; 102 } 103 104 platform_set_drvdata(pdev, bridge->bus); 105 return 0; 106 } 107 108 int pci_host_common_remove(struct platform_device *pdev) 109 { 110 struct pci_bus *bus = platform_get_drvdata(pdev); 111 112 pci_lock_rescan_remove(); 113 pci_stop_root_bus(bus); 114 pci_remove_root_bus(bus); 115 pci_unlock_rescan_remove(); 116 117 return 0; 118 } 119