Merge branches 'pci/arm64' and 'pci/host-hv' into next
* pci/arm64: PCI, of: Move PCI I/O space management to PCI core code PCI: generic, thunder: Use generic ECAM API PCI: Provide common functions for ECAM mapping * pci/host-hv: PCI: hv: Add explicit barriers to config space access
This commit is contained in:
Коммит
e257ef55ce
|
@ -4,6 +4,7 @@
|
|||
#include <linux/ioport.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci_regs.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -673,121 +674,6 @@ const __be32 *of_get_address(struct device_node *dev, int index, u64 *size,
|
|||
}
|
||||
EXPORT_SYMBOL(of_get_address);
|
||||
|
||||
#ifdef PCI_IOBASE
|
||||
struct io_range {
|
||||
struct list_head list;
|
||||
phys_addr_t start;
|
||||
resource_size_t size;
|
||||
};
|
||||
|
||||
static LIST_HEAD(io_range_list);
|
||||
static DEFINE_SPINLOCK(io_range_lock);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Record the PCI IO range (expressed as CPU physical address + size).
|
||||
* Return a negative value if an error has occured, zero otherwise
|
||||
*/
|
||||
int __weak pci_register_io_range(phys_addr_t addr, resource_size_t size)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
#ifdef PCI_IOBASE
|
||||
struct io_range *range;
|
||||
resource_size_t allocated_size = 0;
|
||||
|
||||
/* check if the range hasn't been previously recorded */
|
||||
spin_lock(&io_range_lock);
|
||||
list_for_each_entry(range, &io_range_list, list) {
|
||||
if (addr >= range->start && addr + size <= range->start + size) {
|
||||
/* range already registered, bail out */
|
||||
goto end_register;
|
||||
}
|
||||
allocated_size += range->size;
|
||||
}
|
||||
|
||||
/* range not registed yet, check for available space */
|
||||
if (allocated_size + size - 1 > IO_SPACE_LIMIT) {
|
||||
/* if it's too big check if 64K space can be reserved */
|
||||
if (allocated_size + SZ_64K - 1 > IO_SPACE_LIMIT) {
|
||||
err = -E2BIG;
|
||||
goto end_register;
|
||||
}
|
||||
|
||||
size = SZ_64K;
|
||||
pr_warn("Requested IO range too big, new size set to 64K\n");
|
||||
}
|
||||
|
||||
/* add the range to the list */
|
||||
range = kzalloc(sizeof(*range), GFP_ATOMIC);
|
||||
if (!range) {
|
||||
err = -ENOMEM;
|
||||
goto end_register;
|
||||
}
|
||||
|
||||
range->start = addr;
|
||||
range->size = size;
|
||||
|
||||
list_add_tail(&range->list, &io_range_list);
|
||||
|
||||
end_register:
|
||||
spin_unlock(&io_range_lock);
|
||||
#endif
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
phys_addr_t pci_pio_to_address(unsigned long pio)
|
||||
{
|
||||
phys_addr_t address = (phys_addr_t)OF_BAD_ADDR;
|
||||
|
||||
#ifdef PCI_IOBASE
|
||||
struct io_range *range;
|
||||
resource_size_t allocated_size = 0;
|
||||
|
||||
if (pio > IO_SPACE_LIMIT)
|
||||
return address;
|
||||
|
||||
spin_lock(&io_range_lock);
|
||||
list_for_each_entry(range, &io_range_list, list) {
|
||||
if (pio >= allocated_size && pio < allocated_size + range->size) {
|
||||
address = range->start + pio - allocated_size;
|
||||
break;
|
||||
}
|
||||
allocated_size += range->size;
|
||||
}
|
||||
spin_unlock(&io_range_lock);
|
||||
#endif
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
unsigned long __weak pci_address_to_pio(phys_addr_t address)
|
||||
{
|
||||
#ifdef PCI_IOBASE
|
||||
struct io_range *res;
|
||||
resource_size_t offset = 0;
|
||||
unsigned long addr = -1;
|
||||
|
||||
spin_lock(&io_range_lock);
|
||||
list_for_each_entry(res, &io_range_list, list) {
|
||||
if (address >= res->start && address < res->start + res->size) {
|
||||
addr = address - res->start + offset;
|
||||
break;
|
||||
}
|
||||
offset += res->size;
|
||||
}
|
||||
spin_unlock(&io_range_lock);
|
||||
|
||||
return addr;
|
||||
#else
|
||||
if (address > IO_SPACE_LIMIT)
|
||||
return (unsigned long)-1;
|
||||
|
||||
return (unsigned long) address;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int __of_address_to_resource(struct device_node *dev,
|
||||
const __be32 *addrp, u64 size, unsigned int flags,
|
||||
const char *name, struct resource *r)
|
||||
|
|
|
@ -83,6 +83,9 @@ config HT_IRQ
|
|||
config PCI_ATS
|
||||
bool
|
||||
|
||||
config PCI_ECAM
|
||||
bool
|
||||
|
||||
config PCI_IOV
|
||||
bool "PCI IOV support"
|
||||
depends on PCI
|
||||
|
|
|
@ -55,6 +55,8 @@ obj-$(CONFIG_PCI_SYSCALL) += syscall.o
|
|||
|
||||
obj-$(CONFIG_PCI_STUB) += pci-stub.o
|
||||
|
||||
obj-$(CONFIG_PCI_ECAM) += ecam.o
|
||||
|
||||
obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o
|
||||
|
||||
obj-$(CONFIG_OF) += of.o
|
||||
|
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* Copyright 2016 Broadcom
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2, as
|
||||
* published by the Free Software Foundation (the "GPL").
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License version 2 (GPLv2) for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 (GPLv2) along with this source code.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "ecam.h"
|
||||
|
||||
/*
|
||||
* On 64-bit systems, we do a single ioremap for the whole config space
|
||||
* since we have enough virtual address range available. On 32-bit, we
|
||||
* ioremap the config space for each bus individually.
|
||||
*/
|
||||
static const bool per_bus_mapping = !config_enabled(CONFIG_64BIT);
|
||||
|
||||
/*
|
||||
* Create a PCI config space window
|
||||
* - reserve mem region
|
||||
* - alloc struct pci_config_window with space for all mappings
|
||||
* - ioremap the config space
|
||||
*/
|
||||
struct pci_config_window *pci_ecam_create(struct device *dev,
|
||||
struct resource *cfgres, struct resource *busr,
|
||||
struct pci_ecam_ops *ops)
|
||||
{
|
||||
struct pci_config_window *cfg;
|
||||
unsigned int bus_range, bus_range_max, bsz;
|
||||
struct resource *conflict;
|
||||
int i, err;
|
||||
|
||||
if (busr->start > busr->end)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
|
||||
if (!cfg)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
cfg->ops = ops;
|
||||
cfg->busr.start = busr->start;
|
||||
cfg->busr.end = busr->end;
|
||||
cfg->busr.flags = IORESOURCE_BUS;
|
||||
bus_range = resource_size(&cfg->busr);
|
||||
bus_range_max = resource_size(cfgres) >> ops->bus_shift;
|
||||
if (bus_range > bus_range_max) {
|
||||
bus_range = bus_range_max;
|
||||
cfg->busr.end = busr->start + bus_range - 1;
|
||||
dev_warn(dev, "ECAM area %pR can only accommodate %pR (reduced from %pR desired)\n",
|
||||
cfgres, &cfg->busr, busr);
|
||||
}
|
||||
bsz = 1 << ops->bus_shift;
|
||||
|
||||
cfg->res.start = cfgres->start;
|
||||
cfg->res.end = cfgres->end;
|
||||
cfg->res.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
|
||||
cfg->res.name = "PCI ECAM";
|
||||
|
||||
conflict = request_resource_conflict(&iomem_resource, &cfg->res);
|
||||
if (conflict) {
|
||||
err = -EBUSY;
|
||||
dev_err(dev, "can't claim ECAM area %pR: address conflict with %s %pR\n",
|
||||
&cfg->res, conflict->name, conflict);
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
if (per_bus_mapping) {
|
||||
cfg->winp = kcalloc(bus_range, sizeof(*cfg->winp), GFP_KERNEL);
|
||||
if (!cfg->winp)
|
||||
goto err_exit_malloc;
|
||||
for (i = 0; i < bus_range; i++) {
|
||||
cfg->winp[i] = ioremap(cfgres->start + i * bsz, bsz);
|
||||
if (!cfg->winp[i])
|
||||
goto err_exit_iomap;
|
||||
}
|
||||
} else {
|
||||
cfg->win = ioremap(cfgres->start, bus_range * bsz);
|
||||
if (!cfg->win)
|
||||
goto err_exit_iomap;
|
||||
}
|
||||
|
||||
if (ops->init) {
|
||||
err = ops->init(dev, cfg);
|
||||
if (err)
|
||||
goto err_exit;
|
||||
}
|
||||
dev_info(dev, "ECAM at %pR for %pR\n", &cfg->res, &cfg->busr);
|
||||
return cfg;
|
||||
|
||||
err_exit_iomap:
|
||||
dev_err(dev, "ECAM ioremap failed\n");
|
||||
err_exit_malloc:
|
||||
err = -ENOMEM;
|
||||
err_exit:
|
||||
pci_ecam_free(cfg);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
void pci_ecam_free(struct pci_config_window *cfg)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (per_bus_mapping) {
|
||||
if (cfg->winp) {
|
||||
for (i = 0; i < resource_size(&cfg->busr); i++)
|
||||
if (cfg->winp[i])
|
||||
iounmap(cfg->winp[i]);
|
||||
kfree(cfg->winp);
|
||||
}
|
||||
} else {
|
||||
if (cfg->win)
|
||||
iounmap(cfg->win);
|
||||
}
|
||||
if (cfg->res.parent)
|
||||
release_resource(&cfg->res);
|
||||
kfree(cfg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function to implement the pci_ops ->map_bus method
|
||||
*/
|
||||
void __iomem *pci_ecam_map_bus(struct pci_bus *bus, unsigned int devfn,
|
||||
int where)
|
||||
{
|
||||
struct pci_config_window *cfg = bus->sysdata;
|
||||
unsigned int devfn_shift = cfg->ops->bus_shift - 8;
|
||||
unsigned int busn = bus->number;
|
||||
void __iomem *base;
|
||||
|
||||
if (busn < cfg->busr.start || busn > cfg->busr.end)
|
||||
return NULL;
|
||||
|
||||
busn -= cfg->busr.start;
|
||||
if (per_bus_mapping)
|
||||
base = cfg->winp[busn];
|
||||
else
|
||||
base = cfg->win + (busn << cfg->ops->bus_shift);
|
||||
return base + (devfn << devfn_shift) + where;
|
||||
}
|
||||
|
||||
/* ECAM ops */
|
||||
struct pci_ecam_ops pci_generic_ecam_ops = {
|
||||
.bus_shift = 20,
|
||||
.pci_ops = {
|
||||
.map_bus = pci_ecam_map_bus,
|
||||
.read = pci_generic_config_read,
|
||||
.write = pci_generic_config_write,
|
||||
}
|
||||
};
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright 2016 Broadcom
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2, as
|
||||
* published by the Free Software Foundation (the "GPL").
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License version 2 (GPLv2) for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 (GPLv2) along with this source code.
|
||||
*/
|
||||
#ifndef DRIVERS_PCI_ECAM_H
|
||||
#define DRIVERS_PCI_ECAM_H
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
/*
|
||||
* struct to hold pci ops and bus shift of the config window
|
||||
* for a PCI controller.
|
||||
*/
|
||||
struct pci_config_window;
|
||||
struct pci_ecam_ops {
|
||||
unsigned int bus_shift;
|
||||
struct pci_ops pci_ops;
|
||||
int (*init)(struct device *,
|
||||
struct pci_config_window *);
|
||||
};
|
||||
|
||||
/*
|
||||
* struct to hold the mappings of a config space window. This
|
||||
* is expected to be used as sysdata for PCI controllers that
|
||||
* use ECAM.
|
||||
*/
|
||||
struct pci_config_window {
|
||||
struct resource res;
|
||||
struct resource busr;
|
||||
void *priv;
|
||||
struct pci_ecam_ops *ops;
|
||||
union {
|
||||
void __iomem *win; /* 64-bit single mapping */
|
||||
void __iomem **winp; /* 32-bit per-bus mapping */
|
||||
};
|
||||
};
|
||||
|
||||
/* create and free pci_config_window */
|
||||
struct pci_config_window *pci_ecam_create(struct device *dev,
|
||||
struct resource *cfgres, struct resource *busr,
|
||||
struct pci_ecam_ops *ops);
|
||||
void pci_ecam_free(struct pci_config_window *cfg);
|
||||
|
||||
/* map_bus when ->sysdata is an instance of pci_config_window */
|
||||
void __iomem *pci_ecam_map_bus(struct pci_bus *bus, unsigned int devfn,
|
||||
int where);
|
||||
/* default ECAM ops */
|
||||
extern struct pci_ecam_ops pci_generic_ecam_ops;
|
||||
|
||||
#ifdef CONFIG_PCI_HOST_GENERIC
|
||||
/* for DT-based PCI controllers that support ECAM */
|
||||
int pci_host_common_probe(struct platform_device *pdev,
|
||||
struct pci_ecam_ops *ops);
|
||||
#endif
|
||||
#endif
|
|
@ -79,6 +79,7 @@ config PCI_RCAR_GEN2_PCIE
|
|||
|
||||
config PCI_HOST_COMMON
|
||||
bool
|
||||
select PCI_ECAM
|
||||
|
||||
config PCI_HOST_GENERIC
|
||||
bool "Generic PCI host controller"
|
||||
|
|
|
@ -22,27 +22,21 @@
|
|||
#include <linux/of_pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "pci-host-common.h"
|
||||
#include "../ecam.h"
|
||||
|
||||
static void gen_pci_release_of_pci_ranges(struct gen_pci *pci)
|
||||
{
|
||||
pci_free_resource_list(&pci->resources);
|
||||
}
|
||||
|
||||
static int gen_pci_parse_request_of_pci_ranges(struct gen_pci *pci)
|
||||
static int gen_pci_parse_request_of_pci_ranges(struct device *dev,
|
||||
struct list_head *resources, struct resource **bus_range)
|
||||
{
|
||||
int err, res_valid = 0;
|
||||
struct device *dev = pci->host.dev.parent;
|
||||
struct device_node *np = dev->of_node;
|
||||
resource_size_t iobase;
|
||||
struct resource_entry *win;
|
||||
|
||||
err = of_pci_get_host_bridge_resources(np, 0, 0xff, &pci->resources,
|
||||
&iobase);
|
||||
err = of_pci_get_host_bridge_resources(np, 0, 0xff, resources, &iobase);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
resource_list_for_each_entry(win, &pci->resources) {
|
||||
resource_list_for_each_entry(win, resources) {
|
||||
struct resource *parent, *res = win->res;
|
||||
|
||||
switch (resource_type(res)) {
|
||||
|
@ -60,7 +54,7 @@ static int gen_pci_parse_request_of_pci_ranges(struct gen_pci *pci)
|
|||
res_valid |= !(res->flags & IORESOURCE_PREFETCH);
|
||||
break;
|
||||
case IORESOURCE_BUS:
|
||||
pci->cfg.bus_range = res;
|
||||
*bus_range = res;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
@ -79,65 +73,60 @@ static int gen_pci_parse_request_of_pci_ranges(struct gen_pci *pci)
|
|||
return 0;
|
||||
|
||||
out_release_res:
|
||||
gen_pci_release_of_pci_ranges(pci);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci)
|
||||
static void gen_pci_unmap_cfg(void *ptr)
|
||||
{
|
||||
pci_ecam_free((struct pci_config_window *)ptr);
|
||||
}
|
||||
|
||||
static struct pci_config_window *gen_pci_init(struct device *dev,
|
||||
struct list_head *resources, struct pci_ecam_ops *ops)
|
||||
{
|
||||
int err;
|
||||
u8 bus_max;
|
||||
resource_size_t busn;
|
||||
struct resource *bus_range;
|
||||
struct device *dev = pci->host.dev.parent;
|
||||
struct device_node *np = dev->of_node;
|
||||
u32 sz = 1 << pci->cfg.ops->bus_shift;
|
||||
struct resource cfgres;
|
||||
struct resource *bus_range = NULL;
|
||||
struct pci_config_window *cfg;
|
||||
|
||||
err = of_address_to_resource(np, 0, &pci->cfg.res);
|
||||
/* Parse our PCI ranges and request their resources */
|
||||
err = gen_pci_parse_request_of_pci_ranges(dev, resources, &bus_range);
|
||||
if (err)
|
||||
goto err_out;
|
||||
|
||||
err = of_address_to_resource(dev->of_node, 0, &cfgres);
|
||||
if (err) {
|
||||
dev_err(dev, "missing \"reg\" property\n");
|
||||
return err;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
/* Limit the bus-range to fit within reg */
|
||||
bus_max = pci->cfg.bus_range->start +
|
||||
(resource_size(&pci->cfg.res) >> pci->cfg.ops->bus_shift) - 1;
|
||||
pci->cfg.bus_range->end = min_t(resource_size_t,
|
||||
pci->cfg.bus_range->end, bus_max);
|
||||
|
||||
pci->cfg.win = devm_kcalloc(dev, resource_size(pci->cfg.bus_range),
|
||||
sizeof(*pci->cfg.win), GFP_KERNEL);
|
||||
if (!pci->cfg.win)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Map our Configuration Space windows */
|
||||
if (!devm_request_mem_region(dev, pci->cfg.res.start,
|
||||
resource_size(&pci->cfg.res),
|
||||
"Configuration Space"))
|
||||
return -ENOMEM;
|
||||
|
||||
bus_range = pci->cfg.bus_range;
|
||||
for (busn = bus_range->start; busn <= bus_range->end; ++busn) {
|
||||
u32 idx = busn - bus_range->start;
|
||||
|
||||
pci->cfg.win[idx] = devm_ioremap(dev,
|
||||
pci->cfg.res.start + idx * sz,
|
||||
sz);
|
||||
if (!pci->cfg.win[idx])
|
||||
return -ENOMEM;
|
||||
cfg = pci_ecam_create(dev, &cfgres, bus_range, ops);
|
||||
if (IS_ERR(cfg)) {
|
||||
err = PTR_ERR(cfg);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err = devm_add_action(dev, gen_pci_unmap_cfg, cfg);
|
||||
if (err) {
|
||||
gen_pci_unmap_cfg(cfg);
|
||||
goto err_out;
|
||||
}
|
||||
return cfg;
|
||||
|
||||
err_out:
|
||||
pci_free_resource_list(resources);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
int pci_host_common_probe(struct platform_device *pdev,
|
||||
struct gen_pci *pci)
|
||||
struct pci_ecam_ops *ops)
|
||||
{
|
||||
int err;
|
||||
const char *type;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct pci_bus *bus, *child;
|
||||
struct pci_config_window *cfg;
|
||||
struct list_head resources;
|
||||
|
||||
type = of_get_property(np, "device_type", NULL);
|
||||
if (!type || strcmp(type, "pci")) {
|
||||
|
@ -147,29 +136,18 @@ int pci_host_common_probe(struct platform_device *pdev,
|
|||
|
||||
of_pci_check_probe_only();
|
||||
|
||||
pci->host.dev.parent = dev;
|
||||
INIT_LIST_HEAD(&pci->host.windows);
|
||||
INIT_LIST_HEAD(&pci->resources);
|
||||
|
||||
/* Parse our PCI ranges and request their resources */
|
||||
err = gen_pci_parse_request_of_pci_ranges(pci);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Parse and map our Configuration Space windows */
|
||||
err = gen_pci_parse_map_cfg_windows(pci);
|
||||
if (err) {
|
||||
gen_pci_release_of_pci_ranges(pci);
|
||||
return err;
|
||||
}
|
||||
INIT_LIST_HEAD(&resources);
|
||||
cfg = gen_pci_init(dev, &resources, ops);
|
||||
if (IS_ERR(cfg))
|
||||
return PTR_ERR(cfg);
|
||||
|
||||
/* Do not reassign resources if probe only */
|
||||
if (!pci_has_flag(PCI_PROBE_ONLY))
|
||||
pci_add_flags(PCI_REASSIGN_ALL_RSRC | PCI_REASSIGN_ALL_BUS);
|
||||
|
||||
|
||||
bus = pci_scan_root_bus(dev, pci->cfg.bus_range->start,
|
||||
&pci->cfg.ops->ops, pci, &pci->resources);
|
||||
bus = pci_scan_root_bus(dev, cfg->busr.start, &ops->pci_ops, cfg,
|
||||
&resources);
|
||||
if (!bus) {
|
||||
dev_err(dev, "Scanning rootbus failed");
|
||||
return -ENODEV;
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright (C) 2014 ARM Limited
|
||||
*
|
||||
* Author: Will Deacon <will.deacon@arm.com>
|
||||
*/
|
||||
|
||||
#ifndef _PCI_HOST_COMMON_H
|
||||
#define _PCI_HOST_COMMON_H
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
struct gen_pci_cfg_bus_ops {
|
||||
u32 bus_shift;
|
||||
struct pci_ops ops;
|
||||
};
|
||||
|
||||
struct gen_pci_cfg_windows {
|
||||
struct resource res;
|
||||
struct resource *bus_range;
|
||||
void __iomem **win;
|
||||
|
||||
struct gen_pci_cfg_bus_ops *ops;
|
||||
};
|
||||
|
||||
struct gen_pci {
|
||||
struct pci_host_bridge host;
|
||||
struct gen_pci_cfg_windows cfg;
|
||||
struct list_head resources;
|
||||
};
|
||||
|
||||
int pci_host_common_probe(struct platform_device *pdev,
|
||||
struct gen_pci *pci);
|
||||
|
||||
#endif /* _PCI_HOST_COMMON_H */
|
|
@ -25,41 +25,12 @@
|
|||
#include <linux/of_pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "pci-host-common.h"
|
||||
#include "../ecam.h"
|
||||
|
||||
static void __iomem *gen_pci_map_cfg_bus_cam(struct pci_bus *bus,
|
||||
unsigned int devfn,
|
||||
int where)
|
||||
{
|
||||
struct gen_pci *pci = bus->sysdata;
|
||||
resource_size_t idx = bus->number - pci->cfg.bus_range->start;
|
||||
|
||||
return pci->cfg.win[idx] + ((devfn << 8) | where);
|
||||
}
|
||||
|
||||
static struct gen_pci_cfg_bus_ops gen_pci_cfg_cam_bus_ops = {
|
||||
static struct pci_ecam_ops gen_pci_cfg_cam_bus_ops = {
|
||||
.bus_shift = 16,
|
||||
.ops = {
|
||||
.map_bus = gen_pci_map_cfg_bus_cam,
|
||||
.read = pci_generic_config_read,
|
||||
.write = pci_generic_config_write,
|
||||
}
|
||||
};
|
||||
|
||||
static void __iomem *gen_pci_map_cfg_bus_ecam(struct pci_bus *bus,
|
||||
unsigned int devfn,
|
||||
int where)
|
||||
{
|
||||
struct gen_pci *pci = bus->sysdata;
|
||||
resource_size_t idx = bus->number - pci->cfg.bus_range->start;
|
||||
|
||||
return pci->cfg.win[idx] + ((devfn << 12) | where);
|
||||
}
|
||||
|
||||
static struct gen_pci_cfg_bus_ops gen_pci_cfg_ecam_bus_ops = {
|
||||
.bus_shift = 20,
|
||||
.ops = {
|
||||
.map_bus = gen_pci_map_cfg_bus_ecam,
|
||||
.pci_ops = {
|
||||
.map_bus = pci_ecam_map_bus,
|
||||
.read = pci_generic_config_read,
|
||||
.write = pci_generic_config_write,
|
||||
}
|
||||
|
@ -70,25 +41,22 @@ static const struct of_device_id gen_pci_of_match[] = {
|
|||
.data = &gen_pci_cfg_cam_bus_ops },
|
||||
|
||||
{ .compatible = "pci-host-ecam-generic",
|
||||
.data = &gen_pci_cfg_ecam_bus_ops },
|
||||
.data = &pci_generic_ecam_ops },
|
||||
|
||||
{ },
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, gen_pci_of_match);
|
||||
|
||||
static int gen_pci_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
const struct of_device_id *of_id;
|
||||
struct gen_pci *pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
|
||||
struct pci_ecam_ops *ops;
|
||||
|
||||
if (!pci)
|
||||
return -ENOMEM;
|
||||
of_id = of_match_node(gen_pci_of_match, pdev->dev.of_node);
|
||||
ops = (struct pci_ecam_ops *)of_id->data;
|
||||
|
||||
of_id = of_match_node(gen_pci_of_match, dev->of_node);
|
||||
pci->cfg.ops = (struct gen_pci_cfg_bus_ops *)of_id->data;
|
||||
|
||||
return pci_host_common_probe(pdev, pci);
|
||||
return pci_host_common_probe(pdev, ops);
|
||||
}
|
||||
|
||||
static struct platform_driver gen_pci_driver = {
|
||||
|
|
|
@ -553,6 +553,8 @@ static void _hv_pcifront_read_config(struct hv_pci_dev *hpdev, int where,
|
|||
spin_lock_irqsave(&hpdev->hbus->config_lock, flags);
|
||||
/* Choose the function to be read. (See comment above) */
|
||||
writel(hpdev->desc.win_slot.slot, hpdev->hbus->cfg_addr);
|
||||
/* Make sure the function was chosen before we start reading. */
|
||||
mb();
|
||||
/* Read from that function's config space. */
|
||||
switch (size) {
|
||||
case 1:
|
||||
|
@ -565,6 +567,11 @@ static void _hv_pcifront_read_config(struct hv_pci_dev *hpdev, int where,
|
|||
*val = readl(addr);
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* Make sure the write was done before we release the spinlock
|
||||
* allowing consecutive reads/writes.
|
||||
*/
|
||||
mb();
|
||||
spin_unlock_irqrestore(&hpdev->hbus->config_lock, flags);
|
||||
} else {
|
||||
dev_err(&hpdev->hbus->hdev->device,
|
||||
|
@ -592,6 +599,8 @@ static void _hv_pcifront_write_config(struct hv_pci_dev *hpdev, int where,
|
|||
spin_lock_irqsave(&hpdev->hbus->config_lock, flags);
|
||||
/* Choose the function to be written. (See comment above) */
|
||||
writel(hpdev->desc.win_slot.slot, hpdev->hbus->cfg_addr);
|
||||
/* Make sure the function was chosen before we start writing. */
|
||||
wmb();
|
||||
/* Write to that function's config space. */
|
||||
switch (size) {
|
||||
case 1:
|
||||
|
@ -604,6 +613,11 @@ static void _hv_pcifront_write_config(struct hv_pci_dev *hpdev, int where,
|
|||
writel(val, addr);
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* Make sure the write was done before we release the spinlock
|
||||
* allowing consecutive reads/writes.
|
||||
*/
|
||||
mb();
|
||||
spin_unlock_irqrestore(&hpdev->hbus->config_lock, flags);
|
||||
} else {
|
||||
dev_err(&hpdev->hbus->hdev->device,
|
||||
|
|
|
@ -13,18 +13,7 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "pci-host-common.h"
|
||||
|
||||
/* Mapping is standard ECAM */
|
||||
static void __iomem *thunder_ecam_map_bus(struct pci_bus *bus,
|
||||
unsigned int devfn,
|
||||
int where)
|
||||
{
|
||||
struct gen_pci *pci = bus->sysdata;
|
||||
resource_size_t idx = bus->number - pci->cfg.bus_range->start;
|
||||
|
||||
return pci->cfg.win[idx] + ((devfn << 12) | where);
|
||||
}
|
||||
#include "../ecam.h"
|
||||
|
||||
static void set_val(u32 v, int where, int size, u32 *val)
|
||||
{
|
||||
|
@ -99,7 +88,7 @@ static int handle_ea_bar(u32 e0, int bar, struct pci_bus *bus,
|
|||
static int thunder_ecam_p2_config_read(struct pci_bus *bus, unsigned int devfn,
|
||||
int where, int size, u32 *val)
|
||||
{
|
||||
struct gen_pci *pci = bus->sysdata;
|
||||
struct pci_config_window *cfg = bus->sysdata;
|
||||
int where_a = where & ~3;
|
||||
void __iomem *addr;
|
||||
u32 node_bits;
|
||||
|
@ -129,7 +118,7 @@ static int thunder_ecam_p2_config_read(struct pci_bus *bus, unsigned int devfn,
|
|||
* the config space access window. Since we are working with
|
||||
* the high-order 32 bits, shift everything down by 32 bits.
|
||||
*/
|
||||
node_bits = (pci->cfg.res.start >> 32) & (1 << 12);
|
||||
node_bits = (cfg->res.start >> 32) & (1 << 12);
|
||||
|
||||
v |= node_bits;
|
||||
set_val(v, where, size, val);
|
||||
|
@ -358,36 +347,24 @@ static int thunder_ecam_config_write(struct pci_bus *bus, unsigned int devfn,
|
|||
return pci_generic_config_write(bus, devfn, where, size, val);
|
||||
}
|
||||
|
||||
static struct gen_pci_cfg_bus_ops thunder_ecam_bus_ops = {
|
||||
static struct pci_ecam_ops pci_thunder_ecam_ops = {
|
||||
.bus_shift = 20,
|
||||
.ops = {
|
||||
.map_bus = thunder_ecam_map_bus,
|
||||
.pci_ops = {
|
||||
.map_bus = pci_ecam_map_bus,
|
||||
.read = thunder_ecam_config_read,
|
||||
.write = thunder_ecam_config_write,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct of_device_id thunder_ecam_of_match[] = {
|
||||
{ .compatible = "cavium,pci-host-thunder-ecam",
|
||||
.data = &thunder_ecam_bus_ops },
|
||||
|
||||
{ .compatible = "cavium,pci-host-thunder-ecam" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, thunder_ecam_of_match);
|
||||
|
||||
static int thunder_ecam_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
const struct of_device_id *of_id;
|
||||
struct gen_pci *pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
|
||||
|
||||
if (!pci)
|
||||
return -ENOMEM;
|
||||
|
||||
of_id = of_match_node(thunder_ecam_of_match, dev->of_node);
|
||||
pci->cfg.ops = (struct gen_pci_cfg_bus_ops *)of_id->data;
|
||||
|
||||
return pci_host_common_probe(pdev, pci);
|
||||
return pci_host_common_probe(pdev, &pci_thunder_ecam_ops);
|
||||
}
|
||||
|
||||
static struct platform_driver thunder_ecam_driver = {
|
||||
|
|
|
@ -20,34 +20,22 @@
|
|||
#include <linux/of_pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "pci-host-common.h"
|
||||
#include "../ecam.h"
|
||||
|
||||
#define PEM_CFG_WR 0x28
|
||||
#define PEM_CFG_RD 0x30
|
||||
|
||||
struct thunder_pem_pci {
|
||||
struct gen_pci gen_pci;
|
||||
u32 ea_entry[3];
|
||||
void __iomem *pem_reg_base;
|
||||
};
|
||||
|
||||
static void __iomem *thunder_pem_map_bus(struct pci_bus *bus,
|
||||
unsigned int devfn, int where)
|
||||
{
|
||||
struct gen_pci *pci = bus->sysdata;
|
||||
resource_size_t idx = bus->number - pci->cfg.bus_range->start;
|
||||
|
||||
return pci->cfg.win[idx] + ((devfn << 16) | where);
|
||||
}
|
||||
|
||||
static int thunder_pem_bridge_read(struct pci_bus *bus, unsigned int devfn,
|
||||
int where, int size, u32 *val)
|
||||
{
|
||||
u64 read_val;
|
||||
struct thunder_pem_pci *pem_pci;
|
||||
struct gen_pci *pci = bus->sysdata;
|
||||
|
||||
pem_pci = container_of(pci, struct thunder_pem_pci, gen_pci);
|
||||
struct pci_config_window *cfg = bus->sysdata;
|
||||
struct thunder_pem_pci *pem_pci = (struct thunder_pem_pci *)cfg->priv;
|
||||
|
||||
if (devfn != 0 || where >= 2048) {
|
||||
*val = ~0;
|
||||
|
@ -132,17 +120,17 @@ static int thunder_pem_bridge_read(struct pci_bus *bus, unsigned int devfn,
|
|||
static int thunder_pem_config_read(struct pci_bus *bus, unsigned int devfn,
|
||||
int where, int size, u32 *val)
|
||||
{
|
||||
struct gen_pci *pci = bus->sysdata;
|
||||
struct pci_config_window *cfg = bus->sysdata;
|
||||
|
||||
if (bus->number < pci->cfg.bus_range->start ||
|
||||
bus->number > pci->cfg.bus_range->end)
|
||||
if (bus->number < cfg->busr.start ||
|
||||
bus->number > cfg->busr.end)
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
|
||||
/*
|
||||
* The first device on the bus is the PEM PCIe bridge.
|
||||
* Special case its config access.
|
||||
*/
|
||||
if (bus->number == pci->cfg.bus_range->start)
|
||||
if (bus->number == cfg->busr.start)
|
||||
return thunder_pem_bridge_read(bus, devfn, where, size, val);
|
||||
|
||||
return pci_generic_config_read(bus, devfn, where, size, val);
|
||||
|
@ -208,13 +196,12 @@ static u32 thunder_pem_bridge_w1_bits(u64 where_aligned)
|
|||
static int thunder_pem_bridge_write(struct pci_bus *bus, unsigned int devfn,
|
||||
int where, int size, u32 val)
|
||||
{
|
||||
struct gen_pci *pci = bus->sysdata;
|
||||
struct thunder_pem_pci *pem_pci;
|
||||
struct pci_config_window *cfg = bus->sysdata;
|
||||
struct thunder_pem_pci *pem_pci = (struct thunder_pem_pci *)cfg->priv;
|
||||
u64 write_val, read_val;
|
||||
u64 where_aligned = where & ~3ull;
|
||||
u32 mask = 0;
|
||||
|
||||
pem_pci = container_of(pci, struct thunder_pem_pci, gen_pci);
|
||||
|
||||
if (devfn != 0 || where >= 2048)
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
|
@ -282,53 +269,38 @@ static int thunder_pem_bridge_write(struct pci_bus *bus, unsigned int devfn,
|
|||
static int thunder_pem_config_write(struct pci_bus *bus, unsigned int devfn,
|
||||
int where, int size, u32 val)
|
||||
{
|
||||
struct gen_pci *pci = bus->sysdata;
|
||||
struct pci_config_window *cfg = bus->sysdata;
|
||||
|
||||
if (bus->number < pci->cfg.bus_range->start ||
|
||||
bus->number > pci->cfg.bus_range->end)
|
||||
if (bus->number < cfg->busr.start ||
|
||||
bus->number > cfg->busr.end)
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
/*
|
||||
* The first device on the bus is the PEM PCIe bridge.
|
||||
* Special case its config access.
|
||||
*/
|
||||
if (bus->number == pci->cfg.bus_range->start)
|
||||
if (bus->number == cfg->busr.start)
|
||||
return thunder_pem_bridge_write(bus, devfn, where, size, val);
|
||||
|
||||
|
||||
return pci_generic_config_write(bus, devfn, where, size, val);
|
||||
}
|
||||
|
||||
static struct gen_pci_cfg_bus_ops thunder_pem_bus_ops = {
|
||||
.bus_shift = 24,
|
||||
.ops = {
|
||||
.map_bus = thunder_pem_map_bus,
|
||||
.read = thunder_pem_config_read,
|
||||
.write = thunder_pem_config_write,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct of_device_id thunder_pem_of_match[] = {
|
||||
{ .compatible = "cavium,pci-host-thunder-pem",
|
||||
.data = &thunder_pem_bus_ops },
|
||||
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, thunder_pem_of_match);
|
||||
|
||||
static int thunder_pem_probe(struct platform_device *pdev)
|
||||
static int thunder_pem_init(struct device *dev, struct pci_config_window *cfg)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
const struct of_device_id *of_id;
|
||||
resource_size_t bar4_start;
|
||||
struct resource *res_pem;
|
||||
struct thunder_pem_pci *pem_pci;
|
||||
struct platform_device *pdev;
|
||||
|
||||
/* Only OF support for now */
|
||||
if (!dev->of_node)
|
||||
return -EINVAL;
|
||||
|
||||
pem_pci = devm_kzalloc(dev, sizeof(*pem_pci), GFP_KERNEL);
|
||||
if (!pem_pci)
|
||||
return -ENOMEM;
|
||||
|
||||
of_id = of_match_node(thunder_pem_of_match, dev->of_node);
|
||||
pem_pci->gen_pci.cfg.ops = (struct gen_pci_cfg_bus_ops *)of_id->data;
|
||||
pdev = to_platform_device(dev);
|
||||
|
||||
/*
|
||||
* The second register range is the PEM bridge to the PCIe
|
||||
|
@ -356,7 +328,29 @@ static int thunder_pem_probe(struct platform_device *pdev)
|
|||
pem_pci->ea_entry[1] = (u32)(res_pem->end - bar4_start) & ~3u;
|
||||
pem_pci->ea_entry[2] = (u32)(bar4_start >> 32);
|
||||
|
||||
return pci_host_common_probe(pdev, &pem_pci->gen_pci);
|
||||
cfg->priv = pem_pci;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pci_ecam_ops pci_thunder_pem_ops = {
|
||||
.bus_shift = 24,
|
||||
.init = thunder_pem_init,
|
||||
.pci_ops = {
|
||||
.map_bus = pci_ecam_map_bus,
|
||||
.read = thunder_pem_config_read,
|
||||
.write = thunder_pem_config_write,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct of_device_id thunder_pem_of_match[] = {
|
||||
{ .compatible = "cavium,pci-host-thunder-pem" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, thunder_pem_of_match);
|
||||
|
||||
static int thunder_pem_probe(struct platform_device *pdev)
|
||||
{
|
||||
return pci_host_common_probe(pdev, &pci_thunder_pem_ops);
|
||||
}
|
||||
|
||||
static struct platform_driver thunder_pem_driver = {
|
||||
|
|
|
@ -3019,6 +3019,121 @@ int pci_request_regions_exclusive(struct pci_dev *pdev, const char *res_name)
|
|||
}
|
||||
EXPORT_SYMBOL(pci_request_regions_exclusive);
|
||||
|
||||
#ifdef PCI_IOBASE
|
||||
struct io_range {
|
||||
struct list_head list;
|
||||
phys_addr_t start;
|
||||
resource_size_t size;
|
||||
};
|
||||
|
||||
static LIST_HEAD(io_range_list);
|
||||
static DEFINE_SPINLOCK(io_range_lock);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Record the PCI IO range (expressed as CPU physical address + size).
|
||||
* Return a negative value if an error has occured, zero otherwise
|
||||
*/
|
||||
int __weak pci_register_io_range(phys_addr_t addr, resource_size_t size)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
#ifdef PCI_IOBASE
|
||||
struct io_range *range;
|
||||
resource_size_t allocated_size = 0;
|
||||
|
||||
/* check if the range hasn't been previously recorded */
|
||||
spin_lock(&io_range_lock);
|
||||
list_for_each_entry(range, &io_range_list, list) {
|
||||
if (addr >= range->start && addr + size <= range->start + size) {
|
||||
/* range already registered, bail out */
|
||||
goto end_register;
|
||||
}
|
||||
allocated_size += range->size;
|
||||
}
|
||||
|
||||
/* range not registed yet, check for available space */
|
||||
if (allocated_size + size - 1 > IO_SPACE_LIMIT) {
|
||||
/* if it's too big check if 64K space can be reserved */
|
||||
if (allocated_size + SZ_64K - 1 > IO_SPACE_LIMIT) {
|
||||
err = -E2BIG;
|
||||
goto end_register;
|
||||
}
|
||||
|
||||
size = SZ_64K;
|
||||
pr_warn("Requested IO range too big, new size set to 64K\n");
|
||||
}
|
||||
|
||||
/* add the range to the list */
|
||||
range = kzalloc(sizeof(*range), GFP_ATOMIC);
|
||||
if (!range) {
|
||||
err = -ENOMEM;
|
||||
goto end_register;
|
||||
}
|
||||
|
||||
range->start = addr;
|
||||
range->size = size;
|
||||
|
||||
list_add_tail(&range->list, &io_range_list);
|
||||
|
||||
end_register:
|
||||
spin_unlock(&io_range_lock);
|
||||
#endif
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
phys_addr_t pci_pio_to_address(unsigned long pio)
|
||||
{
|
||||
phys_addr_t address = (phys_addr_t)OF_BAD_ADDR;
|
||||
|
||||
#ifdef PCI_IOBASE
|
||||
struct io_range *range;
|
||||
resource_size_t allocated_size = 0;
|
||||
|
||||
if (pio > IO_SPACE_LIMIT)
|
||||
return address;
|
||||
|
||||
spin_lock(&io_range_lock);
|
||||
list_for_each_entry(range, &io_range_list, list) {
|
||||
if (pio >= allocated_size && pio < allocated_size + range->size) {
|
||||
address = range->start + pio - allocated_size;
|
||||
break;
|
||||
}
|
||||
allocated_size += range->size;
|
||||
}
|
||||
spin_unlock(&io_range_lock);
|
||||
#endif
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
unsigned long __weak pci_address_to_pio(phys_addr_t address)
|
||||
{
|
||||
#ifdef PCI_IOBASE
|
||||
struct io_range *res;
|
||||
resource_size_t offset = 0;
|
||||
unsigned long addr = -1;
|
||||
|
||||
spin_lock(&io_range_lock);
|
||||
list_for_each_entry(res, &io_range_list, list) {
|
||||
if (address >= res->start && address < res->start + res->size) {
|
||||
addr = address - res->start + offset;
|
||||
break;
|
||||
}
|
||||
offset += res->size;
|
||||
}
|
||||
spin_unlock(&io_range_lock);
|
||||
|
||||
return addr;
|
||||
#else
|
||||
if (address > IO_SPACE_LIMIT)
|
||||
return (unsigned long)-1;
|
||||
|
||||
return (unsigned long) address;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_remap_iospace - Remap the memory mapped I/O space
|
||||
* @res: Resource describing the I/O space
|
||||
|
|
|
@ -47,10 +47,6 @@ void __iomem *of_io_request_and_map(struct device_node *device,
|
|||
extern const __be32 *of_get_address(struct device_node *dev, int index,
|
||||
u64 *size, unsigned int *flags);
|
||||
|
||||
extern int pci_register_io_range(phys_addr_t addr, resource_size_t size);
|
||||
extern unsigned long pci_address_to_pio(phys_addr_t addr);
|
||||
extern phys_addr_t pci_pio_to_address(unsigned long pio);
|
||||
|
||||
extern int of_pci_range_parser_init(struct of_pci_range_parser *parser,
|
||||
struct device_node *node);
|
||||
extern struct of_pci_range *of_pci_range_parser_one(
|
||||
|
@ -86,11 +82,6 @@ static inline const __be32 *of_get_address(struct device_node *dev, int index,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static inline phys_addr_t pci_pio_to_address(unsigned long pio)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int of_pci_range_parser_init(struct of_pci_range_parser *parser,
|
||||
struct device_node *node)
|
||||
{
|
||||
|
|
|
@ -1162,6 +1162,9 @@ int __must_check pci_bus_alloc_resource(struct pci_bus *bus,
|
|||
void *alignf_data);
|
||||
|
||||
|
||||
int pci_register_io_range(phys_addr_t addr, resource_size_t size);
|
||||
unsigned long pci_address_to_pio(phys_addr_t addr);
|
||||
phys_addr_t pci_pio_to_address(unsigned long pio);
|
||||
int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr);
|
||||
|
||||
static inline pci_bus_addr_t pci_bus_address(struct pci_dev *pdev, int bar)
|
||||
|
@ -1478,6 +1481,8 @@ static inline int pci_request_regions(struct pci_dev *dev, const char *res_name)
|
|||
{ return -EIO; }
|
||||
static inline void pci_release_regions(struct pci_dev *dev) { }
|
||||
|
||||
static inline unsigned long pci_address_to_pio(phys_addr_t addr) { return -1; }
|
||||
|
||||
static inline void pci_block_cfg_access(struct pci_dev *dev) { }
|
||||
static inline int pci_block_cfg_access_in_atomic(struct pci_dev *dev)
|
||||
{ return 0; }
|
||||
|
|
Загрузка…
Ссылка в новой задаче