18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2020 Loongson Technology Corporation Limited 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci#include <linux/pci.h> 78c2ecf20Sopenharmony_ci#include <linux/acpi.h> 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/irq.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/pci-acpi.h> 128c2ecf20Sopenharmony_ci#include <linux/pci-ecam.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <asm/numa.h> 158c2ecf20Sopenharmony_ci#include <asm/pci.h> 168c2ecf20Sopenharmony_ci#include <loongson.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistruct pci_root_info { 198c2ecf20Sopenharmony_ci struct acpi_pci_root_info common; 208c2ecf20Sopenharmony_ci struct pci_config_window *cfg; 218c2ecf20Sopenharmony_ci}; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_civoid pcibios_add_bus(struct pci_bus *bus) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci acpi_pci_add_bus(bus); 268c2ecf20Sopenharmony_ci} 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ciint pcibios_root_bridge_prepare(struct pci_host_bridge *bridge) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci struct pci_config_window *cfg = bridge->bus->sysdata; 318c2ecf20Sopenharmony_ci struct acpi_device *adev = to_acpi_device(cfg->parent); 328c2ecf20Sopenharmony_ci struct device *bus_dev = &bridge->bus->dev; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci ACPI_COMPANION_SET(&bridge->dev, adev); 358c2ecf20Sopenharmony_ci set_dev_node(bus_dev, pa_to_nid(cfg->res.start)); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci return 0; 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ciint acpi_pci_bus_find_domain_nr(struct pci_bus *bus) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci struct pci_config_window *cfg = bus->sysdata; 438c2ecf20Sopenharmony_ci struct acpi_device *adev = to_acpi_device(cfg->parent); 448c2ecf20Sopenharmony_ci struct acpi_pci_root *root = acpi_driver_data(adev); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci return root->segment; 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic void acpi_release_root_info(struct acpi_pci_root_info *ci) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci struct pci_root_info *info; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci info = container_of(ci, struct pci_root_info, common); 548c2ecf20Sopenharmony_ci pci_ecam_free(info->cfg); 558c2ecf20Sopenharmony_ci kfree(ci->ops); 568c2ecf20Sopenharmony_ci kfree(info); 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic int acpi_prepare_root_resources(struct acpi_pci_root_info *ci) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci struct acpi_device *device = ci->bridge; 628c2ecf20Sopenharmony_ci struct resource_entry *entry, *tmp; 638c2ecf20Sopenharmony_ci int status; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci status = acpi_pci_probe_root_resources(ci); 668c2ecf20Sopenharmony_ci if (status > 0) { 678c2ecf20Sopenharmony_ci resource_list_for_each_entry_safe(entry, tmp, &ci->resources) { 688c2ecf20Sopenharmony_ci if (entry->res->flags & IORESOURCE_MEM) { 698c2ecf20Sopenharmony_ci entry->offset = ci->root->mcfg_addr & GENMASK_ULL(63, 40); 708c2ecf20Sopenharmony_ci entry->res->start |= entry->offset; 718c2ecf20Sopenharmony_ci entry->res->end |= entry->offset; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci return status; 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci resource_list_for_each_entry_safe(entry, tmp, &ci->resources) { 788c2ecf20Sopenharmony_ci dev_printk(KERN_DEBUG, &device->dev, 798c2ecf20Sopenharmony_ci "host bridge window %pR (ignored)\n", entry->res); 808c2ecf20Sopenharmony_ci resource_list_destroy_entry(entry); 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci return 0; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/* 878c2ecf20Sopenharmony_ci * Create a PCI config space window 888c2ecf20Sopenharmony_ci * - reserve mem region 898c2ecf20Sopenharmony_ci * - alloc struct pci_config_window with space for all mappings 908c2ecf20Sopenharmony_ci * - ioremap the config space 918c2ecf20Sopenharmony_ci */ 928c2ecf20Sopenharmony_cistatic struct pci_config_window *arch_pci_ecam_create(struct device *dev, 938c2ecf20Sopenharmony_ci struct resource *cfgres, struct resource *busr, const struct pci_ecam_ops *ops) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci int err; 968c2ecf20Sopenharmony_ci unsigned int bus_range, bsz; 978c2ecf20Sopenharmony_ci struct resource *conflict; 988c2ecf20Sopenharmony_ci struct pci_config_window *cfg; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (busr->start > busr->end) 1018c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); 1048c2ecf20Sopenharmony_ci if (!cfg) 1058c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci cfg->parent = dev; 1088c2ecf20Sopenharmony_ci cfg->ops = ops; 1098c2ecf20Sopenharmony_ci cfg->busr.start = busr->start; 1108c2ecf20Sopenharmony_ci cfg->busr.end = busr->end; 1118c2ecf20Sopenharmony_ci cfg->busr.flags = IORESOURCE_BUS; 1128c2ecf20Sopenharmony_ci bus_range = resource_size(cfgres) >> ops->bus_shift; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci bsz = 1 << ops->bus_shift; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci cfg->res.start = cfgres->start; 1178c2ecf20Sopenharmony_ci cfg->res.end = cfgres->end; 1188c2ecf20Sopenharmony_ci cfg->res.flags = IORESOURCE_MEM | IORESOURCE_BUSY; 1198c2ecf20Sopenharmony_ci cfg->res.name = "PCI ECAM"; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci conflict = request_resource_conflict(&iomem_resource, &cfg->res); 1228c2ecf20Sopenharmony_ci if (conflict) { 1238c2ecf20Sopenharmony_ci err = -EBUSY; 1248c2ecf20Sopenharmony_ci dev_err(dev, "can't claim ECAM area %pR: address conflict with %s %pR\n", 1258c2ecf20Sopenharmony_ci &cfg->res, conflict->name, conflict); 1268c2ecf20Sopenharmony_ci goto err_exit; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci cfg->win = pci_remap_cfgspace(cfgres->start, bus_range * bsz); 1308c2ecf20Sopenharmony_ci if (!cfg->win) 1318c2ecf20Sopenharmony_ci goto err_exit_iomap; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (ops->init) { 1348c2ecf20Sopenharmony_ci err = ops->init(cfg); 1358c2ecf20Sopenharmony_ci if (err) 1368c2ecf20Sopenharmony_ci goto err_exit; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci dev_info(dev, "ECAM at %pR for %pR\n", &cfg->res, &cfg->busr); 1398c2ecf20Sopenharmony_ci return cfg; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cierr_exit_iomap: 1428c2ecf20Sopenharmony_ci err = -ENOMEM; 1438c2ecf20Sopenharmony_ci dev_err(dev, "ECAM ioremap failed\n"); 1448c2ecf20Sopenharmony_cierr_exit: 1458c2ecf20Sopenharmony_ci pci_ecam_free(cfg); 1468c2ecf20Sopenharmony_ci return ERR_PTR(err); 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci/* 1508c2ecf20Sopenharmony_ci * Lookup the bus range for the domain in MCFG, and set up config space 1518c2ecf20Sopenharmony_ci * mapping. 1528c2ecf20Sopenharmony_ci */ 1538c2ecf20Sopenharmony_cistatic struct pci_config_window * 1548c2ecf20Sopenharmony_cipci_acpi_setup_ecam_mapping(struct acpi_pci_root *root) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci int ret, bus_shift; 1578c2ecf20Sopenharmony_ci u16 seg = root->segment; 1588c2ecf20Sopenharmony_ci struct device *dev = &root->device->dev; 1598c2ecf20Sopenharmony_ci struct resource cfgres; 1608c2ecf20Sopenharmony_ci struct resource *bus_res = &root->secondary; 1618c2ecf20Sopenharmony_ci struct pci_config_window *cfg; 1628c2ecf20Sopenharmony_ci const struct pci_ecam_ops *ecam_ops; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci ret = pci_mcfg_lookup(root, &cfgres, &ecam_ops); 1658c2ecf20Sopenharmony_ci if (ret < 0) { 1668c2ecf20Sopenharmony_ci dev_err(dev, "%04x:%pR ECAM region not found, use default value\n", seg, bus_res); 1678c2ecf20Sopenharmony_ci ecam_ops = &loongson_pci_ecam_ops; 1688c2ecf20Sopenharmony_ci root->mcfg_addr = mcfg_addr_init(0); 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci bus_shift = ecam_ops->bus_shift ? : 20; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (bus_shift == 20) 1748c2ecf20Sopenharmony_ci cfg = pci_ecam_create(dev, &cfgres, bus_res, ecam_ops); 1758c2ecf20Sopenharmony_ci else { 1768c2ecf20Sopenharmony_ci cfgres.start = root->mcfg_addr + (bus_res->start << bus_shift); 1778c2ecf20Sopenharmony_ci cfgres.end = cfgres.start + (resource_size(bus_res) << bus_shift) - 1; 1788c2ecf20Sopenharmony_ci cfgres.end |= BIT(28) + (((PCI_CFG_SPACE_EXP_SIZE - 1) & 0xf00) << 16); 1798c2ecf20Sopenharmony_ci cfgres.flags = IORESOURCE_MEM; 1808c2ecf20Sopenharmony_ci cfg = arch_pci_ecam_create(dev, &cfgres, bus_res, ecam_ops); 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci if (IS_ERR(cfg)) { 1848c2ecf20Sopenharmony_ci dev_err(dev, "%04x:%pR error %ld mapping ECAM\n", seg, bus_res, PTR_ERR(cfg)); 1858c2ecf20Sopenharmony_ci return NULL; 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci return cfg; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistruct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct pci_bus *bus; 1948c2ecf20Sopenharmony_ci struct pci_root_info *info; 1958c2ecf20Sopenharmony_ci struct acpi_pci_root_ops *root_ops; 1968c2ecf20Sopenharmony_ci int domain = root->segment; 1978c2ecf20Sopenharmony_ci int busnum = root->secondary.start; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci info = kzalloc(sizeof(*info), GFP_KERNEL); 2008c2ecf20Sopenharmony_ci if (!info) { 2018c2ecf20Sopenharmony_ci printk(KERN_WARNING "pci_bus %04x:%02x: " 2028c2ecf20Sopenharmony_ci "ignored (out of memory)\n", domain, busnum); 2038c2ecf20Sopenharmony_ci return NULL; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci root_ops = kzalloc(sizeof(*root_ops), GFP_KERNEL); 2078c2ecf20Sopenharmony_ci if (!root_ops) { 2088c2ecf20Sopenharmony_ci kfree(info); 2098c2ecf20Sopenharmony_ci return NULL; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci info->cfg = pci_acpi_setup_ecam_mapping(root); 2138c2ecf20Sopenharmony_ci if (!info->cfg) { 2148c2ecf20Sopenharmony_ci kfree(info); 2158c2ecf20Sopenharmony_ci kfree(root_ops); 2168c2ecf20Sopenharmony_ci return NULL; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci root_ops->release_info = acpi_release_root_info; 2208c2ecf20Sopenharmony_ci root_ops->prepare_resources = acpi_prepare_root_resources; 2218c2ecf20Sopenharmony_ci root_ops->pci_ops = (struct pci_ops *)&info->cfg->ops->pci_ops; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci bus = pci_find_bus(domain, busnum); 2248c2ecf20Sopenharmony_ci if (bus) { 2258c2ecf20Sopenharmony_ci memcpy(bus->sysdata, info->cfg, sizeof(struct pci_config_window)); 2268c2ecf20Sopenharmony_ci kfree(info); 2278c2ecf20Sopenharmony_ci } else { 2288c2ecf20Sopenharmony_ci struct pci_bus *child; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci bus = acpi_pci_root_create(root, root_ops, 2318c2ecf20Sopenharmony_ci &info->common, info->cfg); 2328c2ecf20Sopenharmony_ci if (!bus) { 2338c2ecf20Sopenharmony_ci kfree(info); 2348c2ecf20Sopenharmony_ci kfree(root_ops); 2358c2ecf20Sopenharmony_ci return NULL; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci pci_bus_size_bridges(bus); 2398c2ecf20Sopenharmony_ci pci_bus_assign_resources(bus); 2408c2ecf20Sopenharmony_ci list_for_each_entry(child, &bus->children, node) 2418c2ecf20Sopenharmony_ci pcie_bus_configure_settings(child); 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci return bus; 2458c2ecf20Sopenharmony_ci} 246