| xj | b04a402 | 2021-11-25 15:01:52 +0800 | [diff] [blame] | 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 | } |