diff options
Diffstat (limited to 'drivers/pci/controller/pci-host-common.c')
-rw-r--r-- | drivers/pci/controller/pci-host-common.c | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c new file mode 100644 index 000000000000..d8f10451f273 --- /dev/null +++ b/drivers/pci/controller/pci-host-common.c | |||
@@ -0,0 +1,118 @@ | |||
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 | } | ||