509 lines
19 KiB
Diff
509 lines
19 KiB
Diff
|
From 767b70aa55d013f0c7589955f410d488fed5776a Mon Sep 17 00:00:00 2001
|
||
|
From: Peter Robinson <pbrobinson@gmail.com>
|
||
|
Date: Tue, 5 Jul 2016 23:49:39 +0100
|
||
|
Subject: [PATCH 1/4] Some platforms may not be fully compliant with generic
|
||
|
set of PCI config accessors. For these cases we implement the way to
|
||
|
overwrite accessors set. Algorithm traverses available quirk list, matches
|
||
|
against <oem_id, oem_table_id, domain, bus number> tuple and returns
|
||
|
corresponding PCI config ops. oem_id and oem_table_id come from MCFG table
|
||
|
standard header. All quirks can be defined using DECLARE_ACPI_MCFG_FIXUP()
|
||
|
macro and kept self contained. Example:
|
||
|
|
||
|
/* Custom PCI config ops */
|
||
|
static struct pci_generic_ecam_ops foo_pci_ops = {
|
||
|
.bus_shift = 24,
|
||
|
.pci_ops = {
|
||
|
.map_bus = pci_ecam_map_bus,
|
||
|
.read = foo_ecam_config_read,
|
||
|
.write = foo_ecam_config_write,
|
||
|
}
|
||
|
};
|
||
|
|
||
|
DECLARE_ACPI_MCFG_FIXUP(&foo_pci_ops, <oem_id_str>, <oem_table_id>, <domain_nr>, <bus_nr>);
|
||
|
|
||
|
Signed-off-by: Tomasz Nowicki <tn@semihalf.com>
|
||
|
Signed-off-by: Dongdong Liu <liudongdong3@huawei.com>
|
||
|
---
|
||
|
drivers/acpi/pci_mcfg.c | 41 ++++++++++++++++++++++++++++++++++++---
|
||
|
include/asm-generic/vmlinux.lds.h | 7 +++++++
|
||
|
include/linux/pci-acpi.h | 20 +++++++++++++++++++
|
||
|
3 files changed, 65 insertions(+), 3 deletions(-)
|
||
|
|
||
|
diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c
|
||
|
index d3c3e85..deb0077 100644
|
||
|
--- a/drivers/acpi/pci_mcfg.c
|
||
|
+++ b/drivers/acpi/pci_mcfg.c
|
||
|
@@ -22,6 +22,10 @@
|
||
|
#include <linux/kernel.h>
|
||
|
#include <linux/pci.h>
|
||
|
#include <linux/pci-acpi.h>
|
||
|
+#include <linux/pci-ecam.h>
|
||
|
+
|
||
|
+/* Root pointer to the mapped MCFG table */
|
||
|
+static struct acpi_table_mcfg *mcfg_table;
|
||
|
|
||
|
/* Structure to hold entries from the MCFG table */
|
||
|
struct mcfg_entry {
|
||
|
@@ -35,6 +39,38 @@ struct mcfg_entry {
|
||
|
/* List to save mcfg entries */
|
||
|
static LIST_HEAD(pci_mcfg_list);
|
||
|
|
||
|
+extern struct pci_cfg_fixup __start_acpi_mcfg_fixups[];
|
||
|
+extern struct pci_cfg_fixup __end_acpi_mcfg_fixups[];
|
||
|
+
|
||
|
+struct pci_ecam_ops *pci_mcfg_get_ops(struct acpi_pci_root *root)
|
||
|
+{
|
||
|
+ int bus_num = root->secondary.start;
|
||
|
+ int domain = root->segment;
|
||
|
+ struct pci_cfg_fixup *f;
|
||
|
+
|
||
|
+ if (!mcfg_table)
|
||
|
+ return &pci_generic_ecam_ops;
|
||
|
+
|
||
|
+ /*
|
||
|
+ * Match against platform specific quirks and return corresponding
|
||
|
+ * CAM ops.
|
||
|
+ *
|
||
|
+ * First match against PCI topology <domain:bus> then use OEM ID and
|
||
|
+ * OEM revision from MCFG table standard header.
|
||
|
+ */
|
||
|
+ for (f = __start_acpi_mcfg_fixups; f < __end_acpi_mcfg_fixups; f++) {
|
||
|
+ if ((f->domain == domain || f->domain == PCI_MCFG_DOMAIN_ANY) &&
|
||
|
+ (f->bus_num == bus_num || f->bus_num == PCI_MCFG_BUS_ANY) &&
|
||
|
+ (!strncmp(f->oem_id, mcfg_table->header.oem_id,
|
||
|
+ ACPI_OEM_ID_SIZE)) &&
|
||
|
+ (!strncmp(f->oem_table_id, mcfg_table->header.oem_table_id,
|
||
|
+ ACPI_OEM_TABLE_ID_SIZE)))
|
||
|
+ return f->ops;
|
||
|
+ }
|
||
|
+ /* No quirks, use ECAM */
|
||
|
+ return &pci_generic_ecam_ops;
|
||
|
+}
|
||
|
+
|
||
|
phys_addr_t pci_mcfg_lookup(u16 seg, struct resource *bus_res)
|
||
|
{
|
||
|
struct mcfg_entry *e;
|
||
|
@@ -54,7 +90,6 @@ phys_addr_t pci_mcfg_lookup(u16 seg, struct resource *bus_res)
|
||
|
|
||
|
static __init int pci_mcfg_parse(struct acpi_table_header *header)
|
||
|
{
|
||
|
- struct acpi_table_mcfg *mcfg;
|
||
|
struct acpi_mcfg_allocation *mptr;
|
||
|
struct mcfg_entry *e, *arr;
|
||
|
int i, n;
|
||
|
@@ -64,8 +99,8 @@ static __init int pci_mcfg_parse(struct acpi_table_header *header)
|
||
|
|
||
|
n = (header->length - sizeof(struct acpi_table_mcfg)) /
|
||
|
sizeof(struct acpi_mcfg_allocation);
|
||
|
- mcfg = (struct acpi_table_mcfg *)header;
|
||
|
- mptr = (struct acpi_mcfg_allocation *) &mcfg[1];
|
||
|
+ mcfg_table = (struct acpi_table_mcfg *)header;
|
||
|
+ mptr = (struct acpi_mcfg_allocation *) &mcfg_table[1];
|
||
|
|
||
|
arr = kcalloc(n, sizeof(*arr), GFP_KERNEL);
|
||
|
if (!arr)
|
||
|
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
|
||
|
index 6a67ab9..43604fc 100644
|
||
|
--- a/include/asm-generic/vmlinux.lds.h
|
||
|
+++ b/include/asm-generic/vmlinux.lds.h
|
||
|
@@ -300,6 +300,13 @@
|
||
|
VMLINUX_SYMBOL(__end_pci_fixups_suspend_late) = .; \
|
||
|
} \
|
||
|
\
|
||
|
+ /* ACPI MCFG quirks */ \
|
||
|
+ .acpi_fixup : AT(ADDR(.acpi_fixup) - LOAD_OFFSET) { \
|
||
|
+ VMLINUX_SYMBOL(__start_acpi_mcfg_fixups) = .; \
|
||
|
+ *(.acpi_fixup_mcfg) \
|
||
|
+ VMLINUX_SYMBOL(__end_acpi_mcfg_fixups) = .; \
|
||
|
+ } \
|
||
|
+ \
|
||
|
/* Built-in firmware blobs */ \
|
||
|
.builtin_fw : AT(ADDR(.builtin_fw) - LOAD_OFFSET) { \
|
||
|
VMLINUX_SYMBOL(__start_builtin_fw) = .; \
|
||
|
diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h
|
||
|
index 7d63a66..c8a6559 100644
|
||
|
--- a/include/linux/pci-acpi.h
|
||
|
+++ b/include/linux/pci-acpi.h
|
||
|
@@ -25,6 +25,7 @@ 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);
|
||
|
+extern struct pci_ecam_ops *pci_mcfg_get_ops(struct acpi_pci_root *root);
|
||
|
|
||
|
static inline acpi_handle acpi_find_root_bridge_handle(struct pci_dev *pdev)
|
||
|
{
|
||
|
@@ -72,6 +73,25 @@ struct acpi_pci_root_ops {
|
||
|
int (*prepare_resources)(struct acpi_pci_root_info *info);
|
||
|
};
|
||
|
|
||
|
+struct pci_cfg_fixup {
|
||
|
+ struct pci_ecam_ops *ops;
|
||
|
+ char *oem_id;
|
||
|
+ char *oem_table_id;
|
||
|
+ int domain;
|
||
|
+ int bus_num;
|
||
|
+};
|
||
|
+
|
||
|
+#define PCI_MCFG_DOMAIN_ANY -1
|
||
|
+#define PCI_MCFG_BUS_ANY -1
|
||
|
+
|
||
|
+/* Designate a routine to fix up buggy MCFG */
|
||
|
+#define DECLARE_ACPI_MCFG_FIXUP(ops, oem_id, oem_table_id, dom, bus) \
|
||
|
+ static const struct pci_cfg_fixup \
|
||
|
+ __mcfg_fixup_##oem_id##oem_table_id##dom##bus \
|
||
|
+ __used __attribute__((__section__(".acpi_fixup_mcfg"), \
|
||
|
+ aligned((sizeof(void *))))) = \
|
||
|
+ { ops, oem_id, oem_table_id, dom, bus };
|
||
|
+
|
||
|
extern int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info);
|
||
|
extern struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root,
|
||
|
struct acpi_pci_root_ops *ops,
|
||
|
--
|
||
|
2.7.4
|
||
|
|
||
|
From 4f86a9b006b25dd7336043dab26058ed6fb2802d Mon Sep 17 00:00:00 2001
|
||
|
From: Peter Robinson <pbrobinson@gmail.com>
|
||
|
Date: Tue, 5 Jul 2016 23:52:46 +0100
|
||
|
Subject: [PATCH 2/4] pci_generic_ecam_ops is used by default. Since there are
|
||
|
platforms which have non-compliant ECAM space we need to overwrite these
|
||
|
accessors prior to PCI buses enumeration. In order to do that we call
|
||
|
pci_mcfg_get_ops to retrieve pci_ecam_ops structure so that we can use proper
|
||
|
PCI config space accessors and bus_shift.
|
||
|
|
||
|
pci_generic_ecam_ops is still used for platforms free from quirks.
|
||
|
|
||
|
Signed-off-by: Tomasz Nowicki <tn@semihalf.com>
|
||
|
---
|
||
|
arch/arm64/kernel/pci.c | 7 ++++---
|
||
|
1 file changed, 4 insertions(+), 3 deletions(-)
|
||
|
|
||
|
diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c
|
||
|
index 94cd43c..a891bda 100644
|
||
|
--- a/arch/arm64/kernel/pci.c
|
||
|
+++ b/arch/arm64/kernel/pci.c
|
||
|
@@ -139,6 +139,7 @@ pci_acpi_setup_ecam_mapping(struct acpi_pci_root *root)
|
||
|
struct pci_config_window *cfg;
|
||
|
struct resource cfgres;
|
||
|
unsigned int bsz;
|
||
|
+ struct pci_ecam_ops *ops;
|
||
|
|
||
|
/* Use address from _CBA if present, otherwise lookup MCFG */
|
||
|
if (!root->mcfg_addr)
|
||
|
@@ -150,12 +151,12 @@ pci_acpi_setup_ecam_mapping(struct acpi_pci_root *root)
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
- bsz = 1 << pci_generic_ecam_ops.bus_shift;
|
||
|
+ ops = pci_mcfg_get_ops(root);
|
||
|
+ bsz = 1 << 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);
|
||
|
+ cfg = pci_ecam_create(&root->device->dev, &cfgres, bus_res, ops);
|
||
|
if (IS_ERR(cfg)) {
|
||
|
dev_err(&root->device->dev, "%04x:%pR error %ld mapping ECAM\n",
|
||
|
seg, bus_res, PTR_ERR(cfg));
|
||
|
--
|
||
|
2.7.4
|
||
|
|
||
|
From cbdbd697bd6d716eb9d1705ee55445432e73eabb Mon Sep 17 00:00:00 2001
|
||
|
From: Peter Robinson <pbrobinson@gmail.com>
|
||
|
Date: Tue, 5 Jul 2016 23:53:59 +0100
|
||
|
Subject: [PATCH 3/4] The ECAM quirk matching criteria per the discussion on
|
||
|
https://lkml.org/lkml/2016/6/13/944 includes: OEM ID, OEM Table ID and OEM
|
||
|
Revision. So this patch adds OEM Table ID into the check to match platform
|
||
|
specific ECAM quirks as well.
|
||
|
|
||
|
This patch also improve strncmp check using strlen and
|
||
|
min_t to ignore the padding spaces in OEM ID and OEM
|
||
|
Table ID.
|
||
|
|
||
|
Signed-off-by: Duc Dang <dhdang@apm.com>
|
||
|
---
|
||
|
drivers/acpi/pci_mcfg.c | 7 +++++--
|
||
|
include/linux/pci-acpi.h | 7 ++++---
|
||
|
2 files changed, 9 insertions(+), 5 deletions(-)
|
||
|
|
||
|
diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c
|
||
|
index deb0077..307ca9a 100644
|
||
|
--- a/drivers/acpi/pci_mcfg.c
|
||
|
+++ b/drivers/acpi/pci_mcfg.c
|
||
|
@@ -62,9 +62,12 @@ struct pci_ecam_ops *pci_mcfg_get_ops(struct acpi_pci_root *root)
|
||
|
if ((f->domain == domain || f->domain == PCI_MCFG_DOMAIN_ANY) &&
|
||
|
(f->bus_num == bus_num || f->bus_num == PCI_MCFG_BUS_ANY) &&
|
||
|
(!strncmp(f->oem_id, mcfg_table->header.oem_id,
|
||
|
- ACPI_OEM_ID_SIZE)) &&
|
||
|
+ min_t(size_t, strlen(f->oem_id),
|
||
|
+ ACPI_OEM_ID_SIZE))) &&
|
||
|
(!strncmp(f->oem_table_id, mcfg_table->header.oem_table_id,
|
||
|
- ACPI_OEM_TABLE_ID_SIZE)))
|
||
|
+ min_t(size_t, strlen(f->oem_table_id),
|
||
|
+ ACPI_OEM_TABLE_ID_SIZE))) &&
|
||
|
+ (f->oem_revision == mcfg_table->header.oem_revision))
|
||
|
return f->ops;
|
||
|
}
|
||
|
/* No quirks, use ECAM */
|
||
|
diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h
|
||
|
index c8a6559..5148c8d 100644
|
||
|
--- a/include/linux/pci-acpi.h
|
||
|
+++ b/include/linux/pci-acpi.h
|
||
|
@@ -77,6 +77,7 @@ struct pci_cfg_fixup {
|
||
|
struct pci_ecam_ops *ops;
|
||
|
char *oem_id;
|
||
|
char *oem_table_id;
|
||
|
+ u32 oem_revision;
|
||
|
int domain;
|
||
|
int bus_num;
|
||
|
};
|
||
|
@@ -85,12 +86,12 @@ struct pci_cfg_fixup {
|
||
|
#define PCI_MCFG_BUS_ANY -1
|
||
|
|
||
|
/* Designate a routine to fix up buggy MCFG */
|
||
|
-#define DECLARE_ACPI_MCFG_FIXUP(ops, oem_id, oem_table_id, dom, bus) \
|
||
|
+#define DECLARE_ACPI_MCFG_FIXUP(ops, oem_id, oem_table_id, rev, dom, bus) \
|
||
|
static const struct pci_cfg_fixup \
|
||
|
- __mcfg_fixup_##oem_id##oem_table_id##dom##bus \
|
||
|
+ __mcfg_fixup_##oem_id##oem_table_id##rev##dom##bus \
|
||
|
__used __attribute__((__section__(".acpi_fixup_mcfg"), \
|
||
|
aligned((sizeof(void *))))) = \
|
||
|
- { ops, oem_id, oem_table_id, dom, bus };
|
||
|
+ { ops, oem_id, oem_table_id, rev, dom, bus };
|
||
|
|
||
|
extern int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info);
|
||
|
extern struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root,
|
||
|
--
|
||
|
2.7.4
|
||
|
|
||
|
From 78766cf255bc6aafac2f57372a0446f78322da19 Mon Sep 17 00:00:00 2001
|
||
|
From: Peter Robinson <pbrobinson@gmail.com>
|
||
|
Date: Tue, 5 Jul 2016 23:55:11 +0100
|
||
|
Subject: [PATCH 4/4] X-Gene PCIe controller does not fully support ECAM. This
|
||
|
patch adds required ECAM fixup to allow X-Gene PCIe controller to be
|
||
|
functional in ACPI boot mode.
|
||
|
|
||
|
Signed-off-by: Duc Dang <dhdang@apm.com>
|
||
|
---
|
||
|
drivers/pci/host/Makefile | 2 +-
|
||
|
drivers/pci/host/pci-xgene-ecam.c | 194 ++++++++++++++++++++++++++++++++++++++
|
||
|
2 files changed, 195 insertions(+), 1 deletion(-)
|
||
|
create mode 100644 drivers/pci/host/pci-xgene-ecam.c
|
||
|
|
||
|
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
|
||
|
index 9c8698e..3480696 100644
|
||
|
--- a/drivers/pci/host/Makefile
|
||
|
+++ b/drivers/pci/host/Makefile
|
||
|
@@ -14,7 +14,7 @@ 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) += pci-xgene.o pci-xgene-ecam.o
|
||
|
obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
|
||
|
obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
|
||
|
obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
|
||
|
diff --git a/drivers/pci/host/pci-xgene-ecam.c b/drivers/pci/host/pci-xgene-ecam.c
|
||
|
new file mode 100644
|
||
|
index 0000000..1bea63f
|
||
|
--- /dev/null
|
||
|
+++ b/drivers/pci/host/pci-xgene-ecam.c
|
||
|
@@ -0,0 +1,194 @@
|
||
|
+/*
|
||
|
+ * APM X-Gene PCIe ECAM fixup driver
|
||
|
+ *
|
||
|
+ * Copyright (c) 2016, Applied Micro Circuits Corporation
|
||
|
+ * Author:
|
||
|
+ * Duc Dang <dhdang@apm.com>
|
||
|
+ *
|
||
|
+ * 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/>.
|
||
|
+ */
|
||
|
+
|
||
|
+#include <linux/kernel.h>
|
||
|
+#include <linux/module.h>
|
||
|
+#include <linux/of_address.h>
|
||
|
+#include <linux/of_pci.h>
|
||
|
+#include <linux/pci-acpi.h>
|
||
|
+#include <linux/platform_device.h>
|
||
|
+#include <linux/pci-ecam.h>
|
||
|
+
|
||
|
+#ifdef CONFIG_ACPI
|
||
|
+#define RTDID 0x160
|
||
|
+#define ROOT_CAP_AND_CTRL 0x5C
|
||
|
+
|
||
|
+/* PCIe IP version */
|
||
|
+#define XGENE_PCIE_IP_VER_UNKN 0
|
||
|
+#define XGENE_PCIE_IP_VER_1 1
|
||
|
+
|
||
|
+#define APM_OEM_ID "APM"
|
||
|
+#define APM_XGENE_OEM_TABLE_ID "XGENE"
|
||
|
+#define APM_XGENE_OEM_REV 0x00000002
|
||
|
+
|
||
|
+struct xgene_pcie_acpi_root {
|
||
|
+ void __iomem *csr_base;
|
||
|
+ u32 version;
|
||
|
+};
|
||
|
+
|
||
|
+static acpi_status xgene_pcie_find_csr_base(struct acpi_resource *acpi_res,
|
||
|
+ void *data)
|
||
|
+{
|
||
|
+ struct xgene_pcie_acpi_root *root = data;
|
||
|
+ struct acpi_resource_fixed_memory32 *fixed32;
|
||
|
+
|
||
|
+ if (acpi_res->type == ACPI_RESOURCE_TYPE_FIXED_MEMORY32) {
|
||
|
+ fixed32 = &acpi_res->data.fixed_memory32;
|
||
|
+ root->csr_base = ioremap(fixed32->address,
|
||
|
+ fixed32->address_length);
|
||
|
+ return AE_CTRL_TERMINATE;
|
||
|
+ }
|
||
|
+
|
||
|
+ return AE_OK;
|
||
|
+}
|
||
|
+
|
||
|
+static int xgene_pcie_ecam_init(struct pci_config_window *cfg)
|
||
|
+{
|
||
|
+ struct xgene_pcie_acpi_root *xgene_root;
|
||
|
+ struct device *dev = cfg->parent;
|
||
|
+ struct acpi_device *adev = to_acpi_device(dev);
|
||
|
+ acpi_handle handle = acpi_device_handle(adev);
|
||
|
+
|
||
|
+ xgene_root = devm_kzalloc(dev, sizeof(*xgene_root), GFP_KERNEL);
|
||
|
+ if (!xgene_root)
|
||
|
+ return -ENOMEM;
|
||
|
+
|
||
|
+ acpi_walk_resources(handle, METHOD_NAME__CRS,
|
||
|
+ xgene_pcie_find_csr_base, xgene_root);
|
||
|
+
|
||
|
+ if (!xgene_root->csr_base) {
|
||
|
+ kfree(xgene_root);
|
||
|
+ return -ENODEV;
|
||
|
+ }
|
||
|
+
|
||
|
+ xgene_root->version = XGENE_PCIE_IP_VER_1;
|
||
|
+
|
||
|
+ cfg->priv = xgene_root;
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+/*
|
||
|
+ * For Configuration request, RTDID register is used as Bus Number,
|
||
|
+ * Device Number and Function number of the header fields.
|
||
|
+ */
|
||
|
+static void xgene_pcie_set_rtdid_reg(struct pci_bus *bus, uint devfn)
|
||
|
+{
|
||
|
+ struct pci_config_window *cfg = bus->sysdata;
|
||
|
+ struct xgene_pcie_acpi_root *port = cfg->priv;
|
||
|
+ unsigned int b, d, f;
|
||
|
+ u32 rtdid_val = 0;
|
||
|
+
|
||
|
+ b = bus->number;
|
||
|
+ d = PCI_SLOT(devfn);
|
||
|
+ f = PCI_FUNC(devfn);
|
||
|
+
|
||
|
+ if (!pci_is_root_bus(bus))
|
||
|
+ rtdid_val = (b << 8) | (d << 3) | f;
|
||
|
+
|
||
|
+ writel(rtdid_val, port->csr_base + RTDID);
|
||
|
+ /* read the register back to ensure flush */
|
||
|
+ readl(port->csr_base + RTDID);
|
||
|
+}
|
||
|
+
|
||
|
+/*
|
||
|
+ * X-Gene PCIe port uses BAR0-BAR1 of RC's configuration space as
|
||
|
+ * the translation from PCI bus to native BUS. Entire DDR region
|
||
|
+ * is mapped into PCIe space using these registers, so it can be
|
||
|
+ * reached by DMA from EP devices. The BAR0/1 of bridge should be
|
||
|
+ * hidden during enumeration to avoid the sizing and resource allocation
|
||
|
+ * by PCIe core.
|
||
|
+ */
|
||
|
+static bool xgene_pcie_hide_rc_bars(struct pci_bus *bus, int offset)
|
||
|
+{
|
||
|
+ if (pci_is_root_bus(bus) && ((offset == PCI_BASE_ADDRESS_0) ||
|
||
|
+ (offset == PCI_BASE_ADDRESS_1)))
|
||
|
+ return true;
|
||
|
+
|
||
|
+ return false;
|
||
|
+}
|
||
|
+
|
||
|
+void __iomem *xgene_pcie_ecam_map_bus(struct pci_bus *bus,
|
||
|
+ unsigned int devfn, int where)
|
||
|
+{
|
||
|
+ struct pci_config_window *cfg = bus->sysdata;
|
||
|
+ unsigned int busn = bus->number;
|
||
|
+ void __iomem *base;
|
||
|
+
|
||
|
+ if (busn < cfg->busr.start || busn > cfg->busr.end)
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ if ((pci_is_root_bus(bus) && devfn != 0) ||
|
||
|
+ xgene_pcie_hide_rc_bars(bus, where))
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ xgene_pcie_set_rtdid_reg(bus, devfn);
|
||
|
+
|
||
|
+ if (busn > cfg->busr.start)
|
||
|
+ base = cfg->win + (1 << cfg->ops->bus_shift);
|
||
|
+ else
|
||
|
+ base = cfg->win;
|
||
|
+
|
||
|
+ return base + where;
|
||
|
+}
|
||
|
+
|
||
|
+static int xgene_pcie_config_read32(struct pci_bus *bus, unsigned int devfn,
|
||
|
+ int where, int size, u32 *val)
|
||
|
+{
|
||
|
+ struct pci_config_window *cfg = bus->sysdata;
|
||
|
+ struct xgene_pcie_acpi_root *port = cfg->priv;
|
||
|
+
|
||
|
+ if (pci_generic_config_read32(bus, devfn, where & ~0x3, 4, val) !=
|
||
|
+ PCIBIOS_SUCCESSFUL)
|
||
|
+ return PCIBIOS_DEVICE_NOT_FOUND;
|
||
|
+
|
||
|
+ /*
|
||
|
+ * The v1 controller has a bug in its Configuration Request
|
||
|
+ * Retry Status (CRS) logic: when CRS is enabled and we read the
|
||
|
+ * Vendor and Device ID of a non-existent device, the controller
|
||
|
+ * fabricates return data of 0xFFFF0001 ("device exists but is not
|
||
|
+ * ready") instead of 0xFFFFFFFF ("device does not exist"). This
|
||
|
+ * causes the PCI core to retry the read until it times out.
|
||
|
+ * Avoid this by not claiming to support CRS.
|
||
|
+ */
|
||
|
+ if (pci_is_root_bus(bus) && (port->version == XGENE_PCIE_IP_VER_1) &&
|
||
|
+ ((where & ~0x3) == ROOT_CAP_AND_CTRL))
|
||
|
+ *val &= ~(PCI_EXP_RTCAP_CRSVIS << 16);
|
||
|
+
|
||
|
+ if (size <= 2)
|
||
|
+ *val = (*val >> (8 * (where & 3))) & ((1 << (size * 8)) - 1);
|
||
|
+
|
||
|
+ return PCIBIOS_SUCCESSFUL;
|
||
|
+}
|
||
|
+
|
||
|
+static struct pci_ecam_ops xgene_pcie_ecam_ops = {
|
||
|
+ .bus_shift = 16,
|
||
|
+ .init = xgene_pcie_ecam_init,
|
||
|
+ .pci_ops = {
|
||
|
+ .map_bus = xgene_pcie_ecam_map_bus,
|
||
|
+ .read = xgene_pcie_config_read32,
|
||
|
+ .write = pci_generic_config_write,
|
||
|
+ }
|
||
|
+};
|
||
|
+
|
||
|
+DECLARE_ACPI_MCFG_FIXUP(&xgene_pcie_ecam_ops, APM_OEM_ID,
|
||
|
+ APM_XGENE_OEM_TABLE_ID, APM_XGENE_OEM_REV,
|
||
|
+ PCI_MCFG_DOMAIN_ANY, PCI_MCFG_BUS_ANY);
|
||
|
+#endif
|
||
|
--
|
||
|
2.7.4
|
||
|
|