LoongArch: Add PCI controller support
[platform/kernel/linux-rpi.git] / arch / loongarch / pci / acpi.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
4  */
5 #include <linux/pci.h>
6 #include <linux/acpi.h>
7 #include <linux/init.h>
8 #include <linux/irq.h>
9 #include <linux/slab.h>
10 #include <linux/pci-acpi.h>
11 #include <linux/pci-ecam.h>
12
13 #include <asm/pci.h>
14 #include <asm/numa.h>
15 #include <asm/loongson.h>
16
17 struct pci_root_info {
18         struct acpi_pci_root_info common;
19         struct pci_config_window *cfg;
20 };
21
22 void pcibios_add_bus(struct pci_bus *bus)
23 {
24         acpi_pci_add_bus(bus);
25 }
26
27 int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
28 {
29         struct pci_config_window *cfg = bridge->bus->sysdata;
30         struct acpi_device *adev = to_acpi_device(cfg->parent);
31         struct device *bus_dev = &bridge->bus->dev;
32
33         ACPI_COMPANION_SET(&bridge->dev, adev);
34         set_dev_node(bus_dev, pa_to_nid(cfg->res.start));
35
36         return 0;
37 }
38
39 int acpi_pci_bus_find_domain_nr(struct pci_bus *bus)
40 {
41         struct pci_config_window *cfg = bus->sysdata;
42         struct acpi_device *adev = to_acpi_device(cfg->parent);
43         struct acpi_pci_root *root = acpi_driver_data(adev);
44
45         return root->segment;
46 }
47
48 static void acpi_release_root_info(struct acpi_pci_root_info *ci)
49 {
50         struct pci_root_info *info;
51
52         info = container_of(ci, struct pci_root_info, common);
53         pci_ecam_free(info->cfg);
54         kfree(ci->ops);
55         kfree(info);
56 }
57
58 static int acpi_prepare_root_resources(struct acpi_pci_root_info *ci)
59 {
60         int status;
61         struct resource_entry *entry, *tmp;
62         struct acpi_device *device = ci->bridge;
63
64         status = acpi_pci_probe_root_resources(ci);
65         if (status > 0) {
66                 resource_list_for_each_entry_safe(entry, tmp, &ci->resources) {
67                         if (entry->res->flags & IORESOURCE_MEM) {
68                                 entry->offset = ci->root->mcfg_addr & GENMASK_ULL(63, 40);
69                                 entry->res->start |= entry->offset;
70                                 entry->res->end   |= entry->offset;
71                         }
72                 }
73                 return status;
74         }
75
76         resource_list_for_each_entry_safe(entry, tmp, &ci->resources) {
77                 dev_dbg(&device->dev,
78                            "host bridge window %pR (ignored)\n", entry->res);
79                 resource_list_destroy_entry(entry);
80         }
81
82         return 0;
83 }
84
85 /*
86  * Lookup the bus range for the domain in MCFG, and set up config space
87  * mapping.
88  */
89 static struct pci_config_window *
90 pci_acpi_setup_ecam_mapping(struct acpi_pci_root *root)
91 {
92         int ret, bus_shift;
93         u16 seg = root->segment;
94         struct device *dev = &root->device->dev;
95         struct resource cfgres;
96         struct resource *bus_res = &root->secondary;
97         struct pci_config_window *cfg;
98         const struct pci_ecam_ops *ecam_ops;
99
100         ret = pci_mcfg_lookup(root, &cfgres, &ecam_ops);
101         if (ret < 0) {
102                 dev_err(dev, "%04x:%pR ECAM region not found, use default value\n", seg, bus_res);
103                 ecam_ops = &loongson_pci_ecam_ops;
104                 root->mcfg_addr = mcfg_addr_init(0);
105         }
106
107         bus_shift = ecam_ops->bus_shift ? : 20;
108
109         cfgres.start = root->mcfg_addr + (bus_res->start << bus_shift);
110         cfgres.end = cfgres.start + (resource_size(bus_res) << bus_shift) - 1;
111         cfgres.flags = IORESOURCE_MEM;
112
113         cfg = pci_ecam_create(dev, &cfgres, bus_res, ecam_ops);
114         if (IS_ERR(cfg)) {
115                 dev_err(dev, "%04x:%pR error %ld mapping ECAM\n", seg, bus_res, PTR_ERR(cfg));
116                 return NULL;
117         }
118
119         return cfg;
120 }
121
122 struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
123 {
124         struct pci_bus *bus;
125         struct pci_root_info *info;
126         struct acpi_pci_root_ops *root_ops;
127         int domain = root->segment;
128         int busnum = root->secondary.start;
129
130         info = kzalloc(sizeof(*info), GFP_KERNEL);
131         if (!info) {
132                 pr_warn("pci_bus %04x:%02x: ignored (out of memory)\n", domain, busnum);
133                 return NULL;
134         }
135
136         root_ops = kzalloc(sizeof(*root_ops), GFP_KERNEL);
137         if (!root_ops) {
138                 kfree(info);
139                 return NULL;
140         }
141
142         info->cfg = pci_acpi_setup_ecam_mapping(root);
143         if (!info->cfg) {
144                 kfree(info);
145                 kfree(root_ops);
146                 return NULL;
147         }
148
149         root_ops->release_info = acpi_release_root_info;
150         root_ops->prepare_resources = acpi_prepare_root_resources;
151         root_ops->pci_ops = (struct pci_ops *)&info->cfg->ops->pci_ops;
152
153         bus = pci_find_bus(domain, busnum);
154         if (bus) {
155                 memcpy(bus->sysdata, info->cfg, sizeof(struct pci_config_window));
156                 kfree(info);
157         } else {
158                 struct pci_bus *child;
159
160                 bus = acpi_pci_root_create(root, root_ops,
161                                            &info->common, info->cfg);
162                 if (!bus) {
163                         kfree(info);
164                         kfree(root_ops);
165                         return NULL;
166                 }
167
168                 pci_bus_size_bridges(bus);
169                 pci_bus_assign_resources(bus);
170                 list_for_each_entry(child, &bus->children, node)
171                         pcie_bus_configure_settings(child);
172         }
173
174         return bus;
175 }