Commit | Line | Data |
---|---|---|
8cfab3cf | 1 | // SPDX-License-Identifier: GPL-2.0 |
4e64dbe2 | 2 | /* |
4068bd19 PG |
3 | * Generic PCI host driver common code |
4 | * | |
4e64dbe2 DD |
5 | * Copyright (C) 2014 ARM Limited |
6 | * | |
7 | * Author: Will Deacon <will.deacon@arm.com> | |
8 | */ | |
9 | ||
10 | #include <linux/kernel.h> | |
4e64dbe2 DD |
11 | #include <linux/of_address.h> |
12 | #include <linux/of_pci.h> | |
80955f9e | 13 | #include <linux/pci-ecam.h> |
4e64dbe2 DD |
14 | #include <linux/platform_device.h> |
15 | ||
1958e717 J |
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) | |
4e64dbe2 DD |
23 | { |
24 | int err; | |
1958e717 J |
25 | struct resource cfgres; |
26 | struct resource *bus_range = NULL; | |
27 | struct pci_config_window *cfg; | |
4e64dbe2 | 28 | |
1958e717 | 29 | /* Parse our PCI ranges and request their resources */ |
3a8f77e4 | 30 | err = pci_parse_request_of_pci_ranges(dev, resources, &bus_range); |
1958e717 | 31 | if (err) |
3a8f77e4 | 32 | return ERR_PTR(err); |
1958e717 J |
33 | |
34 | err = of_address_to_resource(dev->of_node, 0, &cfgres); | |
4e64dbe2 DD |
35 | if (err) { |
36 | dev_err(dev, "missing \"reg\" property\n"); | |
1958e717 | 37 | goto err_out; |
4e64dbe2 DD |
38 | } |
39 | ||
1958e717 J |
40 | cfg = pci_ecam_create(dev, &cfgres, bus_range, ops); |
41 | if (IS_ERR(cfg)) { | |
42 | err = PTR_ERR(cfg); | |
43 | goto err_out; | |
4e64dbe2 DD |
44 | } |
45 | ||
1958e717 J |
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); | |
4e64dbe2 DD |
56 | } |
57 | ||
58 | int pci_host_common_probe(struct platform_device *pdev, | |
1958e717 | 59 | struct pci_ecam_ops *ops) |
4e64dbe2 | 60 | { |
4e64dbe2 DD |
61 | const char *type; |
62 | struct device *dev = &pdev->dev; | |
63 | struct device_node *np = dev->of_node; | |
4246a864 | 64 | struct pci_host_bridge *bridge; |
1958e717 J |
65 | struct pci_config_window *cfg; |
66 | struct list_head resources; | |
4246a864 LP |
67 | int ret; |
68 | ||
69 | bridge = devm_pci_alloc_host_bridge(dev, 0); | |
70 | if (!bridge) | |
71 | return -ENOMEM; | |
4e64dbe2 DD |
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 | ||
4e64dbe2 | 81 | /* Parse and map our Configuration Space windows */ |
1958e717 J |
82 | cfg = gen_pci_init(dev, &resources, ops); |
83 | if (IS_ERR(cfg)) | |
84 | return PTR_ERR(cfg); | |
4e64dbe2 DD |
85 | |
86 | /* Do not reassign resources if probe only */ | |
87 | if (!pci_has_flag(PCI_PROBE_ONLY)) | |
7153884c | 88 | pci_add_flags(PCI_REASSIGN_ALL_BUS); |
4e64dbe2 | 89 | |
4246a864 LP |
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; | |
6982a068 LP |
95 | bridge->map_irq = of_irq_parse_and_map_pci; |
96 | bridge->swizzle_irq = pci_common_swizzle; | |
4246a864 | 97 | |
49b8e3f3 | 98 | ret = pci_host_probe(bridge); |
4246a864 | 99 | if (ret < 0) { |
c6dd8ecf | 100 | pci_free_resource_list(&resources); |
4246a864 | 101 | return ret; |
4e64dbe2 DD |
102 | } |
103 | ||
4e64dbe2 DD |
104 | return 0; |
105 | } |