diff options
author | Tomasz Nowicki <tn@semihalf.com> | 2016-06-10 15:55:13 -0400 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2016-06-10 19:27:59 -0400 |
commit | 935c760ec8101413248da23b6df45f0a7a643c62 (patch) | |
tree | eb8e54c07bbc7fecebcb7584ba01d9ebf27058cd | |
parent | 0a70abb3806295e039f9b2df5321cc3f7c87f4d6 (diff) |
PCI/ACPI: Add generic MCFG table handling
On ACPI systems that support memory-mapped config space access, i.e., ECAM,
the PCI Firmware Specification says the OS can learn where the ECAM space
is from either:
- the static MCFG table (for non-hotpluggable bridges), or
- the _CBA method (for hotpluggable bridges)
The current MCFG table handling code cannot be easily generalized owing to
x86-specific quirks, which makes it hard to reuse on other architectures.
Implement generic MCFG handling from scratch, including:
- Simple MCFG table parsing (via pci_mmcfg_late_init() as in current x86)
- MCFG region lookup for a (domain, bus_start, bus_end) tuple
[bhelgaas: changelog]
Signed-off-by: Tomasz Nowicki <tn@semihalf.com>
Signed-off-by: Jayachandran C <jchandra@broadcom.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
-rw-r--r-- | drivers/acpi/Kconfig | 3 | ||||
-rw-r--r-- | drivers/acpi/Makefile | 1 | ||||
-rw-r--r-- | drivers/acpi/pci_mcfg.c | 92 | ||||
-rw-r--r-- | include/linux/pci-acpi.h | 2 | ||||
-rw-r--r-- | include/linux/pci.h | 2 |
5 files changed, 99 insertions, 1 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index b7e2e776397d..f98c3287256e 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig | |||
@@ -217,6 +217,9 @@ config ACPI_PROCESSOR_IDLE | |||
217 | bool | 217 | bool |
218 | select CPU_IDLE | 218 | select CPU_IDLE |
219 | 219 | ||
220 | config ACPI_MCFG | ||
221 | bool | ||
222 | |||
220 | config ACPI_CPPC_LIB | 223 | config ACPI_CPPC_LIB |
221 | bool | 224 | bool |
222 | depends on ACPI_PROCESSOR | 225 | depends on ACPI_PROCESSOR |
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 251ce85a66fb..632e81feef69 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile | |||
@@ -40,6 +40,7 @@ acpi-$(CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC) += processor_pdc.o | |||
40 | acpi-y += ec.o | 40 | acpi-y += ec.o |
41 | acpi-$(CONFIG_ACPI_DOCK) += dock.o | 41 | acpi-$(CONFIG_ACPI_DOCK) += dock.o |
42 | acpi-y += pci_root.o pci_link.o pci_irq.o | 42 | acpi-y += pci_root.o pci_link.o pci_irq.o |
43 | obj-$(CONFIG_ACPI_MCFG) += pci_mcfg.o | ||
43 | acpi-y += acpi_lpss.o acpi_apd.o | 44 | acpi-y += acpi_lpss.o acpi_apd.o |
44 | acpi-y += acpi_platform.o | 45 | acpi-y += acpi_platform.o |
45 | acpi-y += acpi_pnp.o | 46 | acpi-y += acpi_pnp.o |
diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c new file mode 100644 index 000000000000..b5b376e081f5 --- /dev/null +++ b/drivers/acpi/pci_mcfg.c | |||
@@ -0,0 +1,92 @@ | |||
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 | } | ||
diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index 89ab0572dbc6..7d63a66e8ed4 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h | |||
@@ -24,6 +24,8 @@ static inline acpi_status pci_acpi_remove_pm_notifier(struct acpi_device *dev) | |||
24 | } | 24 | } |
25 | extern phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle); | 25 | extern phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle); |
26 | 26 | ||
27 | extern phys_addr_t pci_mcfg_lookup(u16 domain, struct resource *bus_res); | ||
28 | |||
27 | static inline acpi_handle acpi_find_root_bridge_handle(struct pci_dev *pdev) | 29 | static inline acpi_handle acpi_find_root_bridge_handle(struct pci_dev *pdev) |
28 | { | 30 | { |
29 | struct pci_bus *pbus = pdev->bus; | 31 | struct pci_bus *pbus = pdev->bus; |
diff --git a/include/linux/pci.h b/include/linux/pci.h index 12349deb688c..ce03d650279b 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h | |||
@@ -1723,7 +1723,7 @@ void pcibios_free_irq(struct pci_dev *dev); | |||
1723 | extern struct dev_pm_ops pcibios_pm_ops; | 1723 | extern struct dev_pm_ops pcibios_pm_ops; |
1724 | #endif | 1724 | #endif |
1725 | 1725 | ||
1726 | #ifdef CONFIG_PCI_MMCONFIG | 1726 | #if defined(CONFIG_PCI_MMCONFIG) || defined(CONFIG_ACPI_MCFG) |
1727 | void __init pci_mmcfg_early_init(void); | 1727 | void __init pci_mmcfg_early_init(void); |
1728 | void __init pci_mmcfg_late_init(void); | 1728 | void __init pci_mmcfg_late_init(void); |
1729 | #else | 1729 | #else |