1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Code borrowed from powerpc/kernel/pci-common.c 4 * 5 * Copyright (C) 2003 Anton Blanchard <anton@au.ibm.com>, IBM 6 * Copyright (C) 2014 ARM Ltd. 7 */ 8 9 #include <linux/acpi.h> 10 #include <linux/init.h> 11 #include <linux/io.h> 12 #include <linux/kernel.h> 13 #include <linux/mm.h> 14 #include <linux/pci.h> 15 #include <linux/pci-acpi.h> 16 #include <linux/pci-ecam.h> 17 #include <linux/slab.h> 18 19 #ifdef CONFIG_ACPI 20 /* 21 * Try to assign the IRQ number when probing a new device 22 */ 23 int pcibios_alloc_irq(struct pci_dev *dev) 24 { 25 if (!acpi_disabled) 26 acpi_pci_irq_enable(dev); 27 28 return 0; 29 } 30 #endif 31 32 /* 33 * raw_pci_read/write - Platform-specific PCI config space access. 34 */ 35 int raw_pci_read(unsigned int domain, unsigned int bus, 36 unsigned int devfn, int reg, int len, u32 *val) 37 { 38 struct pci_bus *b = pci_find_bus(domain, bus); 39 40 if (!b) 41 return PCIBIOS_DEVICE_NOT_FOUND; 42 return b->ops->read(b, devfn, reg, len, val); 43 } 44 45 int raw_pci_write(unsigned int domain, unsigned int bus, 46 unsigned int devfn, int reg, int len, u32 val) 47 { 48 struct pci_bus *b = pci_find_bus(domain, bus); 49 50 if (!b) 51 return PCIBIOS_DEVICE_NOT_FOUND; 52 return b->ops->write(b, devfn, reg, len, val); 53 } 54 55 #ifdef CONFIG_NUMA 56 57 int pcibus_to_node(struct pci_bus *bus) 58 { 59 return dev_to_node(&bus->dev); 60 } 61 EXPORT_SYMBOL(pcibus_to_node); 62 63 #endif 64 65 #ifdef CONFIG_ACPI 66 67 struct acpi_pci_generic_root_info { 68 struct acpi_pci_root_info common; 69 struct pci_config_window *cfg; /* config space mapping */ 70 }; 71 72 int acpi_pci_bus_find_domain_nr(struct pci_bus *bus) 73 { 74 struct pci_config_window *cfg = bus->sysdata; 75 struct acpi_device *adev = to_acpi_device(cfg->parent); 76 struct acpi_pci_root *root = acpi_driver_data(adev); 77 78 return root->segment; 79 } 80 81 int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge) 82 { 83 struct pci_config_window *cfg; 84 struct acpi_device *adev; 85 struct device *bus_dev; 86 87 if (acpi_disabled) 88 return 0; 89 90 cfg = bridge->bus->sysdata; 91 92 /* 93 * On Hyper-V there is no corresponding ACPI device for a root bridge, 94 * therefore ->parent is set as NULL by the driver. And set 'adev' as 95 * NULL in this case because there is no proper ACPI device. 96 */ 97 if (!cfg->parent) 98 adev = NULL; 99 else 100 adev = to_acpi_device(cfg->parent); 101 102 bus_dev = &bridge->bus->dev; 103 104 ACPI_COMPANION_SET(&bridge->dev, adev); 105 set_dev_node(bus_dev, acpi_get_node(acpi_device_handle(adev))); 106 107 return 0; 108 } 109 110 static int pci_acpi_root_prepare_resources(struct acpi_pci_root_info *ci) 111 { 112 struct resource_entry *entry, *tmp; 113 int status; 114 115 status = acpi_pci_probe_root_resources(ci); 116 resource_list_for_each_entry_safe(entry, tmp, &ci->resources) { 117 if (!(entry->res->flags & IORESOURCE_WINDOW)) 118 resource_list_destroy_entry(entry); 119 } 120 return status; 121 } 122 123 /* 124 * Lookup the bus range for the domain in MCFG, and set up config space 125 * mapping. 126 */ 127 static struct pci_config_window * 128 pci_acpi_setup_ecam_mapping(struct acpi_pci_root *root) 129 { 130 struct device *dev = &root->device->dev; 131 struct resource *bus_res = &root->secondary; 132 u16 seg = root->segment; 133 const struct pci_ecam_ops *ecam_ops; 134 struct resource cfgres; 135 struct acpi_device *adev; 136 struct pci_config_window *cfg; 137 int ret; 138 139 ret = pci_mcfg_lookup(root, &cfgres, &ecam_ops); 140 if (ret) { 141 dev_err(dev, "%04x:%pR ECAM region not found\n", seg, bus_res); 142 return NULL; 143 } 144 145 adev = acpi_resource_consumer(&cfgres); 146 if (adev) 147 dev_info(dev, "ECAM area %pR reserved by %s\n", &cfgres, 148 dev_name(&adev->dev)); 149 else 150 dev_warn(dev, FW_BUG "ECAM area %pR not reserved in ACPI namespace\n", 151 &cfgres); 152 153 cfg = pci_ecam_create(dev, &cfgres, bus_res, ecam_ops); 154 if (IS_ERR(cfg)) { 155 dev_err(dev, "%04x:%pR error %ld mapping ECAM\n", seg, bus_res, 156 PTR_ERR(cfg)); 157 return NULL; 158 } 159 160 return cfg; 161 } 162 163 /* release_info: free resources allocated by init_info */ 164 static void pci_acpi_generic_release_info(struct acpi_pci_root_info *ci) 165 { 166 struct acpi_pci_generic_root_info *ri; 167 168 ri = container_of(ci, struct acpi_pci_generic_root_info, common); 169 pci_ecam_free(ri->cfg); 170 kfree(ci->ops); 171 kfree(ri); 172 } 173 174 /* Interface called from ACPI code to setup PCI host controller */ 175 struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) 176 { 177 struct acpi_pci_generic_root_info *ri; 178 struct pci_bus *bus, *child; 179 struct acpi_pci_root_ops *root_ops; 180 struct pci_host_bridge *host; 181 182 ri = kzalloc(sizeof(*ri), GFP_KERNEL); 183 if (!ri) 184 return NULL; 185 186 root_ops = kzalloc(sizeof(*root_ops), GFP_KERNEL); 187 if (!root_ops) { 188 kfree(ri); 189 return NULL; 190 } 191 192 ri->cfg = pci_acpi_setup_ecam_mapping(root); 193 if (!ri->cfg) { 194 kfree(ri); 195 kfree(root_ops); 196 return NULL; 197 } 198 199 root_ops->release_info = pci_acpi_generic_release_info; 200 root_ops->prepare_resources = pci_acpi_root_prepare_resources; 201 root_ops->pci_ops = (struct pci_ops *)&ri->cfg->ops->pci_ops; 202 bus = acpi_pci_root_create(root, root_ops, &ri->common, ri->cfg); 203 if (!bus) 204 return NULL; 205 206 /* If we must preserve the resource configuration, claim now */ 207 host = pci_find_host_bridge(bus); 208 if (host->preserve_config) 209 pci_bus_claim_resources(bus); 210 211 /* 212 * Assign whatever was left unassigned. If we didn't claim above, 213 * this will reassign everything. 214 */ 215 pci_assign_unassigned_root_bus_resources(bus); 216 217 list_for_each_entry(child, &bus->children, node) 218 pcie_bus_configure_settings(child); 219 220 return bus; 221 } 222 223 void pcibios_add_bus(struct pci_bus *bus) 224 { 225 acpi_pci_add_bus(bus); 226 } 227 228 void pcibios_remove_bus(struct pci_bus *bus) 229 { 230 acpi_pci_remove_bus(bus); 231 } 232 233 #endif 234
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.