| Tomasz Nowicki | 935c760 | 2016-06-10 21:55:13 +0200 | [diff] [blame] | 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 | } |