xref: /openbmc/linux/drivers/acpi/pci_mcfg.c (revision 293d5b43)
1 /*
2  * Copyright (C) 2016 Broadcom
3  *	Author: Jayachandran C <jchandra@broadcom.com>
4  * Copyright (C) 2016 Semihalf
5  * 	Author: Tomasz Nowicki <tn@semihalf.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License, version 2, as
9  * published by the Free Software Foundation (the "GPL").
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License version 2 (GPLv2) for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * version 2 (GPLv2) along with this source code.
18  */
19 
20 #define pr_fmt(fmt) "ACPI: " fmt
21 
22 #include <linux/kernel.h>
23 #include <linux/pci.h>
24 #include <linux/pci-acpi.h>
25 
26 /* Structure to hold entries from the MCFG table */
27 struct mcfg_entry {
28 	struct list_head	list;
29 	phys_addr_t		addr;
30 	u16			segment;
31 	u8			bus_start;
32 	u8			bus_end;
33 };
34 
35 /* List to save MCFG entries */
36 static LIST_HEAD(pci_mcfg_list);
37 
38 phys_addr_t pci_mcfg_lookup(u16 seg, struct resource *bus_res)
39 {
40 	struct mcfg_entry *e;
41 
42 	/*
43 	 * We expect exact match, unless MCFG entry end bus covers more than
44 	 * specified by caller.
45 	 */
46 	list_for_each_entry(e, &pci_mcfg_list, list) {
47 		if (e->segment == seg && e->bus_start == bus_res->start &&
48 		    e->bus_end >= bus_res->end)
49 			return e->addr;
50 	}
51 
52 	return 0;
53 }
54 
55 static __init int pci_mcfg_parse(struct acpi_table_header *header)
56 {
57 	struct acpi_table_mcfg *mcfg;
58 	struct acpi_mcfg_allocation *mptr;
59 	struct mcfg_entry *e, *arr;
60 	int i, n;
61 
62 	if (header->length < sizeof(struct acpi_table_mcfg))
63 		return -EINVAL;
64 
65 	n = (header->length - sizeof(struct acpi_table_mcfg)) /
66 					sizeof(struct acpi_mcfg_allocation);
67 	mcfg = (struct acpi_table_mcfg *)header;
68 	mptr = (struct acpi_mcfg_allocation *) &mcfg[1];
69 
70 	arr = kcalloc(n, sizeof(*arr), GFP_KERNEL);
71 	if (!arr)
72 		return -ENOMEM;
73 
74 	for (i = 0, e = arr; i < n; i++, mptr++, e++) {
75 		e->segment = mptr->pci_segment;
76 		e->addr =  mptr->address;
77 		e->bus_start = mptr->start_bus_number;
78 		e->bus_end = mptr->end_bus_number;
79 		list_add(&e->list, &pci_mcfg_list);
80 	}
81 
82 	pr_info("MCFG table detected, %d entries\n", n);
83 	return 0;
84 }
85 
86 /* Interface called by ACPI - parse and save MCFG table */
87 void __init pci_mmcfg_late_init(void)
88 {
89 	int err = acpi_table_parse(ACPI_SIG_MCFG, pci_mcfg_parse);
90 	if (err)
91 		pr_err("Failed to parse MCFG (%d)\n", err);
92 }
93