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 { 2957fc7323SHuacai Chen struct pci_config_window *cfg = bridge->bus->sysdata; 3057fc7323SHuacai Chen struct acpi_device *adev = to_acpi_device(cfg->parent); 3157fc7323SHuacai Chen struct device *bus_dev = &bridge->bus->dev; 3257fc7323SHuacai Chen 3357fc7323SHuacai Chen ACPI_COMPANION_SET(&bridge->dev, adev); 3457fc7323SHuacai Chen set_dev_node(bus_dev, pa_to_nid(cfg->res.start)); 3557fc7323SHuacai Chen 3657fc7323SHuacai Chen return 0; 3757fc7323SHuacai Chen } 3857fc7323SHuacai Chen 3957fc7323SHuacai Chen int acpi_pci_bus_find_domain_nr(struct pci_bus *bus) 4057fc7323SHuacai Chen { 4157fc7323SHuacai Chen struct pci_config_window *cfg = bus->sysdata; 4257fc7323SHuacai Chen struct acpi_device *adev = to_acpi_device(cfg->parent); 4357fc7323SHuacai Chen struct acpi_pci_root *root = acpi_driver_data(adev); 4457fc7323SHuacai Chen 4557fc7323SHuacai Chen return root->segment; 4657fc7323SHuacai Chen } 4757fc7323SHuacai Chen 4857fc7323SHuacai Chen static void acpi_release_root_info(struct acpi_pci_root_info *ci) 4957fc7323SHuacai Chen { 5057fc7323SHuacai Chen struct pci_root_info *info; 5157fc7323SHuacai Chen 5257fc7323SHuacai Chen info = container_of(ci, struct pci_root_info, common); 5357fc7323SHuacai Chen pci_ecam_free(info->cfg); 5457fc7323SHuacai Chen kfree(ci->ops); 5557fc7323SHuacai Chen kfree(info); 5657fc7323SHuacai Chen } 5757fc7323SHuacai Chen 5857fc7323SHuacai Chen static int acpi_prepare_root_resources(struct acpi_pci_root_info *ci) 5957fc7323SHuacai Chen { 6057fc7323SHuacai Chen int status; 6157fc7323SHuacai Chen struct resource_entry *entry, *tmp; 6257fc7323SHuacai Chen struct acpi_device *device = ci->bridge; 6357fc7323SHuacai Chen 6457fc7323SHuacai Chen status = acpi_pci_probe_root_resources(ci); 6557fc7323SHuacai Chen if (status > 0) { 6657fc7323SHuacai Chen resource_list_for_each_entry_safe(entry, tmp, &ci->resources) { 6757fc7323SHuacai Chen if (entry->res->flags & IORESOURCE_MEM) { 6857fc7323SHuacai Chen entry->offset = ci->root->mcfg_addr & GENMASK_ULL(63, 40); 6957fc7323SHuacai Chen entry->res->start |= entry->offset; 7057fc7323SHuacai Chen entry->res->end |= entry->offset; 7157fc7323SHuacai Chen } 7257fc7323SHuacai Chen } 7357fc7323SHuacai Chen return status; 7457fc7323SHuacai Chen } 7557fc7323SHuacai Chen 7657fc7323SHuacai Chen resource_list_for_each_entry_safe(entry, tmp, &ci->resources) { 7757fc7323SHuacai Chen dev_dbg(&device->dev, 7857fc7323SHuacai Chen "host bridge window %pR (ignored)\n", entry->res); 7957fc7323SHuacai Chen resource_list_destroy_entry(entry); 8057fc7323SHuacai Chen } 8157fc7323SHuacai Chen 8257fc7323SHuacai Chen return 0; 8357fc7323SHuacai Chen } 8457fc7323SHuacai Chen 8557fc7323SHuacai Chen /* 86*d2791341SHuacai Chen * Create a PCI config space window 87*d2791341SHuacai Chen * - reserve mem region 88*d2791341SHuacai Chen * - alloc struct pci_config_window with space for all mappings 89*d2791341SHuacai Chen * - ioremap the config space 90*d2791341SHuacai Chen */ 91*d2791341SHuacai Chen static struct pci_config_window *arch_pci_ecam_create(struct device *dev, 92*d2791341SHuacai Chen struct resource *cfgres, struct resource *busr, const struct pci_ecam_ops *ops) 93*d2791341SHuacai Chen { 94*d2791341SHuacai Chen int bsz, bus_range, err; 95*d2791341SHuacai Chen struct resource *conflict; 96*d2791341SHuacai Chen struct pci_config_window *cfg; 97*d2791341SHuacai Chen 98*d2791341SHuacai Chen if (busr->start > busr->end) 99*d2791341SHuacai Chen return ERR_PTR(-EINVAL); 100*d2791341SHuacai Chen 101*d2791341SHuacai Chen cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); 102*d2791341SHuacai Chen if (!cfg) 103*d2791341SHuacai Chen return ERR_PTR(-ENOMEM); 104*d2791341SHuacai Chen 105*d2791341SHuacai Chen cfg->parent = dev; 106*d2791341SHuacai Chen cfg->ops = ops; 107*d2791341SHuacai Chen cfg->busr.start = busr->start; 108*d2791341SHuacai Chen cfg->busr.end = busr->end; 109*d2791341SHuacai Chen cfg->busr.flags = IORESOURCE_BUS; 110*d2791341SHuacai Chen bus_range = resource_size(cfgres) >> ops->bus_shift; 111*d2791341SHuacai Chen 112*d2791341SHuacai Chen bsz = 1 << ops->bus_shift; 113*d2791341SHuacai Chen 114*d2791341SHuacai Chen cfg->res.start = cfgres->start; 115*d2791341SHuacai Chen cfg->res.end = cfgres->end; 116*d2791341SHuacai Chen cfg->res.flags = IORESOURCE_MEM | IORESOURCE_BUSY; 117*d2791341SHuacai Chen cfg->res.name = "PCI ECAM"; 118*d2791341SHuacai Chen 119*d2791341SHuacai Chen conflict = request_resource_conflict(&iomem_resource, &cfg->res); 120*d2791341SHuacai Chen if (conflict) { 121*d2791341SHuacai Chen err = -EBUSY; 122*d2791341SHuacai Chen dev_err(dev, "can't claim ECAM area %pR: address conflict with %s %pR\n", 123*d2791341SHuacai Chen &cfg->res, conflict->name, conflict); 124*d2791341SHuacai Chen goto err_exit; 125*d2791341SHuacai Chen } 126*d2791341SHuacai Chen 127*d2791341SHuacai Chen cfg->win = pci_remap_cfgspace(cfgres->start, bus_range * bsz); 128*d2791341SHuacai Chen if (!cfg->win) 129*d2791341SHuacai Chen goto err_exit_iomap; 130*d2791341SHuacai Chen 131*d2791341SHuacai Chen if (ops->init) { 132*d2791341SHuacai Chen err = ops->init(cfg); 133*d2791341SHuacai Chen if (err) 134*d2791341SHuacai Chen goto err_exit; 135*d2791341SHuacai Chen } 136*d2791341SHuacai Chen dev_info(dev, "ECAM at %pR for %pR\n", &cfg->res, &cfg->busr); 137*d2791341SHuacai Chen 138*d2791341SHuacai Chen return cfg; 139*d2791341SHuacai Chen 140*d2791341SHuacai Chen err_exit_iomap: 141*d2791341SHuacai Chen err = -ENOMEM; 142*d2791341SHuacai Chen dev_err(dev, "ECAM ioremap failed\n"); 143*d2791341SHuacai Chen err_exit: 144*d2791341SHuacai Chen pci_ecam_free(cfg); 145*d2791341SHuacai Chen return ERR_PTR(err); 146*d2791341SHuacai Chen } 147*d2791341SHuacai Chen 148*d2791341SHuacai Chen /* 14957fc7323SHuacai Chen * Lookup the bus range for the domain in MCFG, and set up config space 15057fc7323SHuacai Chen * mapping. 15157fc7323SHuacai Chen */ 15257fc7323SHuacai Chen static struct pci_config_window * 15357fc7323SHuacai Chen pci_acpi_setup_ecam_mapping(struct acpi_pci_root *root) 15457fc7323SHuacai Chen { 15557fc7323SHuacai Chen int ret, bus_shift; 15657fc7323SHuacai Chen u16 seg = root->segment; 15757fc7323SHuacai Chen struct device *dev = &root->device->dev; 15857fc7323SHuacai Chen struct resource cfgres; 15957fc7323SHuacai Chen struct resource *bus_res = &root->secondary; 16057fc7323SHuacai Chen struct pci_config_window *cfg; 16157fc7323SHuacai Chen const struct pci_ecam_ops *ecam_ops; 16257fc7323SHuacai Chen 16357fc7323SHuacai Chen ret = pci_mcfg_lookup(root, &cfgres, &ecam_ops); 16457fc7323SHuacai Chen if (ret < 0) { 16557fc7323SHuacai Chen dev_err(dev, "%04x:%pR ECAM region not found, use default value\n", seg, bus_res); 16657fc7323SHuacai Chen ecam_ops = &loongson_pci_ecam_ops; 16757fc7323SHuacai Chen root->mcfg_addr = mcfg_addr_init(0); 16857fc7323SHuacai Chen } 16957fc7323SHuacai Chen 17057fc7323SHuacai Chen bus_shift = ecam_ops->bus_shift ? : 20; 17157fc7323SHuacai Chen 172*d2791341SHuacai Chen if (bus_shift == 20) 173*d2791341SHuacai Chen cfg = pci_ecam_create(dev, &cfgres, bus_res, ecam_ops); 174*d2791341SHuacai Chen else { 17557fc7323SHuacai Chen cfgres.start = root->mcfg_addr + (bus_res->start << bus_shift); 17657fc7323SHuacai Chen cfgres.end = cfgres.start + (resource_size(bus_res) << bus_shift) - 1; 177*d2791341SHuacai Chen cfgres.end |= BIT(28) + (((PCI_CFG_SPACE_EXP_SIZE - 1) & 0xf00) << 16); 17857fc7323SHuacai Chen cfgres.flags = IORESOURCE_MEM; 179*d2791341SHuacai Chen cfg = arch_pci_ecam_create(dev, &cfgres, bus_res, ecam_ops); 180*d2791341SHuacai Chen } 18157fc7323SHuacai Chen 18257fc7323SHuacai Chen if (IS_ERR(cfg)) { 18357fc7323SHuacai Chen dev_err(dev, "%04x:%pR error %ld mapping ECAM\n", seg, bus_res, PTR_ERR(cfg)); 18457fc7323SHuacai Chen return NULL; 18557fc7323SHuacai Chen } 18657fc7323SHuacai Chen 18757fc7323SHuacai Chen return cfg; 18857fc7323SHuacai Chen } 18957fc7323SHuacai Chen 19057fc7323SHuacai Chen struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) 19157fc7323SHuacai Chen { 19257fc7323SHuacai Chen struct pci_bus *bus; 19357fc7323SHuacai Chen struct pci_root_info *info; 19457fc7323SHuacai Chen struct acpi_pci_root_ops *root_ops; 19557fc7323SHuacai Chen int domain = root->segment; 19657fc7323SHuacai Chen int busnum = root->secondary.start; 19757fc7323SHuacai Chen 19857fc7323SHuacai Chen info = kzalloc(sizeof(*info), GFP_KERNEL); 19957fc7323SHuacai Chen if (!info) { 20057fc7323SHuacai Chen pr_warn("pci_bus %04x:%02x: ignored (out of memory)\n", domain, busnum); 20157fc7323SHuacai Chen return NULL; 20257fc7323SHuacai Chen } 20357fc7323SHuacai Chen 20457fc7323SHuacai Chen root_ops = kzalloc(sizeof(*root_ops), GFP_KERNEL); 20557fc7323SHuacai Chen if (!root_ops) { 20657fc7323SHuacai Chen kfree(info); 20757fc7323SHuacai Chen return NULL; 20857fc7323SHuacai Chen } 20957fc7323SHuacai Chen 21057fc7323SHuacai Chen info->cfg = pci_acpi_setup_ecam_mapping(root); 21157fc7323SHuacai Chen if (!info->cfg) { 21257fc7323SHuacai Chen kfree(info); 21357fc7323SHuacai Chen kfree(root_ops); 21457fc7323SHuacai Chen return NULL; 21557fc7323SHuacai Chen } 21657fc7323SHuacai Chen 21757fc7323SHuacai Chen root_ops->release_info = acpi_release_root_info; 21857fc7323SHuacai Chen root_ops->prepare_resources = acpi_prepare_root_resources; 21957fc7323SHuacai Chen root_ops->pci_ops = (struct pci_ops *)&info->cfg->ops->pci_ops; 22057fc7323SHuacai Chen 22157fc7323SHuacai Chen bus = pci_find_bus(domain, busnum); 22257fc7323SHuacai Chen if (bus) { 22357fc7323SHuacai Chen memcpy(bus->sysdata, info->cfg, sizeof(struct pci_config_window)); 22457fc7323SHuacai Chen kfree(info); 22557fc7323SHuacai Chen } else { 22657fc7323SHuacai Chen struct pci_bus *child; 22757fc7323SHuacai Chen 22857fc7323SHuacai Chen bus = acpi_pci_root_create(root, root_ops, 22957fc7323SHuacai Chen &info->common, info->cfg); 23057fc7323SHuacai Chen if (!bus) { 23157fc7323SHuacai Chen kfree(info); 23257fc7323SHuacai Chen kfree(root_ops); 23357fc7323SHuacai Chen return NULL; 23457fc7323SHuacai Chen } 23557fc7323SHuacai Chen 23657fc7323SHuacai Chen pci_bus_size_bridges(bus); 23757fc7323SHuacai Chen pci_bus_assign_resources(bus); 23857fc7323SHuacai Chen list_for_each_entry(child, &bus->children, node) 23957fc7323SHuacai Chen pcie_bus_configure_settings(child); 24057fc7323SHuacai Chen } 24157fc7323SHuacai Chen 24257fc7323SHuacai Chen return bus; 24357fc7323SHuacai Chen } 244