linux/drivers/acpi/pci_mcfg.c
<<
>>
Prefs
   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 */
  27struct 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 */
  36static LIST_HEAD(pci_mcfg_list);
  37
  38phys_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
  55static __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 */
  87void __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