mirror of https://gitee.com/openkylin/linux.git
Merge branch 'pci/ecam' into next
* pci/ecam: PCI: Explain ARM64 ACPI/MCFG quirk Kconfig and build strategy PCI: Add MCFG quirks for X-Gene host controller PCI: Add MCFG quirks for Cavium ThunderX pass1.x host controller PCI: Add MCFG quirks for Cavium ThunderX pass2.x host controller PCI: thunder-pem: Factor out resource lookup PCI: Add MCFG quirks for HiSilicon Hip05/06/07 host controllers PCI: Add MCFG quirks for Qualcomm QDF2432 host controller PCI/ACPI: Provide acpi_get_rc_resources() for ARM64 platform PCI/ACPI: Check for platform-specific MCFG quirks PCI/ACPI: Extend pci_mcfg_lookup() to return ECAM config accessors arm64: PCI: Exclude ACPI "consumer" resources from host bridge windows arm64: PCI: Manage controller-specific data on per-controller basis arm64: PCI: Search ACPI namespace to ensure ECAM space is reserved arm64: PCI: Add local struct device pointers ACPI: Add acpi_resource_consumer() to find device that claims a resource
This commit is contained in:
commit
5e0ad9f686
|
@ -114,6 +114,19 @@ int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int pci_acpi_root_prepare_resources(struct acpi_pci_root_info *ci)
|
||||
{
|
||||
struct resource_entry *entry, *tmp;
|
||||
int status;
|
||||
|
||||
status = acpi_pci_probe_root_resources(ci);
|
||||
resource_list_for_each_entry_safe(entry, tmp, &ci->resources) {
|
||||
if (!(entry->res->flags & IORESOURCE_WINDOW))
|
||||
resource_list_destroy_entry(entry);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Lookup the bus range for the domain in MCFG, and set up config space
|
||||
* mapping.
|
||||
|
@ -121,31 +134,33 @@ int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
|
|||
static struct pci_config_window *
|
||||
pci_acpi_setup_ecam_mapping(struct acpi_pci_root *root)
|
||||
{
|
||||
struct device *dev = &root->device->dev;
|
||||
struct resource *bus_res = &root->secondary;
|
||||
u16 seg = root->segment;
|
||||
struct pci_config_window *cfg;
|
||||
struct pci_ecam_ops *ecam_ops;
|
||||
struct resource cfgres;
|
||||
unsigned int bsz;
|
||||
struct acpi_device *adev;
|
||||
struct pci_config_window *cfg;
|
||||
int ret;
|
||||
|
||||
/* Use address from _CBA if present, otherwise lookup MCFG */
|
||||
if (!root->mcfg_addr)
|
||||
root->mcfg_addr = pci_mcfg_lookup(seg, bus_res);
|
||||
|
||||
if (!root->mcfg_addr) {
|
||||
dev_err(&root->device->dev, "%04x:%pR ECAM region not found\n",
|
||||
seg, bus_res);
|
||||
ret = pci_mcfg_lookup(root, &cfgres, &ecam_ops);
|
||||
if (ret) {
|
||||
dev_err(dev, "%04x:%pR ECAM region not found\n", seg, bus_res);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bsz = 1 << pci_generic_ecam_ops.bus_shift;
|
||||
cfgres.start = root->mcfg_addr + bus_res->start * bsz;
|
||||
cfgres.end = cfgres.start + resource_size(bus_res) * bsz - 1;
|
||||
cfgres.flags = IORESOURCE_MEM;
|
||||
cfg = pci_ecam_create(&root->device->dev, &cfgres, bus_res,
|
||||
&pci_generic_ecam_ops);
|
||||
adev = acpi_resource_consumer(&cfgres);
|
||||
if (adev)
|
||||
dev_info(dev, "ECAM area %pR reserved by %s\n", &cfgres,
|
||||
dev_name(&adev->dev));
|
||||
else
|
||||
dev_warn(dev, FW_BUG "ECAM area %pR not reserved in ACPI namespace\n",
|
||||
&cfgres);
|
||||
|
||||
cfg = pci_ecam_create(dev, &cfgres, bus_res, ecam_ops);
|
||||
if (IS_ERR(cfg)) {
|
||||
dev_err(&root->device->dev, "%04x:%pR error %ld mapping ECAM\n",
|
||||
seg, bus_res, PTR_ERR(cfg));
|
||||
dev_err(dev, "%04x:%pR error %ld mapping ECAM\n", seg, bus_res,
|
||||
PTR_ERR(cfg));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -159,33 +174,37 @@ static void pci_acpi_generic_release_info(struct acpi_pci_root_info *ci)
|
|||
|
||||
ri = container_of(ci, struct acpi_pci_generic_root_info, common);
|
||||
pci_ecam_free(ri->cfg);
|
||||
kfree(ci->ops);
|
||||
kfree(ri);
|
||||
}
|
||||
|
||||
static struct acpi_pci_root_ops acpi_pci_root_ops = {
|
||||
.release_info = pci_acpi_generic_release_info,
|
||||
};
|
||||
|
||||
/* Interface called from ACPI code to setup PCI host controller */
|
||||
struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
|
||||
{
|
||||
int node = acpi_get_node(root->device->handle);
|
||||
struct acpi_pci_generic_root_info *ri;
|
||||
struct pci_bus *bus, *child;
|
||||
struct acpi_pci_root_ops *root_ops;
|
||||
|
||||
ri = kzalloc_node(sizeof(*ri), GFP_KERNEL, node);
|
||||
if (!ri)
|
||||
return NULL;
|
||||
|
||||
root_ops = kzalloc_node(sizeof(*root_ops), GFP_KERNEL, node);
|
||||
if (!root_ops)
|
||||
return NULL;
|
||||
|
||||
ri->cfg = pci_acpi_setup_ecam_mapping(root);
|
||||
if (!ri->cfg) {
|
||||
kfree(ri);
|
||||
kfree(root_ops);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
acpi_pci_root_ops.pci_ops = &ri->cfg->ops->pci_ops;
|
||||
bus = acpi_pci_root_create(root, &acpi_pci_root_ops, &ri->common,
|
||||
ri->cfg);
|
||||
root_ops->release_info = pci_acpi_generic_release_info;
|
||||
root_ops->prepare_resources = pci_acpi_root_prepare_resources;
|
||||
root_ops->pci_ops = &ri->cfg->ops->pci_ops;
|
||||
bus = acpi_pci_root_create(root, root_ops, &ri->common, ri->cfg);
|
||||
if (!bus)
|
||||
return NULL;
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci-acpi.h>
|
||||
#include <linux/pci-ecam.h>
|
||||
|
||||
/* Structure to hold entries from the MCFG table */
|
||||
struct mcfg_entry {
|
||||
|
@ -32,12 +33,166 @@ struct mcfg_entry {
|
|||
u8 bus_end;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PCI_QUIRKS
|
||||
struct mcfg_fixup {
|
||||
char oem_id[ACPI_OEM_ID_SIZE + 1];
|
||||
char oem_table_id[ACPI_OEM_TABLE_ID_SIZE + 1];
|
||||
u32 oem_revision;
|
||||
u16 segment;
|
||||
struct resource bus_range;
|
||||
struct pci_ecam_ops *ops;
|
||||
struct resource cfgres;
|
||||
};
|
||||
|
||||
#define MCFG_BUS_RANGE(start, end) DEFINE_RES_NAMED((start), \
|
||||
((end) - (start) + 1), \
|
||||
NULL, IORESOURCE_BUS)
|
||||
#define MCFG_BUS_ANY MCFG_BUS_RANGE(0x0, 0xff)
|
||||
|
||||
static struct mcfg_fixup mcfg_quirks[] = {
|
||||
/* { OEM_ID, OEM_TABLE_ID, REV, SEGMENT, BUS_RANGE, ops, cfgres }, */
|
||||
|
||||
#define QCOM_ECAM32(seg) \
|
||||
{ "QCOM ", "QDF2432 ", 1, seg, MCFG_BUS_ANY, &pci_32b_ops }
|
||||
QCOM_ECAM32(0),
|
||||
QCOM_ECAM32(1),
|
||||
QCOM_ECAM32(2),
|
||||
QCOM_ECAM32(3),
|
||||
QCOM_ECAM32(4),
|
||||
QCOM_ECAM32(5),
|
||||
QCOM_ECAM32(6),
|
||||
QCOM_ECAM32(7),
|
||||
|
||||
#define HISI_QUAD_DOM(table_id, seg, ops) \
|
||||
{ "HISI ", table_id, 0, (seg) + 0, MCFG_BUS_ANY, ops }, \
|
||||
{ "HISI ", table_id, 0, (seg) + 1, MCFG_BUS_ANY, ops }, \
|
||||
{ "HISI ", table_id, 0, (seg) + 2, MCFG_BUS_ANY, ops }, \
|
||||
{ "HISI ", table_id, 0, (seg) + 3, MCFG_BUS_ANY, ops }
|
||||
HISI_QUAD_DOM("HIP05 ", 0, &hisi_pcie_ops),
|
||||
HISI_QUAD_DOM("HIP06 ", 0, &hisi_pcie_ops),
|
||||
HISI_QUAD_DOM("HIP07 ", 0, &hisi_pcie_ops),
|
||||
HISI_QUAD_DOM("HIP07 ", 4, &hisi_pcie_ops),
|
||||
HISI_QUAD_DOM("HIP07 ", 8, &hisi_pcie_ops),
|
||||
HISI_QUAD_DOM("HIP07 ", 12, &hisi_pcie_ops),
|
||||
|
||||
#define THUNDER_PEM_RES(addr, node) \
|
||||
DEFINE_RES_MEM((addr) + ((u64) (node) << 44), 0x39 * SZ_16M)
|
||||
#define THUNDER_PEM_QUIRK(rev, node) \
|
||||
{ "CAVIUM", "THUNDERX", rev, 4 + (10 * (node)), MCFG_BUS_ANY, \
|
||||
&thunder_pem_ecam_ops, THUNDER_PEM_RES(0x88001f000000UL, node) }, \
|
||||
{ "CAVIUM", "THUNDERX", rev, 5 + (10 * (node)), MCFG_BUS_ANY, \
|
||||
&thunder_pem_ecam_ops, THUNDER_PEM_RES(0x884057000000UL, node) }, \
|
||||
{ "CAVIUM", "THUNDERX", rev, 6 + (10 * (node)), MCFG_BUS_ANY, \
|
||||
&thunder_pem_ecam_ops, THUNDER_PEM_RES(0x88808f000000UL, node) }, \
|
||||
{ "CAVIUM", "THUNDERX", rev, 7 + (10 * (node)), MCFG_BUS_ANY, \
|
||||
&thunder_pem_ecam_ops, THUNDER_PEM_RES(0x89001f000000UL, node) }, \
|
||||
{ "CAVIUM", "THUNDERX", rev, 8 + (10 * (node)), MCFG_BUS_ANY, \
|
||||
&thunder_pem_ecam_ops, THUNDER_PEM_RES(0x894057000000UL, node) }, \
|
||||
{ "CAVIUM", "THUNDERX", rev, 9 + (10 * (node)), MCFG_BUS_ANY, \
|
||||
&thunder_pem_ecam_ops, THUNDER_PEM_RES(0x89808f000000UL, node) }
|
||||
/* SoC pass2.x */
|
||||
THUNDER_PEM_QUIRK(1, 0),
|
||||
THUNDER_PEM_QUIRK(1, 1),
|
||||
|
||||
#define THUNDER_ECAM_QUIRK(rev, seg) \
|
||||
{ "CAVIUM", "THUNDERX", rev, seg, MCFG_BUS_ANY, \
|
||||
&pci_thunder_ecam_ops }
|
||||
/* SoC pass1.x */
|
||||
THUNDER_PEM_QUIRK(2, 0), /* off-chip devices */
|
||||
THUNDER_PEM_QUIRK(2, 1), /* off-chip devices */
|
||||
THUNDER_ECAM_QUIRK(2, 0),
|
||||
THUNDER_ECAM_QUIRK(2, 1),
|
||||
THUNDER_ECAM_QUIRK(2, 2),
|
||||
THUNDER_ECAM_QUIRK(2, 3),
|
||||
THUNDER_ECAM_QUIRK(2, 10),
|
||||
THUNDER_ECAM_QUIRK(2, 11),
|
||||
THUNDER_ECAM_QUIRK(2, 12),
|
||||
THUNDER_ECAM_QUIRK(2, 13),
|
||||
|
||||
#define XGENE_V1_ECAM_MCFG(rev, seg) \
|
||||
{"APM ", "XGENE ", rev, seg, MCFG_BUS_ANY, \
|
||||
&xgene_v1_pcie_ecam_ops }
|
||||
#define XGENE_V2_ECAM_MCFG(rev, seg) \
|
||||
{"APM ", "XGENE ", rev, seg, MCFG_BUS_ANY, \
|
||||
&xgene_v2_pcie_ecam_ops }
|
||||
/* X-Gene SoC with v1 PCIe controller */
|
||||
XGENE_V1_ECAM_MCFG(1, 0),
|
||||
XGENE_V1_ECAM_MCFG(1, 1),
|
||||
XGENE_V1_ECAM_MCFG(1, 2),
|
||||
XGENE_V1_ECAM_MCFG(1, 3),
|
||||
XGENE_V1_ECAM_MCFG(1, 4),
|
||||
XGENE_V1_ECAM_MCFG(2, 0),
|
||||
XGENE_V1_ECAM_MCFG(2, 1),
|
||||
XGENE_V1_ECAM_MCFG(2, 2),
|
||||
XGENE_V1_ECAM_MCFG(2, 3),
|
||||
XGENE_V1_ECAM_MCFG(2, 4),
|
||||
/* X-Gene SoC with v2.1 PCIe controller */
|
||||
XGENE_V2_ECAM_MCFG(3, 0),
|
||||
XGENE_V2_ECAM_MCFG(3, 1),
|
||||
/* X-Gene SoC with v2.2 PCIe controller */
|
||||
XGENE_V2_ECAM_MCFG(4, 0),
|
||||
XGENE_V2_ECAM_MCFG(4, 1),
|
||||
XGENE_V2_ECAM_MCFG(4, 2),
|
||||
};
|
||||
|
||||
static char mcfg_oem_id[ACPI_OEM_ID_SIZE];
|
||||
static char mcfg_oem_table_id[ACPI_OEM_TABLE_ID_SIZE];
|
||||
static u32 mcfg_oem_revision;
|
||||
|
||||
static int pci_mcfg_quirk_matches(struct mcfg_fixup *f, u16 segment,
|
||||
struct resource *bus_range)
|
||||
{
|
||||
if (!memcmp(f->oem_id, mcfg_oem_id, ACPI_OEM_ID_SIZE) &&
|
||||
!memcmp(f->oem_table_id, mcfg_oem_table_id,
|
||||
ACPI_OEM_TABLE_ID_SIZE) &&
|
||||
f->oem_revision == mcfg_oem_revision &&
|
||||
f->segment == segment &&
|
||||
resource_contains(&f->bus_range, bus_range))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void pci_mcfg_apply_quirks(struct acpi_pci_root *root,
|
||||
struct resource *cfgres,
|
||||
struct pci_ecam_ops **ecam_ops)
|
||||
{
|
||||
#ifdef CONFIG_PCI_QUIRKS
|
||||
u16 segment = root->segment;
|
||||
struct resource *bus_range = &root->secondary;
|
||||
struct mcfg_fixup *f;
|
||||
int i;
|
||||
|
||||
for (i = 0, f = mcfg_quirks; i < ARRAY_SIZE(mcfg_quirks); i++, f++) {
|
||||
if (pci_mcfg_quirk_matches(f, segment, bus_range)) {
|
||||
if (f->cfgres.start)
|
||||
*cfgres = f->cfgres;
|
||||
if (f->ops)
|
||||
*ecam_ops = f->ops;
|
||||
dev_info(&root->device->dev, "MCFG quirk: ECAM at %pR for %pR with %ps\n",
|
||||
cfgres, bus_range, *ecam_ops);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* List to save MCFG entries */
|
||||
static LIST_HEAD(pci_mcfg_list);
|
||||
|
||||
phys_addr_t pci_mcfg_lookup(u16 seg, struct resource *bus_res)
|
||||
int pci_mcfg_lookup(struct acpi_pci_root *root, struct resource *cfgres,
|
||||
struct pci_ecam_ops **ecam_ops)
|
||||
{
|
||||
struct pci_ecam_ops *ops = &pci_generic_ecam_ops;
|
||||
struct resource *bus_res = &root->secondary;
|
||||
u16 seg = root->segment;
|
||||
struct mcfg_entry *e;
|
||||
struct resource res;
|
||||
|
||||
/* Use address from _CBA if present, otherwise lookup MCFG */
|
||||
if (root->mcfg_addr)
|
||||
goto skip_lookup;
|
||||
|
||||
/*
|
||||
* We expect exact match, unless MCFG entry end bus covers more than
|
||||
|
@ -45,10 +200,32 @@ phys_addr_t pci_mcfg_lookup(u16 seg, struct resource *bus_res)
|
|||
*/
|
||||
list_for_each_entry(e, &pci_mcfg_list, list) {
|
||||
if (e->segment == seg && e->bus_start == bus_res->start &&
|
||||
e->bus_end >= bus_res->end)
|
||||
return e->addr;
|
||||
e->bus_end >= bus_res->end) {
|
||||
root->mcfg_addr = e->addr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
skip_lookup:
|
||||
memset(&res, 0, sizeof(res));
|
||||
if (root->mcfg_addr) {
|
||||
res.start = root->mcfg_addr + (bus_res->start << 20);
|
||||
res.end = res.start + (resource_size(bus_res) << 20) - 1;
|
||||
res.flags = IORESOURCE_MEM;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allow quirks to override default ECAM ops and CFG resource
|
||||
* range. This may even fabricate a CFG resource range in case
|
||||
* MCFG does not have it. Invalid CFG start address means MCFG
|
||||
* firmware bug or we need another quirk in array.
|
||||
*/
|
||||
pci_mcfg_apply_quirks(root, &res, &ops);
|
||||
if (!res.start)
|
||||
return -ENXIO;
|
||||
|
||||
*cfgres = res;
|
||||
*ecam_ops = ops;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -79,6 +256,13 @@ static __init int pci_mcfg_parse(struct acpi_table_header *header)
|
|||
list_add(&e->list, &pci_mcfg_list);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PCI_QUIRKS
|
||||
/* Save MCFG IDs and revision for quirks matching */
|
||||
memcpy(mcfg_oem_id, header->oem_id, ACPI_OEM_ID_SIZE);
|
||||
memcpy(mcfg_oem_table_id, header->oem_table_id, ACPI_OEM_TABLE_ID_SIZE);
|
||||
mcfg_oem_revision = header->oem_revision;
|
||||
#endif
|
||||
|
||||
pr_info("MCFG table detected, %d entries\n", n);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -664,3 +664,60 @@ int acpi_dev_filter_resource_type(struct acpi_resource *ares,
|
|||
return (type & types) ? 0 : 1;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_dev_filter_resource_type);
|
||||
|
||||
static int acpi_dev_consumes_res(struct acpi_device *adev, struct resource *res)
|
||||
{
|
||||
struct list_head resource_list;
|
||||
struct resource_entry *rentry;
|
||||
int ret, found = 0;
|
||||
|
||||
INIT_LIST_HEAD(&resource_list);
|
||||
ret = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
|
||||
list_for_each_entry(rentry, &resource_list, node) {
|
||||
if (resource_contains(rentry->res, res)) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
acpi_dev_free_resource_list(&resource_list);
|
||||
return found;
|
||||
}
|
||||
|
||||
static acpi_status acpi_res_consumer_cb(acpi_handle handle, u32 depth,
|
||||
void *context, void **ret)
|
||||
{
|
||||
struct resource *res = context;
|
||||
struct acpi_device **consumer = (struct acpi_device **) ret;
|
||||
struct acpi_device *adev;
|
||||
|
||||
if (acpi_bus_get_device(handle, &adev))
|
||||
return AE_OK;
|
||||
|
||||
if (acpi_dev_consumes_res(adev, res)) {
|
||||
*consumer = adev;
|
||||
return AE_CTRL_TERMINATE;
|
||||
}
|
||||
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* acpi_resource_consumer - Find the ACPI device that consumes @res.
|
||||
* @res: Resource to search for.
|
||||
*
|
||||
* Search the current resource settings (_CRS) of every ACPI device node
|
||||
* for @res. If we find an ACPI device whose _CRS includes @res, return
|
||||
* it. Otherwise, return NULL.
|
||||
*/
|
||||
struct acpi_device *acpi_resource_consumer(struct resource *res)
|
||||
{
|
||||
struct acpi_device *consumer = NULL;
|
||||
|
||||
acpi_get_devices(NULL, acpi_res_consumer_cb, res, (void **) &consumer);
|
||||
return consumer;
|
||||
}
|
||||
|
|
|
@ -162,3 +162,15 @@ struct pci_ecam_ops pci_generic_ecam_ops = {
|
|||
.write = pci_generic_config_write,
|
||||
}
|
||||
};
|
||||
|
||||
#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)
|
||||
/* ECAM ops for 32-bit access only (non-compliant) */
|
||||
struct pci_ecam_ops pci_32b_ops = {
|
||||
.bus_shift = 20,
|
||||
.pci_ops = {
|
||||
.map_bus = pci_ecam_map_bus,
|
||||
.read = pci_generic_config_read32,
|
||||
.write = pci_generic_config_write32,
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -133,8 +133,8 @@ config PCIE_XILINX
|
|||
|
||||
config PCI_XGENE
|
||||
bool "X-Gene PCIe controller"
|
||||
depends on ARCH_XGENE
|
||||
depends on OF
|
||||
depends on ARM64
|
||||
depends on OF || (ACPI && PCI_QUIRKS)
|
||||
select PCIEPORTBUS
|
||||
help
|
||||
Say Y here if you want internal PCI support on APM X-Gene SoC.
|
||||
|
@ -240,14 +240,16 @@ config PCIE_QCOM
|
|||
|
||||
config PCI_HOST_THUNDER_PEM
|
||||
bool "Cavium Thunder PCIe controller to off-chip devices"
|
||||
depends on OF && ARM64
|
||||
depends on ARM64
|
||||
depends on OF || (ACPI && PCI_QUIRKS)
|
||||
select PCI_HOST_COMMON
|
||||
help
|
||||
Say Y here if you want PCIe support for CN88XX Cavium Thunder SoCs.
|
||||
|
||||
config PCI_HOST_THUNDER_ECAM
|
||||
bool "Cavium Thunder ECAM controller to on-chip devices on pass-1.x silicon"
|
||||
depends on OF && ARM64
|
||||
depends on ARM64
|
||||
depends on OF || (ACPI && PCI_QUIRKS)
|
||||
select PCI_HOST_COMMON
|
||||
help
|
||||
Say Y here if you want ECAM support for CN88XX-Pass-1.x Cavium Thunder SoCs.
|
||||
|
|
|
@ -15,7 +15,6 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
|
|||
obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
|
||||
obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
|
||||
obj-$(CONFIG_PCIE_XILINX_NWL) += pcie-xilinx-nwl.o
|
||||
obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
|
||||
obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
|
||||
obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
|
||||
obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
|
||||
|
@ -25,11 +24,23 @@ obj-$(CONFIG_PCIE_IPROC_PLATFORM) += pcie-iproc-platform.o
|
|||
obj-$(CONFIG_PCIE_IPROC_BCMA) += pcie-iproc-bcma.o
|
||||
obj-$(CONFIG_PCIE_ALTERA) += pcie-altera.o
|
||||
obj-$(CONFIG_PCIE_ALTERA_MSI) += pcie-altera-msi.o
|
||||
obj-$(CONFIG_PCI_HISI) += pcie-hisi.o
|
||||
obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o
|
||||
obj-$(CONFIG_PCI_HOST_THUNDER_ECAM) += pci-thunder-ecam.o
|
||||
obj-$(CONFIG_PCI_HOST_THUNDER_PEM) += pci-thunder-pem.o
|
||||
obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o
|
||||
obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o
|
||||
obj-$(CONFIG_PCIE_ROCKCHIP) += pcie-rockchip.o
|
||||
obj-$(CONFIG_VMD) += vmd.o
|
||||
|
||||
# The following drivers are for devices that use the generic ACPI
|
||||
# pci_root.c driver but don't support standard ECAM config access.
|
||||
# They contain MCFG quirks to replace the generic ECAM accessors with
|
||||
# device-specific ones that are shared with the DT driver.
|
||||
|
||||
# The ACPI driver is generic and should not require driver-specific
|
||||
# config options to be enabled, so we always build these drivers on
|
||||
# ARM64 and use internal ifdefs to only build the pieces we need
|
||||
# depending on whether ACPI, the DT driver, or both are enabled.
|
||||
|
||||
obj-$(CONFIG_ARM64) += pcie-hisi.o
|
||||
obj-$(CONFIG_ARM64) += pci-thunder-ecam.o
|
||||
obj-$(CONFIG_ARM64) += pci-thunder-pem.o
|
||||
obj-$(CONFIG_ARM64) += pci-xgene.o
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
#include <linux/pci-ecam.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#if defined(CONFIG_PCI_HOST_THUNDER_ECAM) || (defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS))
|
||||
|
||||
static void set_val(u32 v, int where, int size, u32 *val)
|
||||
{
|
||||
int shift = (where & 3) * 8;
|
||||
|
@ -346,7 +348,7 @@ 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 pci_ecam_ops pci_thunder_ecam_ops = {
|
||||
struct pci_ecam_ops pci_thunder_ecam_ops = {
|
||||
.bus_shift = 20,
|
||||
.pci_ops = {
|
||||
.map_bus = pci_ecam_map_bus,
|
||||
|
@ -355,6 +357,8 @@ static struct pci_ecam_ops pci_thunder_ecam_ops = {
|
|||
}
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PCI_HOST_THUNDER_ECAM
|
||||
|
||||
static const struct of_device_id thunder_ecam_of_match[] = {
|
||||
{ .compatible = "cavium,pci-host-thunder-ecam" },
|
||||
{ },
|
||||
|
@ -373,3 +377,6 @@ static struct platform_driver thunder_ecam_driver = {
|
|||
.probe = thunder_ecam_probe,
|
||||
};
|
||||
builtin_platform_driver(thunder_ecam_driver);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -18,8 +18,12 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_pci.h>
|
||||
#include <linux/pci-acpi.h>
|
||||
#include <linux/pci-ecam.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include "../pci.h"
|
||||
|
||||
#if defined(CONFIG_PCI_HOST_THUNDER_PEM) || (defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS))
|
||||
|
||||
#define PEM_CFG_WR 0x28
|
||||
#define PEM_CFG_RD 0x30
|
||||
|
@ -284,35 +288,16 @@ static int thunder_pem_config_write(struct pci_bus *bus, unsigned int devfn,
|
|||
return pci_generic_config_write(bus, devfn, where, size, val);
|
||||
}
|
||||
|
||||
static int thunder_pem_init(struct pci_config_window *cfg)
|
||||
static int thunder_pem_init(struct device *dev, struct pci_config_window *cfg,
|
||||
struct resource *res_pem)
|
||||
{
|
||||
struct device *dev = cfg->parent;
|
||||
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;
|
||||
resource_size_t bar4_start;
|
||||
|
||||
pem_pci = devm_kzalloc(dev, sizeof(*pem_pci), GFP_KERNEL);
|
||||
if (!pem_pci)
|
||||
return -ENOMEM;
|
||||
|
||||
pdev = to_platform_device(dev);
|
||||
|
||||
/*
|
||||
* The second register range is the PEM bridge to the PCIe
|
||||
* bus. It has a different config access method than those
|
||||
* devices behind the bridge.
|
||||
*/
|
||||
res_pem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
if (!res_pem) {
|
||||
dev_err(dev, "missing \"reg[1]\"property\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pem_pci->pem_reg_base = devm_ioremap(dev, res_pem->start, 0x10000);
|
||||
if (!pem_pci->pem_reg_base)
|
||||
return -ENOMEM;
|
||||
|
@ -332,9 +317,69 @@ static int thunder_pem_init(struct pci_config_window *cfg)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)
|
||||
|
||||
static int thunder_pem_acpi_init(struct pci_config_window *cfg)
|
||||
{
|
||||
struct device *dev = cfg->parent;
|
||||
struct acpi_device *adev = to_acpi_device(dev);
|
||||
struct acpi_pci_root *root = acpi_driver_data(adev);
|
||||
struct resource *res_pem;
|
||||
int ret;
|
||||
|
||||
res_pem = devm_kzalloc(&adev->dev, sizeof(*res_pem), GFP_KERNEL);
|
||||
if (!res_pem)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = acpi_get_rc_resources(dev, "THRX0002", root->segment, res_pem);
|
||||
if (ret) {
|
||||
dev_err(dev, "can't get rc base address\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return thunder_pem_init(dev, cfg, res_pem);
|
||||
}
|
||||
|
||||
struct pci_ecam_ops thunder_pem_ecam_ops = {
|
||||
.bus_shift = 24,
|
||||
.init = thunder_pem_acpi_init,
|
||||
.pci_ops = {
|
||||
.map_bus = pci_ecam_map_bus,
|
||||
.read = thunder_pem_config_read,
|
||||
.write = thunder_pem_config_write,
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PCI_HOST_THUNDER_PEM
|
||||
|
||||
static int thunder_pem_platform_init(struct pci_config_window *cfg)
|
||||
{
|
||||
struct device *dev = cfg->parent;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct resource *res_pem;
|
||||
|
||||
if (!dev->of_node)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* The second register range is the PEM bridge to the PCIe
|
||||
* bus. It has a different config access method than those
|
||||
* devices behind the bridge.
|
||||
*/
|
||||
res_pem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
if (!res_pem) {
|
||||
dev_err(dev, "missing \"reg[1]\"property\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return thunder_pem_init(dev, cfg, res_pem);
|
||||
}
|
||||
|
||||
static struct pci_ecam_ops pci_thunder_pem_ops = {
|
||||
.bus_shift = 24,
|
||||
.init = thunder_pem_init,
|
||||
.init = thunder_pem_platform_init,
|
||||
.pci_ops = {
|
||||
.map_bus = pci_ecam_map_bus,
|
||||
.read = thunder_pem_config_read,
|
||||
|
@ -360,3 +405,6 @@ static struct platform_driver thunder_pem_driver = {
|
|||
.probe = thunder_pem_probe,
|
||||
};
|
||||
builtin_platform_driver(thunder_pem_driver);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
#include <linux/of_irq.h>
|
||||
#include <linux/of_pci.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci-acpi.h>
|
||||
#include <linux/pci-ecam.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
|
@ -64,7 +66,9 @@
|
|||
/* PCIe IP version */
|
||||
#define XGENE_PCIE_IP_VER_UNKN 0
|
||||
#define XGENE_PCIE_IP_VER_1 1
|
||||
#define XGENE_PCIE_IP_VER_2 2
|
||||
|
||||
#if defined(CONFIG_PCI_XGENE) || (defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS))
|
||||
struct xgene_pcie_port {
|
||||
struct device_node *node;
|
||||
struct device *dev;
|
||||
|
@ -91,13 +95,24 @@ static inline u32 pcie_bar_low_val(u32 addr, u32 flags)
|
|||
return (addr & PCI_BASE_ADDRESS_MEM_MASK) | flags;
|
||||
}
|
||||
|
||||
static inline struct xgene_pcie_port *pcie_bus_to_port(struct pci_bus *bus)
|
||||
{
|
||||
struct pci_config_window *cfg;
|
||||
|
||||
if (acpi_disabled)
|
||||
return (struct xgene_pcie_port *)(bus->sysdata);
|
||||
|
||||
cfg = bus->sysdata;
|
||||
return (struct xgene_pcie_port *)(cfg->priv);
|
||||
}
|
||||
|
||||
/*
|
||||
* When the address bit [17:16] is 2'b01, the Configuration access will be
|
||||
* treated as Type 1 and it will be forwarded to external PCIe device.
|
||||
*/
|
||||
static void __iomem *xgene_pcie_get_cfg_base(struct pci_bus *bus)
|
||||
{
|
||||
struct xgene_pcie_port *port = bus->sysdata;
|
||||
struct xgene_pcie_port *port = pcie_bus_to_port(bus);
|
||||
|
||||
if (bus->number >= (bus->primary + 1))
|
||||
return port->cfg_base + AXI_EP_CFG_ACCESS;
|
||||
|
@ -111,7 +126,7 @@ static void __iomem *xgene_pcie_get_cfg_base(struct pci_bus *bus)
|
|||
*/
|
||||
static void xgene_pcie_set_rtdid_reg(struct pci_bus *bus, uint devfn)
|
||||
{
|
||||
struct xgene_pcie_port *port = bus->sysdata;
|
||||
struct xgene_pcie_port *port = pcie_bus_to_port(bus);
|
||||
unsigned int b, d, f;
|
||||
u32 rtdid_val = 0;
|
||||
|
||||
|
@ -158,7 +173,7 @@ static void __iomem *xgene_pcie_map_bus(struct pci_bus *bus, unsigned int devfn,
|
|||
static int xgene_pcie_config_read32(struct pci_bus *bus, unsigned int devfn,
|
||||
int where, int size, u32 *val)
|
||||
{
|
||||
struct xgene_pcie_port *port = bus->sysdata;
|
||||
struct xgene_pcie_port *port = pcie_bus_to_port(bus);
|
||||
|
||||
if (pci_generic_config_read32(bus, devfn, where & ~0x3, 4, val) !=
|
||||
PCIBIOS_SUCCESSFUL)
|
||||
|
@ -182,13 +197,103 @@ static int xgene_pcie_config_read32(struct pci_bus *bus, unsigned int devfn,
|
|||
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct pci_ops xgene_pcie_ops = {
|
||||
.map_bus = xgene_pcie_map_bus,
|
||||
.read = xgene_pcie_config_read32,
|
||||
.write = pci_generic_config_write32,
|
||||
#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)
|
||||
static int xgene_get_csr_resource(struct acpi_device *adev,
|
||||
struct resource *res)
|
||||
{
|
||||
struct device *dev = &adev->dev;
|
||||
struct resource_entry *entry;
|
||||
struct list_head list;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
INIT_LIST_HEAD(&list);
|
||||
flags = IORESOURCE_MEM;
|
||||
ret = acpi_dev_get_resources(adev, &list,
|
||||
acpi_dev_filter_resource_type_cb,
|
||||
(void *) flags);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to parse _CRS method, error code %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
dev_err(dev, "no IO and memory resources present in _CRS\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
entry = list_first_entry(&list, struct resource_entry, node);
|
||||
*res = *entry->res;
|
||||
acpi_dev_free_resource_list(&list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xgene_pcie_ecam_init(struct pci_config_window *cfg, u32 ipversion)
|
||||
{
|
||||
struct device *dev = cfg->parent;
|
||||
struct acpi_device *adev = to_acpi_device(dev);
|
||||
struct xgene_pcie_port *port;
|
||||
struct resource csr;
|
||||
int ret;
|
||||
|
||||
port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
|
||||
if (!port)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = xgene_get_csr_resource(adev, &csr);
|
||||
if (ret) {
|
||||
dev_err(dev, "can't get CSR resource\n");
|
||||
kfree(port);
|
||||
return ret;
|
||||
}
|
||||
port->csr_base = devm_ioremap_resource(dev, &csr);
|
||||
if (IS_ERR(port->csr_base)) {
|
||||
kfree(port);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
port->cfg_base = cfg->win;
|
||||
port->version = ipversion;
|
||||
|
||||
cfg->priv = port;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xgene_v1_pcie_ecam_init(struct pci_config_window *cfg)
|
||||
{
|
||||
return xgene_pcie_ecam_init(cfg, XGENE_PCIE_IP_VER_1);
|
||||
}
|
||||
|
||||
struct pci_ecam_ops xgene_v1_pcie_ecam_ops = {
|
||||
.bus_shift = 16,
|
||||
.init = xgene_v1_pcie_ecam_init,
|
||||
.pci_ops = {
|
||||
.map_bus = xgene_pcie_map_bus,
|
||||
.read = xgene_pcie_config_read32,
|
||||
.write = pci_generic_config_write,
|
||||
}
|
||||
};
|
||||
|
||||
static int xgene_v2_pcie_ecam_init(struct pci_config_window *cfg)
|
||||
{
|
||||
return xgene_pcie_ecam_init(cfg, XGENE_PCIE_IP_VER_2);
|
||||
}
|
||||
|
||||
struct pci_ecam_ops xgene_v2_pcie_ecam_ops = {
|
||||
.bus_shift = 16,
|
||||
.init = xgene_v2_pcie_ecam_init,
|
||||
.pci_ops = {
|
||||
.map_bus = xgene_pcie_map_bus,
|
||||
.read = xgene_pcie_config_read32,
|
||||
.write = pci_generic_config_write,
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_PCI_XGENE)
|
||||
static u64 xgene_pcie_set_ib_mask(struct xgene_pcie_port *port, u32 addr,
|
||||
u32 flags, u64 size)
|
||||
{
|
||||
|
@ -521,6 +626,12 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct pci_ops xgene_pcie_ops = {
|
||||
.map_bus = xgene_pcie_map_bus,
|
||||
.read = xgene_pcie_config_read32,
|
||||
.write = pci_generic_config_write32,
|
||||
};
|
||||
|
||||
static int xgene_pcie_probe_bridge(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
@ -591,3 +702,4 @@ static struct platform_driver xgene_pcie_driver = {
|
|||
.probe = xgene_pcie_probe_bridge,
|
||||
};
|
||||
builtin_platform_driver(xgene_pcie_driver);
|
||||
#endif
|
||||
|
|
|
@ -18,7 +18,106 @@
|
|||
#include <linux/of_pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci-acpi.h>
|
||||
#include <linux/pci-ecam.h>
|
||||
#include <linux/regmap.h>
|
||||
#include "../pci.h"
|
||||
|
||||
#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)
|
||||
|
||||
static int hisi_pcie_acpi_rd_conf(struct pci_bus *bus, u32 devfn, int where,
|
||||
int size, u32 *val)
|
||||
{
|
||||
struct pci_config_window *cfg = bus->sysdata;
|
||||
int dev = PCI_SLOT(devfn);
|
||||
|
||||
if (bus->number == cfg->busr.start) {
|
||||
/* access only one slot on each root port */
|
||||
if (dev > 0)
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
else
|
||||
return pci_generic_config_read32(bus, devfn, where,
|
||||
size, val);
|
||||
}
|
||||
|
||||
return pci_generic_config_read(bus, devfn, where, size, val);
|
||||
}
|
||||
|
||||
static int hisi_pcie_acpi_wr_conf(struct pci_bus *bus, u32 devfn,
|
||||
int where, int size, u32 val)
|
||||
{
|
||||
struct pci_config_window *cfg = bus->sysdata;
|
||||
int dev = PCI_SLOT(devfn);
|
||||
|
||||
if (bus->number == cfg->busr.start) {
|
||||
/* access only one slot on each root port */
|
||||
if (dev > 0)
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
else
|
||||
return pci_generic_config_write32(bus, devfn, where,
|
||||
size, val);
|
||||
}
|
||||
|
||||
return pci_generic_config_write(bus, devfn, where, size, val);
|
||||
}
|
||||
|
||||
static void __iomem *hisi_pcie_map_bus(struct pci_bus *bus, unsigned int devfn,
|
||||
int where)
|
||||
{
|
||||
struct pci_config_window *cfg = bus->sysdata;
|
||||
void __iomem *reg_base = cfg->priv;
|
||||
|
||||
if (bus->number == cfg->busr.start)
|
||||
return reg_base + where;
|
||||
else
|
||||
return pci_ecam_map_bus(bus, devfn, where);
|
||||
}
|
||||
|
||||
static int hisi_pcie_init(struct pci_config_window *cfg)
|
||||
{
|
||||
struct device *dev = cfg->parent;
|
||||
struct acpi_device *adev = to_acpi_device(dev);
|
||||
struct acpi_pci_root *root = acpi_driver_data(adev);
|
||||
struct resource *res;
|
||||
void __iomem *reg_base;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Retrieve RC base and size from a HISI0081 device with _UID
|
||||
* matching our segment.
|
||||
*/
|
||||
res = devm_kzalloc(dev, sizeof(*res), GFP_KERNEL);
|
||||
if (!res)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = acpi_get_rc_resources(dev, "HISI0081", root->segment, res);
|
||||
if (ret) {
|
||||
dev_err(dev, "can't get rc base address\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
reg_base = devm_ioremap(dev, res->start, resource_size(res));
|
||||
if (!reg_base)
|
||||
return -ENOMEM;
|
||||
|
||||
cfg->priv = reg_base;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct pci_ecam_ops hisi_pcie_ops = {
|
||||
.bus_shift = 20,
|
||||
.init = hisi_pcie_init,
|
||||
.pci_ops = {
|
||||
.map_bus = hisi_pcie_map_bus,
|
||||
.read = hisi_pcie_acpi_rd_conf,
|
||||
.write = hisi_pcie_acpi_wr_conf,
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PCI_HISI
|
||||
|
||||
#include "pcie-designware.h"
|
||||
|
||||
|
@ -227,3 +326,5 @@ static struct platform_driver hisi_pcie_driver = {
|
|||
},
|
||||
};
|
||||
builtin_platform_driver(hisi_pcie_driver);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -29,6 +29,82 @@ const u8 pci_acpi_dsm_uuid[] = {
|
|||
0x91, 0x17, 0xea, 0x4d, 0x19, 0xc3, 0x43, 0x4d
|
||||
};
|
||||
|
||||
#if defined(CONFIG_PCI_QUIRKS) && defined(CONFIG_ARM64)
|
||||
static int acpi_get_rc_addr(struct acpi_device *adev, struct resource *res)
|
||||
{
|
||||
struct device *dev = &adev->dev;
|
||||
struct resource_entry *entry;
|
||||
struct list_head list;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
INIT_LIST_HEAD(&list);
|
||||
flags = IORESOURCE_MEM;
|
||||
ret = acpi_dev_get_resources(adev, &list,
|
||||
acpi_dev_filter_resource_type_cb,
|
||||
(void *) flags);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to parse _CRS method, error code %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
dev_err(dev, "no IO and memory resources present in _CRS\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
entry = list_first_entry(&list, struct resource_entry, node);
|
||||
*res = *entry->res;
|
||||
acpi_dev_free_resource_list(&list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static acpi_status acpi_match_rc(acpi_handle handle, u32 lvl, void *context,
|
||||
void **retval)
|
||||
{
|
||||
u16 *segment = context;
|
||||
unsigned long long uid;
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_evaluate_integer(handle, "_UID", NULL, &uid);
|
||||
if (ACPI_FAILURE(status) || uid != *segment)
|
||||
return AE_CTRL_DEPTH;
|
||||
|
||||
*(acpi_handle *)retval = handle;
|
||||
return AE_CTRL_TERMINATE;
|
||||
}
|
||||
|
||||
int acpi_get_rc_resources(struct device *dev, const char *hid, u16 segment,
|
||||
struct resource *res)
|
||||
{
|
||||
struct acpi_device *adev;
|
||||
acpi_status status;
|
||||
acpi_handle handle;
|
||||
int ret;
|
||||
|
||||
status = acpi_get_devices(hid, acpi_match_rc, &segment, &handle);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
dev_err(dev, "can't find _HID %s device to locate resources\n",
|
||||
hid);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = acpi_bus_get_device(handle, &adev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = acpi_get_rc_addr(adev, res);
|
||||
if (ret) {
|
||||
dev_err(dev, "can't get resource from %s\n",
|
||||
dev_name(&adev->dev));
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle)
|
||||
{
|
||||
acpi_status status = AE_NOT_EXIST;
|
||||
|
|
|
@ -356,4 +356,9 @@ static inline int pci_dev_specific_reset(struct pci_dev *dev, int probe)
|
|||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_PCI_QUIRKS) && defined(CONFIG_ARM64)
|
||||
int acpi_get_rc_resources(struct device *dev, const char *hid, u16 segment,
|
||||
struct resource *res);
|
||||
#endif
|
||||
|
||||
#endif /* DRIVERS_PCI_H */
|
||||
|
|
|
@ -419,6 +419,8 @@ static inline int acpi_dev_filter_resource_type_cb(struct acpi_resource *ares,
|
|||
return acpi_dev_filter_resource_type(ares, (unsigned long)arg);
|
||||
}
|
||||
|
||||
struct acpi_device *acpi_resource_consumer(struct resource *res);
|
||||
|
||||
int acpi_check_resource_conflict(const struct resource *res);
|
||||
|
||||
int acpi_check_region(resource_size_t start, resource_size_t n,
|
||||
|
@ -762,6 +764,11 @@ static inline int acpi_reconfig_notifier_unregister(struct notifier_block *nb)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline struct acpi_device *acpi_resource_consumer(struct resource *res)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* !CONFIG_ACPI */
|
||||
|
||||
#ifdef CONFIG_ACPI_HOTPLUG_IOAPIC
|
||||
|
|
|
@ -24,7 +24,9 @@ static inline acpi_status pci_acpi_remove_pm_notifier(struct acpi_device *dev)
|
|||
}
|
||||
extern phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle);
|
||||
|
||||
extern phys_addr_t pci_mcfg_lookup(u16 domain, struct resource *bus_res);
|
||||
struct pci_ecam_ops;
|
||||
extern int pci_mcfg_lookup(struct acpi_pci_root *root, struct resource *cfgres,
|
||||
struct pci_ecam_ops **ecam_ops);
|
||||
|
||||
static inline acpi_handle acpi_find_root_bridge_handle(struct pci_dev *pdev)
|
||||
{
|
||||
|
|
|
@ -59,6 +59,15 @@ void __iomem *pci_ecam_map_bus(struct pci_bus *bus, unsigned int devfn,
|
|||
/* default ECAM ops */
|
||||
extern struct pci_ecam_ops pci_generic_ecam_ops;
|
||||
|
||||
#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)
|
||||
extern struct pci_ecam_ops pci_32b_ops; /* 32-bit accesses only */
|
||||
extern struct pci_ecam_ops hisi_pcie_ops; /* HiSilicon */
|
||||
extern struct pci_ecam_ops thunder_pem_ecam_ops; /* Cavium ThunderX 1.x & 2.x */
|
||||
extern struct pci_ecam_ops pci_thunder_ecam_ops; /* Cavium ThunderX 1.x */
|
||||
extern struct pci_ecam_ops xgene_v1_pcie_ecam_ops; /* APM X-Gene PCIe v1 */
|
||||
extern struct pci_ecam_ops xgene_v2_pcie_ecam_ops; /* APM X-Gene PCIe v2.x */
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PCI_HOST_GENERIC
|
||||
/* for DT-based PCI controllers that support ECAM */
|
||||
int pci_host_common_probe(struct platform_device *pdev,
|
||||
|
|
Loading…
Reference in New Issue