234 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright 2016 Broadcom4 */5 6#include <linux/device.h>7#include <linux/io.h>8#include <linux/kernel.h>9#include <linux/module.h>10#include <linux/pci.h>11#include <linux/pci-ecam.h>12#include <linux/slab.h>13 14/*15 * On 64-bit systems, we do a single ioremap for the whole config space16 * since we have enough virtual address range available. On 32-bit, we17 * ioremap the config space for each bus individually.18 */19static const bool per_bus_mapping = !IS_ENABLED(CONFIG_64BIT);20 21/*22 * Create a PCI config space window23 * - reserve mem region24 * - alloc struct pci_config_window with space for all mappings25 * - ioremap the config space26 */27struct pci_config_window *pci_ecam_create(struct device *dev,28 struct resource *cfgres, struct resource *busr,29 const struct pci_ecam_ops *ops)30{31 unsigned int bus_shift = ops->bus_shift;32 struct pci_config_window *cfg;33 unsigned int bus_range, bus_range_max, bsz;34 struct resource *conflict;35 int err;36 37 if (busr->start > busr->end)38 return ERR_PTR(-EINVAL);39 40 cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);41 if (!cfg)42 return ERR_PTR(-ENOMEM);43 44 /* ECAM-compliant platforms need not supply ops->bus_shift */45 if (!bus_shift)46 bus_shift = PCIE_ECAM_BUS_SHIFT;47 48 cfg->parent = dev;49 cfg->ops = ops;50 cfg->busr.start = busr->start;51 cfg->busr.end = busr->end;52 cfg->busr.flags = IORESOURCE_BUS;53 cfg->bus_shift = bus_shift;54 bus_range = resource_size(&cfg->busr);55 bus_range_max = resource_size(cfgres) >> bus_shift;56 if (bus_range > bus_range_max) {57 bus_range = bus_range_max;58 cfg->busr.end = busr->start + bus_range - 1;59 dev_warn(dev, "ECAM area %pR can only accommodate %pR (reduced from %pR desired)\n",60 cfgres, &cfg->busr, busr);61 }62 bsz = 1 << bus_shift;63 64 cfg->res.start = cfgres->start;65 cfg->res.end = cfgres->end;66 cfg->res.flags = IORESOURCE_MEM | IORESOURCE_BUSY;67 cfg->res.name = "PCI ECAM";68 69 conflict = request_resource_conflict(&iomem_resource, &cfg->res);70 if (conflict) {71 err = -EBUSY;72 dev_err(dev, "can't claim ECAM area %pR: address conflict with %s %pR\n",73 &cfg->res, conflict->name, conflict);74 goto err_exit;75 }76 77 if (per_bus_mapping) {78 cfg->winp = kcalloc(bus_range, sizeof(*cfg->winp), GFP_KERNEL);79 if (!cfg->winp)80 goto err_exit_malloc;81 } else {82 cfg->win = pci_remap_cfgspace(cfgres->start, bus_range * bsz);83 if (!cfg->win)84 goto err_exit_iomap;85 }86 87 if (ops->init) {88 err = ops->init(cfg);89 if (err)90 goto err_exit;91 }92 dev_info(dev, "ECAM at %pR for %pR\n", &cfg->res, &cfg->busr);93 return cfg;94 95err_exit_iomap:96 dev_err(dev, "ECAM ioremap failed\n");97err_exit_malloc:98 err = -ENOMEM;99err_exit:100 pci_ecam_free(cfg);101 return ERR_PTR(err);102}103EXPORT_SYMBOL_GPL(pci_ecam_create);104 105void pci_ecam_free(struct pci_config_window *cfg)106{107 int i;108 109 if (per_bus_mapping) {110 if (cfg->winp) {111 for (i = 0; i < resource_size(&cfg->busr); i++)112 if (cfg->winp[i])113 iounmap(cfg->winp[i]);114 kfree(cfg->winp);115 }116 } else {117 if (cfg->win)118 iounmap(cfg->win);119 }120 if (cfg->res.parent)121 release_resource(&cfg->res);122 kfree(cfg);123}124EXPORT_SYMBOL_GPL(pci_ecam_free);125 126static int pci_ecam_add_bus(struct pci_bus *bus)127{128 struct pci_config_window *cfg = bus->sysdata;129 unsigned int bsz = 1 << cfg->bus_shift;130 unsigned int busn = bus->number;131 phys_addr_t start;132 133 if (!per_bus_mapping)134 return 0;135 136 if (busn < cfg->busr.start || busn > cfg->busr.end)137 return -EINVAL;138 139 busn -= cfg->busr.start;140 start = cfg->res.start + busn * bsz;141 142 cfg->winp[busn] = pci_remap_cfgspace(start, bsz);143 if (!cfg->winp[busn])144 return -ENOMEM;145 146 return 0;147}148 149static void pci_ecam_remove_bus(struct pci_bus *bus)150{151 struct pci_config_window *cfg = bus->sysdata;152 unsigned int busn = bus->number;153 154 if (!per_bus_mapping || busn < cfg->busr.start || busn > cfg->busr.end)155 return;156 157 busn -= cfg->busr.start;158 if (cfg->winp[busn]) {159 iounmap(cfg->winp[busn]);160 cfg->winp[busn] = NULL;161 }162}163 164/*165 * Function to implement the pci_ops ->map_bus method166 */167void __iomem *pci_ecam_map_bus(struct pci_bus *bus, unsigned int devfn,168 int where)169{170 struct pci_config_window *cfg = bus->sysdata;171 unsigned int bus_shift = cfg->ops->bus_shift;172 unsigned int devfn_shift = cfg->ops->bus_shift - 8;173 unsigned int busn = bus->number;174 void __iomem *base;175 u32 bus_offset, devfn_offset;176 177 if (busn < cfg->busr.start || busn > cfg->busr.end)178 return NULL;179 180 busn -= cfg->busr.start;181 if (per_bus_mapping) {182 base = cfg->winp[busn];183 busn = 0;184 } else185 base = cfg->win;186 187 if (cfg->ops->bus_shift) {188 bus_offset = (busn & PCIE_ECAM_BUS_MASK) << bus_shift;189 devfn_offset = (devfn & PCIE_ECAM_DEVFN_MASK) << devfn_shift;190 where &= PCIE_ECAM_REG_MASK;191 192 return base + (bus_offset | devfn_offset | where);193 }194 195 return base + PCIE_ECAM_OFFSET(busn, devfn, where);196}197EXPORT_SYMBOL_GPL(pci_ecam_map_bus);198 199/* ECAM ops */200const struct pci_ecam_ops pci_generic_ecam_ops = {201 .pci_ops = {202 .add_bus = pci_ecam_add_bus,203 .remove_bus = pci_ecam_remove_bus,204 .map_bus = pci_ecam_map_bus,205 .read = pci_generic_config_read,206 .write = pci_generic_config_write,207 }208};209EXPORT_SYMBOL_GPL(pci_generic_ecam_ops);210 211#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)212/* ECAM ops for 32-bit access only (non-compliant) */213const struct pci_ecam_ops pci_32b_ops = {214 .pci_ops = {215 .add_bus = pci_ecam_add_bus,216 .remove_bus = pci_ecam_remove_bus,217 .map_bus = pci_ecam_map_bus,218 .read = pci_generic_config_read32,219 .write = pci_generic_config_write32,220 }221};222 223/* ECAM ops for 32-bit read only (non-compliant) */224const struct pci_ecam_ops pci_32b_read_ops = {225 .pci_ops = {226 .add_bus = pci_ecam_add_bus,227 .remove_bus = pci_ecam_remove_bus,228 .map_bus = pci_ecam_map_bus,229 .read = pci_generic_config_read32,230 .write = pci_generic_config_write,231 }232};233#endif234