summaryrefslogtreecommitdiffstats
path: root/arm64-pcie-quirks-xgene.patch
blob: 8e6805df66022314c8d49ac293a9db4c55a2c293 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
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