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