Commit | Line | Data |
---|---|---|
935c760e TN |
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 | } |