157fc7323SHuacai Chen // SPDX-License-Identifier: GPL-2.0 257fc7323SHuacai Chen /* 357fc7323SHuacai Chen * Copyright (C) 2020-2022 Loongson Technology Corporation Limited 457fc7323SHuacai Chen */ 557fc7323SHuacai Chen #include <linux/pci.h> 657fc7323SHuacai Chen #include <linux/acpi.h> 757fc7323SHuacai Chen #include <linux/init.h> 857fc7323SHuacai Chen #include <linux/irq.h> 957fc7323SHuacai Chen #include <linux/slab.h> 1057fc7323SHuacai Chen #include <linux/pci-acpi.h> 1157fc7323SHuacai Chen #include <linux/pci-ecam.h> 1257fc7323SHuacai Chen 1357fc7323SHuacai Chen #include <asm/pci.h> 1457fc7323SHuacai Chen #include <asm/numa.h> 1557fc7323SHuacai Chen #include <asm/loongson.h> 1657fc7323SHuacai Chen 1757fc7323SHuacai Chen struct pci_root_info { 1857fc7323SHuacai Chen struct acpi_pci_root_info common; 1957fc7323SHuacai Chen struct pci_config_window *cfg; 2057fc7323SHuacai Chen }; 2157fc7323SHuacai Chen 2257fc7323SHuacai Chen void pcibios_add_bus(struct pci_bus *bus) 2357fc7323SHuacai Chen { 2457fc7323SHuacai Chen acpi_pci_add_bus(bus); 2557fc7323SHuacai Chen } 2657fc7323SHuacai Chen 2757fc7323SHuacai Chen int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge) 2857fc7323SHuacai Chen { 2988d4d957SBinbin Zhou struct acpi_device *adev = NULL; 3057fc7323SHuacai Chen struct device *bus_dev = &bridge->bus->dev; 3188d4d957SBinbin Zhou struct pci_config_window *cfg = bridge->bus->sysdata; 3288d4d957SBinbin Zhou 3388d4d957SBinbin Zhou if (!acpi_disabled) 3488d4d957SBinbin Zhou adev = to_acpi_device(cfg->parent); 3557fc7323SHuacai Chen 3657fc7323SHuacai Chen ACPI_COMPANION_SET(&bridge->dev, adev); 3757fc7323SHuacai Chen set_dev_node(bus_dev, pa_to_nid(cfg->res.start)); 3857fc7323SHuacai Chen 3957fc7323SHuacai Chen return 0; 4057fc7323SHuacai Chen } 4157fc7323SHuacai Chen 4257fc7323SHuacai Chen int acpi_pci_bus_find_domain_nr(struct pci_bus *bus) 4357fc7323SHuacai Chen { 4457fc7323SHuacai Chen struct pci_config_window *cfg = bus->sysdata; 4557fc7323SHuacai Chen struct acpi_device *adev = to_acpi_device(cfg->parent); 4657fc7323SHuacai Chen struct acpi_pci_root *root = acpi_driver_data(adev); 4757fc7323SHuacai Chen 4857fc7323SHuacai Chen return root->segment; 4957fc7323SHuacai Chen } 5057fc7323SHuacai Chen 5157fc7323SHuacai Chen static void acpi_release_root_info(struct acpi_pci_root_info *ci) 5257fc7323SHuacai Chen { 5357fc7323SHuacai Chen struct pci_root_info *info; 5457fc7323SHuacai Chen 5557fc7323SHuacai Chen info = container_of(ci, struct pci_root_info, common); 5657fc7323SHuacai Chen pci_ecam_free(info->cfg); 5757fc7323SHuacai Chen kfree(ci->ops); 5857fc7323SHuacai Chen kfree(info); 5957fc7323SHuacai Chen } 6057fc7323SHuacai Chen 6157fc7323SHuacai Chen static int acpi_prepare_root_resources(struct acpi_pci_root_info *ci) 6257fc7323SHuacai Chen { 6357fc7323SHuacai Chen int status; 6457fc7323SHuacai Chen struct resource_entry *entry, *tmp; 6557fc7323SHuacai Chen struct acpi_device *device = ci->bridge; 6657fc7323SHuacai Chen 6757fc7323SHuacai Chen status = acpi_pci_probe_root_resources(ci); 6857fc7323SHuacai Chen if (status > 0) { 6957fc7323SHuacai Chen resource_list_for_each_entry_safe(entry, tmp, &ci->resources) { 7057fc7323SHuacai Chen if (entry->res->flags & IORESOURCE_MEM) { 7157fc7323SHuacai Chen entry->offset = ci->root->mcfg_addr & GENMASK_ULL(63, 40); 7257fc7323SHuacai Chen entry->res->start |= entry->offset; 7357fc7323SHuacai Chen entry->res->end |= entry->offset; 7457fc7323SHuacai Chen } 7557fc7323SHuacai Chen } 7657fc7323SHuacai Chen return status; 7757fc7323SHuacai Chen } 7857fc7323SHuacai Chen 7957fc7323SHuacai Chen resource_list_for_each_entry_safe(entry, tmp, &ci->resources) { 8057fc7323SHuacai Chen dev_dbg(&device->dev, 8157fc7323SHuacai Chen "host bridge window %pR (ignored)\n", entry->res); 8257fc7323SHuacai Chen resource_list_destroy_entry(entry); 8357fc7323SHuacai Chen } 8457fc7323SHuacai Chen 8557fc7323SHuacai Chen return 0; 8657fc7323SHuacai Chen } 8757fc7323SHuacai Chen 8857fc7323SHuacai Chen /* 89d2791341SHuacai Chen * Create a PCI config space window 90d2791341SHuacai Chen * - reserve mem region 91d2791341SHuacai Chen * - alloc struct pci_config_window with space for all mappings 92d2791341SHuacai Chen * - ioremap the config space 93d2791341SHuacai Chen */ 94d2791341SHuacai Chen static struct pci_config_window *arch_pci_ecam_create(struct device *dev, 95d2791341SHuacai Chen struct resource *cfgres, struct resource *busr, const struct pci_ecam_ops *ops) 96d2791341SHuacai Chen { 97d2791341SHuacai Chen int bsz, bus_range, err; 98d2791341SHuacai Chen struct resource *conflict; 99d2791341SHuacai Chen struct pci_config_window *cfg; 100d2791341SHuacai Chen 101d2791341SHuacai Chen if (busr->start > busr->end) 102d2791341SHuacai Chen return ERR_PTR(-EINVAL); 103d2791341SHuacai Chen 104d2791341SHuacai Chen cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); 105d2791341SHuacai Chen if (!cfg) 106d2791341SHuacai Chen return ERR_PTR(-ENOMEM); 107d2791341SHuacai Chen 108d2791341SHuacai Chen cfg->parent = dev; 109d2791341SHuacai Chen cfg->ops = ops; 110d2791341SHuacai Chen cfg->busr.start = busr->start; 111d2791341SHuacai Chen cfg->busr.end = busr->end; 112d2791341SHuacai Chen cfg->busr.flags = IORESOURCE_BUS; 113d2791341SHuacai Chen bus_range = resource_size(cfgres) >> ops->bus_shift; 114d2791341SHuacai Chen 115d2791341SHuacai Chen bsz = 1 << ops->bus_shift; 116d2791341SHuacai Chen 117d2791341SHuacai Chen cfg->res.start = cfgres->start; 118d2791341SHuacai Chen cfg->res.end = cfgres->end; 119d2791341SHuacai Chen cfg->res.flags = IORESOURCE_MEM | IORESOURCE_BUSY; 120d2791341SHuacai Chen cfg->res.name = "PCI ECAM"; 121d2791341SHuacai Chen 122d2791341SHuacai Chen conflict = request_resource_conflict(&iomem_resource, &cfg->res); 123d2791341SHuacai Chen if (conflict) { 124d2791341SHuacai Chen err = -EBUSY; 125d2791341SHuacai Chen dev_err(dev, "can't claim ECAM area %pR: address conflict with %s %pR\n", 126d2791341SHuacai Chen &cfg->res, conflict->name, conflict); 127d2791341SHuacai Chen goto err_exit; 128d2791341SHuacai Chen } 129d2791341SHuacai Chen 130d2791341SHuacai Chen cfg->win = pci_remap_cfgspace(cfgres->start, bus_range * bsz); 131d2791341SHuacai Chen if (!cfg->win) 132d2791341SHuacai Chen goto err_exit_iomap; 133d2791341SHuacai Chen 134d2791341SHuacai Chen if (ops->init) { 135d2791341SHuacai Chen err = ops->init(cfg); 136d2791341SHuacai Chen if (err) 137d2791341SHuacai Chen goto err_exit; 138d2791341SHuacai Chen } 139d2791341SHuacai Chen dev_info(dev, "ECAM at %pR for %pR\n", &cfg->res, &cfg->busr); 140d2791341SHuacai Chen 141d2791341SHuacai Chen return cfg; 142d2791341SHuacai Chen 143d2791341SHuacai Chen err_exit_iomap: 144d2791341SHuacai Chen err = -ENOMEM; 145d2791341SHuacai Chen dev_err(dev, "ECAM ioremap failed\n"); 146d2791341SHuacai Chen err_exit: 147d2791341SHuacai Chen pci_ecam_free(cfg); 148d2791341SHuacai Chen return ERR_PTR(err); 149d2791341SHuacai Chen } 150d2791341SHuacai Chen 151d2791341SHuacai Chen /* 15257fc7323SHuacai Chen * Lookup the bus range for the domain in MCFG, and set up config space 15357fc7323SHuacai Chen * mapping. 15457fc7323SHuacai Chen */ 15557fc7323SHuacai Chen static struct pci_config_window * 15657fc7323SHuacai Chen pci_acpi_setup_ecam_mapping(struct acpi_pci_root *root) 15757fc7323SHuacai Chen { 15857fc7323SHuacai Chen int ret, bus_shift; 15957fc7323SHuacai Chen u16 seg = root->segment; 16057fc7323SHuacai Chen struct device *dev = &root->device->dev; 16157fc7323SHuacai Chen struct resource cfgres; 16257fc7323SHuacai Chen struct resource *bus_res = &root->secondary; 16357fc7323SHuacai Chen struct pci_config_window *cfg; 16457fc7323SHuacai Chen const struct pci_ecam_ops *ecam_ops; 16557fc7323SHuacai Chen 16657fc7323SHuacai Chen ret = pci_mcfg_lookup(root, &cfgres, &ecam_ops); 16757fc7323SHuacai Chen if (ret < 0) { 16857fc7323SHuacai Chen dev_err(dev, "%04x:%pR ECAM region not found, use default value\n", seg, bus_res); 16957fc7323SHuacai Chen ecam_ops = &loongson_pci_ecam_ops; 17057fc7323SHuacai Chen root->mcfg_addr = mcfg_addr_init(0); 17157fc7323SHuacai Chen } 17257fc7323SHuacai Chen 17357fc7323SHuacai Chen bus_shift = ecam_ops->bus_shift ? : 20; 17457fc7323SHuacai Chen 175d2791341SHuacai Chen if (bus_shift == 20) 176d2791341SHuacai Chen cfg = pci_ecam_create(dev, &cfgres, bus_res, ecam_ops); 177d2791341SHuacai Chen else { 17857fc7323SHuacai Chen cfgres.start = root->mcfg_addr + (bus_res->start << bus_shift); 17957fc7323SHuacai Chen cfgres.end = cfgres.start + (resource_size(bus_res) << bus_shift) - 1; 180d2791341SHuacai Chen cfgres.end |= BIT(28) + (((PCI_CFG_SPACE_EXP_SIZE - 1) & 0xf00) << 16); 18157fc7323SHuacai Chen cfgres.flags = IORESOURCE_MEM; 182d2791341SHuacai Chen cfg = arch_pci_ecam_create(dev, &cfgres, bus_res, ecam_ops); 183d2791341SHuacai Chen } 18457fc7323SHuacai Chen 18557fc7323SHuacai Chen if (IS_ERR(cfg)) { 18657fc7323SHuacai Chen dev_err(dev, "%04x:%pR error %ld mapping ECAM\n", seg, bus_res, PTR_ERR(cfg)); 18757fc7323SHuacai Chen return NULL; 18857fc7323SHuacai Chen } 18957fc7323SHuacai Chen 19057fc7323SHuacai Chen return cfg; 19157fc7323SHuacai Chen } 19257fc7323SHuacai Chen 19357fc7323SHuacai Chen struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) 19457fc7323SHuacai Chen { 19557fc7323SHuacai Chen struct pci_bus *bus; 19657fc7323SHuacai Chen struct pci_root_info *info; 19757fc7323SHuacai Chen struct acpi_pci_root_ops *root_ops; 19857fc7323SHuacai Chen int domain = root->segment; 19957fc7323SHuacai Chen int busnum = root->secondary.start; 20057fc7323SHuacai Chen 20157fc7323SHuacai Chen info = kzalloc(sizeof(*info), GFP_KERNEL); 20257fc7323SHuacai Chen if (!info) { 20357fc7323SHuacai Chen pr_warn("pci_bus %04x:%02x: ignored (out of memory)\n", domain, busnum); 20457fc7323SHuacai Chen return NULL; 20557fc7323SHuacai Chen } 20657fc7323SHuacai Chen 20757fc7323SHuacai Chen root_ops = kzalloc(sizeof(*root_ops), GFP_KERNEL); 20857fc7323SHuacai Chen if (!root_ops) { 20957fc7323SHuacai Chen kfree(info); 21057fc7323SHuacai Chen return NULL; 21157fc7323SHuacai Chen } 21257fc7323SHuacai Chen 21357fc7323SHuacai Chen info->cfg = pci_acpi_setup_ecam_mapping(root); 21457fc7323SHuacai Chen if (!info->cfg) { 21557fc7323SHuacai Chen kfree(info); 21657fc7323SHuacai Chen kfree(root_ops); 21757fc7323SHuacai Chen return NULL; 21857fc7323SHuacai Chen } 21957fc7323SHuacai Chen 22057fc7323SHuacai Chen root_ops->release_info = acpi_release_root_info; 22157fc7323SHuacai Chen root_ops->prepare_resources = acpi_prepare_root_resources; 22257fc7323SHuacai Chen root_ops->pci_ops = (struct pci_ops *)&info->cfg->ops->pci_ops; 22357fc7323SHuacai Chen 22457fc7323SHuacai Chen bus = pci_find_bus(domain, busnum); 22557fc7323SHuacai Chen if (bus) { 22657fc7323SHuacai Chen memcpy(bus->sysdata, info->cfg, sizeof(struct pci_config_window)); 22757fc7323SHuacai Chen kfree(info); 228*5016c3a3SWentao Guan kfree(root_ops); 22957fc7323SHuacai Chen } else { 23057fc7323SHuacai Chen struct pci_bus *child; 23157fc7323SHuacai Chen 23257fc7323SHuacai Chen bus = acpi_pci_root_create(root, root_ops, 23357fc7323SHuacai Chen &info->common, info->cfg); 23457fc7323SHuacai Chen if (!bus) { 23557fc7323SHuacai Chen kfree(info); 23657fc7323SHuacai Chen kfree(root_ops); 23757fc7323SHuacai Chen return NULL; 23857fc7323SHuacai Chen } 23957fc7323SHuacai Chen 24057fc7323SHuacai Chen pci_bus_size_bridges(bus); 24157fc7323SHuacai Chen pci_bus_assign_resources(bus); 24257fc7323SHuacai Chen list_for_each_entry(child, &bus->children, node) 24357fc7323SHuacai Chen pcie_bus_configure_settings(child); 24457fc7323SHuacai Chen } 24557fc7323SHuacai Chen 24657fc7323SHuacai Chen return bus; 24757fc7323SHuacai Chen } 248