Blame view

drivers/acpi/pci_mcfg.c 8.69 KB
cb849fc5f   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
935c760ec   Tomasz Nowicki   PCI/ACPI: Add gen...
2
3
4
5
6
  /*
   * Copyright (C) 2016 Broadcom
   *	Author: Jayachandran C <jchandra@broadcom.com>
   * Copyright (C) 2016 Semihalf
   * 	Author: Tomasz Nowicki <tn@semihalf.com>
935c760ec   Tomasz Nowicki   PCI/ACPI: Add gen...
7
8
9
10
11
12
13
   */
  
  #define pr_fmt(fmt) "ACPI: " fmt
  
  #include <linux/kernel.h>
  #include <linux/pci.h>
  #include <linux/pci-acpi.h>
13983eb89   Tomasz Nowicki   PCI/ACPI: Extend ...
14
  #include <linux/pci-ecam.h>
935c760ec   Tomasz Nowicki   PCI/ACPI: Add gen...
15
16
17
18
19
20
21
22
23
  
  /* Structure to hold entries from the MCFG table */
  struct mcfg_entry {
  	struct list_head	list;
  	phys_addr_t		addr;
  	u16			segment;
  	u8			bus_start;
  	u8			bus_end;
  };
5b69b85ba   Tomasz Nowicki   PCI/ACPI: Check f...
24
25
26
27
28
29
30
  #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;
0b104773b   Rob Herring   PCI: Constify str...
31
  	const struct pci_ecam_ops *ops;
5b69b85ba   Tomasz Nowicki   PCI/ACPI: Check f...
32
33
34
35
36
37
38
39
40
41
  	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 }, */
2ca5b8ddc   Christopher Covington   PCI: Add MCFG qui...
42

4166bfe53   Jonathan Chocron   PCI: al: Add Amaz...
43
44
45
46
47
48
49
50
51
52
53
  #define AL_ECAM(table_id, rev, seg, ops) \
  	{ "AMAZON", table_id, rev, seg, MCFG_BUS_ANY, ops }
  
  	AL_ECAM("GRAVITON", 0, 0, &al_pcie_ops),
  	AL_ECAM("GRAVITON", 0, 1, &al_pcie_ops),
  	AL_ECAM("GRAVITON", 0, 2, &al_pcie_ops),
  	AL_ECAM("GRAVITON", 0, 3, &al_pcie_ops),
  	AL_ECAM("GRAVITON", 0, 4, &al_pcie_ops),
  	AL_ECAM("GRAVITON", 0, 5, &al_pcie_ops),
  	AL_ECAM("GRAVITON", 0, 6, &al_pcie_ops),
  	AL_ECAM("GRAVITON", 0, 7, &al_pcie_ops),
2ca5b8ddc   Christopher Covington   PCI: Add MCFG qui...
54
55
  #define QCOM_ECAM32(seg) \
  	{ "QCOM  ", "QDF2432 ", 1, seg, MCFG_BUS_ANY, &pci_32b_ops }
ced414a14   Bjorn Helgaas   PCI/ACPI: Tidy up...
56

2ca5b8ddc   Christopher Covington   PCI: Add MCFG qui...
57
58
59
60
61
62
63
64
  	QCOM_ECAM32(0),
  	QCOM_ECAM32(1),
  	QCOM_ECAM32(2),
  	QCOM_ECAM32(3),
  	QCOM_ECAM32(4),
  	QCOM_ECAM32(5),
  	QCOM_ECAM32(6),
  	QCOM_ECAM32(7),
5f00f1a01   Dongdong Liu   PCI: Add MCFG qui...
65
66
67
68
69
70
  
  #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 }
ced414a14   Bjorn Helgaas   PCI/ACPI: Tidy up...
71

5f00f1a01   Dongdong Liu   PCI: Add MCFG qui...
72
73
74
75
76
77
  	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),
44f22bd91   Tomasz Nowicki   PCI: Add MCFG qui...
78
79
80
  
  #define THUNDER_PEM_RES(addr, node) \
  	DEFINE_RES_MEM((addr) + ((u64) (node) << 44), 0x39 * SZ_16M)
ced414a14   Bjorn Helgaas   PCI/ACPI: Tidy up...
81

44f22bd91   Tomasz Nowicki   PCI: Add MCFG qui...
82
83
84
85
86
87
88
89
90
91
92
93
94
  #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) }
648d93fc7   Tomasz Nowicki   PCI: Add MCFG qui...
95
96
97
98
  
  #define THUNDER_ECAM_QUIRK(rev, seg)					\
  	{ "CAVIUM", "THUNDERX", rev, seg, MCFG_BUS_ANY,			\
  	&pci_thunder_ecam_ops }
ced414a14   Bjorn Helgaas   PCI/ACPI: Tidy up...
99
100
101
102
  
  	/* SoC pass2.x */
  	THUNDER_PEM_QUIRK(1, 0),
  	THUNDER_PEM_QUIRK(1, 1),
cd1837404   Tomasz Nowicki   PCI/ACPI: Add Thu...
103
  	THUNDER_ECAM_QUIRK(1, 10),
ced414a14   Bjorn Helgaas   PCI/ACPI: Tidy up...
104

648d93fc7   Tomasz Nowicki   PCI: Add MCFG qui...
105
106
107
108
109
110
111
112
113
114
115
  	/* 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),
c5d460396   Duc Dang   PCI: Add MCFG qui...
116
117
118
119
  
  #define XGENE_V1_ECAM_MCFG(rev, seg) \
  	{"APM   ", "XGENE   ", rev, seg, MCFG_BUS_ANY, \
  		&xgene_v1_pcie_ecam_ops }
ced414a14   Bjorn Helgaas   PCI/ACPI: Tidy up...
120

c5d460396   Duc Dang   PCI: Add MCFG qui...
121
122
123
  #define XGENE_V2_ECAM_MCFG(rev, seg) \
  	{"APM   ", "XGENE   ", rev, seg, MCFG_BUS_ANY, \
  		&xgene_v2_pcie_ecam_ops }
ced414a14   Bjorn Helgaas   PCI/ACPI: Tidy up...
124

c5d460396   Duc Dang   PCI: Add MCFG qui...
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
  	/* 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),
877c1a5f7   Tuan Phan   PCI/ACPI: Add Amp...
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
  
  #define ALTRA_ECAM_QUIRK(rev, seg) \
  	{ "Ampere", "Altra   ", rev, seg, MCFG_BUS_ANY, &pci_32b_read_ops }
  
  	ALTRA_ECAM_QUIRK(1, 0),
  	ALTRA_ECAM_QUIRK(1, 1),
  	ALTRA_ECAM_QUIRK(1, 2),
  	ALTRA_ECAM_QUIRK(1, 3),
  	ALTRA_ECAM_QUIRK(1, 4),
  	ALTRA_ECAM_QUIRK(1, 5),
  	ALTRA_ECAM_QUIRK(1, 6),
  	ALTRA_ECAM_QUIRK(1, 7),
  	ALTRA_ECAM_QUIRK(1, 8),
  	ALTRA_ECAM_QUIRK(1, 9),
  	ALTRA_ECAM_QUIRK(1, 10),
  	ALTRA_ECAM_QUIRK(1, 11),
  	ALTRA_ECAM_QUIRK(1, 12),
  	ALTRA_ECAM_QUIRK(1, 13),
  	ALTRA_ECAM_QUIRK(1, 14),
  	ALTRA_ECAM_QUIRK(1, 15),
5b69b85ba   Tomasz Nowicki   PCI/ACPI: Check f...
163
164
165
166
167
168
169
170
171
172
173
  };
  
  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,
c6237b210   Maximilian Luz   ACPI: Fix whitesp...
174
  		    ACPI_OEM_TABLE_ID_SIZE) &&
5b69b85ba   Tomasz Nowicki   PCI/ACPI: Check f...
175
176
177
178
179
180
181
182
183
184
185
  	    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,
0b104773b   Rob Herring   PCI: Constify str...
186
  				  const struct pci_ecam_ops **ecam_ops)
5b69b85ba   Tomasz Nowicki   PCI/ACPI: Check f...
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
  {
  #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
  ",
  				 cfgres, bus_range, *ecam_ops);
  			return;
  		}
  	}
  #endif
  }
935c760ec   Tomasz Nowicki   PCI/ACPI: Add gen...
208
209
  /* List to save MCFG entries */
  static LIST_HEAD(pci_mcfg_list);
13983eb89   Tomasz Nowicki   PCI/ACPI: Extend ...
210
  int pci_mcfg_lookup(struct acpi_pci_root *root, struct resource *cfgres,
0b104773b   Rob Herring   PCI: Constify str...
211
  		    const struct pci_ecam_ops **ecam_ops)
935c760ec   Tomasz Nowicki   PCI/ACPI: Add gen...
212
  {
0b104773b   Rob Herring   PCI: Constify str...
213
  	const struct pci_ecam_ops *ops = &pci_generic_ecam_ops;
13983eb89   Tomasz Nowicki   PCI/ACPI: Extend ...
214
215
  	struct resource *bus_res = &root->secondary;
  	u16 seg = root->segment;
935c760ec   Tomasz Nowicki   PCI/ACPI: Add gen...
216
  	struct mcfg_entry *e;
13983eb89   Tomasz Nowicki   PCI/ACPI: Extend ...
217
218
219
220
221
  	struct resource res;
  
  	/* Use address from _CBA if present, otherwise lookup MCFG */
  	if (root->mcfg_addr)
  		goto skip_lookup;
935c760ec   Tomasz Nowicki   PCI/ACPI: Add gen...
222
223
  
  	/*
53762ba81   Zhou Wang   PCI/ACPI: Fix bus...
224
  	 * We expect the range in bus_res in the coverage of MCFG bus range.
935c760ec   Tomasz Nowicki   PCI/ACPI: Add gen...
225
226
  	 */
  	list_for_each_entry(e, &pci_mcfg_list, list) {
53762ba81   Zhou Wang   PCI/ACPI: Fix bus...
227
  		if (e->segment == seg && e->bus_start <= bus_res->start &&
13983eb89   Tomasz Nowicki   PCI/ACPI: Extend ...
228
229
230
  		    e->bus_end >= bus_res->end) {
  			root->mcfg_addr = e->addr;
  		}
935c760ec   Tomasz Nowicki   PCI/ACPI: Add gen...
231
  	}
13983eb89   Tomasz Nowicki   PCI/ACPI: Extend ...
232
233
  skip_lookup:
  	memset(&res, 0, sizeof(res));
5b69b85ba   Tomasz Nowicki   PCI/ACPI: Check f...
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
  	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;
13983eb89   Tomasz Nowicki   PCI/ACPI: Extend ...
249
250
  	*cfgres = res;
  	*ecam_ops = ops;
935c760ec   Tomasz Nowicki   PCI/ACPI: Add gen...
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
  	return 0;
  }
  
  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;
  
  	if (header->length < sizeof(struct acpi_table_mcfg))
  		return -EINVAL;
  
  	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];
  
  	arr = kcalloc(n, sizeof(*arr), GFP_KERNEL);
  	if (!arr)
  		return -ENOMEM;
  
  	for (i = 0, e = arr; i < n; i++, mptr++, e++) {
  		e->segment = mptr->pci_segment;
  		e->addr =  mptr->address;
  		e->bus_start = mptr->start_bus_number;
  		e->bus_end = mptr->end_bus_number;
  		list_add(&e->list, &pci_mcfg_list);
  	}
5b69b85ba   Tomasz Nowicki   PCI/ACPI: Check f...
280
281
282
283
284
285
  #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
935c760ec   Tomasz Nowicki   PCI/ACPI: Add gen...
286
287
288
289
290
291
292
293
294
295
  	pr_info("MCFG table detected, %d entries
  ", n);
  	return 0;
  }
  
  /* Interface called by ACPI - parse and save MCFG table */
  void __init pci_mmcfg_late_init(void)
  {
  	int err = acpi_table_parse(ACPI_SIG_MCFG, pci_mcfg_parse);
  	if (err)
d24e12457   Jeremy Linton   PCI/ACPI: Tone do...
296
297
  		pr_debug("Failed to parse MCFG (%d)
  ", err);
935c760ec   Tomasz Nowicki   PCI/ACPI: Add gen...
298
  }