All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v6 0/5] ACPI based PCI support for arm64
@ 2016-01-14  7:32 ` Jayachandran C
  0 siblings, 0 replies; 31+ messages in thread
From: Jayachandran C @ 2016-01-14  7:32 UTC (permalink / raw)
  To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann,
	linux-arm-kernel, Rafael J. Wysocki
  Cc: Jayachandran C, Lorenzo Pieralisi, Tomasz Nowicki, xen-devel

This patchset provides a generic ACPI based PCI host controller
implementation and uses it on arm64.

The first patch moves the common code to handle MCFG ACPI table from
arch/x86 to drivers/acpi/pci_mcfg.c. The last patch in the patchset
provides the generic implementation of an ACPI based PCI host
controller with a new file drivers/acpi/pci_host_acpi.c. The other
patches are to fix up arm64 and ACPI code to work with these two
patches.

The pci host controller implementation keeps a reference to
pci_mmcfg_region entry so that config space access is done with a
simple mapping and generic PCI config read/write. There is also an
implementation of raw_pci_read/raw_pci_write provided by walking the
pci_mmcfg_list

The patchset is against 4.4, but it can be applied to pci/next
with a very minor fixup.  This is tested with arm64 QEMU and OVMF
and on x86 with qemu.

More testing or comments are welcome. The idea is to provide a
simpler alternative for ACPI PCI support in arm64, any feedback
from PCI or ACPI maintainers would be appreciated on this front.

Thanks,
JC.

v5->v6:
 - fix arm64 with xen compile issue reported by kbuild bot

v4->v5:
 - Fix arm build issue reported by the kbuild bot
 - Use config_enabled instead of ifdef in pci_mcfg.c

v3->v4:
 - Handle suggestions from Arnd Bergmann <arnd@arndb.de>
    * move the implementation of host controller to drivers/acpi
      under config option CONFIG_ACPI_PCI_HOST_GENERIC
    * remove unnecessary arch hooks (this done as much as possible
      without affecting the scope of the patch, will need another
      separate patchset for the rest).
    * remove pcibios_assign_resources fs_initcall that was added
 - fixup domain_nr assignment code, and move ACPI companion set
   to PCI code.
 - Call map resource correctly in setup code
 - fix kbuild robot report
 - More testing

v2->v3:
 - Move maintenance of the pci_mmcfg_list to drivers/acpi/pci_mcfg.c
   without changing x86 logic
 - use the pci_mmcfg_list in ARM64 implementation
 - provide raw_pci_read/raw_pci_write

v1->v2:
 - use CONFIG_PCI_MMCONFIG on arm64, provide a weak implementation
   of pci_mmcfg_late_init for arm64.
 - The real implementation of pci_mmcfg_late_init is in pci-host-acpi.c
   and it will save the MCFG table entries to an array. Earlier this
   was done with an arch_init call
 - remove unneeded pci_bus_add_devices call and fix MCFG saving code
 - Added a patch to ACPI pci_root.c to handle arm64 PCI IO resources

Jayachandran C (5):
  APCI: MCFG: Move mmcfg_list management to drivers/acpi
  PCI: Handle ACPI companion and domain number
  ACPI: PCI: Support platforms that need pci_remap_iospace
  arm64: pci: Add ACPI support
  PCI: ACPI: Add a generic ACPI based host controller

 arch/arm64/Kconfig             |   3 +
 arch/arm64/kernel/pci.c        |  34 ++++-
 arch/x86/include/asm/pci_x86.h |  24 +---
 arch/x86/pci/mmconfig-shared.c | 269 +++++------------------------------
 arch/x86/pci/mmconfig_32.c     |   1 +
 arch/x86/pci/mmconfig_64.c     |   1 +
 arch/x86/pci/numachip.c        |   1 +
 drivers/acpi/Kconfig           |   8 ++
 drivers/acpi/Makefile          |   2 +
 drivers/acpi/pci_host_acpi.c   | 186 ++++++++++++++++++++++++
 drivers/acpi/pci_mcfg.c        | 312 +++++++++++++++++++++++++++++++++++++++++
 drivers/acpi/pci_root.c        |  62 +++++++-
 drivers/acpi/resource.c        |   2 +
 drivers/pci/pci.c              |  15 +-
 drivers/pci/probe.c            |   2 +
 drivers/xen/pci.c              |   5 +-
 include/linux/pci-acpi.h       |  59 ++++++++
 17 files changed, 717 insertions(+), 269 deletions(-)
 create mode 100644 drivers/acpi/pci_host_acpi.c
 create mode 100644 drivers/acpi/pci_mcfg.c

-- 
1.9.1


^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH v6 0/5] ACPI based PCI support for arm64
@ 2016-01-14  7:32 ` Jayachandran C
  0 siblings, 0 replies; 31+ messages in thread
From: Jayachandran C @ 2016-01-14  7:32 UTC (permalink / raw)
  To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann,
	linux-arm-kernel, Rafael J. Wysocki
  Cc: Jayachandran C, Lorenzo Pieralisi, Tomasz Nowicki, xen-devel

This patchset provides a generic ACPI based PCI host controller
implementation and uses it on arm64.

The first patch moves the common code to handle MCFG ACPI table from
arch/x86 to drivers/acpi/pci_mcfg.c. The last patch in the patchset
provides the generic implementation of an ACPI based PCI host
controller with a new file drivers/acpi/pci_host_acpi.c. The other
patches are to fix up arm64 and ACPI code to work with these two
patches.

The pci host controller implementation keeps a reference to
pci_mmcfg_region entry so that config space access is done with a
simple mapping and generic PCI config read/write. There is also an
implementation of raw_pci_read/raw_pci_write provided by walking the
pci_mmcfg_list

The patchset is against 4.4, but it can be applied to pci/next
with a very minor fixup.  This is tested with arm64 QEMU and OVMF
and on x86 with qemu.

More testing or comments are welcome. The idea is to provide a
simpler alternative for ACPI PCI support in arm64, any feedback
from PCI or ACPI maintainers would be appreciated on this front.

Thanks,
JC.

v5->v6:
 - fix arm64 with xen compile issue reported by kbuild bot

v4->v5:
 - Fix arm build issue reported by the kbuild bot
 - Use config_enabled instead of ifdef in pci_mcfg.c

v3->v4:
 - Handle suggestions from Arnd Bergmann <arnd@arndb.de>
    * move the implementation of host controller to drivers/acpi
      under config option CONFIG_ACPI_PCI_HOST_GENERIC
    * remove unnecessary arch hooks (this done as much as possible
      without affecting the scope of the patch, will need another
      separate patchset for the rest).
    * remove pcibios_assign_resources fs_initcall that was added
 - fixup domain_nr assignment code, and move ACPI companion set
   to PCI code.
 - Call map resource correctly in setup code
 - fix kbuild robot report
 - More testing

v2->v3:
 - Move maintenance of the pci_mmcfg_list to drivers/acpi/pci_mcfg.c
   without changing x86 logic
 - use the pci_mmcfg_list in ARM64 implementation
 - provide raw_pci_read/raw_pci_write

v1->v2:
 - use CONFIG_PCI_MMCONFIG on arm64, provide a weak implementation
   of pci_mmcfg_late_init for arm64.
 - The real implementation of pci_mmcfg_late_init is in pci-host-acpi.c
   and it will save the MCFG table entries to an array. Earlier this
   was done with an arch_init call
 - remove unneeded pci_bus_add_devices call and fix MCFG saving code
 - Added a patch to ACPI pci_root.c to handle arm64 PCI IO resources

Jayachandran C (5):
  APCI: MCFG: Move mmcfg_list management to drivers/acpi
  PCI: Handle ACPI companion and domain number
  ACPI: PCI: Support platforms that need pci_remap_iospace
  arm64: pci: Add ACPI support
  PCI: ACPI: Add a generic ACPI based host controller

 arch/arm64/Kconfig             |   3 +
 arch/arm64/kernel/pci.c        |  34 ++++-
 arch/x86/include/asm/pci_x86.h |  24 +---
 arch/x86/pci/mmconfig-shared.c | 269 +++++------------------------------
 arch/x86/pci/mmconfig_32.c     |   1 +
 arch/x86/pci/mmconfig_64.c     |   1 +
 arch/x86/pci/numachip.c        |   1 +
 drivers/acpi/Kconfig           |   8 ++
 drivers/acpi/Makefile          |   2 +
 drivers/acpi/pci_host_acpi.c   | 186 ++++++++++++++++++++++++
 drivers/acpi/pci_mcfg.c        | 312 +++++++++++++++++++++++++++++++++++++++++
 drivers/acpi/pci_root.c        |  62 +++++++-
 drivers/acpi/resource.c        |   2 +
 drivers/pci/pci.c              |  15 +-
 drivers/pci/probe.c            |   2 +
 drivers/xen/pci.c              |   5 +-
 include/linux/pci-acpi.h       |  59 ++++++++
 17 files changed, 717 insertions(+), 269 deletions(-)
 create mode 100644 drivers/acpi/pci_host_acpi.c
 create mode 100644 drivers/acpi/pci_mcfg.c

-- 
1.9.1


^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH v6 0/5] ACPI based PCI support for arm64
@ 2016-01-14  7:32 ` Jayachandran C
  0 siblings, 0 replies; 31+ messages in thread
From: Jayachandran C @ 2016-01-14  7:32 UTC (permalink / raw)
  To: linux-arm-kernel

This patchset provides a generic ACPI based PCI host controller
implementation and uses it on arm64.

The first patch moves the common code to handle MCFG ACPI table from
arch/x86 to drivers/acpi/pci_mcfg.c. The last patch in the patchset
provides the generic implementation of an ACPI based PCI host
controller with a new file drivers/acpi/pci_host_acpi.c. The other
patches are to fix up arm64 and ACPI code to work with these two
patches.

The pci host controller implementation keeps a reference to
pci_mmcfg_region entry so that config space access is done with a
simple mapping and generic PCI config read/write. There is also an
implementation of raw_pci_read/raw_pci_write provided by walking the
pci_mmcfg_list

The patchset is against 4.4, but it can be applied to pci/next
with a very minor fixup.  This is tested with arm64 QEMU and OVMF
and on x86 with qemu.

More testing or comments are welcome. The idea is to provide a
simpler alternative for ACPI PCI support in arm64, any feedback
from PCI or ACPI maintainers would be appreciated on this front.

Thanks,
JC.

v5->v6:
 - fix arm64 with xen compile issue reported by kbuild bot

v4->v5:
 - Fix arm build issue reported by the kbuild bot
 - Use config_enabled instead of ifdef in pci_mcfg.c

v3->v4:
 - Handle suggestions from Arnd Bergmann <arnd@arndb.de>
    * move the implementation of host controller to drivers/acpi
      under config option CONFIG_ACPI_PCI_HOST_GENERIC
    * remove unnecessary arch hooks (this done as much as possible
      without affecting the scope of the patch, will need another
      separate patchset for the rest).
    * remove pcibios_assign_resources fs_initcall that was added
 - fixup domain_nr assignment code, and move ACPI companion set
   to PCI code.
 - Call map resource correctly in setup code
 - fix kbuild robot report
 - More testing

v2->v3:
 - Move maintenance of the pci_mmcfg_list to drivers/acpi/pci_mcfg.c
   without changing x86 logic
 - use the pci_mmcfg_list in ARM64 implementation
 - provide raw_pci_read/raw_pci_write

v1->v2:
 - use CONFIG_PCI_MMCONFIG on arm64, provide a weak implementation
   of pci_mmcfg_late_init for arm64.
 - The real implementation of pci_mmcfg_late_init is in pci-host-acpi.c
   and it will save the MCFG table entries to an array. Earlier this
   was done with an arch_init call
 - remove unneeded pci_bus_add_devices call and fix MCFG saving code
 - Added a patch to ACPI pci_root.c to handle arm64 PCI IO resources

Jayachandran C (5):
  APCI: MCFG: Move mmcfg_list management to drivers/acpi
  PCI: Handle ACPI companion and domain number
  ACPI: PCI: Support platforms that need pci_remap_iospace
  arm64: pci: Add ACPI support
  PCI: ACPI: Add a generic ACPI based host controller

 arch/arm64/Kconfig             |   3 +
 arch/arm64/kernel/pci.c        |  34 ++++-
 arch/x86/include/asm/pci_x86.h |  24 +---
 arch/x86/pci/mmconfig-shared.c | 269 +++++------------------------------
 arch/x86/pci/mmconfig_32.c     |   1 +
 arch/x86/pci/mmconfig_64.c     |   1 +
 arch/x86/pci/numachip.c        |   1 +
 drivers/acpi/Kconfig           |   8 ++
 drivers/acpi/Makefile          |   2 +
 drivers/acpi/pci_host_acpi.c   | 186 ++++++++++++++++++++++++
 drivers/acpi/pci_mcfg.c        | 312 +++++++++++++++++++++++++++++++++++++++++
 drivers/acpi/pci_root.c        |  62 +++++++-
 drivers/acpi/resource.c        |   2 +
 drivers/pci/pci.c              |  15 +-
 drivers/pci/probe.c            |   2 +
 drivers/xen/pci.c              |   5 +-
 include/linux/pci-acpi.h       |  59 ++++++++
 17 files changed, 717 insertions(+), 269 deletions(-)
 create mode 100644 drivers/acpi/pci_host_acpi.c
 create mode 100644 drivers/acpi/pci_mcfg.c

-- 
1.9.1

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH v6 1/5] APCI: MCFG: Move mmcfg_list management to drivers/acpi
  2016-01-14  7:32 ` Jayachandran C
  (?)
@ 2016-01-14  7:32   ` Jayachandran C
  -1 siblings, 0 replies; 31+ messages in thread
From: Jayachandran C @ 2016-01-14  7:32 UTC (permalink / raw)
  To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann,
	linux-arm-kernel, Rafael J. Wysocki
  Cc: Jayachandran C, Lorenzo Pieralisi, Tomasz Nowicki, xen-devel

Move pci_mmcfg_list handling to a drivers/acpi/pci_mcfg.c. This is
to share the API and code with ARM64 later. The corresponding
declarations are moved from asm/pci_x86.h to linux/pci-acpi.h

As a part of this we introduce three functions that can be
implemented by the arch code: pci_mmconfig_map_resource() to map a
mcfg entry, pci_mmconfig_unmap_resource to do the corresponding
unmap and pci_mmconfig_enabled to see if the arch setup of
mcfg entries was successful. We also provide weak implementations
of these, which will be used from ARM64. On x86, we retain the
old logic by providing platform specific implementation.

This patch is purely rearranging code, it should not have any
impact on the logic of MCFG parsing or list handling.

Signed-off-by: Jayachandran C <jchandra@broadcom.com>
---
 arch/x86/include/asm/pci_x86.h |  24 +---
 arch/x86/pci/mmconfig-shared.c | 269 +++++------------------------------
 arch/x86/pci/mmconfig_32.c     |   1 +
 arch/x86/pci/mmconfig_64.c     |   1 +
 arch/x86/pci/numachip.c        |   1 +
 drivers/acpi/Makefile          |   1 +
 drivers/acpi/pci_mcfg.c        | 312 +++++++++++++++++++++++++++++++++++++++++
 drivers/xen/pci.c              |   5 +-
 include/linux/pci-acpi.h       |  33 +++++
 9 files changed, 386 insertions(+), 261 deletions(-)
 create mode 100644 drivers/acpi/pci_mcfg.c

diff --git a/arch/x86/include/asm/pci_x86.h b/arch/x86/include/asm/pci_x86.h
index fa1195d..83d74cb 100644
--- a/arch/x86/include/asm/pci_x86.h
+++ b/arch/x86/include/asm/pci_x86.h
@@ -122,33 +122,11 @@ extern int pci_legacy_init(void);
 extern void pcibios_fixup_irqs(void);
 
 /* pci-mmconfig.c */
-
-/* "PCI MMCONFIG %04x [bus %02x-%02x]" */
-#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2)
-
-struct pci_mmcfg_region {
-	struct list_head list;
-	struct resource res;
-	u64 address;
-	char __iomem *virt;
-	u16 segment;
-	u8 start_bus;
-	u8 end_bus;
-	char name[PCI_MMCFG_RESOURCE_NAME_LEN];
-};
-
+struct pci_mmcfg_region;
 extern int __init pci_mmcfg_arch_init(void);
 extern void __init pci_mmcfg_arch_free(void);
 extern int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg);
 extern void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg);
-extern int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
-			       phys_addr_t addr);
-extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end);
-extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus);
-
-extern struct list_head pci_mmcfg_list;
-
-#define PCI_MMCFG_BUS_OFFSET(bus)      ((bus) << 20)
 
 /*
  * AMD Fam10h CPUs are buggy, and cannot access MMIO config space
diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c
index dd30b7e..626710b 100644
--- a/arch/x86/pci/mmconfig-shared.c
+++ b/arch/x86/pci/mmconfig-shared.c
@@ -12,13 +12,12 @@
 
 #include <linux/pci.h>
 #include <linux/init.h>
-#include <linux/sfi_acpi.h>
 #include <linux/bitmap.h>
-#include <linux/dmi.h>
 #include <linux/slab.h>
 #include <linux/mutex.h>
 #include <linux/rculist.h>
 #include <asm/e820.h>
+#include <linux/pci-acpi.h>
 #include <asm/pci_x86.h>
 #include <asm/acpi.h>
 
@@ -27,9 +26,6 @@
 /* Indicate if the mmcfg resources have been placed into the resource table. */
 static bool pci_mmcfg_running_state;
 static bool pci_mmcfg_arch_init_failed;
-static DEFINE_MUTEX(pci_mmcfg_lock);
-
-LIST_HEAD(pci_mmcfg_list);
 
 static void __init pci_mmconfig_remove(struct pci_mmcfg_region *cfg)
 {
@@ -48,83 +44,6 @@ static void __init free_all_mmcfg(void)
 		pci_mmconfig_remove(cfg);
 }
 
-static void list_add_sorted(struct pci_mmcfg_region *new)
-{
-	struct pci_mmcfg_region *cfg;
-
-	/* keep list sorted by segment and starting bus number */
-	list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) {
-		if (cfg->segment > new->segment ||
-		    (cfg->segment == new->segment &&
-		     cfg->start_bus >= new->start_bus)) {
-			list_add_tail_rcu(&new->list, &cfg->list);
-			return;
-		}
-	}
-	list_add_tail_rcu(&new->list, &pci_mmcfg_list);
-}
-
-static struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start,
-						   int end, u64 addr)
-{
-	struct pci_mmcfg_region *new;
-	struct resource *res;
-
-	if (addr == 0)
-		return NULL;
-
-	new = kzalloc(sizeof(*new), GFP_KERNEL);
-	if (!new)
-		return NULL;
-
-	new->address = addr;
-	new->segment = segment;
-	new->start_bus = start;
-	new->end_bus = end;
-
-	res = &new->res;
-	res->start = addr + PCI_MMCFG_BUS_OFFSET(start);
-	res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1;
-	res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
-	snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN,
-		 "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end);
-	res->name = new->name;
-
-	return new;
-}
-
-static struct pci_mmcfg_region *__init pci_mmconfig_add(int segment, int start,
-							int end, u64 addr)
-{
-	struct pci_mmcfg_region *new;
-
-	new = pci_mmconfig_alloc(segment, start, end, addr);
-	if (new) {
-		mutex_lock(&pci_mmcfg_lock);
-		list_add_sorted(new);
-		mutex_unlock(&pci_mmcfg_lock);
-
-		pr_info(PREFIX
-		       "MMCONFIG for domain %04x [bus %02x-%02x] at %pR "
-		       "(base %#lx)\n",
-		       segment, start, end, &new->res, (unsigned long)addr);
-	}
-
-	return new;
-}
-
-struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus)
-{
-	struct pci_mmcfg_region *cfg;
-
-	list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
-		if (cfg->segment == segment &&
-		    cfg->start_bus <= bus && bus <= cfg->end_bus)
-			return cfg;
-
-	return NULL;
-}
-
 static const char *__init pci_mmcfg_e7520(void)
 {
 	u32 win;
@@ -543,73 +462,6 @@ static void __init pci_mmcfg_reject_broken(int early)
 	}
 }
 
-static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg,
-					struct acpi_mcfg_allocation *cfg)
-{
-	int year;
-
-	if (cfg->address < 0xFFFFFFFF)
-		return 0;
-
-	if (!strncmp(mcfg->header.oem_id, "SGI", 3))
-		return 0;
-
-	if (mcfg->header.revision >= 1) {
-		if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) &&
-		    year >= 2010)
-			return 0;
-	}
-
-	pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx "
-	       "is above 4GB, ignored\n", cfg->pci_segment,
-	       cfg->start_bus_number, cfg->end_bus_number, cfg->address);
-	return -EINVAL;
-}
-
-static int __init pci_parse_mcfg(struct acpi_table_header *header)
-{
-	struct acpi_table_mcfg *mcfg;
-	struct acpi_mcfg_allocation *cfg_table, *cfg;
-	unsigned long i;
-	int entries;
-
-	if (!header)
-		return -EINVAL;
-
-	mcfg = (struct acpi_table_mcfg *)header;
-
-	/* how many config structures do we have */
-	free_all_mmcfg();
-	entries = 0;
-	i = header->length - sizeof(struct acpi_table_mcfg);
-	while (i >= sizeof(struct acpi_mcfg_allocation)) {
-		entries++;
-		i -= sizeof(struct acpi_mcfg_allocation);
-	}
-	if (entries == 0) {
-		pr_err(PREFIX "MMCONFIG has no entries\n");
-		return -ENODEV;
-	}
-
-	cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1];
-	for (i = 0; i < entries; i++) {
-		cfg = &cfg_table[i];
-		if (acpi_mcfg_check_entry(mcfg, cfg)) {
-			free_all_mmcfg();
-			return -ENODEV;
-		}
-
-		if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number,
-				   cfg->end_bus_number, cfg->address) == NULL) {
-			pr_warn(PREFIX "no memory for MCFG entries\n");
-			free_all_mmcfg();
-			return -ENOMEM;
-		}
-	}
-
-	return 0;
-}
-
 #ifdef CONFIG_ACPI_APEI
 extern int (*arch_apei_filter_addr)(int (*func)(__u64 start, __u64 size,
 				     void *data), void *data);
@@ -662,13 +514,20 @@ static void __init __pci_mmcfg_init(int early)
 
 static int __initdata known_bridge;
 
+static void __init pci_mmcfg_list_setup(void)
+{
+	free_all_mmcfg();
+	if (pci_mmconfig_parse_table())
+		free_all_mmcfg();
+}
+
 void __init pci_mmcfg_early_init(void)
 {
 	if (pci_probe & PCI_PROBE_MMCONF) {
 		if (pci_mmcfg_check_hostbridge())
 			known_bridge = 1;
 		else
-			acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
+			pci_mmcfg_list_setup();
 		__pci_mmcfg_init(1);
 
 		set_apei_filter();
@@ -686,7 +545,7 @@ void __init pci_mmcfg_late_init(void)
 
 	/* MMCONFIG hasn't been enabled yet, try again */
 	if (pci_probe & PCI_PROBE_MASK & ~PCI_PROBE_MMCONF) {
-		acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
+		pci_mmcfg_list_setup();
 		__pci_mmcfg_init(0);
 	}
 }
@@ -720,99 +579,41 @@ static int __init pci_mmcfg_late_insert_resources(void)
  */
 late_initcall(pci_mmcfg_late_insert_resources);
 
-/* Add MMCFG information for host bridges */
-int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
-			phys_addr_t addr)
+int pci_mmconfig_map_resource(struct device *dev, struct pci_mmcfg_region *cfg)
 {
-	int rc;
-	struct resource *tmp = NULL;
-	struct pci_mmcfg_region *cfg;
+	struct resource *tmp;
 
-	if (!(pci_probe & PCI_PROBE_MMCONF) || pci_mmcfg_arch_init_failed)
-		return -ENODEV;
-
-	if (start > end)
-		return -EINVAL;
-
-	mutex_lock(&pci_mmcfg_lock);
-	cfg = pci_mmconfig_lookup(seg, start);
-	if (cfg) {
-		if (cfg->end_bus < end)
-			dev_info(dev, FW_INFO
-				 "MMCONFIG for "
-				 "domain %04x [bus %02x-%02x] "
-				 "only partially covers this bridge\n",
-				  cfg->segment, cfg->start_bus, cfg->end_bus);
-		mutex_unlock(&pci_mmcfg_lock);
-		return -EEXIST;
-	}
-
-	if (!addr) {
-		mutex_unlock(&pci_mmcfg_lock);
-		return -EINVAL;
-	}
-
-	rc = -EBUSY;
-	cfg = pci_mmconfig_alloc(seg, start, end, addr);
-	if (cfg == NULL) {
-		dev_warn(dev, "fail to add MMCONFIG (out of memory)\n");
-		rc = -ENOMEM;
-	} else if (!pci_mmcfg_check_reserved(dev, cfg, 0)) {
+	if (!pci_mmcfg_check_reserved(dev, cfg, 0)) {
 		dev_warn(dev, FW_BUG "MMCONFIG %pR isn't reserved\n",
 			 &cfg->res);
-	} else {
-		/* Insert resource if it's not in boot stage */
-		if (pci_mmcfg_running_state)
-			tmp = insert_resource_conflict(&iomem_resource,
-						       &cfg->res);
-
+		return -EBUSY;
+	}
+	/* Insert resource if it's not in boot stage */
+	if (pci_mmcfg_running_state) {
+		tmp = insert_resource_conflict(&iomem_resource,
+					       &cfg->res);
 		if (tmp) {
-			dev_warn(dev,
-				 "MMCONFIG %pR conflicts with "
-				 "%s %pR\n",
-				 &cfg->res, tmp->name, tmp);
-		} else if (pci_mmcfg_arch_map(cfg)) {
-			dev_warn(dev, "fail to map MMCONFIG %pR.\n",
-				 &cfg->res);
-		} else {
-			list_add_sorted(cfg);
-			dev_info(dev, "MMCONFIG at %pR (base %#lx)\n",
-				 &cfg->res, (unsigned long)addr);
-			cfg = NULL;
-			rc = 0;
+			dev_warn(dev, "MMCONFIG %pR conflicts with %s %pR\n",
+				&cfg->res, tmp->name, tmp);
+			return -EBUSY;
 		}
 	}
-
-	if (cfg) {
-		if (cfg->res.parent)
-			release_resource(&cfg->res);
-		kfree(cfg);
+	if (pci_mmcfg_arch_map(cfg)) {
+		dev_warn(dev, "fail to map MMCONFIG %pR.\n", &cfg->res);
+		return -EBUSY;
 	}
-
-	mutex_unlock(&pci_mmcfg_lock);
-
-	return rc;
+	return 0;
 }
 
-/* Delete MMCFG information for host bridges */
-int pci_mmconfig_delete(u16 seg, u8 start, u8 end)
+void pci_mmconfig_unmap_resource(struct pci_mmcfg_region *cfg)
 {
-	struct pci_mmcfg_region *cfg;
-
-	mutex_lock(&pci_mmcfg_lock);
-	list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
-		if (cfg->segment == seg && cfg->start_bus == start &&
-		    cfg->end_bus == end) {
-			list_del_rcu(&cfg->list);
-			synchronize_rcu();
-			pci_mmcfg_arch_unmap(cfg);
-			if (cfg->res.parent)
-				release_resource(&cfg->res);
-			mutex_unlock(&pci_mmcfg_lock);
-			kfree(cfg);
-			return 0;
-		}
-	mutex_unlock(&pci_mmcfg_lock);
+	pci_mmcfg_arch_unmap(cfg);
+	if (cfg->res.parent)
+		release_resource(&cfg->res);
+	cfg->res.parent = NULL;
+}
 
-	return -ENOENT;
+int pci_mmconfig_enabled(void)
+{
+	return (pci_probe & PCI_PROBE_MMCONF) && !pci_mmcfg_arch_init_failed;
 }
diff --git a/arch/x86/pci/mmconfig_32.c b/arch/x86/pci/mmconfig_32.c
index 43984bc..38a37f8 100644
--- a/arch/x86/pci/mmconfig_32.c
+++ b/arch/x86/pci/mmconfig_32.c
@@ -12,6 +12,7 @@
 #include <linux/pci.h>
 #include <linux/init.h>
 #include <linux/rcupdate.h>
+#include <linux/pci-acpi.h>
 #include <asm/e820.h>
 #include <asm/pci_x86.h>
 
diff --git a/arch/x86/pci/mmconfig_64.c b/arch/x86/pci/mmconfig_64.c
index bea5249..29253ec 100644
--- a/arch/x86/pci/mmconfig_64.c
+++ b/arch/x86/pci/mmconfig_64.c
@@ -10,6 +10,7 @@
 #include <linux/acpi.h>
 #include <linux/bitmap.h>
 #include <linux/rcupdate.h>
+#include <linux/pci-acpi.h>
 #include <asm/e820.h>
 #include <asm/pci_x86.h>
 
diff --git a/arch/x86/pci/numachip.c b/arch/x86/pci/numachip.c
index 2e565e6..c181eeb 100644
--- a/arch/x86/pci/numachip.c
+++ b/arch/x86/pci/numachip.c
@@ -14,6 +14,7 @@
  */
 
 #include <linux/pci.h>
+#include <linux/pci-acpi.h>
 #include <asm/pci_x86.h>
 
 static u8 limit __read_mostly;
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 675eaf3..3f65311 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -40,6 +40,7 @@ acpi-$(CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC) += processor_pdc.o
 acpi-y				+= ec.o
 acpi-$(CONFIG_ACPI_DOCK)	+= dock.o
 acpi-y				+= pci_root.o pci_link.o pci_irq.o
+acpi-$(CONFIG_PCI_MMCONFIG)	+= pci_mcfg.o
 acpi-y				+= acpi_lpss.o acpi_apd.o
 acpi-y				+= acpi_platform.o
 acpi-y				+= acpi_pnp.o
diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c
new file mode 100644
index 0000000..ea84365
--- /dev/null
+++ b/drivers/acpi/pci_mcfg.c
@@ -0,0 +1,312 @@
+/*
+ * pci_mcfg.c
+ *
+ * Common code to maintain the MCFG areas and mappings
+ *
+ * This has been extracted from arch/x86/pci/mmconfig-shared.c
+ * and moved here so that other architectures can use this code.
+ */
+
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/dmi.h>
+#include <linux/pci-acpi.h>
+#include <linux/sfi_acpi.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/rculist.h>
+
+#define PREFIX	"ACPI: "
+
+static DEFINE_MUTEX(pci_mmcfg_lock);
+LIST_HEAD(pci_mmcfg_list);
+
+static void list_add_sorted(struct pci_mmcfg_region *new)
+{
+	struct pci_mmcfg_region *cfg;
+
+	/* keep list sorted by segment and starting bus number */
+	list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) {
+		if (cfg->segment > new->segment ||
+		    (cfg->segment == new->segment &&
+		     cfg->start_bus >= new->start_bus)) {
+			list_add_tail_rcu(&new->list, &cfg->list);
+			return;
+		}
+	}
+	list_add_tail_rcu(&new->list, &pci_mmcfg_list);
+}
+
+static struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start,
+						   int end, u64 addr)
+{
+	struct pci_mmcfg_region *new;
+	struct resource *res;
+
+	if (addr == 0)
+		return NULL;
+
+	new = kzalloc(sizeof(*new), GFP_KERNEL);
+	if (!new)
+		return NULL;
+
+	new->address = addr;
+	new->segment = segment;
+	new->start_bus = start;
+	new->end_bus = end;
+
+	res = &new->res;
+	res->start = addr + PCI_MMCFG_BUS_OFFSET(start);
+	res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1;
+	res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+	snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN,
+		 "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end);
+	res->name = new->name;
+
+	return new;
+}
+
+struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start,
+							int end, u64 addr)
+{
+	struct pci_mmcfg_region *new;
+
+	new = pci_mmconfig_alloc(segment, start, end, addr);
+	if (new) {
+		mutex_lock(&pci_mmcfg_lock);
+		list_add_sorted(new);
+		mutex_unlock(&pci_mmcfg_lock);
+
+		pr_info(PREFIX
+		       "MMCONFIG for domain %04x [bus %02x-%02x] at %pR "
+		       "(base %#lx)\n",
+		       segment, start, end, &new->res, (unsigned long)addr);
+	}
+
+	return new;
+}
+
+struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus)
+{
+	struct pci_mmcfg_region *cfg;
+
+	list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
+		if (cfg->segment == segment &&
+		    cfg->start_bus <= bus && bus <= cfg->end_bus)
+			return cfg;
+
+	return NULL;
+}
+
+/*
+ * Map a pci_mmcfg_region, can be overrriden by arch
+ */
+int __weak pci_mmconfig_map_resource(struct device *dev,
+	struct pci_mmcfg_region *mcfg)
+{
+	struct resource *tmp;
+	void __iomem *vaddr;
+
+	tmp = insert_resource_conflict(&iomem_resource, &mcfg->res);
+	if (tmp) {
+		dev_warn(dev, "MMCONFIG %pR conflicts with %s %pR\n",
+			&mcfg->res, tmp->name, tmp);
+		return -EBUSY;
+	}
+
+	vaddr =  ioremap(mcfg->res.start, resource_size(&mcfg->res));
+	if (!vaddr) {
+		release_resource(&mcfg->res);
+		return -ENOMEM;
+	}
+
+	mcfg->virt = vaddr;
+	return 0;
+}
+
+/*
+ * Unmap a pci_mmcfg_region, can be overrriden by arch
+ */
+void __weak pci_mmconfig_unmap_resource(struct pci_mmcfg_region *mcfg)
+{
+	if (mcfg->virt) {
+		iounmap(mcfg->virt);
+		mcfg->virt = NULL;
+	}
+	if (mcfg->res.parent) {
+		release_resource(&mcfg->res);
+		mcfg->res.parent = NULL;
+	}
+}
+
+/*
+ * check if the mmconfig is enabled and configured
+ */
+int __weak pci_mmconfig_enabled(void)
+{
+	return 1;
+}
+
+/* Add MMCFG information for host bridges */
+int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
+			phys_addr_t addr)
+{
+	struct pci_mmcfg_region *cfg;
+	int rc;
+
+	if (!pci_mmconfig_enabled())
+		return -ENODEV;
+	if (start > end)
+		return -EINVAL;
+
+	mutex_lock(&pci_mmcfg_lock);
+	cfg = pci_mmconfig_lookup(seg, start);
+	if (cfg) {
+		if (cfg->end_bus < end)
+			dev_info(dev, FW_INFO
+				 "MMCONFIG for "
+				 "domain %04x [bus %02x-%02x] "
+				 "only partially covers this bridge\n",
+				  cfg->segment, cfg->start_bus, cfg->end_bus);
+		rc = -EEXIST;
+		goto err;
+	}
+
+	if (!addr) {
+		rc = -EINVAL;
+		goto err;
+	}
+
+	cfg = pci_mmconfig_alloc(seg, start, end, addr);
+	if (cfg == NULL) {
+		dev_warn(dev, "fail to add MMCONFIG (out of memory)\n");
+		rc = -ENOMEM;
+		goto err;
+	}
+	rc = pci_mmconfig_map_resource(dev, cfg);
+	if (!rc) {
+		list_add_sorted(cfg);
+		dev_info(dev, "MMCONFIG at %pR (base %#lx)\n",
+				 &cfg->res, (unsigned long)addr);
+		return 0;
+	} else {
+		if (cfg->res.parent)
+			release_resource(&cfg->res);
+		kfree(cfg);
+	}
+
+err:
+	mutex_unlock(&pci_mmcfg_lock);
+	return rc;
+}
+
+/* Delete MMCFG information for host bridges */
+int pci_mmconfig_delete(u16 seg, u8 start, u8 end)
+{
+	struct pci_mmcfg_region *cfg;
+
+	mutex_lock(&pci_mmcfg_lock);
+	list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
+		if (cfg->segment == seg && cfg->start_bus == start &&
+		    cfg->end_bus == end) {
+			list_del_rcu(&cfg->list);
+			synchronize_rcu();
+			pci_mmconfig_unmap_resource(cfg);
+			mutex_unlock(&pci_mmcfg_lock);
+			kfree(cfg);
+			return 0;
+		}
+	mutex_unlock(&pci_mmcfg_lock);
+
+	return -ENOENT;
+}
+
+static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg,
+					struct acpi_mcfg_allocation *cfg)
+{
+	int year;
+
+	if (!config_enabled(CONFIG_X86))
+		return 0;
+
+	if (cfg->address < 0xFFFFFFFF)
+		return 0;
+
+	if (!strncmp(mcfg->header.oem_id, "SGI", 3))
+		return 0;
+
+	if (mcfg->header.revision >= 1) {
+		if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) &&
+		    year >= 2010)
+			return 0;
+	}
+
+	pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx "
+	       "is above 4GB, ignored\n", cfg->pci_segment,
+	       cfg->start_bus_number, cfg->end_bus_number, cfg->address);
+	return -EINVAL;
+}
+
+static int __init pci_parse_mcfg(struct acpi_table_header *header)
+{
+	struct acpi_table_mcfg *mcfg;
+	struct acpi_mcfg_allocation *cfg_table, *cfg;
+	unsigned long i;
+	int entries;
+
+	if (!header)
+		return -EINVAL;
+
+	mcfg = (struct acpi_table_mcfg *)header;
+
+	/* how many config structures do we have */
+	entries = 0;
+	i = header->length - sizeof(struct acpi_table_mcfg);
+	while (i >= sizeof(struct acpi_mcfg_allocation)) {
+		entries++;
+		i -= sizeof(struct acpi_mcfg_allocation);
+	}
+	if (entries == 0) {
+		pr_err(PREFIX "MMCONFIG has no entries\n");
+		return -ENODEV;
+	}
+
+	cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1];
+	for (i = 0; i < entries; i++) {
+		cfg = &cfg_table[i];
+		if (acpi_mcfg_check_entry(mcfg, cfg))
+			return -ENODEV;
+
+		if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number,
+				   cfg->end_bus_number, cfg->address) == NULL) {
+			pr_warn(PREFIX "no memory for MCFG entries\n");
+			return -ENOMEM;
+		}
+	}
+
+	return 0;
+}
+
+int __init pci_mmconfig_parse_table(void)
+{
+	return acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
+}
+
+void __weak __init pci_mmcfg_late_init(void)
+{
+	int err, n = 0;
+	struct pci_mmcfg_region *cfg;
+
+	err = pci_mmconfig_parse_table();
+	if (err) {
+		pr_err(PREFIX " Failed to parse MCFG (%d)\n", err);
+		return;
+	}
+
+	list_for_each_entry(cfg, &pci_mmcfg_list, list) {
+		pci_mmconfig_map_resource(NULL, cfg);
+		n++;
+	}
+
+	pr_info(PREFIX " MCFG table loaded %d entries\n", n);
+}
diff --git a/drivers/xen/pci.c b/drivers/xen/pci.c
index 7494dbe..97aa9d3 100644
--- a/drivers/xen/pci.c
+++ b/drivers/xen/pci.c
@@ -27,9 +27,6 @@
 #include <asm/xen/hypervisor.h>
 #include <asm/xen/hypercall.h>
 #include "../pci/pci.h"
-#ifdef CONFIG_PCI_MMCONFIG
-#include <asm/pci_x86.h>
-#endif
 
 static bool __read_mostly pci_seg_supported = true;
 
@@ -221,7 +218,7 @@ static int __init xen_mcfg_late(void)
 	if (!xen_initial_domain())
 		return 0;
 
-	if ((pci_probe & PCI_PROBE_MMCONF) == 0)
+	if (!pci_mmconfig_enabled())
 		return 0;
 
 	if (list_empty(&pci_mmcfg_list))
diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h
index 89ab057..e9450ef 100644
--- a/include/linux/pci-acpi.h
+++ b/include/linux/pci-acpi.h
@@ -106,6 +106,39 @@ extern const u8 pci_acpi_dsm_uuid[];
 #define RESET_DELAY_DSM		0x08
 #define FUNCTION_DELAY_DSM	0x09
 
+/* common API to maintain list of MCFG regions */
+/* "PCI MMCONFIG %04x [bus %02x-%02x]" */
+#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2)
+
+struct pci_mmcfg_region {
+	struct list_head list;
+	struct resource res;
+	u64 address;
+	char __iomem *virt;
+	u16 segment;
+	u8 start_bus;
+	u8 end_bus;
+	char name[PCI_MMCFG_RESOURCE_NAME_LEN];
+};
+
+extern int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
+			       phys_addr_t addr);
+extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end);
+
+extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus);
+extern struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start,
+							int end, u64 addr);
+extern int pci_mmconfig_map_resource(struct device *dev,
+	struct pci_mmcfg_region *mcfg);
+extern void pci_mmconfig_unmap_resource(struct pci_mmcfg_region *mcfg);
+extern int pci_mmconfig_enabled(void);
+extern int __init pci_mmconfig_parse_table(void);
+
+extern struct list_head pci_mmcfg_list;
+
+#define PCI_MMCFG_BUS_OFFSET(bus)      ((bus) << 20)
+#define PCI_MMCFG_OFFSET(bus, devfn)   ((bus) << 20 | (devfn) << 12)
+
 #else	/* CONFIG_ACPI */
 static inline void acpi_pci_add_bus(struct pci_bus *bus) { }
 static inline void acpi_pci_remove_bus(struct pci_bus *bus) { }
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v6 1/5] APCI: MCFG: Move mmcfg_list management to drivers/acpi
@ 2016-01-14  7:32   ` Jayachandran C
  0 siblings, 0 replies; 31+ messages in thread
From: Jayachandran C @ 2016-01-14  7:32 UTC (permalink / raw)
  To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann,
	linux-arm-kernel, Rafael J. Wysocki
  Cc: Jayachandran C, Lorenzo Pieralisi, Tomasz Nowicki, xen-devel

Move pci_mmcfg_list handling to a drivers/acpi/pci_mcfg.c. This is
to share the API and code with ARM64 later. The corresponding
declarations are moved from asm/pci_x86.h to linux/pci-acpi.h

As a part of this we introduce three functions that can be
implemented by the arch code: pci_mmconfig_map_resource() to map a
mcfg entry, pci_mmconfig_unmap_resource to do the corresponding
unmap and pci_mmconfig_enabled to see if the arch setup of
mcfg entries was successful. We also provide weak implementations
of these, which will be used from ARM64. On x86, we retain the
old logic by providing platform specific implementation.

This patch is purely rearranging code, it should not have any
impact on the logic of MCFG parsing or list handling.

Signed-off-by: Jayachandran C <jchandra@broadcom.com>
---
 arch/x86/include/asm/pci_x86.h |  24 +---
 arch/x86/pci/mmconfig-shared.c | 269 +++++------------------------------
 arch/x86/pci/mmconfig_32.c     |   1 +
 arch/x86/pci/mmconfig_64.c     |   1 +
 arch/x86/pci/numachip.c        |   1 +
 drivers/acpi/Makefile          |   1 +
 drivers/acpi/pci_mcfg.c        | 312 +++++++++++++++++++++++++++++++++++++++++
 drivers/xen/pci.c              |   5 +-
 include/linux/pci-acpi.h       |  33 +++++
 9 files changed, 386 insertions(+), 261 deletions(-)
 create mode 100644 drivers/acpi/pci_mcfg.c

diff --git a/arch/x86/include/asm/pci_x86.h b/arch/x86/include/asm/pci_x86.h
index fa1195d..83d74cb 100644
--- a/arch/x86/include/asm/pci_x86.h
+++ b/arch/x86/include/asm/pci_x86.h
@@ -122,33 +122,11 @@ extern int pci_legacy_init(void);
 extern void pcibios_fixup_irqs(void);
 
 /* pci-mmconfig.c */
-
-/* "PCI MMCONFIG %04x [bus %02x-%02x]" */
-#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2)
-
-struct pci_mmcfg_region {
-	struct list_head list;
-	struct resource res;
-	u64 address;
-	char __iomem *virt;
-	u16 segment;
-	u8 start_bus;
-	u8 end_bus;
-	char name[PCI_MMCFG_RESOURCE_NAME_LEN];
-};
-
+struct pci_mmcfg_region;
 extern int __init pci_mmcfg_arch_init(void);
 extern void __init pci_mmcfg_arch_free(void);
 extern int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg);
 extern void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg);
-extern int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
-			       phys_addr_t addr);
-extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end);
-extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus);
-
-extern struct list_head pci_mmcfg_list;
-
-#define PCI_MMCFG_BUS_OFFSET(bus)      ((bus) << 20)
 
 /*
  * AMD Fam10h CPUs are buggy, and cannot access MMIO config space
diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c
index dd30b7e..626710b 100644
--- a/arch/x86/pci/mmconfig-shared.c
+++ b/arch/x86/pci/mmconfig-shared.c
@@ -12,13 +12,12 @@
 
 #include <linux/pci.h>
 #include <linux/init.h>
-#include <linux/sfi_acpi.h>
 #include <linux/bitmap.h>
-#include <linux/dmi.h>
 #include <linux/slab.h>
 #include <linux/mutex.h>
 #include <linux/rculist.h>
 #include <asm/e820.h>
+#include <linux/pci-acpi.h>
 #include <asm/pci_x86.h>
 #include <asm/acpi.h>
 
@@ -27,9 +26,6 @@
 /* Indicate if the mmcfg resources have been placed into the resource table. */
 static bool pci_mmcfg_running_state;
 static bool pci_mmcfg_arch_init_failed;
-static DEFINE_MUTEX(pci_mmcfg_lock);
-
-LIST_HEAD(pci_mmcfg_list);
 
 static void __init pci_mmconfig_remove(struct pci_mmcfg_region *cfg)
 {
@@ -48,83 +44,6 @@ static void __init free_all_mmcfg(void)
 		pci_mmconfig_remove(cfg);
 }
 
-static void list_add_sorted(struct pci_mmcfg_region *new)
-{
-	struct pci_mmcfg_region *cfg;
-
-	/* keep list sorted by segment and starting bus number */
-	list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) {
-		if (cfg->segment > new->segment ||
-		    (cfg->segment == new->segment &&
-		     cfg->start_bus >= new->start_bus)) {
-			list_add_tail_rcu(&new->list, &cfg->list);
-			return;
-		}
-	}
-	list_add_tail_rcu(&new->list, &pci_mmcfg_list);
-}
-
-static struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start,
-						   int end, u64 addr)
-{
-	struct pci_mmcfg_region *new;
-	struct resource *res;
-
-	if (addr == 0)
-		return NULL;
-
-	new = kzalloc(sizeof(*new), GFP_KERNEL);
-	if (!new)
-		return NULL;
-
-	new->address = addr;
-	new->segment = segment;
-	new->start_bus = start;
-	new->end_bus = end;
-
-	res = &new->res;
-	res->start = addr + PCI_MMCFG_BUS_OFFSET(start);
-	res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1;
-	res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
-	snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN,
-		 "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end);
-	res->name = new->name;
-
-	return new;
-}
-
-static struct pci_mmcfg_region *__init pci_mmconfig_add(int segment, int start,
-							int end, u64 addr)
-{
-	struct pci_mmcfg_region *new;
-
-	new = pci_mmconfig_alloc(segment, start, end, addr);
-	if (new) {
-		mutex_lock(&pci_mmcfg_lock);
-		list_add_sorted(new);
-		mutex_unlock(&pci_mmcfg_lock);
-
-		pr_info(PREFIX
-		       "MMCONFIG for domain %04x [bus %02x-%02x] at %pR "
-		       "(base %#lx)\n",
-		       segment, start, end, &new->res, (unsigned long)addr);
-	}
-
-	return new;
-}
-
-struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus)
-{
-	struct pci_mmcfg_region *cfg;
-
-	list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
-		if (cfg->segment == segment &&
-		    cfg->start_bus <= bus && bus <= cfg->end_bus)
-			return cfg;
-
-	return NULL;
-}
-
 static const char *__init pci_mmcfg_e7520(void)
 {
 	u32 win;
@@ -543,73 +462,6 @@ static void __init pci_mmcfg_reject_broken(int early)
 	}
 }
 
-static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg,
-					struct acpi_mcfg_allocation *cfg)
-{
-	int year;
-
-	if (cfg->address < 0xFFFFFFFF)
-		return 0;
-
-	if (!strncmp(mcfg->header.oem_id, "SGI", 3))
-		return 0;
-
-	if (mcfg->header.revision >= 1) {
-		if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) &&
-		    year >= 2010)
-			return 0;
-	}
-
-	pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx "
-	       "is above 4GB, ignored\n", cfg->pci_segment,
-	       cfg->start_bus_number, cfg->end_bus_number, cfg->address);
-	return -EINVAL;
-}
-
-static int __init pci_parse_mcfg(struct acpi_table_header *header)
-{
-	struct acpi_table_mcfg *mcfg;
-	struct acpi_mcfg_allocation *cfg_table, *cfg;
-	unsigned long i;
-	int entries;
-
-	if (!header)
-		return -EINVAL;
-
-	mcfg = (struct acpi_table_mcfg *)header;
-
-	/* how many config structures do we have */
-	free_all_mmcfg();
-	entries = 0;
-	i = header->length - sizeof(struct acpi_table_mcfg);
-	while (i >= sizeof(struct acpi_mcfg_allocation)) {
-		entries++;
-		i -= sizeof(struct acpi_mcfg_allocation);
-	}
-	if (entries == 0) {
-		pr_err(PREFIX "MMCONFIG has no entries\n");
-		return -ENODEV;
-	}
-
-	cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1];
-	for (i = 0; i < entries; i++) {
-		cfg = &cfg_table[i];
-		if (acpi_mcfg_check_entry(mcfg, cfg)) {
-			free_all_mmcfg();
-			return -ENODEV;
-		}
-
-		if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number,
-				   cfg->end_bus_number, cfg->address) == NULL) {
-			pr_warn(PREFIX "no memory for MCFG entries\n");
-			free_all_mmcfg();
-			return -ENOMEM;
-		}
-	}
-
-	return 0;
-}
-
 #ifdef CONFIG_ACPI_APEI
 extern int (*arch_apei_filter_addr)(int (*func)(__u64 start, __u64 size,
 				     void *data), void *data);
@@ -662,13 +514,20 @@ static void __init __pci_mmcfg_init(int early)
 
 static int __initdata known_bridge;
 
+static void __init pci_mmcfg_list_setup(void)
+{
+	free_all_mmcfg();
+	if (pci_mmconfig_parse_table())
+		free_all_mmcfg();
+}
+
 void __init pci_mmcfg_early_init(void)
 {
 	if (pci_probe & PCI_PROBE_MMCONF) {
 		if (pci_mmcfg_check_hostbridge())
 			known_bridge = 1;
 		else
-			acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
+			pci_mmcfg_list_setup();
 		__pci_mmcfg_init(1);
 
 		set_apei_filter();
@@ -686,7 +545,7 @@ void __init pci_mmcfg_late_init(void)
 
 	/* MMCONFIG hasn't been enabled yet, try again */
 	if (pci_probe & PCI_PROBE_MASK & ~PCI_PROBE_MMCONF) {
-		acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
+		pci_mmcfg_list_setup();
 		__pci_mmcfg_init(0);
 	}
 }
@@ -720,99 +579,41 @@ static int __init pci_mmcfg_late_insert_resources(void)
  */
 late_initcall(pci_mmcfg_late_insert_resources);
 
-/* Add MMCFG information for host bridges */
-int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
-			phys_addr_t addr)
+int pci_mmconfig_map_resource(struct device *dev, struct pci_mmcfg_region *cfg)
 {
-	int rc;
-	struct resource *tmp = NULL;
-	struct pci_mmcfg_region *cfg;
+	struct resource *tmp;
 
-	if (!(pci_probe & PCI_PROBE_MMCONF) || pci_mmcfg_arch_init_failed)
-		return -ENODEV;
-
-	if (start > end)
-		return -EINVAL;
-
-	mutex_lock(&pci_mmcfg_lock);
-	cfg = pci_mmconfig_lookup(seg, start);
-	if (cfg) {
-		if (cfg->end_bus < end)
-			dev_info(dev, FW_INFO
-				 "MMCONFIG for "
-				 "domain %04x [bus %02x-%02x] "
-				 "only partially covers this bridge\n",
-				  cfg->segment, cfg->start_bus, cfg->end_bus);
-		mutex_unlock(&pci_mmcfg_lock);
-		return -EEXIST;
-	}
-
-	if (!addr) {
-		mutex_unlock(&pci_mmcfg_lock);
-		return -EINVAL;
-	}
-
-	rc = -EBUSY;
-	cfg = pci_mmconfig_alloc(seg, start, end, addr);
-	if (cfg == NULL) {
-		dev_warn(dev, "fail to add MMCONFIG (out of memory)\n");
-		rc = -ENOMEM;
-	} else if (!pci_mmcfg_check_reserved(dev, cfg, 0)) {
+	if (!pci_mmcfg_check_reserved(dev, cfg, 0)) {
 		dev_warn(dev, FW_BUG "MMCONFIG %pR isn't reserved\n",
 			 &cfg->res);
-	} else {
-		/* Insert resource if it's not in boot stage */
-		if (pci_mmcfg_running_state)
-			tmp = insert_resource_conflict(&iomem_resource,
-						       &cfg->res);
-
+		return -EBUSY;
+	}
+	/* Insert resource if it's not in boot stage */
+	if (pci_mmcfg_running_state) {
+		tmp = insert_resource_conflict(&iomem_resource,
+					       &cfg->res);
 		if (tmp) {
-			dev_warn(dev,
-				 "MMCONFIG %pR conflicts with "
-				 "%s %pR\n",
-				 &cfg->res, tmp->name, tmp);
-		} else if (pci_mmcfg_arch_map(cfg)) {
-			dev_warn(dev, "fail to map MMCONFIG %pR.\n",
-				 &cfg->res);
-		} else {
-			list_add_sorted(cfg);
-			dev_info(dev, "MMCONFIG at %pR (base %#lx)\n",
-				 &cfg->res, (unsigned long)addr);
-			cfg = NULL;
-			rc = 0;
+			dev_warn(dev, "MMCONFIG %pR conflicts with %s %pR\n",
+				&cfg->res, tmp->name, tmp);
+			return -EBUSY;
 		}
 	}
-
-	if (cfg) {
-		if (cfg->res.parent)
-			release_resource(&cfg->res);
-		kfree(cfg);
+	if (pci_mmcfg_arch_map(cfg)) {
+		dev_warn(dev, "fail to map MMCONFIG %pR.\n", &cfg->res);
+		return -EBUSY;
 	}
-
-	mutex_unlock(&pci_mmcfg_lock);
-
-	return rc;
+	return 0;
 }
 
-/* Delete MMCFG information for host bridges */
-int pci_mmconfig_delete(u16 seg, u8 start, u8 end)
+void pci_mmconfig_unmap_resource(struct pci_mmcfg_region *cfg)
 {
-	struct pci_mmcfg_region *cfg;
-
-	mutex_lock(&pci_mmcfg_lock);
-	list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
-		if (cfg->segment == seg && cfg->start_bus == start &&
-		    cfg->end_bus == end) {
-			list_del_rcu(&cfg->list);
-			synchronize_rcu();
-			pci_mmcfg_arch_unmap(cfg);
-			if (cfg->res.parent)
-				release_resource(&cfg->res);
-			mutex_unlock(&pci_mmcfg_lock);
-			kfree(cfg);
-			return 0;
-		}
-	mutex_unlock(&pci_mmcfg_lock);
+	pci_mmcfg_arch_unmap(cfg);
+	if (cfg->res.parent)
+		release_resource(&cfg->res);
+	cfg->res.parent = NULL;
+}
 
-	return -ENOENT;
+int pci_mmconfig_enabled(void)
+{
+	return (pci_probe & PCI_PROBE_MMCONF) && !pci_mmcfg_arch_init_failed;
 }
diff --git a/arch/x86/pci/mmconfig_32.c b/arch/x86/pci/mmconfig_32.c
index 43984bc..38a37f8 100644
--- a/arch/x86/pci/mmconfig_32.c
+++ b/arch/x86/pci/mmconfig_32.c
@@ -12,6 +12,7 @@
 #include <linux/pci.h>
 #include <linux/init.h>
 #include <linux/rcupdate.h>
+#include <linux/pci-acpi.h>
 #include <asm/e820.h>
 #include <asm/pci_x86.h>
 
diff --git a/arch/x86/pci/mmconfig_64.c b/arch/x86/pci/mmconfig_64.c
index bea5249..29253ec 100644
--- a/arch/x86/pci/mmconfig_64.c
+++ b/arch/x86/pci/mmconfig_64.c
@@ -10,6 +10,7 @@
 #include <linux/acpi.h>
 #include <linux/bitmap.h>
 #include <linux/rcupdate.h>
+#include <linux/pci-acpi.h>
 #include <asm/e820.h>
 #include <asm/pci_x86.h>
 
diff --git a/arch/x86/pci/numachip.c b/arch/x86/pci/numachip.c
index 2e565e6..c181eeb 100644
--- a/arch/x86/pci/numachip.c
+++ b/arch/x86/pci/numachip.c
@@ -14,6 +14,7 @@
  */
 
 #include <linux/pci.h>
+#include <linux/pci-acpi.h>
 #include <asm/pci_x86.h>
 
 static u8 limit __read_mostly;
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 675eaf3..3f65311 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -40,6 +40,7 @@ acpi-$(CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC) += processor_pdc.o
 acpi-y				+= ec.o
 acpi-$(CONFIG_ACPI_DOCK)	+= dock.o
 acpi-y				+= pci_root.o pci_link.o pci_irq.o
+acpi-$(CONFIG_PCI_MMCONFIG)	+= pci_mcfg.o
 acpi-y				+= acpi_lpss.o acpi_apd.o
 acpi-y				+= acpi_platform.o
 acpi-y				+= acpi_pnp.o
diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c
new file mode 100644
index 0000000..ea84365
--- /dev/null
+++ b/drivers/acpi/pci_mcfg.c
@@ -0,0 +1,312 @@
+/*
+ * pci_mcfg.c
+ *
+ * Common code to maintain the MCFG areas and mappings
+ *
+ * This has been extracted from arch/x86/pci/mmconfig-shared.c
+ * and moved here so that other architectures can use this code.
+ */
+
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/dmi.h>
+#include <linux/pci-acpi.h>
+#include <linux/sfi_acpi.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/rculist.h>
+
+#define PREFIX	"ACPI: "
+
+static DEFINE_MUTEX(pci_mmcfg_lock);
+LIST_HEAD(pci_mmcfg_list);
+
+static void list_add_sorted(struct pci_mmcfg_region *new)
+{
+	struct pci_mmcfg_region *cfg;
+
+	/* keep list sorted by segment and starting bus number */
+	list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) {
+		if (cfg->segment > new->segment ||
+		    (cfg->segment == new->segment &&
+		     cfg->start_bus >= new->start_bus)) {
+			list_add_tail_rcu(&new->list, &cfg->list);
+			return;
+		}
+	}
+	list_add_tail_rcu(&new->list, &pci_mmcfg_list);
+}
+
+static struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start,
+						   int end, u64 addr)
+{
+	struct pci_mmcfg_region *new;
+	struct resource *res;
+
+	if (addr == 0)
+		return NULL;
+
+	new = kzalloc(sizeof(*new), GFP_KERNEL);
+	if (!new)
+		return NULL;
+
+	new->address = addr;
+	new->segment = segment;
+	new->start_bus = start;
+	new->end_bus = end;
+
+	res = &new->res;
+	res->start = addr + PCI_MMCFG_BUS_OFFSET(start);
+	res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1;
+	res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+	snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN,
+		 "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end);
+	res->name = new->name;
+
+	return new;
+}
+
+struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start,
+							int end, u64 addr)
+{
+	struct pci_mmcfg_region *new;
+
+	new = pci_mmconfig_alloc(segment, start, end, addr);
+	if (new) {
+		mutex_lock(&pci_mmcfg_lock);
+		list_add_sorted(new);
+		mutex_unlock(&pci_mmcfg_lock);
+
+		pr_info(PREFIX
+		       "MMCONFIG for domain %04x [bus %02x-%02x] at %pR "
+		       "(base %#lx)\n",
+		       segment, start, end, &new->res, (unsigned long)addr);
+	}
+
+	return new;
+}
+
+struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus)
+{
+	struct pci_mmcfg_region *cfg;
+
+	list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
+		if (cfg->segment == segment &&
+		    cfg->start_bus <= bus && bus <= cfg->end_bus)
+			return cfg;
+
+	return NULL;
+}
+
+/*
+ * Map a pci_mmcfg_region, can be overrriden by arch
+ */
+int __weak pci_mmconfig_map_resource(struct device *dev,
+	struct pci_mmcfg_region *mcfg)
+{
+	struct resource *tmp;
+	void __iomem *vaddr;
+
+	tmp = insert_resource_conflict(&iomem_resource, &mcfg->res);
+	if (tmp) {
+		dev_warn(dev, "MMCONFIG %pR conflicts with %s %pR\n",
+			&mcfg->res, tmp->name, tmp);
+		return -EBUSY;
+	}
+
+	vaddr =  ioremap(mcfg->res.start, resource_size(&mcfg->res));
+	if (!vaddr) {
+		release_resource(&mcfg->res);
+		return -ENOMEM;
+	}
+
+	mcfg->virt = vaddr;
+	return 0;
+}
+
+/*
+ * Unmap a pci_mmcfg_region, can be overrriden by arch
+ */
+void __weak pci_mmconfig_unmap_resource(struct pci_mmcfg_region *mcfg)
+{
+	if (mcfg->virt) {
+		iounmap(mcfg->virt);
+		mcfg->virt = NULL;
+	}
+	if (mcfg->res.parent) {
+		release_resource(&mcfg->res);
+		mcfg->res.parent = NULL;
+	}
+}
+
+/*
+ * check if the mmconfig is enabled and configured
+ */
+int __weak pci_mmconfig_enabled(void)
+{
+	return 1;
+}
+
+/* Add MMCFG information for host bridges */
+int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
+			phys_addr_t addr)
+{
+	struct pci_mmcfg_region *cfg;
+	int rc;
+
+	if (!pci_mmconfig_enabled())
+		return -ENODEV;
+	if (start > end)
+		return -EINVAL;
+
+	mutex_lock(&pci_mmcfg_lock);
+	cfg = pci_mmconfig_lookup(seg, start);
+	if (cfg) {
+		if (cfg->end_bus < end)
+			dev_info(dev, FW_INFO
+				 "MMCONFIG for "
+				 "domain %04x [bus %02x-%02x] "
+				 "only partially covers this bridge\n",
+				  cfg->segment, cfg->start_bus, cfg->end_bus);
+		rc = -EEXIST;
+		goto err;
+	}
+
+	if (!addr) {
+		rc = -EINVAL;
+		goto err;
+	}
+
+	cfg = pci_mmconfig_alloc(seg, start, end, addr);
+	if (cfg == NULL) {
+		dev_warn(dev, "fail to add MMCONFIG (out of memory)\n");
+		rc = -ENOMEM;
+		goto err;
+	}
+	rc = pci_mmconfig_map_resource(dev, cfg);
+	if (!rc) {
+		list_add_sorted(cfg);
+		dev_info(dev, "MMCONFIG at %pR (base %#lx)\n",
+				 &cfg->res, (unsigned long)addr);
+		return 0;
+	} else {
+		if (cfg->res.parent)
+			release_resource(&cfg->res);
+		kfree(cfg);
+	}
+
+err:
+	mutex_unlock(&pci_mmcfg_lock);
+	return rc;
+}
+
+/* Delete MMCFG information for host bridges */
+int pci_mmconfig_delete(u16 seg, u8 start, u8 end)
+{
+	struct pci_mmcfg_region *cfg;
+
+	mutex_lock(&pci_mmcfg_lock);
+	list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
+		if (cfg->segment == seg && cfg->start_bus == start &&
+		    cfg->end_bus == end) {
+			list_del_rcu(&cfg->list);
+			synchronize_rcu();
+			pci_mmconfig_unmap_resource(cfg);
+			mutex_unlock(&pci_mmcfg_lock);
+			kfree(cfg);
+			return 0;
+		}
+	mutex_unlock(&pci_mmcfg_lock);
+
+	return -ENOENT;
+}
+
+static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg,
+					struct acpi_mcfg_allocation *cfg)
+{
+	int year;
+
+	if (!config_enabled(CONFIG_X86))
+		return 0;
+
+	if (cfg->address < 0xFFFFFFFF)
+		return 0;
+
+	if (!strncmp(mcfg->header.oem_id, "SGI", 3))
+		return 0;
+
+	if (mcfg->header.revision >= 1) {
+		if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) &&
+		    year >= 2010)
+			return 0;
+	}
+
+	pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx "
+	       "is above 4GB, ignored\n", cfg->pci_segment,
+	       cfg->start_bus_number, cfg->end_bus_number, cfg->address);
+	return -EINVAL;
+}
+
+static int __init pci_parse_mcfg(struct acpi_table_header *header)
+{
+	struct acpi_table_mcfg *mcfg;
+	struct acpi_mcfg_allocation *cfg_table, *cfg;
+	unsigned long i;
+	int entries;
+
+	if (!header)
+		return -EINVAL;
+
+	mcfg = (struct acpi_table_mcfg *)header;
+
+	/* how many config structures do we have */
+	entries = 0;
+	i = header->length - sizeof(struct acpi_table_mcfg);
+	while (i >= sizeof(struct acpi_mcfg_allocation)) {
+		entries++;
+		i -= sizeof(struct acpi_mcfg_allocation);
+	}
+	if (entries == 0) {
+		pr_err(PREFIX "MMCONFIG has no entries\n");
+		return -ENODEV;
+	}
+
+	cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1];
+	for (i = 0; i < entries; i++) {
+		cfg = &cfg_table[i];
+		if (acpi_mcfg_check_entry(mcfg, cfg))
+			return -ENODEV;
+
+		if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number,
+				   cfg->end_bus_number, cfg->address) == NULL) {
+			pr_warn(PREFIX "no memory for MCFG entries\n");
+			return -ENOMEM;
+		}
+	}
+
+	return 0;
+}
+
+int __init pci_mmconfig_parse_table(void)
+{
+	return acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
+}
+
+void __weak __init pci_mmcfg_late_init(void)
+{
+	int err, n = 0;
+	struct pci_mmcfg_region *cfg;
+
+	err = pci_mmconfig_parse_table();
+	if (err) {
+		pr_err(PREFIX " Failed to parse MCFG (%d)\n", err);
+		return;
+	}
+
+	list_for_each_entry(cfg, &pci_mmcfg_list, list) {
+		pci_mmconfig_map_resource(NULL, cfg);
+		n++;
+	}
+
+	pr_info(PREFIX " MCFG table loaded %d entries\n", n);
+}
diff --git a/drivers/xen/pci.c b/drivers/xen/pci.c
index 7494dbe..97aa9d3 100644
--- a/drivers/xen/pci.c
+++ b/drivers/xen/pci.c
@@ -27,9 +27,6 @@
 #include <asm/xen/hypervisor.h>
 #include <asm/xen/hypercall.h>
 #include "../pci/pci.h"
-#ifdef CONFIG_PCI_MMCONFIG
-#include <asm/pci_x86.h>
-#endif
 
 static bool __read_mostly pci_seg_supported = true;
 
@@ -221,7 +218,7 @@ static int __init xen_mcfg_late(void)
 	if (!xen_initial_domain())
 		return 0;
 
-	if ((pci_probe & PCI_PROBE_MMCONF) == 0)
+	if (!pci_mmconfig_enabled())
 		return 0;
 
 	if (list_empty(&pci_mmcfg_list))
diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h
index 89ab057..e9450ef 100644
--- a/include/linux/pci-acpi.h
+++ b/include/linux/pci-acpi.h
@@ -106,6 +106,39 @@ extern const u8 pci_acpi_dsm_uuid[];
 #define RESET_DELAY_DSM		0x08
 #define FUNCTION_DELAY_DSM	0x09
 
+/* common API to maintain list of MCFG regions */
+/* "PCI MMCONFIG %04x [bus %02x-%02x]" */
+#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2)
+
+struct pci_mmcfg_region {
+	struct list_head list;
+	struct resource res;
+	u64 address;
+	char __iomem *virt;
+	u16 segment;
+	u8 start_bus;
+	u8 end_bus;
+	char name[PCI_MMCFG_RESOURCE_NAME_LEN];
+};
+
+extern int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
+			       phys_addr_t addr);
+extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end);
+
+extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus);
+extern struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start,
+							int end, u64 addr);
+extern int pci_mmconfig_map_resource(struct device *dev,
+	struct pci_mmcfg_region *mcfg);
+extern void pci_mmconfig_unmap_resource(struct pci_mmcfg_region *mcfg);
+extern int pci_mmconfig_enabled(void);
+extern int __init pci_mmconfig_parse_table(void);
+
+extern struct list_head pci_mmcfg_list;
+
+#define PCI_MMCFG_BUS_OFFSET(bus)      ((bus) << 20)
+#define PCI_MMCFG_OFFSET(bus, devfn)   ((bus) << 20 | (devfn) << 12)
+
 #else	/* CONFIG_ACPI */
 static inline void acpi_pci_add_bus(struct pci_bus *bus) { }
 static inline void acpi_pci_remove_bus(struct pci_bus *bus) { }
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v6 1/5] APCI: MCFG: Move mmcfg_list management to drivers/acpi
@ 2016-01-14  7:32   ` Jayachandran C
  0 siblings, 0 replies; 31+ messages in thread
From: Jayachandran C @ 2016-01-14  7:32 UTC (permalink / raw)
  To: linux-arm-kernel

Move pci_mmcfg_list handling to a drivers/acpi/pci_mcfg.c. This is
to share the API and code with ARM64 later. The corresponding
declarations are moved from asm/pci_x86.h to linux/pci-acpi.h

As a part of this we introduce three functions that can be
implemented by the arch code: pci_mmconfig_map_resource() to map a
mcfg entry, pci_mmconfig_unmap_resource to do the corresponding
unmap and pci_mmconfig_enabled to see if the arch setup of
mcfg entries was successful. We also provide weak implementations
of these, which will be used from ARM64. On x86, we retain the
old logic by providing platform specific implementation.

This patch is purely rearranging code, it should not have any
impact on the logic of MCFG parsing or list handling.

Signed-off-by: Jayachandran C <jchandra@broadcom.com>
---
 arch/x86/include/asm/pci_x86.h |  24 +---
 arch/x86/pci/mmconfig-shared.c | 269 +++++------------------------------
 arch/x86/pci/mmconfig_32.c     |   1 +
 arch/x86/pci/mmconfig_64.c     |   1 +
 arch/x86/pci/numachip.c        |   1 +
 drivers/acpi/Makefile          |   1 +
 drivers/acpi/pci_mcfg.c        | 312 +++++++++++++++++++++++++++++++++++++++++
 drivers/xen/pci.c              |   5 +-
 include/linux/pci-acpi.h       |  33 +++++
 9 files changed, 386 insertions(+), 261 deletions(-)
 create mode 100644 drivers/acpi/pci_mcfg.c

diff --git a/arch/x86/include/asm/pci_x86.h b/arch/x86/include/asm/pci_x86.h
index fa1195d..83d74cb 100644
--- a/arch/x86/include/asm/pci_x86.h
+++ b/arch/x86/include/asm/pci_x86.h
@@ -122,33 +122,11 @@ extern int pci_legacy_init(void);
 extern void pcibios_fixup_irqs(void);
 
 /* pci-mmconfig.c */
-
-/* "PCI MMCONFIG %04x [bus %02x-%02x]" */
-#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2)
-
-struct pci_mmcfg_region {
-	struct list_head list;
-	struct resource res;
-	u64 address;
-	char __iomem *virt;
-	u16 segment;
-	u8 start_bus;
-	u8 end_bus;
-	char name[PCI_MMCFG_RESOURCE_NAME_LEN];
-};
-
+struct pci_mmcfg_region;
 extern int __init pci_mmcfg_arch_init(void);
 extern void __init pci_mmcfg_arch_free(void);
 extern int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg);
 extern void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg);
-extern int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
-			       phys_addr_t addr);
-extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end);
-extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus);
-
-extern struct list_head pci_mmcfg_list;
-
-#define PCI_MMCFG_BUS_OFFSET(bus)      ((bus) << 20)
 
 /*
  * AMD Fam10h CPUs are buggy, and cannot access MMIO config space
diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c
index dd30b7e..626710b 100644
--- a/arch/x86/pci/mmconfig-shared.c
+++ b/arch/x86/pci/mmconfig-shared.c
@@ -12,13 +12,12 @@
 
 #include <linux/pci.h>
 #include <linux/init.h>
-#include <linux/sfi_acpi.h>
 #include <linux/bitmap.h>
-#include <linux/dmi.h>
 #include <linux/slab.h>
 #include <linux/mutex.h>
 #include <linux/rculist.h>
 #include <asm/e820.h>
+#include <linux/pci-acpi.h>
 #include <asm/pci_x86.h>
 #include <asm/acpi.h>
 
@@ -27,9 +26,6 @@
 /* Indicate if the mmcfg resources have been placed into the resource table. */
 static bool pci_mmcfg_running_state;
 static bool pci_mmcfg_arch_init_failed;
-static DEFINE_MUTEX(pci_mmcfg_lock);
-
-LIST_HEAD(pci_mmcfg_list);
 
 static void __init pci_mmconfig_remove(struct pci_mmcfg_region *cfg)
 {
@@ -48,83 +44,6 @@ static void __init free_all_mmcfg(void)
 		pci_mmconfig_remove(cfg);
 }
 
-static void list_add_sorted(struct pci_mmcfg_region *new)
-{
-	struct pci_mmcfg_region *cfg;
-
-	/* keep list sorted by segment and starting bus number */
-	list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) {
-		if (cfg->segment > new->segment ||
-		    (cfg->segment == new->segment &&
-		     cfg->start_bus >= new->start_bus)) {
-			list_add_tail_rcu(&new->list, &cfg->list);
-			return;
-		}
-	}
-	list_add_tail_rcu(&new->list, &pci_mmcfg_list);
-}
-
-static struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start,
-						   int end, u64 addr)
-{
-	struct pci_mmcfg_region *new;
-	struct resource *res;
-
-	if (addr == 0)
-		return NULL;
-
-	new = kzalloc(sizeof(*new), GFP_KERNEL);
-	if (!new)
-		return NULL;
-
-	new->address = addr;
-	new->segment = segment;
-	new->start_bus = start;
-	new->end_bus = end;
-
-	res = &new->res;
-	res->start = addr + PCI_MMCFG_BUS_OFFSET(start);
-	res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1;
-	res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
-	snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN,
-		 "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end);
-	res->name = new->name;
-
-	return new;
-}
-
-static struct pci_mmcfg_region *__init pci_mmconfig_add(int segment, int start,
-							int end, u64 addr)
-{
-	struct pci_mmcfg_region *new;
-
-	new = pci_mmconfig_alloc(segment, start, end, addr);
-	if (new) {
-		mutex_lock(&pci_mmcfg_lock);
-		list_add_sorted(new);
-		mutex_unlock(&pci_mmcfg_lock);
-
-		pr_info(PREFIX
-		       "MMCONFIG for domain %04x [bus %02x-%02x] at %pR "
-		       "(base %#lx)\n",
-		       segment, start, end, &new->res, (unsigned long)addr);
-	}
-
-	return new;
-}
-
-struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus)
-{
-	struct pci_mmcfg_region *cfg;
-
-	list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
-		if (cfg->segment == segment &&
-		    cfg->start_bus <= bus && bus <= cfg->end_bus)
-			return cfg;
-
-	return NULL;
-}
-
 static const char *__init pci_mmcfg_e7520(void)
 {
 	u32 win;
@@ -543,73 +462,6 @@ static void __init pci_mmcfg_reject_broken(int early)
 	}
 }
 
-static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg,
-					struct acpi_mcfg_allocation *cfg)
-{
-	int year;
-
-	if (cfg->address < 0xFFFFFFFF)
-		return 0;
-
-	if (!strncmp(mcfg->header.oem_id, "SGI", 3))
-		return 0;
-
-	if (mcfg->header.revision >= 1) {
-		if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) &&
-		    year >= 2010)
-			return 0;
-	}
-
-	pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx "
-	       "is above 4GB, ignored\n", cfg->pci_segment,
-	       cfg->start_bus_number, cfg->end_bus_number, cfg->address);
-	return -EINVAL;
-}
-
-static int __init pci_parse_mcfg(struct acpi_table_header *header)
-{
-	struct acpi_table_mcfg *mcfg;
-	struct acpi_mcfg_allocation *cfg_table, *cfg;
-	unsigned long i;
-	int entries;
-
-	if (!header)
-		return -EINVAL;
-
-	mcfg = (struct acpi_table_mcfg *)header;
-
-	/* how many config structures do we have */
-	free_all_mmcfg();
-	entries = 0;
-	i = header->length - sizeof(struct acpi_table_mcfg);
-	while (i >= sizeof(struct acpi_mcfg_allocation)) {
-		entries++;
-		i -= sizeof(struct acpi_mcfg_allocation);
-	}
-	if (entries == 0) {
-		pr_err(PREFIX "MMCONFIG has no entries\n");
-		return -ENODEV;
-	}
-
-	cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1];
-	for (i = 0; i < entries; i++) {
-		cfg = &cfg_table[i];
-		if (acpi_mcfg_check_entry(mcfg, cfg)) {
-			free_all_mmcfg();
-			return -ENODEV;
-		}
-
-		if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number,
-				   cfg->end_bus_number, cfg->address) == NULL) {
-			pr_warn(PREFIX "no memory for MCFG entries\n");
-			free_all_mmcfg();
-			return -ENOMEM;
-		}
-	}
-
-	return 0;
-}
-
 #ifdef CONFIG_ACPI_APEI
 extern int (*arch_apei_filter_addr)(int (*func)(__u64 start, __u64 size,
 				     void *data), void *data);
@@ -662,13 +514,20 @@ static void __init __pci_mmcfg_init(int early)
 
 static int __initdata known_bridge;
 
+static void __init pci_mmcfg_list_setup(void)
+{
+	free_all_mmcfg();
+	if (pci_mmconfig_parse_table())
+		free_all_mmcfg();
+}
+
 void __init pci_mmcfg_early_init(void)
 {
 	if (pci_probe & PCI_PROBE_MMCONF) {
 		if (pci_mmcfg_check_hostbridge())
 			known_bridge = 1;
 		else
-			acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
+			pci_mmcfg_list_setup();
 		__pci_mmcfg_init(1);
 
 		set_apei_filter();
@@ -686,7 +545,7 @@ void __init pci_mmcfg_late_init(void)
 
 	/* MMCONFIG hasn't been enabled yet, try again */
 	if (pci_probe & PCI_PROBE_MASK & ~PCI_PROBE_MMCONF) {
-		acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
+		pci_mmcfg_list_setup();
 		__pci_mmcfg_init(0);
 	}
 }
@@ -720,99 +579,41 @@ static int __init pci_mmcfg_late_insert_resources(void)
  */
 late_initcall(pci_mmcfg_late_insert_resources);
 
-/* Add MMCFG information for host bridges */
-int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
-			phys_addr_t addr)
+int pci_mmconfig_map_resource(struct device *dev, struct pci_mmcfg_region *cfg)
 {
-	int rc;
-	struct resource *tmp = NULL;
-	struct pci_mmcfg_region *cfg;
+	struct resource *tmp;
 
-	if (!(pci_probe & PCI_PROBE_MMCONF) || pci_mmcfg_arch_init_failed)
-		return -ENODEV;
-
-	if (start > end)
-		return -EINVAL;
-
-	mutex_lock(&pci_mmcfg_lock);
-	cfg = pci_mmconfig_lookup(seg, start);
-	if (cfg) {
-		if (cfg->end_bus < end)
-			dev_info(dev, FW_INFO
-				 "MMCONFIG for "
-				 "domain %04x [bus %02x-%02x] "
-				 "only partially covers this bridge\n",
-				  cfg->segment, cfg->start_bus, cfg->end_bus);
-		mutex_unlock(&pci_mmcfg_lock);
-		return -EEXIST;
-	}
-
-	if (!addr) {
-		mutex_unlock(&pci_mmcfg_lock);
-		return -EINVAL;
-	}
-
-	rc = -EBUSY;
-	cfg = pci_mmconfig_alloc(seg, start, end, addr);
-	if (cfg == NULL) {
-		dev_warn(dev, "fail to add MMCONFIG (out of memory)\n");
-		rc = -ENOMEM;
-	} else if (!pci_mmcfg_check_reserved(dev, cfg, 0)) {
+	if (!pci_mmcfg_check_reserved(dev, cfg, 0)) {
 		dev_warn(dev, FW_BUG "MMCONFIG %pR isn't reserved\n",
 			 &cfg->res);
-	} else {
-		/* Insert resource if it's not in boot stage */
-		if (pci_mmcfg_running_state)
-			tmp = insert_resource_conflict(&iomem_resource,
-						       &cfg->res);
-
+		return -EBUSY;
+	}
+	/* Insert resource if it's not in boot stage */
+	if (pci_mmcfg_running_state) {
+		tmp = insert_resource_conflict(&iomem_resource,
+					       &cfg->res);
 		if (tmp) {
-			dev_warn(dev,
-				 "MMCONFIG %pR conflicts with "
-				 "%s %pR\n",
-				 &cfg->res, tmp->name, tmp);
-		} else if (pci_mmcfg_arch_map(cfg)) {
-			dev_warn(dev, "fail to map MMCONFIG %pR.\n",
-				 &cfg->res);
-		} else {
-			list_add_sorted(cfg);
-			dev_info(dev, "MMCONFIG at %pR (base %#lx)\n",
-				 &cfg->res, (unsigned long)addr);
-			cfg = NULL;
-			rc = 0;
+			dev_warn(dev, "MMCONFIG %pR conflicts with %s %pR\n",
+				&cfg->res, tmp->name, tmp);
+			return -EBUSY;
 		}
 	}
-
-	if (cfg) {
-		if (cfg->res.parent)
-			release_resource(&cfg->res);
-		kfree(cfg);
+	if (pci_mmcfg_arch_map(cfg)) {
+		dev_warn(dev, "fail to map MMCONFIG %pR.\n", &cfg->res);
+		return -EBUSY;
 	}
-
-	mutex_unlock(&pci_mmcfg_lock);
-
-	return rc;
+	return 0;
 }
 
-/* Delete MMCFG information for host bridges */
-int pci_mmconfig_delete(u16 seg, u8 start, u8 end)
+void pci_mmconfig_unmap_resource(struct pci_mmcfg_region *cfg)
 {
-	struct pci_mmcfg_region *cfg;
-
-	mutex_lock(&pci_mmcfg_lock);
-	list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
-		if (cfg->segment == seg && cfg->start_bus == start &&
-		    cfg->end_bus == end) {
-			list_del_rcu(&cfg->list);
-			synchronize_rcu();
-			pci_mmcfg_arch_unmap(cfg);
-			if (cfg->res.parent)
-				release_resource(&cfg->res);
-			mutex_unlock(&pci_mmcfg_lock);
-			kfree(cfg);
-			return 0;
-		}
-	mutex_unlock(&pci_mmcfg_lock);
+	pci_mmcfg_arch_unmap(cfg);
+	if (cfg->res.parent)
+		release_resource(&cfg->res);
+	cfg->res.parent = NULL;
+}
 
-	return -ENOENT;
+int pci_mmconfig_enabled(void)
+{
+	return (pci_probe & PCI_PROBE_MMCONF) && !pci_mmcfg_arch_init_failed;
 }
diff --git a/arch/x86/pci/mmconfig_32.c b/arch/x86/pci/mmconfig_32.c
index 43984bc..38a37f8 100644
--- a/arch/x86/pci/mmconfig_32.c
+++ b/arch/x86/pci/mmconfig_32.c
@@ -12,6 +12,7 @@
 #include <linux/pci.h>
 #include <linux/init.h>
 #include <linux/rcupdate.h>
+#include <linux/pci-acpi.h>
 #include <asm/e820.h>
 #include <asm/pci_x86.h>
 
diff --git a/arch/x86/pci/mmconfig_64.c b/arch/x86/pci/mmconfig_64.c
index bea5249..29253ec 100644
--- a/arch/x86/pci/mmconfig_64.c
+++ b/arch/x86/pci/mmconfig_64.c
@@ -10,6 +10,7 @@
 #include <linux/acpi.h>
 #include <linux/bitmap.h>
 #include <linux/rcupdate.h>
+#include <linux/pci-acpi.h>
 #include <asm/e820.h>
 #include <asm/pci_x86.h>
 
diff --git a/arch/x86/pci/numachip.c b/arch/x86/pci/numachip.c
index 2e565e6..c181eeb 100644
--- a/arch/x86/pci/numachip.c
+++ b/arch/x86/pci/numachip.c
@@ -14,6 +14,7 @@
  */
 
 #include <linux/pci.h>
+#include <linux/pci-acpi.h>
 #include <asm/pci_x86.h>
 
 static u8 limit __read_mostly;
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 675eaf3..3f65311 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -40,6 +40,7 @@ acpi-$(CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC) += processor_pdc.o
 acpi-y				+= ec.o
 acpi-$(CONFIG_ACPI_DOCK)	+= dock.o
 acpi-y				+= pci_root.o pci_link.o pci_irq.o
+acpi-$(CONFIG_PCI_MMCONFIG)	+= pci_mcfg.o
 acpi-y				+= acpi_lpss.o acpi_apd.o
 acpi-y				+= acpi_platform.o
 acpi-y				+= acpi_pnp.o
diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c
new file mode 100644
index 0000000..ea84365
--- /dev/null
+++ b/drivers/acpi/pci_mcfg.c
@@ -0,0 +1,312 @@
+/*
+ * pci_mcfg.c
+ *
+ * Common code to maintain the MCFG areas and mappings
+ *
+ * This has been extracted from arch/x86/pci/mmconfig-shared.c
+ * and moved here so that other architectures can use this code.
+ */
+
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/dmi.h>
+#include <linux/pci-acpi.h>
+#include <linux/sfi_acpi.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/rculist.h>
+
+#define PREFIX	"ACPI: "
+
+static DEFINE_MUTEX(pci_mmcfg_lock);
+LIST_HEAD(pci_mmcfg_list);
+
+static void list_add_sorted(struct pci_mmcfg_region *new)
+{
+	struct pci_mmcfg_region *cfg;
+
+	/* keep list sorted by segment and starting bus number */
+	list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) {
+		if (cfg->segment > new->segment ||
+		    (cfg->segment == new->segment &&
+		     cfg->start_bus >= new->start_bus)) {
+			list_add_tail_rcu(&new->list, &cfg->list);
+			return;
+		}
+	}
+	list_add_tail_rcu(&new->list, &pci_mmcfg_list);
+}
+
+static struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start,
+						   int end, u64 addr)
+{
+	struct pci_mmcfg_region *new;
+	struct resource *res;
+
+	if (addr == 0)
+		return NULL;
+
+	new = kzalloc(sizeof(*new), GFP_KERNEL);
+	if (!new)
+		return NULL;
+
+	new->address = addr;
+	new->segment = segment;
+	new->start_bus = start;
+	new->end_bus = end;
+
+	res = &new->res;
+	res->start = addr + PCI_MMCFG_BUS_OFFSET(start);
+	res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1;
+	res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+	snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN,
+		 "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end);
+	res->name = new->name;
+
+	return new;
+}
+
+struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start,
+							int end, u64 addr)
+{
+	struct pci_mmcfg_region *new;
+
+	new = pci_mmconfig_alloc(segment, start, end, addr);
+	if (new) {
+		mutex_lock(&pci_mmcfg_lock);
+		list_add_sorted(new);
+		mutex_unlock(&pci_mmcfg_lock);
+
+		pr_info(PREFIX
+		       "MMCONFIG for domain %04x [bus %02x-%02x] at %pR "
+		       "(base %#lx)\n",
+		       segment, start, end, &new->res, (unsigned long)addr);
+	}
+
+	return new;
+}
+
+struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus)
+{
+	struct pci_mmcfg_region *cfg;
+
+	list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
+		if (cfg->segment == segment &&
+		    cfg->start_bus <= bus && bus <= cfg->end_bus)
+			return cfg;
+
+	return NULL;
+}
+
+/*
+ * Map a pci_mmcfg_region, can be overrriden by arch
+ */
+int __weak pci_mmconfig_map_resource(struct device *dev,
+	struct pci_mmcfg_region *mcfg)
+{
+	struct resource *tmp;
+	void __iomem *vaddr;
+
+	tmp = insert_resource_conflict(&iomem_resource, &mcfg->res);
+	if (tmp) {
+		dev_warn(dev, "MMCONFIG %pR conflicts with %s %pR\n",
+			&mcfg->res, tmp->name, tmp);
+		return -EBUSY;
+	}
+
+	vaddr =  ioremap(mcfg->res.start, resource_size(&mcfg->res));
+	if (!vaddr) {
+		release_resource(&mcfg->res);
+		return -ENOMEM;
+	}
+
+	mcfg->virt = vaddr;
+	return 0;
+}
+
+/*
+ * Unmap a pci_mmcfg_region, can be overrriden by arch
+ */
+void __weak pci_mmconfig_unmap_resource(struct pci_mmcfg_region *mcfg)
+{
+	if (mcfg->virt) {
+		iounmap(mcfg->virt);
+		mcfg->virt = NULL;
+	}
+	if (mcfg->res.parent) {
+		release_resource(&mcfg->res);
+		mcfg->res.parent = NULL;
+	}
+}
+
+/*
+ * check if the mmconfig is enabled and configured
+ */
+int __weak pci_mmconfig_enabled(void)
+{
+	return 1;
+}
+
+/* Add MMCFG information for host bridges */
+int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
+			phys_addr_t addr)
+{
+	struct pci_mmcfg_region *cfg;
+	int rc;
+
+	if (!pci_mmconfig_enabled())
+		return -ENODEV;
+	if (start > end)
+		return -EINVAL;
+
+	mutex_lock(&pci_mmcfg_lock);
+	cfg = pci_mmconfig_lookup(seg, start);
+	if (cfg) {
+		if (cfg->end_bus < end)
+			dev_info(dev, FW_INFO
+				 "MMCONFIG for "
+				 "domain %04x [bus %02x-%02x] "
+				 "only partially covers this bridge\n",
+				  cfg->segment, cfg->start_bus, cfg->end_bus);
+		rc = -EEXIST;
+		goto err;
+	}
+
+	if (!addr) {
+		rc = -EINVAL;
+		goto err;
+	}
+
+	cfg = pci_mmconfig_alloc(seg, start, end, addr);
+	if (cfg == NULL) {
+		dev_warn(dev, "fail to add MMCONFIG (out of memory)\n");
+		rc = -ENOMEM;
+		goto err;
+	}
+	rc = pci_mmconfig_map_resource(dev, cfg);
+	if (!rc) {
+		list_add_sorted(cfg);
+		dev_info(dev, "MMCONFIG at %pR (base %#lx)\n",
+				 &cfg->res, (unsigned long)addr);
+		return 0;
+	} else {
+		if (cfg->res.parent)
+			release_resource(&cfg->res);
+		kfree(cfg);
+	}
+
+err:
+	mutex_unlock(&pci_mmcfg_lock);
+	return rc;
+}
+
+/* Delete MMCFG information for host bridges */
+int pci_mmconfig_delete(u16 seg, u8 start, u8 end)
+{
+	struct pci_mmcfg_region *cfg;
+
+	mutex_lock(&pci_mmcfg_lock);
+	list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
+		if (cfg->segment == seg && cfg->start_bus == start &&
+		    cfg->end_bus == end) {
+			list_del_rcu(&cfg->list);
+			synchronize_rcu();
+			pci_mmconfig_unmap_resource(cfg);
+			mutex_unlock(&pci_mmcfg_lock);
+			kfree(cfg);
+			return 0;
+		}
+	mutex_unlock(&pci_mmcfg_lock);
+
+	return -ENOENT;
+}
+
+static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg,
+					struct acpi_mcfg_allocation *cfg)
+{
+	int year;
+
+	if (!config_enabled(CONFIG_X86))
+		return 0;
+
+	if (cfg->address < 0xFFFFFFFF)
+		return 0;
+
+	if (!strncmp(mcfg->header.oem_id, "SGI", 3))
+		return 0;
+
+	if (mcfg->header.revision >= 1) {
+		if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) &&
+		    year >= 2010)
+			return 0;
+	}
+
+	pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx "
+	       "is above 4GB, ignored\n", cfg->pci_segment,
+	       cfg->start_bus_number, cfg->end_bus_number, cfg->address);
+	return -EINVAL;
+}
+
+static int __init pci_parse_mcfg(struct acpi_table_header *header)
+{
+	struct acpi_table_mcfg *mcfg;
+	struct acpi_mcfg_allocation *cfg_table, *cfg;
+	unsigned long i;
+	int entries;
+
+	if (!header)
+		return -EINVAL;
+
+	mcfg = (struct acpi_table_mcfg *)header;
+
+	/* how many config structures do we have */
+	entries = 0;
+	i = header->length - sizeof(struct acpi_table_mcfg);
+	while (i >= sizeof(struct acpi_mcfg_allocation)) {
+		entries++;
+		i -= sizeof(struct acpi_mcfg_allocation);
+	}
+	if (entries == 0) {
+		pr_err(PREFIX "MMCONFIG has no entries\n");
+		return -ENODEV;
+	}
+
+	cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1];
+	for (i = 0; i < entries; i++) {
+		cfg = &cfg_table[i];
+		if (acpi_mcfg_check_entry(mcfg, cfg))
+			return -ENODEV;
+
+		if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number,
+				   cfg->end_bus_number, cfg->address) == NULL) {
+			pr_warn(PREFIX "no memory for MCFG entries\n");
+			return -ENOMEM;
+		}
+	}
+
+	return 0;
+}
+
+int __init pci_mmconfig_parse_table(void)
+{
+	return acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
+}
+
+void __weak __init pci_mmcfg_late_init(void)
+{
+	int err, n = 0;
+	struct pci_mmcfg_region *cfg;
+
+	err = pci_mmconfig_parse_table();
+	if (err) {
+		pr_err(PREFIX " Failed to parse MCFG (%d)\n", err);
+		return;
+	}
+
+	list_for_each_entry(cfg, &pci_mmcfg_list, list) {
+		pci_mmconfig_map_resource(NULL, cfg);
+		n++;
+	}
+
+	pr_info(PREFIX " MCFG table loaded %d entries\n", n);
+}
diff --git a/drivers/xen/pci.c b/drivers/xen/pci.c
index 7494dbe..97aa9d3 100644
--- a/drivers/xen/pci.c
+++ b/drivers/xen/pci.c
@@ -27,9 +27,6 @@
 #include <asm/xen/hypervisor.h>
 #include <asm/xen/hypercall.h>
 #include "../pci/pci.h"
-#ifdef CONFIG_PCI_MMCONFIG
-#include <asm/pci_x86.h>
-#endif
 
 static bool __read_mostly pci_seg_supported = true;
 
@@ -221,7 +218,7 @@ static int __init xen_mcfg_late(void)
 	if (!xen_initial_domain())
 		return 0;
 
-	if ((pci_probe & PCI_PROBE_MMCONF) == 0)
+	if (!pci_mmconfig_enabled())
 		return 0;
 
 	if (list_empty(&pci_mmcfg_list))
diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h
index 89ab057..e9450ef 100644
--- a/include/linux/pci-acpi.h
+++ b/include/linux/pci-acpi.h
@@ -106,6 +106,39 @@ extern const u8 pci_acpi_dsm_uuid[];
 #define RESET_DELAY_DSM		0x08
 #define FUNCTION_DELAY_DSM	0x09
 
+/* common API to maintain list of MCFG regions */
+/* "PCI MMCONFIG %04x [bus %02x-%02x]" */
+#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2)
+
+struct pci_mmcfg_region {
+	struct list_head list;
+	struct resource res;
+	u64 address;
+	char __iomem *virt;
+	u16 segment;
+	u8 start_bus;
+	u8 end_bus;
+	char name[PCI_MMCFG_RESOURCE_NAME_LEN];
+};
+
+extern int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
+			       phys_addr_t addr);
+extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end);
+
+extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus);
+extern struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start,
+							int end, u64 addr);
+extern int pci_mmconfig_map_resource(struct device *dev,
+	struct pci_mmcfg_region *mcfg);
+extern void pci_mmconfig_unmap_resource(struct pci_mmcfg_region *mcfg);
+extern int pci_mmconfig_enabled(void);
+extern int __init pci_mmconfig_parse_table(void);
+
+extern struct list_head pci_mmcfg_list;
+
+#define PCI_MMCFG_BUS_OFFSET(bus)      ((bus) << 20)
+#define PCI_MMCFG_OFFSET(bus, devfn)   ((bus) << 20 | (devfn) << 12)
+
 #else	/* CONFIG_ACPI */
 static inline void acpi_pci_add_bus(struct pci_bus *bus) { }
 static inline void acpi_pci_remove_bus(struct pci_bus *bus) { }
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v6 1/5] APCI: MCFG: Move mmcfg_list management to drivers/acpi
  2016-01-14  7:32 ` Jayachandran C
  (?)
  (?)
@ 2016-01-14  7:32 ` Jayachandran C
  -1 siblings, 0 replies; 31+ messages in thread
From: Jayachandran C @ 2016-01-14  7:32 UTC (permalink / raw)
  To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann,
	linux-arm-kernel, Rafael J. Wysocki
  Cc: Tomasz Nowicki, Lorenzo Pieralisi, Jayachandran C, xen-devel

Move pci_mmcfg_list handling to a drivers/acpi/pci_mcfg.c. This is
to share the API and code with ARM64 later. The corresponding
declarations are moved from asm/pci_x86.h to linux/pci-acpi.h

As a part of this we introduce three functions that can be
implemented by the arch code: pci_mmconfig_map_resource() to map a
mcfg entry, pci_mmconfig_unmap_resource to do the corresponding
unmap and pci_mmconfig_enabled to see if the arch setup of
mcfg entries was successful. We also provide weak implementations
of these, which will be used from ARM64. On x86, we retain the
old logic by providing platform specific implementation.

This patch is purely rearranging code, it should not have any
impact on the logic of MCFG parsing or list handling.

Signed-off-by: Jayachandran C <jchandra@broadcom.com>
---
 arch/x86/include/asm/pci_x86.h |  24 +---
 arch/x86/pci/mmconfig-shared.c | 269 +++++------------------------------
 arch/x86/pci/mmconfig_32.c     |   1 +
 arch/x86/pci/mmconfig_64.c     |   1 +
 arch/x86/pci/numachip.c        |   1 +
 drivers/acpi/Makefile          |   1 +
 drivers/acpi/pci_mcfg.c        | 312 +++++++++++++++++++++++++++++++++++++++++
 drivers/xen/pci.c              |   5 +-
 include/linux/pci-acpi.h       |  33 +++++
 9 files changed, 386 insertions(+), 261 deletions(-)
 create mode 100644 drivers/acpi/pci_mcfg.c

diff --git a/arch/x86/include/asm/pci_x86.h b/arch/x86/include/asm/pci_x86.h
index fa1195d..83d74cb 100644
--- a/arch/x86/include/asm/pci_x86.h
+++ b/arch/x86/include/asm/pci_x86.h
@@ -122,33 +122,11 @@ extern int pci_legacy_init(void);
 extern void pcibios_fixup_irqs(void);
 
 /* pci-mmconfig.c */
-
-/* "PCI MMCONFIG %04x [bus %02x-%02x]" */
-#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2)
-
-struct pci_mmcfg_region {
-	struct list_head list;
-	struct resource res;
-	u64 address;
-	char __iomem *virt;
-	u16 segment;
-	u8 start_bus;
-	u8 end_bus;
-	char name[PCI_MMCFG_RESOURCE_NAME_LEN];
-};
-
+struct pci_mmcfg_region;
 extern int __init pci_mmcfg_arch_init(void);
 extern void __init pci_mmcfg_arch_free(void);
 extern int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg);
 extern void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg);
-extern int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
-			       phys_addr_t addr);
-extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end);
-extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus);
-
-extern struct list_head pci_mmcfg_list;
-
-#define PCI_MMCFG_BUS_OFFSET(bus)      ((bus) << 20)
 
 /*
  * AMD Fam10h CPUs are buggy, and cannot access MMIO config space
diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c
index dd30b7e..626710b 100644
--- a/arch/x86/pci/mmconfig-shared.c
+++ b/arch/x86/pci/mmconfig-shared.c
@@ -12,13 +12,12 @@
 
 #include <linux/pci.h>
 #include <linux/init.h>
-#include <linux/sfi_acpi.h>
 #include <linux/bitmap.h>
-#include <linux/dmi.h>
 #include <linux/slab.h>
 #include <linux/mutex.h>
 #include <linux/rculist.h>
 #include <asm/e820.h>
+#include <linux/pci-acpi.h>
 #include <asm/pci_x86.h>
 #include <asm/acpi.h>
 
@@ -27,9 +26,6 @@
 /* Indicate if the mmcfg resources have been placed into the resource table. */
 static bool pci_mmcfg_running_state;
 static bool pci_mmcfg_arch_init_failed;
-static DEFINE_MUTEX(pci_mmcfg_lock);
-
-LIST_HEAD(pci_mmcfg_list);
 
 static void __init pci_mmconfig_remove(struct pci_mmcfg_region *cfg)
 {
@@ -48,83 +44,6 @@ static void __init free_all_mmcfg(void)
 		pci_mmconfig_remove(cfg);
 }
 
-static void list_add_sorted(struct pci_mmcfg_region *new)
-{
-	struct pci_mmcfg_region *cfg;
-
-	/* keep list sorted by segment and starting bus number */
-	list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) {
-		if (cfg->segment > new->segment ||
-		    (cfg->segment == new->segment &&
-		     cfg->start_bus >= new->start_bus)) {
-			list_add_tail_rcu(&new->list, &cfg->list);
-			return;
-		}
-	}
-	list_add_tail_rcu(&new->list, &pci_mmcfg_list);
-}
-
-static struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start,
-						   int end, u64 addr)
-{
-	struct pci_mmcfg_region *new;
-	struct resource *res;
-
-	if (addr == 0)
-		return NULL;
-
-	new = kzalloc(sizeof(*new), GFP_KERNEL);
-	if (!new)
-		return NULL;
-
-	new->address = addr;
-	new->segment = segment;
-	new->start_bus = start;
-	new->end_bus = end;
-
-	res = &new->res;
-	res->start = addr + PCI_MMCFG_BUS_OFFSET(start);
-	res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1;
-	res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
-	snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN,
-		 "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end);
-	res->name = new->name;
-
-	return new;
-}
-
-static struct pci_mmcfg_region *__init pci_mmconfig_add(int segment, int start,
-							int end, u64 addr)
-{
-	struct pci_mmcfg_region *new;
-
-	new = pci_mmconfig_alloc(segment, start, end, addr);
-	if (new) {
-		mutex_lock(&pci_mmcfg_lock);
-		list_add_sorted(new);
-		mutex_unlock(&pci_mmcfg_lock);
-
-		pr_info(PREFIX
-		       "MMCONFIG for domain %04x [bus %02x-%02x] at %pR "
-		       "(base %#lx)\n",
-		       segment, start, end, &new->res, (unsigned long)addr);
-	}
-
-	return new;
-}
-
-struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus)
-{
-	struct pci_mmcfg_region *cfg;
-
-	list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
-		if (cfg->segment == segment &&
-		    cfg->start_bus <= bus && bus <= cfg->end_bus)
-			return cfg;
-
-	return NULL;
-}
-
 static const char *__init pci_mmcfg_e7520(void)
 {
 	u32 win;
@@ -543,73 +462,6 @@ static void __init pci_mmcfg_reject_broken(int early)
 	}
 }
 
-static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg,
-					struct acpi_mcfg_allocation *cfg)
-{
-	int year;
-
-	if (cfg->address < 0xFFFFFFFF)
-		return 0;
-
-	if (!strncmp(mcfg->header.oem_id, "SGI", 3))
-		return 0;
-
-	if (mcfg->header.revision >= 1) {
-		if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) &&
-		    year >= 2010)
-			return 0;
-	}
-
-	pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx "
-	       "is above 4GB, ignored\n", cfg->pci_segment,
-	       cfg->start_bus_number, cfg->end_bus_number, cfg->address);
-	return -EINVAL;
-}
-
-static int __init pci_parse_mcfg(struct acpi_table_header *header)
-{
-	struct acpi_table_mcfg *mcfg;
-	struct acpi_mcfg_allocation *cfg_table, *cfg;
-	unsigned long i;
-	int entries;
-
-	if (!header)
-		return -EINVAL;
-
-	mcfg = (struct acpi_table_mcfg *)header;
-
-	/* how many config structures do we have */
-	free_all_mmcfg();
-	entries = 0;
-	i = header->length - sizeof(struct acpi_table_mcfg);
-	while (i >= sizeof(struct acpi_mcfg_allocation)) {
-		entries++;
-		i -= sizeof(struct acpi_mcfg_allocation);
-	}
-	if (entries == 0) {
-		pr_err(PREFIX "MMCONFIG has no entries\n");
-		return -ENODEV;
-	}
-
-	cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1];
-	for (i = 0; i < entries; i++) {
-		cfg = &cfg_table[i];
-		if (acpi_mcfg_check_entry(mcfg, cfg)) {
-			free_all_mmcfg();
-			return -ENODEV;
-		}
-
-		if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number,
-				   cfg->end_bus_number, cfg->address) == NULL) {
-			pr_warn(PREFIX "no memory for MCFG entries\n");
-			free_all_mmcfg();
-			return -ENOMEM;
-		}
-	}
-
-	return 0;
-}
-
 #ifdef CONFIG_ACPI_APEI
 extern int (*arch_apei_filter_addr)(int (*func)(__u64 start, __u64 size,
 				     void *data), void *data);
@@ -662,13 +514,20 @@ static void __init __pci_mmcfg_init(int early)
 
 static int __initdata known_bridge;
 
+static void __init pci_mmcfg_list_setup(void)
+{
+	free_all_mmcfg();
+	if (pci_mmconfig_parse_table())
+		free_all_mmcfg();
+}
+
 void __init pci_mmcfg_early_init(void)
 {
 	if (pci_probe & PCI_PROBE_MMCONF) {
 		if (pci_mmcfg_check_hostbridge())
 			known_bridge = 1;
 		else
-			acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
+			pci_mmcfg_list_setup();
 		__pci_mmcfg_init(1);
 
 		set_apei_filter();
@@ -686,7 +545,7 @@ void __init pci_mmcfg_late_init(void)
 
 	/* MMCONFIG hasn't been enabled yet, try again */
 	if (pci_probe & PCI_PROBE_MASK & ~PCI_PROBE_MMCONF) {
-		acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
+		pci_mmcfg_list_setup();
 		__pci_mmcfg_init(0);
 	}
 }
@@ -720,99 +579,41 @@ static int __init pci_mmcfg_late_insert_resources(void)
  */
 late_initcall(pci_mmcfg_late_insert_resources);
 
-/* Add MMCFG information for host bridges */
-int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
-			phys_addr_t addr)
+int pci_mmconfig_map_resource(struct device *dev, struct pci_mmcfg_region *cfg)
 {
-	int rc;
-	struct resource *tmp = NULL;
-	struct pci_mmcfg_region *cfg;
+	struct resource *tmp;
 
-	if (!(pci_probe & PCI_PROBE_MMCONF) || pci_mmcfg_arch_init_failed)
-		return -ENODEV;
-
-	if (start > end)
-		return -EINVAL;
-
-	mutex_lock(&pci_mmcfg_lock);
-	cfg = pci_mmconfig_lookup(seg, start);
-	if (cfg) {
-		if (cfg->end_bus < end)
-			dev_info(dev, FW_INFO
-				 "MMCONFIG for "
-				 "domain %04x [bus %02x-%02x] "
-				 "only partially covers this bridge\n",
-				  cfg->segment, cfg->start_bus, cfg->end_bus);
-		mutex_unlock(&pci_mmcfg_lock);
-		return -EEXIST;
-	}
-
-	if (!addr) {
-		mutex_unlock(&pci_mmcfg_lock);
-		return -EINVAL;
-	}
-
-	rc = -EBUSY;
-	cfg = pci_mmconfig_alloc(seg, start, end, addr);
-	if (cfg == NULL) {
-		dev_warn(dev, "fail to add MMCONFIG (out of memory)\n");
-		rc = -ENOMEM;
-	} else if (!pci_mmcfg_check_reserved(dev, cfg, 0)) {
+	if (!pci_mmcfg_check_reserved(dev, cfg, 0)) {
 		dev_warn(dev, FW_BUG "MMCONFIG %pR isn't reserved\n",
 			 &cfg->res);
-	} else {
-		/* Insert resource if it's not in boot stage */
-		if (pci_mmcfg_running_state)
-			tmp = insert_resource_conflict(&iomem_resource,
-						       &cfg->res);
-
+		return -EBUSY;
+	}
+	/* Insert resource if it's not in boot stage */
+	if (pci_mmcfg_running_state) {
+		tmp = insert_resource_conflict(&iomem_resource,
+					       &cfg->res);
 		if (tmp) {
-			dev_warn(dev,
-				 "MMCONFIG %pR conflicts with "
-				 "%s %pR\n",
-				 &cfg->res, tmp->name, tmp);
-		} else if (pci_mmcfg_arch_map(cfg)) {
-			dev_warn(dev, "fail to map MMCONFIG %pR.\n",
-				 &cfg->res);
-		} else {
-			list_add_sorted(cfg);
-			dev_info(dev, "MMCONFIG at %pR (base %#lx)\n",
-				 &cfg->res, (unsigned long)addr);
-			cfg = NULL;
-			rc = 0;
+			dev_warn(dev, "MMCONFIG %pR conflicts with %s %pR\n",
+				&cfg->res, tmp->name, tmp);
+			return -EBUSY;
 		}
 	}
-
-	if (cfg) {
-		if (cfg->res.parent)
-			release_resource(&cfg->res);
-		kfree(cfg);
+	if (pci_mmcfg_arch_map(cfg)) {
+		dev_warn(dev, "fail to map MMCONFIG %pR.\n", &cfg->res);
+		return -EBUSY;
 	}
-
-	mutex_unlock(&pci_mmcfg_lock);
-
-	return rc;
+	return 0;
 }
 
-/* Delete MMCFG information for host bridges */
-int pci_mmconfig_delete(u16 seg, u8 start, u8 end)
+void pci_mmconfig_unmap_resource(struct pci_mmcfg_region *cfg)
 {
-	struct pci_mmcfg_region *cfg;
-
-	mutex_lock(&pci_mmcfg_lock);
-	list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
-		if (cfg->segment == seg && cfg->start_bus == start &&
-		    cfg->end_bus == end) {
-			list_del_rcu(&cfg->list);
-			synchronize_rcu();
-			pci_mmcfg_arch_unmap(cfg);
-			if (cfg->res.parent)
-				release_resource(&cfg->res);
-			mutex_unlock(&pci_mmcfg_lock);
-			kfree(cfg);
-			return 0;
-		}
-	mutex_unlock(&pci_mmcfg_lock);
+	pci_mmcfg_arch_unmap(cfg);
+	if (cfg->res.parent)
+		release_resource(&cfg->res);
+	cfg->res.parent = NULL;
+}
 
-	return -ENOENT;
+int pci_mmconfig_enabled(void)
+{
+	return (pci_probe & PCI_PROBE_MMCONF) && !pci_mmcfg_arch_init_failed;
 }
diff --git a/arch/x86/pci/mmconfig_32.c b/arch/x86/pci/mmconfig_32.c
index 43984bc..38a37f8 100644
--- a/arch/x86/pci/mmconfig_32.c
+++ b/arch/x86/pci/mmconfig_32.c
@@ -12,6 +12,7 @@
 #include <linux/pci.h>
 #include <linux/init.h>
 #include <linux/rcupdate.h>
+#include <linux/pci-acpi.h>
 #include <asm/e820.h>
 #include <asm/pci_x86.h>
 
diff --git a/arch/x86/pci/mmconfig_64.c b/arch/x86/pci/mmconfig_64.c
index bea5249..29253ec 100644
--- a/arch/x86/pci/mmconfig_64.c
+++ b/arch/x86/pci/mmconfig_64.c
@@ -10,6 +10,7 @@
 #include <linux/acpi.h>
 #include <linux/bitmap.h>
 #include <linux/rcupdate.h>
+#include <linux/pci-acpi.h>
 #include <asm/e820.h>
 #include <asm/pci_x86.h>
 
diff --git a/arch/x86/pci/numachip.c b/arch/x86/pci/numachip.c
index 2e565e6..c181eeb 100644
--- a/arch/x86/pci/numachip.c
+++ b/arch/x86/pci/numachip.c
@@ -14,6 +14,7 @@
  */
 
 #include <linux/pci.h>
+#include <linux/pci-acpi.h>
 #include <asm/pci_x86.h>
 
 static u8 limit __read_mostly;
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 675eaf3..3f65311 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -40,6 +40,7 @@ acpi-$(CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC) += processor_pdc.o
 acpi-y				+= ec.o
 acpi-$(CONFIG_ACPI_DOCK)	+= dock.o
 acpi-y				+= pci_root.o pci_link.o pci_irq.o
+acpi-$(CONFIG_PCI_MMCONFIG)	+= pci_mcfg.o
 acpi-y				+= acpi_lpss.o acpi_apd.o
 acpi-y				+= acpi_platform.o
 acpi-y				+= acpi_pnp.o
diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c
new file mode 100644
index 0000000..ea84365
--- /dev/null
+++ b/drivers/acpi/pci_mcfg.c
@@ -0,0 +1,312 @@
+/*
+ * pci_mcfg.c
+ *
+ * Common code to maintain the MCFG areas and mappings
+ *
+ * This has been extracted from arch/x86/pci/mmconfig-shared.c
+ * and moved here so that other architectures can use this code.
+ */
+
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/dmi.h>
+#include <linux/pci-acpi.h>
+#include <linux/sfi_acpi.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/rculist.h>
+
+#define PREFIX	"ACPI: "
+
+static DEFINE_MUTEX(pci_mmcfg_lock);
+LIST_HEAD(pci_mmcfg_list);
+
+static void list_add_sorted(struct pci_mmcfg_region *new)
+{
+	struct pci_mmcfg_region *cfg;
+
+	/* keep list sorted by segment and starting bus number */
+	list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) {
+		if (cfg->segment > new->segment ||
+		    (cfg->segment == new->segment &&
+		     cfg->start_bus >= new->start_bus)) {
+			list_add_tail_rcu(&new->list, &cfg->list);
+			return;
+		}
+	}
+	list_add_tail_rcu(&new->list, &pci_mmcfg_list);
+}
+
+static struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start,
+						   int end, u64 addr)
+{
+	struct pci_mmcfg_region *new;
+	struct resource *res;
+
+	if (addr == 0)
+		return NULL;
+
+	new = kzalloc(sizeof(*new), GFP_KERNEL);
+	if (!new)
+		return NULL;
+
+	new->address = addr;
+	new->segment = segment;
+	new->start_bus = start;
+	new->end_bus = end;
+
+	res = &new->res;
+	res->start = addr + PCI_MMCFG_BUS_OFFSET(start);
+	res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1;
+	res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+	snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN,
+		 "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end);
+	res->name = new->name;
+
+	return new;
+}
+
+struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start,
+							int end, u64 addr)
+{
+	struct pci_mmcfg_region *new;
+
+	new = pci_mmconfig_alloc(segment, start, end, addr);
+	if (new) {
+		mutex_lock(&pci_mmcfg_lock);
+		list_add_sorted(new);
+		mutex_unlock(&pci_mmcfg_lock);
+
+		pr_info(PREFIX
+		       "MMCONFIG for domain %04x [bus %02x-%02x] at %pR "
+		       "(base %#lx)\n",
+		       segment, start, end, &new->res, (unsigned long)addr);
+	}
+
+	return new;
+}
+
+struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus)
+{
+	struct pci_mmcfg_region *cfg;
+
+	list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
+		if (cfg->segment == segment &&
+		    cfg->start_bus <= bus && bus <= cfg->end_bus)
+			return cfg;
+
+	return NULL;
+}
+
+/*
+ * Map a pci_mmcfg_region, can be overrriden by arch
+ */
+int __weak pci_mmconfig_map_resource(struct device *dev,
+	struct pci_mmcfg_region *mcfg)
+{
+	struct resource *tmp;
+	void __iomem *vaddr;
+
+	tmp = insert_resource_conflict(&iomem_resource, &mcfg->res);
+	if (tmp) {
+		dev_warn(dev, "MMCONFIG %pR conflicts with %s %pR\n",
+			&mcfg->res, tmp->name, tmp);
+		return -EBUSY;
+	}
+
+	vaddr =  ioremap(mcfg->res.start, resource_size(&mcfg->res));
+	if (!vaddr) {
+		release_resource(&mcfg->res);
+		return -ENOMEM;
+	}
+
+	mcfg->virt = vaddr;
+	return 0;
+}
+
+/*
+ * Unmap a pci_mmcfg_region, can be overrriden by arch
+ */
+void __weak pci_mmconfig_unmap_resource(struct pci_mmcfg_region *mcfg)
+{
+	if (mcfg->virt) {
+		iounmap(mcfg->virt);
+		mcfg->virt = NULL;
+	}
+	if (mcfg->res.parent) {
+		release_resource(&mcfg->res);
+		mcfg->res.parent = NULL;
+	}
+}
+
+/*
+ * check if the mmconfig is enabled and configured
+ */
+int __weak pci_mmconfig_enabled(void)
+{
+	return 1;
+}
+
+/* Add MMCFG information for host bridges */
+int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
+			phys_addr_t addr)
+{
+	struct pci_mmcfg_region *cfg;
+	int rc;
+
+	if (!pci_mmconfig_enabled())
+		return -ENODEV;
+	if (start > end)
+		return -EINVAL;
+
+	mutex_lock(&pci_mmcfg_lock);
+	cfg = pci_mmconfig_lookup(seg, start);
+	if (cfg) {
+		if (cfg->end_bus < end)
+			dev_info(dev, FW_INFO
+				 "MMCONFIG for "
+				 "domain %04x [bus %02x-%02x] "
+				 "only partially covers this bridge\n",
+				  cfg->segment, cfg->start_bus, cfg->end_bus);
+		rc = -EEXIST;
+		goto err;
+	}
+
+	if (!addr) {
+		rc = -EINVAL;
+		goto err;
+	}
+
+	cfg = pci_mmconfig_alloc(seg, start, end, addr);
+	if (cfg == NULL) {
+		dev_warn(dev, "fail to add MMCONFIG (out of memory)\n");
+		rc = -ENOMEM;
+		goto err;
+	}
+	rc = pci_mmconfig_map_resource(dev, cfg);
+	if (!rc) {
+		list_add_sorted(cfg);
+		dev_info(dev, "MMCONFIG at %pR (base %#lx)\n",
+				 &cfg->res, (unsigned long)addr);
+		return 0;
+	} else {
+		if (cfg->res.parent)
+			release_resource(&cfg->res);
+		kfree(cfg);
+	}
+
+err:
+	mutex_unlock(&pci_mmcfg_lock);
+	return rc;
+}
+
+/* Delete MMCFG information for host bridges */
+int pci_mmconfig_delete(u16 seg, u8 start, u8 end)
+{
+	struct pci_mmcfg_region *cfg;
+
+	mutex_lock(&pci_mmcfg_lock);
+	list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
+		if (cfg->segment == seg && cfg->start_bus == start &&
+		    cfg->end_bus == end) {
+			list_del_rcu(&cfg->list);
+			synchronize_rcu();
+			pci_mmconfig_unmap_resource(cfg);
+			mutex_unlock(&pci_mmcfg_lock);
+			kfree(cfg);
+			return 0;
+		}
+	mutex_unlock(&pci_mmcfg_lock);
+
+	return -ENOENT;
+}
+
+static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg,
+					struct acpi_mcfg_allocation *cfg)
+{
+	int year;
+
+	if (!config_enabled(CONFIG_X86))
+		return 0;
+
+	if (cfg->address < 0xFFFFFFFF)
+		return 0;
+
+	if (!strncmp(mcfg->header.oem_id, "SGI", 3))
+		return 0;
+
+	if (mcfg->header.revision >= 1) {
+		if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) &&
+		    year >= 2010)
+			return 0;
+	}
+
+	pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx "
+	       "is above 4GB, ignored\n", cfg->pci_segment,
+	       cfg->start_bus_number, cfg->end_bus_number, cfg->address);
+	return -EINVAL;
+}
+
+static int __init pci_parse_mcfg(struct acpi_table_header *header)
+{
+	struct acpi_table_mcfg *mcfg;
+	struct acpi_mcfg_allocation *cfg_table, *cfg;
+	unsigned long i;
+	int entries;
+
+	if (!header)
+		return -EINVAL;
+
+	mcfg = (struct acpi_table_mcfg *)header;
+
+	/* how many config structures do we have */
+	entries = 0;
+	i = header->length - sizeof(struct acpi_table_mcfg);
+	while (i >= sizeof(struct acpi_mcfg_allocation)) {
+		entries++;
+		i -= sizeof(struct acpi_mcfg_allocation);
+	}
+	if (entries == 0) {
+		pr_err(PREFIX "MMCONFIG has no entries\n");
+		return -ENODEV;
+	}
+
+	cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1];
+	for (i = 0; i < entries; i++) {
+		cfg = &cfg_table[i];
+		if (acpi_mcfg_check_entry(mcfg, cfg))
+			return -ENODEV;
+
+		if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number,
+				   cfg->end_bus_number, cfg->address) == NULL) {
+			pr_warn(PREFIX "no memory for MCFG entries\n");
+			return -ENOMEM;
+		}
+	}
+
+	return 0;
+}
+
+int __init pci_mmconfig_parse_table(void)
+{
+	return acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
+}
+
+void __weak __init pci_mmcfg_late_init(void)
+{
+	int err, n = 0;
+	struct pci_mmcfg_region *cfg;
+
+	err = pci_mmconfig_parse_table();
+	if (err) {
+		pr_err(PREFIX " Failed to parse MCFG (%d)\n", err);
+		return;
+	}
+
+	list_for_each_entry(cfg, &pci_mmcfg_list, list) {
+		pci_mmconfig_map_resource(NULL, cfg);
+		n++;
+	}
+
+	pr_info(PREFIX " MCFG table loaded %d entries\n", n);
+}
diff --git a/drivers/xen/pci.c b/drivers/xen/pci.c
index 7494dbe..97aa9d3 100644
--- a/drivers/xen/pci.c
+++ b/drivers/xen/pci.c
@@ -27,9 +27,6 @@
 #include <asm/xen/hypervisor.h>
 #include <asm/xen/hypercall.h>
 #include "../pci/pci.h"
-#ifdef CONFIG_PCI_MMCONFIG
-#include <asm/pci_x86.h>
-#endif
 
 static bool __read_mostly pci_seg_supported = true;
 
@@ -221,7 +218,7 @@ static int __init xen_mcfg_late(void)
 	if (!xen_initial_domain())
 		return 0;
 
-	if ((pci_probe & PCI_PROBE_MMCONF) == 0)
+	if (!pci_mmconfig_enabled())
 		return 0;
 
 	if (list_empty(&pci_mmcfg_list))
diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h
index 89ab057..e9450ef 100644
--- a/include/linux/pci-acpi.h
+++ b/include/linux/pci-acpi.h
@@ -106,6 +106,39 @@ extern const u8 pci_acpi_dsm_uuid[];
 #define RESET_DELAY_DSM		0x08
 #define FUNCTION_DELAY_DSM	0x09
 
+/* common API to maintain list of MCFG regions */
+/* "PCI MMCONFIG %04x [bus %02x-%02x]" */
+#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2)
+
+struct pci_mmcfg_region {
+	struct list_head list;
+	struct resource res;
+	u64 address;
+	char __iomem *virt;
+	u16 segment;
+	u8 start_bus;
+	u8 end_bus;
+	char name[PCI_MMCFG_RESOURCE_NAME_LEN];
+};
+
+extern int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
+			       phys_addr_t addr);
+extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end);
+
+extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus);
+extern struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start,
+							int end, u64 addr);
+extern int pci_mmconfig_map_resource(struct device *dev,
+	struct pci_mmcfg_region *mcfg);
+extern void pci_mmconfig_unmap_resource(struct pci_mmcfg_region *mcfg);
+extern int pci_mmconfig_enabled(void);
+extern int __init pci_mmconfig_parse_table(void);
+
+extern struct list_head pci_mmcfg_list;
+
+#define PCI_MMCFG_BUS_OFFSET(bus)      ((bus) << 20)
+#define PCI_MMCFG_OFFSET(bus, devfn)   ((bus) << 20 | (devfn) << 12)
+
 #else	/* CONFIG_ACPI */
 static inline void acpi_pci_add_bus(struct pci_bus *bus) { }
 static inline void acpi_pci_remove_bus(struct pci_bus *bus) { }
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v6 2/5] PCI: Handle ACPI companion and domain number
  2016-01-14  7:32 ` Jayachandran C
  (?)
@ 2016-01-14  7:32   ` Jayachandran C
  -1 siblings, 0 replies; 31+ messages in thread
From: Jayachandran C @ 2016-01-14  7:32 UTC (permalink / raw)
  To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann,
	linux-arm-kernel, Rafael J. Wysocki
  Cc: Jayachandran C, Lorenzo Pieralisi, Tomasz Nowicki, xen-devel

pci_create_root_bus is called with NULL as parent in ACPI. This
ends up calling pci_bus_assign_domain_nr with a NULL parent, which
crashes when dereferencing parent.

Fix this by providing a way to set the ACPI domain number and ACPI
companion in PCI code. We define pci_acpi_set_companion() to set
the ACPI companion pointer and acpi_pci_get_segment() and to get
the PCI domain number. This is just defined for the generic ACPI
PCI controller, using the corresponding sysdata.

pci_bus_assign_domain_nr is updated to call acpi_pci_get_segment()
to get the domain number to set on the root bus, in case of ACPI.

Signed-off-by: Jayachandran C <jchandra@broadcom.com>
---
 drivers/pci/pci.c        | 15 ++++++++++++++-
 drivers/pci/probe.c      |  2 ++
 include/linux/pci-acpi.h | 28 +++++++++++++++++++++++++++-
 3 files changed, 43 insertions(+), 2 deletions(-)

diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 314db8c..39d2bcc 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -20,6 +20,7 @@
 #include <linux/string.h>
 #include <linux/log2.h>
 #include <linux/pci-aspm.h>
+#include <linux/pci-acpi.h>
 #include <linux/pm_wakeup.h>
 #include <linux/interrupt.h>
 #include <linux/device.h>
@@ -4772,9 +4773,21 @@ int pci_get_new_domain_nr(void)
 void pci_bus_assign_domain_nr(struct pci_bus *bus, struct device *parent)
 {
 	static int use_dt_domains = -1;
-	int domain = of_get_pci_domain_nr(parent->of_node);
+	int domain;
 
 	/*
+	 * Handle ACPI early
+	 *
+	 * The companion is not set at this point, and ACPI sets parent to
+	 * NULL, we have to try to get the segment from acpi root info.
+	 */
+	if (!parent || !parent->of_node) {
+		bus->domain_nr = acpi_pci_get_segment(bus->sysdata);
+		return;
+	}
+
+	domain = of_get_pci_domain_nr(parent->of_node);
+	/*
 	 * Check DT domain and use_dt_domains values.
 	 *
 	 * If DT domain property is valid (domain >= 0) and
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index edb1984..5af66496 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/cpumask.h>
 #include <linux/pci-aspm.h>
+#include <linux/pci-acpi.h>
 #include <linux/aer.h>
 #include <linux/acpi.h>
 #include <asm-generic/pci-bridge.h>
@@ -2105,6 +2106,7 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
 	bridge->dev.parent = parent;
 	bridge->dev.release = pci_release_host_bridge_dev;
 	dev_set_name(&bridge->dev, "pci%04x:%02x", pci_domain_nr(b), bus);
+	pci_acpi_set_companion(bridge);
 	error = pcibios_root_bridge_prepare(bridge);
 	if (error) {
 		kfree(bridge);
diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h
index e9450ef..f8d62e3 100644
--- a/include/linux/pci-acpi.h
+++ b/include/linux/pci-acpi.h
@@ -138,12 +138,38 @@ extern struct list_head pci_mmcfg_list;
 
 #define PCI_MMCFG_BUS_OFFSET(bus)      ((bus) << 20)
 #define PCI_MMCFG_OFFSET(bus, devfn)   ((bus) << 20 | (devfn) << 12)
-
 #else	/* CONFIG_ACPI */
 static inline void acpi_pci_add_bus(struct pci_bus *bus) { }
 static inline void acpi_pci_remove_bus(struct pci_bus *bus) { }
 #endif	/* CONFIG_ACPI */
 
+#if defined(CONFIG_ACPI) && defined(CONFIG_ACPI_PCI_HOST_GENERIC)
+static inline void pci_acpi_set_companion(struct pci_host_bridge *bridge)
+{
+	struct pci_bus *b = bridge->bus;
+	struct acpi_pci_root_info *ci = (struct acpi_pci_root_info *)b->sysdata;
+
+	ACPI_COMPANION_SET(&bridge->dev, ci->bridge);
+}
+
+static inline u16 acpi_pci_get_segment(void *sysdata)
+{
+	struct acpi_pci_root_info *ci = (struct acpi_pci_root_info *)sysdata;
+
+	return ci->root->segment;
+}
+#else
+static inline void pci_acpi_set_companion(struct pci_host_bridge *bridge)
+{
+	/* leave it to the platform for now */
+}
+
+static inline u16 acpi_pci_get_segment(void *sysdata)
+{
+	return 0;
+}
+#endif
+
 #ifdef CONFIG_ACPI_APEI
 extern bool aer_acpi_firmware_first(void);
 #else
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v6 2/5] PCI: Handle ACPI companion and domain number
@ 2016-01-14  7:32   ` Jayachandran C
  0 siblings, 0 replies; 31+ messages in thread
From: Jayachandran C @ 2016-01-14  7:32 UTC (permalink / raw)
  To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann,
	linux-arm-kernel, Rafael J. Wysocki
  Cc: Jayachandran C, Lorenzo Pieralisi, Tomasz Nowicki, xen-devel

pci_create_root_bus is called with NULL as parent in ACPI. This
ends up calling pci_bus_assign_domain_nr with a NULL parent, which
crashes when dereferencing parent.

Fix this by providing a way to set the ACPI domain number and ACPI
companion in PCI code. We define pci_acpi_set_companion() to set
the ACPI companion pointer and acpi_pci_get_segment() and to get
the PCI domain number. This is just defined for the generic ACPI
PCI controller, using the corresponding sysdata.

pci_bus_assign_domain_nr is updated to call acpi_pci_get_segment()
to get the domain number to set on the root bus, in case of ACPI.

Signed-off-by: Jayachandran C <jchandra@broadcom.com>
---
 drivers/pci/pci.c        | 15 ++++++++++++++-
 drivers/pci/probe.c      |  2 ++
 include/linux/pci-acpi.h | 28 +++++++++++++++++++++++++++-
 3 files changed, 43 insertions(+), 2 deletions(-)

diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 314db8c..39d2bcc 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -20,6 +20,7 @@
 #include <linux/string.h>
 #include <linux/log2.h>
 #include <linux/pci-aspm.h>
+#include <linux/pci-acpi.h>
 #include <linux/pm_wakeup.h>
 #include <linux/interrupt.h>
 #include <linux/device.h>
@@ -4772,9 +4773,21 @@ int pci_get_new_domain_nr(void)
 void pci_bus_assign_domain_nr(struct pci_bus *bus, struct device *parent)
 {
 	static int use_dt_domains = -1;
-	int domain = of_get_pci_domain_nr(parent->of_node);
+	int domain;
 
 	/*
+	 * Handle ACPI early
+	 *
+	 * The companion is not set at this point, and ACPI sets parent to
+	 * NULL, we have to try to get the segment from acpi root info.
+	 */
+	if (!parent || !parent->of_node) {
+		bus->domain_nr = acpi_pci_get_segment(bus->sysdata);
+		return;
+	}
+
+	domain = of_get_pci_domain_nr(parent->of_node);
+	/*
 	 * Check DT domain and use_dt_domains values.
 	 *
 	 * If DT domain property is valid (domain >= 0) and
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index edb1984..5af66496 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/cpumask.h>
 #include <linux/pci-aspm.h>
+#include <linux/pci-acpi.h>
 #include <linux/aer.h>
 #include <linux/acpi.h>
 #include <asm-generic/pci-bridge.h>
@@ -2105,6 +2106,7 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
 	bridge->dev.parent = parent;
 	bridge->dev.release = pci_release_host_bridge_dev;
 	dev_set_name(&bridge->dev, "pci%04x:%02x", pci_domain_nr(b), bus);
+	pci_acpi_set_companion(bridge);
 	error = pcibios_root_bridge_prepare(bridge);
 	if (error) {
 		kfree(bridge);
diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h
index e9450ef..f8d62e3 100644
--- a/include/linux/pci-acpi.h
+++ b/include/linux/pci-acpi.h
@@ -138,12 +138,38 @@ extern struct list_head pci_mmcfg_list;
 
 #define PCI_MMCFG_BUS_OFFSET(bus)      ((bus) << 20)
 #define PCI_MMCFG_OFFSET(bus, devfn)   ((bus) << 20 | (devfn) << 12)
-
 #else	/* CONFIG_ACPI */
 static inline void acpi_pci_add_bus(struct pci_bus *bus) { }
 static inline void acpi_pci_remove_bus(struct pci_bus *bus) { }
 #endif	/* CONFIG_ACPI */
 
+#if defined(CONFIG_ACPI) && defined(CONFIG_ACPI_PCI_HOST_GENERIC)
+static inline void pci_acpi_set_companion(struct pci_host_bridge *bridge)
+{
+	struct pci_bus *b = bridge->bus;
+	struct acpi_pci_root_info *ci = (struct acpi_pci_root_info *)b->sysdata;
+
+	ACPI_COMPANION_SET(&bridge->dev, ci->bridge);
+}
+
+static inline u16 acpi_pci_get_segment(void *sysdata)
+{
+	struct acpi_pci_root_info *ci = (struct acpi_pci_root_info *)sysdata;
+
+	return ci->root->segment;
+}
+#else
+static inline void pci_acpi_set_companion(struct pci_host_bridge *bridge)
+{
+	/* leave it to the platform for now */
+}
+
+static inline u16 acpi_pci_get_segment(void *sysdata)
+{
+	return 0;
+}
+#endif
+
 #ifdef CONFIG_ACPI_APEI
 extern bool aer_acpi_firmware_first(void);
 #else
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v6 2/5] PCI: Handle ACPI companion and domain number
@ 2016-01-14  7:32   ` Jayachandran C
  0 siblings, 0 replies; 31+ messages in thread
From: Jayachandran C @ 2016-01-14  7:32 UTC (permalink / raw)
  To: linux-arm-kernel

pci_create_root_bus is called with NULL as parent in ACPI. This
ends up calling pci_bus_assign_domain_nr with a NULL parent, which
crashes when dereferencing parent.

Fix this by providing a way to set the ACPI domain number and ACPI
companion in PCI code. We define pci_acpi_set_companion() to set
the ACPI companion pointer and acpi_pci_get_segment() and to get
the PCI domain number. This is just defined for the generic ACPI
PCI controller, using the corresponding sysdata.

pci_bus_assign_domain_nr is updated to call acpi_pci_get_segment()
to get the domain number to set on the root bus, in case of ACPI.

Signed-off-by: Jayachandran C <jchandra@broadcom.com>
---
 drivers/pci/pci.c        | 15 ++++++++++++++-
 drivers/pci/probe.c      |  2 ++
 include/linux/pci-acpi.h | 28 +++++++++++++++++++++++++++-
 3 files changed, 43 insertions(+), 2 deletions(-)

diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 314db8c..39d2bcc 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -20,6 +20,7 @@
 #include <linux/string.h>
 #include <linux/log2.h>
 #include <linux/pci-aspm.h>
+#include <linux/pci-acpi.h>
 #include <linux/pm_wakeup.h>
 #include <linux/interrupt.h>
 #include <linux/device.h>
@@ -4772,9 +4773,21 @@ int pci_get_new_domain_nr(void)
 void pci_bus_assign_domain_nr(struct pci_bus *bus, struct device *parent)
 {
 	static int use_dt_domains = -1;
-	int domain = of_get_pci_domain_nr(parent->of_node);
+	int domain;
 
 	/*
+	 * Handle ACPI early
+	 *
+	 * The companion is not set at this point, and ACPI sets parent to
+	 * NULL, we have to try to get the segment from acpi root info.
+	 */
+	if (!parent || !parent->of_node) {
+		bus->domain_nr = acpi_pci_get_segment(bus->sysdata);
+		return;
+	}
+
+	domain = of_get_pci_domain_nr(parent->of_node);
+	/*
 	 * Check DT domain and use_dt_domains values.
 	 *
 	 * If DT domain property is valid (domain >= 0) and
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index edb1984..5af66496 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/cpumask.h>
 #include <linux/pci-aspm.h>
+#include <linux/pci-acpi.h>
 #include <linux/aer.h>
 #include <linux/acpi.h>
 #include <asm-generic/pci-bridge.h>
@@ -2105,6 +2106,7 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
 	bridge->dev.parent = parent;
 	bridge->dev.release = pci_release_host_bridge_dev;
 	dev_set_name(&bridge->dev, "pci%04x:%02x", pci_domain_nr(b), bus);
+	pci_acpi_set_companion(bridge);
 	error = pcibios_root_bridge_prepare(bridge);
 	if (error) {
 		kfree(bridge);
diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h
index e9450ef..f8d62e3 100644
--- a/include/linux/pci-acpi.h
+++ b/include/linux/pci-acpi.h
@@ -138,12 +138,38 @@ extern struct list_head pci_mmcfg_list;
 
 #define PCI_MMCFG_BUS_OFFSET(bus)      ((bus) << 20)
 #define PCI_MMCFG_OFFSET(bus, devfn)   ((bus) << 20 | (devfn) << 12)
-
 #else	/* CONFIG_ACPI */
 static inline void acpi_pci_add_bus(struct pci_bus *bus) { }
 static inline void acpi_pci_remove_bus(struct pci_bus *bus) { }
 #endif	/* CONFIG_ACPI */
 
+#if defined(CONFIG_ACPI) && defined(CONFIG_ACPI_PCI_HOST_GENERIC)
+static inline void pci_acpi_set_companion(struct pci_host_bridge *bridge)
+{
+	struct pci_bus *b = bridge->bus;
+	struct acpi_pci_root_info *ci = (struct acpi_pci_root_info *)b->sysdata;
+
+	ACPI_COMPANION_SET(&bridge->dev, ci->bridge);
+}
+
+static inline u16 acpi_pci_get_segment(void *sysdata)
+{
+	struct acpi_pci_root_info *ci = (struct acpi_pci_root_info *)sysdata;
+
+	return ci->root->segment;
+}
+#else
+static inline void pci_acpi_set_companion(struct pci_host_bridge *bridge)
+{
+	/* leave it to the platform for now */
+}
+
+static inline u16 acpi_pci_get_segment(void *sysdata)
+{
+	return 0;
+}
+#endif
+
 #ifdef CONFIG_ACPI_APEI
 extern bool aer_acpi_firmware_first(void);
 #else
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v6 2/5] PCI: Handle ACPI companion and domain number
  2016-01-14  7:32 ` Jayachandran C
                   ` (4 preceding siblings ...)
  (?)
@ 2016-01-14  7:32 ` Jayachandran C
  -1 siblings, 0 replies; 31+ messages in thread
From: Jayachandran C @ 2016-01-14  7:32 UTC (permalink / raw)
  To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann,
	linux-arm-kernel, Rafael J. Wysocki
  Cc: Tomasz Nowicki, Lorenzo Pieralisi, Jayachandran C, xen-devel

pci_create_root_bus is called with NULL as parent in ACPI. This
ends up calling pci_bus_assign_domain_nr with a NULL parent, which
crashes when dereferencing parent.

Fix this by providing a way to set the ACPI domain number and ACPI
companion in PCI code. We define pci_acpi_set_companion() to set
the ACPI companion pointer and acpi_pci_get_segment() and to get
the PCI domain number. This is just defined for the generic ACPI
PCI controller, using the corresponding sysdata.

pci_bus_assign_domain_nr is updated to call acpi_pci_get_segment()
to get the domain number to set on the root bus, in case of ACPI.

Signed-off-by: Jayachandran C <jchandra@broadcom.com>
---
 drivers/pci/pci.c        | 15 ++++++++++++++-
 drivers/pci/probe.c      |  2 ++
 include/linux/pci-acpi.h | 28 +++++++++++++++++++++++++++-
 3 files changed, 43 insertions(+), 2 deletions(-)

diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 314db8c..39d2bcc 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -20,6 +20,7 @@
 #include <linux/string.h>
 #include <linux/log2.h>
 #include <linux/pci-aspm.h>
+#include <linux/pci-acpi.h>
 #include <linux/pm_wakeup.h>
 #include <linux/interrupt.h>
 #include <linux/device.h>
@@ -4772,9 +4773,21 @@ int pci_get_new_domain_nr(void)
 void pci_bus_assign_domain_nr(struct pci_bus *bus, struct device *parent)
 {
 	static int use_dt_domains = -1;
-	int domain = of_get_pci_domain_nr(parent->of_node);
+	int domain;
 
 	/*
+	 * Handle ACPI early
+	 *
+	 * The companion is not set at this point, and ACPI sets parent to
+	 * NULL, we have to try to get the segment from acpi root info.
+	 */
+	if (!parent || !parent->of_node) {
+		bus->domain_nr = acpi_pci_get_segment(bus->sysdata);
+		return;
+	}
+
+	domain = of_get_pci_domain_nr(parent->of_node);
+	/*
 	 * Check DT domain and use_dt_domains values.
 	 *
 	 * If DT domain property is valid (domain >= 0) and
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index edb1984..5af66496 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/cpumask.h>
 #include <linux/pci-aspm.h>
+#include <linux/pci-acpi.h>
 #include <linux/aer.h>
 #include <linux/acpi.h>
 #include <asm-generic/pci-bridge.h>
@@ -2105,6 +2106,7 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
 	bridge->dev.parent = parent;
 	bridge->dev.release = pci_release_host_bridge_dev;
 	dev_set_name(&bridge->dev, "pci%04x:%02x", pci_domain_nr(b), bus);
+	pci_acpi_set_companion(bridge);
 	error = pcibios_root_bridge_prepare(bridge);
 	if (error) {
 		kfree(bridge);
diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h
index e9450ef..f8d62e3 100644
--- a/include/linux/pci-acpi.h
+++ b/include/linux/pci-acpi.h
@@ -138,12 +138,38 @@ extern struct list_head pci_mmcfg_list;
 
 #define PCI_MMCFG_BUS_OFFSET(bus)      ((bus) << 20)
 #define PCI_MMCFG_OFFSET(bus, devfn)   ((bus) << 20 | (devfn) << 12)
-
 #else	/* CONFIG_ACPI */
 static inline void acpi_pci_add_bus(struct pci_bus *bus) { }
 static inline void acpi_pci_remove_bus(struct pci_bus *bus) { }
 #endif	/* CONFIG_ACPI */
 
+#if defined(CONFIG_ACPI) && defined(CONFIG_ACPI_PCI_HOST_GENERIC)
+static inline void pci_acpi_set_companion(struct pci_host_bridge *bridge)
+{
+	struct pci_bus *b = bridge->bus;
+	struct acpi_pci_root_info *ci = (struct acpi_pci_root_info *)b->sysdata;
+
+	ACPI_COMPANION_SET(&bridge->dev, ci->bridge);
+}
+
+static inline u16 acpi_pci_get_segment(void *sysdata)
+{
+	struct acpi_pci_root_info *ci = (struct acpi_pci_root_info *)sysdata;
+
+	return ci->root->segment;
+}
+#else
+static inline void pci_acpi_set_companion(struct pci_host_bridge *bridge)
+{
+	/* leave it to the platform for now */
+}
+
+static inline u16 acpi_pci_get_segment(void *sysdata)
+{
+	return 0;
+}
+#endif
+
 #ifdef CONFIG_ACPI_APEI
 extern bool aer_acpi_firmware_first(void);
 #else
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v6 3/5] ACPI: PCI: Support platforms that need pci_remap_iospace
  2016-01-14  7:32 ` Jayachandran C
  (?)
@ 2016-01-14  7:32   ` Jayachandran C
  -1 siblings, 0 replies; 31+ messages in thread
From: Jayachandran C @ 2016-01-14  7:32 UTC (permalink / raw)
  To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann,
	linux-arm-kernel, Rafael J. Wysocki
  Cc: Jayachandran C, Lorenzo Pieralisi, Tomasz Nowicki, xen-devel

On some platforms (in this case ARM64), the PCI iospace needs to
be mapped with pci_remap_iospace and the resources have to be
adjusted for the iospace physical address.

This has to be done before acpi_pci_root_validate_resources()
checks and removes resource windows. Handle this by adding
a function acpi_pci_root_remap_iospace that is called in
acpi_pci_probe_root_resources(), before the validate call.

Also fix the address check in acpi_dev_ioresource_flags for
similar platforms.

Signed-off-by: Jayachandran C <jchandra@broadcom.com>
---
 drivers/acpi/pci_root.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++--
 drivers/acpi/resource.c |  2 ++
 2 files changed, 62 insertions(+), 2 deletions(-)

diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
index ae3fe4e..fcaa484 100644
--- a/drivers/acpi/pci_root.c
+++ b/drivers/acpi/pci_root.c
@@ -720,6 +720,61 @@ next:
 	}
 }
 
+#ifdef PCI_IOBASE
+/*
+ * The IO ports are mapped to a memory range, fixup IO resources to
+ * handle that
+ */
+static int acpi_pci_root_remap_iospace(struct acpi_pci_root_info *ci)
+{
+	struct resource_entry *entry;
+	struct resource iores;
+	resource_size_t iostart;
+	int err;
+
+	iores.flags = IORESOURCE_IO;
+	iores.start = (resource_size_t)-1;
+	iores.end = 0;
+	resource_list_for_each_entry(entry, &ci->resources) {
+		if (entry->res->flags & IORESOURCE_IO) {
+			iores.start = min(entry->res->start, iores.start);
+			iores.end = max(entry->res->end, iores.end);
+		}
+	}
+	if (iores.end == 0)
+		return 0;
+	iostart = iores.start;
+
+	resource_list_for_each_entry(entry, &ci->resources) {
+		if (entry->res->flags & IORESOURCE_IO) {
+			entry->res->start -= iostart;
+			entry->res->end -= iostart;
+			entry->offset -= iostart;
+		}
+	}
+	iores.start -= iostart;
+	iores.end -= iostart;
+
+	err = pci_remap_iospace(&iores, iostart);
+	if (err) {
+		pr_err("PCI: ACPI: err %d mapping IO %pR\n", err, &iores);
+		return -ENODEV;
+	}
+	pr_info(PREFIX "Mapped %pR at %#lx for IO.\n",
+			&iores, (unsigned long)iostart);
+	return 0;
+}
+#else
+/*
+ * The IO ports are mapped to a memory range, fixup IO resources to
+ * handle that
+ */
+static int acpi_pci_root_remap_iospace(struct acpi_pci_root_info *ci)
+{
+	return 0;
+}
+#endif /* PCI_IOBASE */
+
 int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info)
 {
 	int ret;
@@ -745,10 +800,13 @@ int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info)
 			else
 				entry->res->name = info->name;
 		}
-		acpi_pci_root_validate_resources(&device->dev, list,
+		ret = acpi_pci_root_remap_iospace(info);
+		if (ret >= 0) {
+			acpi_pci_root_validate_resources(&device->dev, list,
 						 IORESOURCE_MEM);
-		acpi_pci_root_validate_resources(&device->dev, list,
+			acpi_pci_root_validate_resources(&device->dev, list,
 						 IORESOURCE_IO);
+		}
 	}
 
 	return ret;
diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c
index cdc5c25..530cad4 100644
--- a/drivers/acpi/resource.c
+++ b/drivers/acpi/resource.c
@@ -126,8 +126,10 @@ static void acpi_dev_ioresource_flags(struct resource *res, u64 len,
 	if (!acpi_dev_resource_len_valid(res->start, res->end, len, true))
 		res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET;
 
+#ifndef PCI_IOBASE
 	if (res->end >= 0x10003)
 		res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET;
+#endif
 
 	if (io_decode == ACPI_DECODE_16)
 		res->flags |= IORESOURCE_IO_16BIT_ADDR;
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v6 3/5] ACPI: PCI: Support platforms that need pci_remap_iospace
@ 2016-01-14  7:32   ` Jayachandran C
  0 siblings, 0 replies; 31+ messages in thread
From: Jayachandran C @ 2016-01-14  7:32 UTC (permalink / raw)
  To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann,
	linux-arm-kernel, Rafael J. Wysocki
  Cc: Jayachandran C, Lorenzo Pieralisi, Tomasz Nowicki, xen-devel

On some platforms (in this case ARM64), the PCI iospace needs to
be mapped with pci_remap_iospace and the resources have to be
adjusted for the iospace physical address.

This has to be done before acpi_pci_root_validate_resources()
checks and removes resource windows. Handle this by adding
a function acpi_pci_root_remap_iospace that is called in
acpi_pci_probe_root_resources(), before the validate call.

Also fix the address check in acpi_dev_ioresource_flags for
similar platforms.

Signed-off-by: Jayachandran C <jchandra@broadcom.com>
---
 drivers/acpi/pci_root.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++--
 drivers/acpi/resource.c |  2 ++
 2 files changed, 62 insertions(+), 2 deletions(-)

diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
index ae3fe4e..fcaa484 100644
--- a/drivers/acpi/pci_root.c
+++ b/drivers/acpi/pci_root.c
@@ -720,6 +720,61 @@ next:
 	}
 }
 
+#ifdef PCI_IOBASE
+/*
+ * The IO ports are mapped to a memory range, fixup IO resources to
+ * handle that
+ */
+static int acpi_pci_root_remap_iospace(struct acpi_pci_root_info *ci)
+{
+	struct resource_entry *entry;
+	struct resource iores;
+	resource_size_t iostart;
+	int err;
+
+	iores.flags = IORESOURCE_IO;
+	iores.start = (resource_size_t)-1;
+	iores.end = 0;
+	resource_list_for_each_entry(entry, &ci->resources) {
+		if (entry->res->flags & IORESOURCE_IO) {
+			iores.start = min(entry->res->start, iores.start);
+			iores.end = max(entry->res->end, iores.end);
+		}
+	}
+	if (iores.end == 0)
+		return 0;
+	iostart = iores.start;
+
+	resource_list_for_each_entry(entry, &ci->resources) {
+		if (entry->res->flags & IORESOURCE_IO) {
+			entry->res->start -= iostart;
+			entry->res->end -= iostart;
+			entry->offset -= iostart;
+		}
+	}
+	iores.start -= iostart;
+	iores.end -= iostart;
+
+	err = pci_remap_iospace(&iores, iostart);
+	if (err) {
+		pr_err("PCI: ACPI: err %d mapping IO %pR\n", err, &iores);
+		return -ENODEV;
+	}
+	pr_info(PREFIX "Mapped %pR at %#lx for IO.\n",
+			&iores, (unsigned long)iostart);
+	return 0;
+}
+#else
+/*
+ * The IO ports are mapped to a memory range, fixup IO resources to
+ * handle that
+ */
+static int acpi_pci_root_remap_iospace(struct acpi_pci_root_info *ci)
+{
+	return 0;
+}
+#endif /* PCI_IOBASE */
+
 int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info)
 {
 	int ret;
@@ -745,10 +800,13 @@ int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info)
 			else
 				entry->res->name = info->name;
 		}
-		acpi_pci_root_validate_resources(&device->dev, list,
+		ret = acpi_pci_root_remap_iospace(info);
+		if (ret >= 0) {
+			acpi_pci_root_validate_resources(&device->dev, list,
 						 IORESOURCE_MEM);
-		acpi_pci_root_validate_resources(&device->dev, list,
+			acpi_pci_root_validate_resources(&device->dev, list,
 						 IORESOURCE_IO);
+		}
 	}
 
 	return ret;
diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c
index cdc5c25..530cad4 100644
--- a/drivers/acpi/resource.c
+++ b/drivers/acpi/resource.c
@@ -126,8 +126,10 @@ static void acpi_dev_ioresource_flags(struct resource *res, u64 len,
 	if (!acpi_dev_resource_len_valid(res->start, res->end, len, true))
 		res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET;
 
+#ifndef PCI_IOBASE
 	if (res->end >= 0x10003)
 		res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET;
+#endif
 
 	if (io_decode == ACPI_DECODE_16)
 		res->flags |= IORESOURCE_IO_16BIT_ADDR;
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v6 3/5] ACPI: PCI: Support platforms that need pci_remap_iospace
@ 2016-01-14  7:32   ` Jayachandran C
  0 siblings, 0 replies; 31+ messages in thread
From: Jayachandran C @ 2016-01-14  7:32 UTC (permalink / raw)
  To: linux-arm-kernel

On some platforms (in this case ARM64), the PCI iospace needs to
be mapped with pci_remap_iospace and the resources have to be
adjusted for the iospace physical address.

This has to be done before acpi_pci_root_validate_resources()
checks and removes resource windows. Handle this by adding
a function acpi_pci_root_remap_iospace that is called in
acpi_pci_probe_root_resources(), before the validate call.

Also fix the address check in acpi_dev_ioresource_flags for
similar platforms.

Signed-off-by: Jayachandran C <jchandra@broadcom.com>
---
 drivers/acpi/pci_root.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++--
 drivers/acpi/resource.c |  2 ++
 2 files changed, 62 insertions(+), 2 deletions(-)

diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
index ae3fe4e..fcaa484 100644
--- a/drivers/acpi/pci_root.c
+++ b/drivers/acpi/pci_root.c
@@ -720,6 +720,61 @@ next:
 	}
 }
 
+#ifdef PCI_IOBASE
+/*
+ * The IO ports are mapped to a memory range, fixup IO resources to
+ * handle that
+ */
+static int acpi_pci_root_remap_iospace(struct acpi_pci_root_info *ci)
+{
+	struct resource_entry *entry;
+	struct resource iores;
+	resource_size_t iostart;
+	int err;
+
+	iores.flags = IORESOURCE_IO;
+	iores.start = (resource_size_t)-1;
+	iores.end = 0;
+	resource_list_for_each_entry(entry, &ci->resources) {
+		if (entry->res->flags & IORESOURCE_IO) {
+			iores.start = min(entry->res->start, iores.start);
+			iores.end = max(entry->res->end, iores.end);
+		}
+	}
+	if (iores.end == 0)
+		return 0;
+	iostart = iores.start;
+
+	resource_list_for_each_entry(entry, &ci->resources) {
+		if (entry->res->flags & IORESOURCE_IO) {
+			entry->res->start -= iostart;
+			entry->res->end -= iostart;
+			entry->offset -= iostart;
+		}
+	}
+	iores.start -= iostart;
+	iores.end -= iostart;
+
+	err = pci_remap_iospace(&iores, iostart);
+	if (err) {
+		pr_err("PCI: ACPI: err %d mapping IO %pR\n", err, &iores);
+		return -ENODEV;
+	}
+	pr_info(PREFIX "Mapped %pR at %#lx for IO.\n",
+			&iores, (unsigned long)iostart);
+	return 0;
+}
+#else
+/*
+ * The IO ports are mapped to a memory range, fixup IO resources to
+ * handle that
+ */
+static int acpi_pci_root_remap_iospace(struct acpi_pci_root_info *ci)
+{
+	return 0;
+}
+#endif /* PCI_IOBASE */
+
 int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info)
 {
 	int ret;
@@ -745,10 +800,13 @@ int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info)
 			else
 				entry->res->name = info->name;
 		}
-		acpi_pci_root_validate_resources(&device->dev, list,
+		ret = acpi_pci_root_remap_iospace(info);
+		if (ret >= 0) {
+			acpi_pci_root_validate_resources(&device->dev, list,
 						 IORESOURCE_MEM);
-		acpi_pci_root_validate_resources(&device->dev, list,
+			acpi_pci_root_validate_resources(&device->dev, list,
 						 IORESOURCE_IO);
+		}
 	}
 
 	return ret;
diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c
index cdc5c25..530cad4 100644
--- a/drivers/acpi/resource.c
+++ b/drivers/acpi/resource.c
@@ -126,8 +126,10 @@ static void acpi_dev_ioresource_flags(struct resource *res, u64 len,
 	if (!acpi_dev_resource_len_valid(res->start, res->end, len, true))
 		res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET;
 
+#ifndef PCI_IOBASE
 	if (res->end >= 0x10003)
 		res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET;
+#endif
 
 	if (io_decode == ACPI_DECODE_16)
 		res->flags |= IORESOURCE_IO_16BIT_ADDR;
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v6 3/5] ACPI: PCI: Support platforms that need pci_remap_iospace
  2016-01-14  7:32 ` Jayachandran C
                   ` (5 preceding siblings ...)
  (?)
@ 2016-01-14  7:32 ` Jayachandran C
  -1 siblings, 0 replies; 31+ messages in thread
From: Jayachandran C @ 2016-01-14  7:32 UTC (permalink / raw)
  To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann,
	linux-arm-kernel, Rafael J. Wysocki
  Cc: Tomasz Nowicki, Lorenzo Pieralisi, Jayachandran C, xen-devel

On some platforms (in this case ARM64), the PCI iospace needs to
be mapped with pci_remap_iospace and the resources have to be
adjusted for the iospace physical address.

This has to be done before acpi_pci_root_validate_resources()
checks and removes resource windows. Handle this by adding
a function acpi_pci_root_remap_iospace that is called in
acpi_pci_probe_root_resources(), before the validate call.

Also fix the address check in acpi_dev_ioresource_flags for
similar platforms.

Signed-off-by: Jayachandran C <jchandra@broadcom.com>
---
 drivers/acpi/pci_root.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++--
 drivers/acpi/resource.c |  2 ++
 2 files changed, 62 insertions(+), 2 deletions(-)

diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
index ae3fe4e..fcaa484 100644
--- a/drivers/acpi/pci_root.c
+++ b/drivers/acpi/pci_root.c
@@ -720,6 +720,61 @@ next:
 	}
 }
 
+#ifdef PCI_IOBASE
+/*
+ * The IO ports are mapped to a memory range, fixup IO resources to
+ * handle that
+ */
+static int acpi_pci_root_remap_iospace(struct acpi_pci_root_info *ci)
+{
+	struct resource_entry *entry;
+	struct resource iores;
+	resource_size_t iostart;
+	int err;
+
+	iores.flags = IORESOURCE_IO;
+	iores.start = (resource_size_t)-1;
+	iores.end = 0;
+	resource_list_for_each_entry(entry, &ci->resources) {
+		if (entry->res->flags & IORESOURCE_IO) {
+			iores.start = min(entry->res->start, iores.start);
+			iores.end = max(entry->res->end, iores.end);
+		}
+	}
+	if (iores.end == 0)
+		return 0;
+	iostart = iores.start;
+
+	resource_list_for_each_entry(entry, &ci->resources) {
+		if (entry->res->flags & IORESOURCE_IO) {
+			entry->res->start -= iostart;
+			entry->res->end -= iostart;
+			entry->offset -= iostart;
+		}
+	}
+	iores.start -= iostart;
+	iores.end -= iostart;
+
+	err = pci_remap_iospace(&iores, iostart);
+	if (err) {
+		pr_err("PCI: ACPI: err %d mapping IO %pR\n", err, &iores);
+		return -ENODEV;
+	}
+	pr_info(PREFIX "Mapped %pR at %#lx for IO.\n",
+			&iores, (unsigned long)iostart);
+	return 0;
+}
+#else
+/*
+ * The IO ports are mapped to a memory range, fixup IO resources to
+ * handle that
+ */
+static int acpi_pci_root_remap_iospace(struct acpi_pci_root_info *ci)
+{
+	return 0;
+}
+#endif /* PCI_IOBASE */
+
 int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info)
 {
 	int ret;
@@ -745,10 +800,13 @@ int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info)
 			else
 				entry->res->name = info->name;
 		}
-		acpi_pci_root_validate_resources(&device->dev, list,
+		ret = acpi_pci_root_remap_iospace(info);
+		if (ret >= 0) {
+			acpi_pci_root_validate_resources(&device->dev, list,
 						 IORESOURCE_MEM);
-		acpi_pci_root_validate_resources(&device->dev, list,
+			acpi_pci_root_validate_resources(&device->dev, list,
 						 IORESOURCE_IO);
+		}
 	}
 
 	return ret;
diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c
index cdc5c25..530cad4 100644
--- a/drivers/acpi/resource.c
+++ b/drivers/acpi/resource.c
@@ -126,8 +126,10 @@ static void acpi_dev_ioresource_flags(struct resource *res, u64 len,
 	if (!acpi_dev_resource_len_valid(res->start, res->end, len, true))
 		res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET;
 
+#ifndef PCI_IOBASE
 	if (res->end >= 0x10003)
 		res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET;
+#endif
 
 	if (io_decode == ACPI_DECODE_16)
 		res->flags |= IORESOURCE_IO_16BIT_ADDR;
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v6 4/5] arm64: pci: Add ACPI support
  2016-01-14  7:32 ` Jayachandran C
  (?)
@ 2016-01-14  7:32   ` Jayachandran C
  -1 siblings, 0 replies; 31+ messages in thread
From: Jayachandran C @ 2016-01-14  7:32 UTC (permalink / raw)
  To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann,
	linux-arm-kernel, Rafael J. Wysocki
  Cc: Jayachandran C, Lorenzo Pieralisi, Tomasz Nowicki, xen-devel

Add functions needed for ACPI support.

pci_acpi_scan_root(), raw_pci_read and raw_pci_write are marked as
weak so that it can be implemented by the generic ACPI PCI driver.

pcibios_enable_device and pcibios_disable_device handle acpi irq enable
and disable. And, pcibios_add_bus and pcibios_remove_bus have been
added call the corresponding ACPI functions.

Signed-off-by: Jayachandran C <jchandra@broadcom.com>
---
 arch/arm64/Kconfig      |  3 +++
 arch/arm64/kernel/pci.c | 34 +++++++++++++++++++++++++++++-----
 2 files changed, 32 insertions(+), 5 deletions(-)

diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 871f217..8c93af2 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -203,6 +203,9 @@ config PCI_DOMAINS_GENERIC
 config PCI_SYSCALL
 	def_bool PCI
 
+config PCI_MMCONFIG
+	def_bool PCI && ACPI
+
 source "drivers/pci/Kconfig"
 source "drivers/pci/pcie/Kconfig"
 source "drivers/pci/hotplug/Kconfig"
diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c
index b3d098b..db0a6a9 100644
--- a/arch/arm64/kernel/pci.c
+++ b/arch/arm64/kernel/pci.c
@@ -17,6 +17,7 @@
 #include <linux/mm.h>
 #include <linux/of_pci.h>
 #include <linux/of_platform.h>
+#include <linux/pci-acpi.h>
 #include <linux/slab.h>
 
 #include <asm/pci-bridge.h>
@@ -48,9 +49,22 @@ int pcibios_enable_device(struct pci_dev *dev, int mask)
 	if (pci_has_flag(PCI_PROBE_ONLY))
 		return 0;
 
+#ifdef CONFIG_ACPI
+	if (acpi_find_root_bridge_handle(dev))
+		acpi_pci_irq_enable(dev);
+#endif
+
 	return pci_enable_resources(dev, mask);
 }
 
+void pcibios_disable_device(struct pci_dev *dev)
+{
+#ifdef CONFIG_ACPI
+	if (acpi_find_root_bridge_handle(dev))
+		acpi_pci_irq_disable(dev);
+#endif
+}
+
 /*
  * Try to assign the IRQ number from DT when adding a new device
  */
@@ -62,15 +76,15 @@ int pcibios_add_device(struct pci_dev *dev)
 }
 
 /*
- * raw_pci_read/write - Platform-specific PCI config space access.
+ * ACPI uses these - leave it to the generic ACPI PCI driver
  */
-int raw_pci_read(unsigned int domain, unsigned int bus,
+int __weak raw_pci_read(unsigned int domain, unsigned int bus,
 		  unsigned int devfn, int reg, int len, u32 *val)
 {
 	return -ENXIO;
 }
 
-int raw_pci_write(unsigned int domain, unsigned int bus,
+int __weak raw_pci_write(unsigned int domain, unsigned int bus,
 		unsigned int devfn, int reg, int len, u32 val)
 {
 	return -ENXIO;
@@ -78,9 +92,19 @@ int raw_pci_write(unsigned int domain, unsigned int bus,
 
 #ifdef CONFIG_ACPI
 /* Root bridge scanning */
-struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
+struct pci_bus * __weak pci_acpi_scan_root(struct acpi_pci_root *root)
 {
-	/* TODO: Should be revisited when implementing PCI on ACPI */
 	return NULL;
 }
+
+void pcibios_add_bus(struct pci_bus *bus)
+{
+	acpi_pci_add_bus(bus);
+}
+
+void pcibios_remove_bus(struct pci_bus *bus)
+{
+	acpi_pci_remove_bus(bus);
+}
+
 #endif
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v6 4/5] arm64: pci: Add ACPI support
@ 2016-01-14  7:32   ` Jayachandran C
  0 siblings, 0 replies; 31+ messages in thread
From: Jayachandran C @ 2016-01-14  7:32 UTC (permalink / raw)
  To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann,
	linux-arm-kernel, Rafael J. Wysocki
  Cc: Jayachandran C, Lorenzo Pieralisi, Tomasz Nowicki, xen-devel

Add functions needed for ACPI support.

pci_acpi_scan_root(), raw_pci_read and raw_pci_write are marked as
weak so that it can be implemented by the generic ACPI PCI driver.

pcibios_enable_device and pcibios_disable_device handle acpi irq enable
and disable. And, pcibios_add_bus and pcibios_remove_bus have been
added call the corresponding ACPI functions.

Signed-off-by: Jayachandran C <jchandra@broadcom.com>
---
 arch/arm64/Kconfig      |  3 +++
 arch/arm64/kernel/pci.c | 34 +++++++++++++++++++++++++++++-----
 2 files changed, 32 insertions(+), 5 deletions(-)

diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 871f217..8c93af2 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -203,6 +203,9 @@ config PCI_DOMAINS_GENERIC
 config PCI_SYSCALL
 	def_bool PCI
 
+config PCI_MMCONFIG
+	def_bool PCI && ACPI
+
 source "drivers/pci/Kconfig"
 source "drivers/pci/pcie/Kconfig"
 source "drivers/pci/hotplug/Kconfig"
diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c
index b3d098b..db0a6a9 100644
--- a/arch/arm64/kernel/pci.c
+++ b/arch/arm64/kernel/pci.c
@@ -17,6 +17,7 @@
 #include <linux/mm.h>
 #include <linux/of_pci.h>
 #include <linux/of_platform.h>
+#include <linux/pci-acpi.h>
 #include <linux/slab.h>
 
 #include <asm/pci-bridge.h>
@@ -48,9 +49,22 @@ int pcibios_enable_device(struct pci_dev *dev, int mask)
 	if (pci_has_flag(PCI_PROBE_ONLY))
 		return 0;
 
+#ifdef CONFIG_ACPI
+	if (acpi_find_root_bridge_handle(dev))
+		acpi_pci_irq_enable(dev);
+#endif
+
 	return pci_enable_resources(dev, mask);
 }
 
+void pcibios_disable_device(struct pci_dev *dev)
+{
+#ifdef CONFIG_ACPI
+	if (acpi_find_root_bridge_handle(dev))
+		acpi_pci_irq_disable(dev);
+#endif
+}
+
 /*
  * Try to assign the IRQ number from DT when adding a new device
  */
@@ -62,15 +76,15 @@ int pcibios_add_device(struct pci_dev *dev)
 }
 
 /*
- * raw_pci_read/write - Platform-specific PCI config space access.
+ * ACPI uses these - leave it to the generic ACPI PCI driver
  */
-int raw_pci_read(unsigned int domain, unsigned int bus,
+int __weak raw_pci_read(unsigned int domain, unsigned int bus,
 		  unsigned int devfn, int reg, int len, u32 *val)
 {
 	return -ENXIO;
 }
 
-int raw_pci_write(unsigned int domain, unsigned int bus,
+int __weak raw_pci_write(unsigned int domain, unsigned int bus,
 		unsigned int devfn, int reg, int len, u32 val)
 {
 	return -ENXIO;
@@ -78,9 +92,19 @@ int raw_pci_write(unsigned int domain, unsigned int bus,
 
 #ifdef CONFIG_ACPI
 /* Root bridge scanning */
-struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
+struct pci_bus * __weak pci_acpi_scan_root(struct acpi_pci_root *root)
 {
-	/* TODO: Should be revisited when implementing PCI on ACPI */
 	return NULL;
 }
+
+void pcibios_add_bus(struct pci_bus *bus)
+{
+	acpi_pci_add_bus(bus);
+}
+
+void pcibios_remove_bus(struct pci_bus *bus)
+{
+	acpi_pci_remove_bus(bus);
+}
+
 #endif
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v6 4/5] arm64: pci: Add ACPI support
@ 2016-01-14  7:32   ` Jayachandran C
  0 siblings, 0 replies; 31+ messages in thread
From: Jayachandran C @ 2016-01-14  7:32 UTC (permalink / raw)
  To: linux-arm-kernel

Add functions needed for ACPI support.

pci_acpi_scan_root(), raw_pci_read and raw_pci_write are marked as
weak so that it can be implemented by the generic ACPI PCI driver.

pcibios_enable_device and pcibios_disable_device handle acpi irq enable
and disable. And, pcibios_add_bus and pcibios_remove_bus have been
added call the corresponding ACPI functions.

Signed-off-by: Jayachandran C <jchandra@broadcom.com>
---
 arch/arm64/Kconfig      |  3 +++
 arch/arm64/kernel/pci.c | 34 +++++++++++++++++++++++++++++-----
 2 files changed, 32 insertions(+), 5 deletions(-)

diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 871f217..8c93af2 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -203,6 +203,9 @@ config PCI_DOMAINS_GENERIC
 config PCI_SYSCALL
 	def_bool PCI
 
+config PCI_MMCONFIG
+	def_bool PCI && ACPI
+
 source "drivers/pci/Kconfig"
 source "drivers/pci/pcie/Kconfig"
 source "drivers/pci/hotplug/Kconfig"
diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c
index b3d098b..db0a6a9 100644
--- a/arch/arm64/kernel/pci.c
+++ b/arch/arm64/kernel/pci.c
@@ -17,6 +17,7 @@
 #include <linux/mm.h>
 #include <linux/of_pci.h>
 #include <linux/of_platform.h>
+#include <linux/pci-acpi.h>
 #include <linux/slab.h>
 
 #include <asm/pci-bridge.h>
@@ -48,9 +49,22 @@ int pcibios_enable_device(struct pci_dev *dev, int mask)
 	if (pci_has_flag(PCI_PROBE_ONLY))
 		return 0;
 
+#ifdef CONFIG_ACPI
+	if (acpi_find_root_bridge_handle(dev))
+		acpi_pci_irq_enable(dev);
+#endif
+
 	return pci_enable_resources(dev, mask);
 }
 
+void pcibios_disable_device(struct pci_dev *dev)
+{
+#ifdef CONFIG_ACPI
+	if (acpi_find_root_bridge_handle(dev))
+		acpi_pci_irq_disable(dev);
+#endif
+}
+
 /*
  * Try to assign the IRQ number from DT when adding a new device
  */
@@ -62,15 +76,15 @@ int pcibios_add_device(struct pci_dev *dev)
 }
 
 /*
- * raw_pci_read/write - Platform-specific PCI config space access.
+ * ACPI uses these - leave it to the generic ACPI PCI driver
  */
-int raw_pci_read(unsigned int domain, unsigned int bus,
+int __weak raw_pci_read(unsigned int domain, unsigned int bus,
 		  unsigned int devfn, int reg, int len, u32 *val)
 {
 	return -ENXIO;
 }
 
-int raw_pci_write(unsigned int domain, unsigned int bus,
+int __weak raw_pci_write(unsigned int domain, unsigned int bus,
 		unsigned int devfn, int reg, int len, u32 val)
 {
 	return -ENXIO;
@@ -78,9 +92,19 @@ int raw_pci_write(unsigned int domain, unsigned int bus,
 
 #ifdef CONFIG_ACPI
 /* Root bridge scanning */
-struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
+struct pci_bus * __weak pci_acpi_scan_root(struct acpi_pci_root *root)
 {
-	/* TODO: Should be revisited when implementing PCI on ACPI */
 	return NULL;
 }
+
+void pcibios_add_bus(struct pci_bus *bus)
+{
+	acpi_pci_add_bus(bus);
+}
+
+void pcibios_remove_bus(struct pci_bus *bus)
+{
+	acpi_pci_remove_bus(bus);
+}
+
 #endif
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v6 4/5] arm64: pci: Add ACPI support
  2016-01-14  7:32 ` Jayachandran C
                   ` (8 preceding siblings ...)
  (?)
@ 2016-01-14  7:32 ` Jayachandran C
  -1 siblings, 0 replies; 31+ messages in thread
From: Jayachandran C @ 2016-01-14  7:32 UTC (permalink / raw)
  To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann,
	linux-arm-kernel, Rafael J. Wysocki
  Cc: Tomasz Nowicki, Lorenzo Pieralisi, Jayachandran C, xen-devel

Add functions needed for ACPI support.

pci_acpi_scan_root(), raw_pci_read and raw_pci_write are marked as
weak so that it can be implemented by the generic ACPI PCI driver.

pcibios_enable_device and pcibios_disable_device handle acpi irq enable
and disable. And, pcibios_add_bus and pcibios_remove_bus have been
added call the corresponding ACPI functions.

Signed-off-by: Jayachandran C <jchandra@broadcom.com>
---
 arch/arm64/Kconfig      |  3 +++
 arch/arm64/kernel/pci.c | 34 +++++++++++++++++++++++++++++-----
 2 files changed, 32 insertions(+), 5 deletions(-)

diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 871f217..8c93af2 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -203,6 +203,9 @@ config PCI_DOMAINS_GENERIC
 config PCI_SYSCALL
 	def_bool PCI
 
+config PCI_MMCONFIG
+	def_bool PCI && ACPI
+
 source "drivers/pci/Kconfig"
 source "drivers/pci/pcie/Kconfig"
 source "drivers/pci/hotplug/Kconfig"
diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c
index b3d098b..db0a6a9 100644
--- a/arch/arm64/kernel/pci.c
+++ b/arch/arm64/kernel/pci.c
@@ -17,6 +17,7 @@
 #include <linux/mm.h>
 #include <linux/of_pci.h>
 #include <linux/of_platform.h>
+#include <linux/pci-acpi.h>
 #include <linux/slab.h>
 
 #include <asm/pci-bridge.h>
@@ -48,9 +49,22 @@ int pcibios_enable_device(struct pci_dev *dev, int mask)
 	if (pci_has_flag(PCI_PROBE_ONLY))
 		return 0;
 
+#ifdef CONFIG_ACPI
+	if (acpi_find_root_bridge_handle(dev))
+		acpi_pci_irq_enable(dev);
+#endif
+
 	return pci_enable_resources(dev, mask);
 }
 
+void pcibios_disable_device(struct pci_dev *dev)
+{
+#ifdef CONFIG_ACPI
+	if (acpi_find_root_bridge_handle(dev))
+		acpi_pci_irq_disable(dev);
+#endif
+}
+
 /*
  * Try to assign the IRQ number from DT when adding a new device
  */
@@ -62,15 +76,15 @@ int pcibios_add_device(struct pci_dev *dev)
 }
 
 /*
- * raw_pci_read/write - Platform-specific PCI config space access.
+ * ACPI uses these - leave it to the generic ACPI PCI driver
  */
-int raw_pci_read(unsigned int domain, unsigned int bus,
+int __weak raw_pci_read(unsigned int domain, unsigned int bus,
 		  unsigned int devfn, int reg, int len, u32 *val)
 {
 	return -ENXIO;
 }
 
-int raw_pci_write(unsigned int domain, unsigned int bus,
+int __weak raw_pci_write(unsigned int domain, unsigned int bus,
 		unsigned int devfn, int reg, int len, u32 val)
 {
 	return -ENXIO;
@@ -78,9 +92,19 @@ int raw_pci_write(unsigned int domain, unsigned int bus,
 
 #ifdef CONFIG_ACPI
 /* Root bridge scanning */
-struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
+struct pci_bus * __weak pci_acpi_scan_root(struct acpi_pci_root *root)
 {
-	/* TODO: Should be revisited when implementing PCI on ACPI */
 	return NULL;
 }
+
+void pcibios_add_bus(struct pci_bus *bus)
+{
+	acpi_pci_add_bus(bus);
+}
+
+void pcibios_remove_bus(struct pci_bus *bus)
+{
+	acpi_pci_remove_bus(bus);
+}
+
 #endif
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v6 5/5] PCI: ACPI: Add a generic ACPI based host controller
  2016-01-14  7:32 ` Jayachandran C
  (?)
@ 2016-01-14  7:32   ` Jayachandran C
  -1 siblings, 0 replies; 31+ messages in thread
From: Jayachandran C @ 2016-01-14  7:32 UTC (permalink / raw)
  To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann,
	linux-arm-kernel, Rafael J. Wysocki
  Cc: Jayachandran C, Lorenzo Pieralisi, Tomasz Nowicki, xen-devel

Add a simple ACPI based PCI host controller. This is done by
providing a simple implementation for pci_acpi_scan_root().

The pci_mmcfg_list handling is done by the ACPI code, we
keep a reference to the pci_mmcfg_region which is already
mapped.

Provide implementations of raw_pci_read and raw_pci_write based
on the pci_mmcfg_list

This is enabled only for ARM64 now.

Signed-off-by: Jayachandran C <jchandra@broadcom.com>
---
 drivers/acpi/Kconfig         |   8 ++
 drivers/acpi/Makefile        |   1 +
 drivers/acpi/pci_host_acpi.c | 186 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 195 insertions(+)
 create mode 100644 drivers/acpi/pci_host_acpi.c

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 5eef4cb..c5738be 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -283,6 +283,14 @@ config ACPI_NUMA
 	depends on (X86 || IA64)
 	default y if IA64_GENERIC || IA64_SGI_SN2
 
+config ACPI_PCI_HOST_GENERIC
+	bool "Generic ACPI PCI host controller"
+	depends on ARM64 && ACPI
+	help
+	  Say Y here if you want to support a simple generic ACPI PCI host
+	  controller.
+
+
 config ACPI_CUSTOM_DSDT_FILE
 	string "Custom DSDT Table file to include"
 	default ""
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 3f65311..9fffdf2 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -41,6 +41,7 @@ acpi-y				+= ec.o
 acpi-$(CONFIG_ACPI_DOCK)	+= dock.o
 acpi-y				+= pci_root.o pci_link.o pci_irq.o
 acpi-$(CONFIG_PCI_MMCONFIG)	+= pci_mcfg.o
+acpi-$(CONFIG_ACPI_PCI_HOST_GENERIC) += pci_host_acpi.o
 acpi-y				+= acpi_lpss.o acpi_apd.o
 acpi-y				+= acpi_platform.o
 acpi-y				+= acpi_pnp.o
diff --git a/drivers/acpi/pci_host_acpi.c b/drivers/acpi/pci_host_acpi.c
new file mode 100644
index 0000000..9dbdd81
--- /dev/null
+++ b/drivers/acpi/pci_host_acpi.c
@@ -0,0 +1,186 @@
+/*
+ * Generic PCI host controller driver for ACPI based systems
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (c) 2015 Broadcom Corporation
+ *
+ * Based on drivers/pci/host/pci-host-generic.c
+ * Copyright (C) 2014 ARM Limited
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/acpi.h>
+#include <linux/pci-acpi.h>
+#include <linux/sfi_acpi.h>
+#include <linux/slab.h>
+
+#define PREFIX			"pci-host-acpi:"
+
+/* sysdata pointer is ->root_info */
+struct gen_acpi_root_info {
+	struct acpi_pci_root_info	common;
+	struct pci_mmcfg_region		*mcfg;
+	bool				mcfg_added;
+};
+
+/* find mapping of a MCFG area */
+static void __iomem *gen_acpi_map_cfg_bus(struct pci_bus *bus,
+			unsigned int devfn, int where)
+{
+	struct gen_acpi_root_info *pci = bus->sysdata;
+	struct pci_mmcfg_region *mcfg = pci->mcfg;
+
+	if (bus->number < mcfg->start_bus || bus->number > mcfg->end_bus)
+		return NULL;
+
+	return mcfg->virt +
+		PCI_MMCFG_OFFSET(bus->number - mcfg->start_bus, devfn) +
+		where;
+}
+
+static struct pci_ops gen_acpi_pci_ops = {
+	.map_bus	= gen_acpi_map_cfg_bus,
+	.read		= pci_generic_config_read,
+	.write		= pci_generic_config_write,
+};
+
+/* Insert the ECFG area for a root bus */
+static int pci_acpi_root_init_info(struct acpi_pci_root_info *ci)
+{
+	struct gen_acpi_root_info *info;
+	struct acpi_pci_root *root = ci->root;
+	struct device *dev = &ci->bridge->dev;
+	int err;
+
+	info = container_of(ci, struct gen_acpi_root_info, common);
+	err = pci_mmconfig_insert(dev, root->segment, root->secondary.start,
+			root->secondary.end, root->mcfg_addr);
+	if (err && err != -EEXIST)
+		return err;
+
+	info->mcfg = pci_mmconfig_lookup(root->segment, root->secondary.start);
+	WARN_ON(info->mcfg == NULL);
+	info->mcfg_added = (err == -EEXIST);
+	return 0;
+}
+
+static void pci_acpi_root_release_info(struct acpi_pci_root_info *ci)
+{
+	struct gen_acpi_root_info *info;
+	struct acpi_pci_root *root = ci->root;
+
+	info = container_of(ci, struct gen_acpi_root_info, common);
+	if (info->mcfg_added)
+		pci_mmconfig_delete(root->segment, root->secondary.start,
+					root->secondary.end);
+	info->mcfg = NULL;
+}
+
+static struct acpi_pci_root_ops pci_acpi_root_ops = {
+	.pci_ops = &gen_acpi_pci_ops,
+	.init_info = pci_acpi_root_init_info,
+	.release_info = pci_acpi_root_release_info,
+};
+
+struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
+{
+	struct acpi_device *device = root->device;
+	struct gen_acpi_root_info *ri;
+	struct pci_bus *bus, *child;
+
+	/* allocate acpi_info/sysdata */
+	ri = devm_kzalloc(&device->dev, sizeof(*ri), GFP_KERNEL);
+	if (!ri) {
+		dev_err(&device->dev,
+			"pci_bus %04x:%02x: ignored (out of memory)\n",
+			root->segment, (int)root->secondary.start);
+		return NULL;
+	}
+
+	bus =  acpi_pci_root_create(root, &pci_acpi_root_ops,
+					&ri->common, ri);
+	if (!bus) {
+		dev_err(&device->dev, "Scanning rootbus failed");
+		return NULL;
+	}
+
+	pci_bus_size_bridges(bus);
+	pci_bus_assign_resources(bus);
+	list_for_each_entry(child, &bus->children, node)
+		pcie_bus_configure_settings(child);
+
+	return bus;
+}
+
+int raw_pci_read(unsigned int seg, unsigned int bus,
+		  unsigned int devfn, int reg, int len, u32 *val)
+{
+	struct pci_mmcfg_region *mcfg;
+	void __iomem *addr;
+	int err = -EINVAL;
+
+	rcu_read_lock();
+	mcfg = pci_mmconfig_lookup(seg, bus);
+	if (!mcfg || !mcfg->virt)
+		goto err;
+
+	addr = mcfg->virt + PCI_MMCFG_OFFSET(bus, devfn);
+	switch (len) {
+	case 1:
+		*val = readb(addr + reg);
+		break;
+	case 2:
+		*val = readw(addr + reg);
+		break;
+	case 4:
+		*val = readl(addr + reg);
+		break;
+	}
+	err = 0;
+err:
+	rcu_read_unlock();
+	return err;
+}
+
+int raw_pci_write(unsigned int seg, unsigned int bus,
+		unsigned int devfn, int reg, int len, u32 val)
+{
+	struct pci_mmcfg_region *mcfg;
+	void __iomem *addr;
+	int err = -EINVAL;
+
+	rcu_read_lock();
+	mcfg = pci_mmconfig_lookup(seg, bus);
+	if (!mcfg || !mcfg->virt)
+		goto err;
+
+	addr = mcfg->virt + PCI_MMCFG_OFFSET(bus, devfn);
+	switch (len) {
+	case 1:
+		writeb(val, addr + reg);
+		break;
+	case 2:
+		writew(val, addr + reg);
+		break;
+	case 4:
+		writel(val, addr + reg);
+		break;
+	}
+	err = 0;
+err:
+	rcu_read_unlock();
+	return err;
+}
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v6 5/5] PCI: ACPI: Add a generic ACPI based host controller
@ 2016-01-14  7:32   ` Jayachandran C
  0 siblings, 0 replies; 31+ messages in thread
From: Jayachandran C @ 2016-01-14  7:32 UTC (permalink / raw)
  To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann,
	linux-arm-kernel, Rafael J. Wysocki
  Cc: Jayachandran C, Lorenzo Pieralisi, Tomasz Nowicki, xen-devel

Add a simple ACPI based PCI host controller. This is done by
providing a simple implementation for pci_acpi_scan_root().

The pci_mmcfg_list handling is done by the ACPI code, we
keep a reference to the pci_mmcfg_region which is already
mapped.

Provide implementations of raw_pci_read and raw_pci_write based
on the pci_mmcfg_list

This is enabled only for ARM64 now.

Signed-off-by: Jayachandran C <jchandra@broadcom.com>
---
 drivers/acpi/Kconfig         |   8 ++
 drivers/acpi/Makefile        |   1 +
 drivers/acpi/pci_host_acpi.c | 186 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 195 insertions(+)
 create mode 100644 drivers/acpi/pci_host_acpi.c

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 5eef4cb..c5738be 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -283,6 +283,14 @@ config ACPI_NUMA
 	depends on (X86 || IA64)
 	default y if IA64_GENERIC || IA64_SGI_SN2
 
+config ACPI_PCI_HOST_GENERIC
+	bool "Generic ACPI PCI host controller"
+	depends on ARM64 && ACPI
+	help
+	  Say Y here if you want to support a simple generic ACPI PCI host
+	  controller.
+
+
 config ACPI_CUSTOM_DSDT_FILE
 	string "Custom DSDT Table file to include"
 	default ""
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 3f65311..9fffdf2 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -41,6 +41,7 @@ acpi-y				+= ec.o
 acpi-$(CONFIG_ACPI_DOCK)	+= dock.o
 acpi-y				+= pci_root.o pci_link.o pci_irq.o
 acpi-$(CONFIG_PCI_MMCONFIG)	+= pci_mcfg.o
+acpi-$(CONFIG_ACPI_PCI_HOST_GENERIC) += pci_host_acpi.o
 acpi-y				+= acpi_lpss.o acpi_apd.o
 acpi-y				+= acpi_platform.o
 acpi-y				+= acpi_pnp.o
diff --git a/drivers/acpi/pci_host_acpi.c b/drivers/acpi/pci_host_acpi.c
new file mode 100644
index 0000000..9dbdd81
--- /dev/null
+++ b/drivers/acpi/pci_host_acpi.c
@@ -0,0 +1,186 @@
+/*
+ * Generic PCI host controller driver for ACPI based systems
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (c) 2015 Broadcom Corporation
+ *
+ * Based on drivers/pci/host/pci-host-generic.c
+ * Copyright (C) 2014 ARM Limited
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/acpi.h>
+#include <linux/pci-acpi.h>
+#include <linux/sfi_acpi.h>
+#include <linux/slab.h>
+
+#define PREFIX			"pci-host-acpi:"
+
+/* sysdata pointer is ->root_info */
+struct gen_acpi_root_info {
+	struct acpi_pci_root_info	common;
+	struct pci_mmcfg_region		*mcfg;
+	bool				mcfg_added;
+};
+
+/* find mapping of a MCFG area */
+static void __iomem *gen_acpi_map_cfg_bus(struct pci_bus *bus,
+			unsigned int devfn, int where)
+{
+	struct gen_acpi_root_info *pci = bus->sysdata;
+	struct pci_mmcfg_region *mcfg = pci->mcfg;
+
+	if (bus->number < mcfg->start_bus || bus->number > mcfg->end_bus)
+		return NULL;
+
+	return mcfg->virt +
+		PCI_MMCFG_OFFSET(bus->number - mcfg->start_bus, devfn) +
+		where;
+}
+
+static struct pci_ops gen_acpi_pci_ops = {
+	.map_bus	= gen_acpi_map_cfg_bus,
+	.read		= pci_generic_config_read,
+	.write		= pci_generic_config_write,
+};
+
+/* Insert the ECFG area for a root bus */
+static int pci_acpi_root_init_info(struct acpi_pci_root_info *ci)
+{
+	struct gen_acpi_root_info *info;
+	struct acpi_pci_root *root = ci->root;
+	struct device *dev = &ci->bridge->dev;
+	int err;
+
+	info = container_of(ci, struct gen_acpi_root_info, common);
+	err = pci_mmconfig_insert(dev, root->segment, root->secondary.start,
+			root->secondary.end, root->mcfg_addr);
+	if (err && err != -EEXIST)
+		return err;
+
+	info->mcfg = pci_mmconfig_lookup(root->segment, root->secondary.start);
+	WARN_ON(info->mcfg == NULL);
+	info->mcfg_added = (err == -EEXIST);
+	return 0;
+}
+
+static void pci_acpi_root_release_info(struct acpi_pci_root_info *ci)
+{
+	struct gen_acpi_root_info *info;
+	struct acpi_pci_root *root = ci->root;
+
+	info = container_of(ci, struct gen_acpi_root_info, common);
+	if (info->mcfg_added)
+		pci_mmconfig_delete(root->segment, root->secondary.start,
+					root->secondary.end);
+	info->mcfg = NULL;
+}
+
+static struct acpi_pci_root_ops pci_acpi_root_ops = {
+	.pci_ops = &gen_acpi_pci_ops,
+	.init_info = pci_acpi_root_init_info,
+	.release_info = pci_acpi_root_release_info,
+};
+
+struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
+{
+	struct acpi_device *device = root->device;
+	struct gen_acpi_root_info *ri;
+	struct pci_bus *bus, *child;
+
+	/* allocate acpi_info/sysdata */
+	ri = devm_kzalloc(&device->dev, sizeof(*ri), GFP_KERNEL);
+	if (!ri) {
+		dev_err(&device->dev,
+			"pci_bus %04x:%02x: ignored (out of memory)\n",
+			root->segment, (int)root->secondary.start);
+		return NULL;
+	}
+
+	bus =  acpi_pci_root_create(root, &pci_acpi_root_ops,
+					&ri->common, ri);
+	if (!bus) {
+		dev_err(&device->dev, "Scanning rootbus failed");
+		return NULL;
+	}
+
+	pci_bus_size_bridges(bus);
+	pci_bus_assign_resources(bus);
+	list_for_each_entry(child, &bus->children, node)
+		pcie_bus_configure_settings(child);
+
+	return bus;
+}
+
+int raw_pci_read(unsigned int seg, unsigned int bus,
+		  unsigned int devfn, int reg, int len, u32 *val)
+{
+	struct pci_mmcfg_region *mcfg;
+	void __iomem *addr;
+	int err = -EINVAL;
+
+	rcu_read_lock();
+	mcfg = pci_mmconfig_lookup(seg, bus);
+	if (!mcfg || !mcfg->virt)
+		goto err;
+
+	addr = mcfg->virt + PCI_MMCFG_OFFSET(bus, devfn);
+	switch (len) {
+	case 1:
+		*val = readb(addr + reg);
+		break;
+	case 2:
+		*val = readw(addr + reg);
+		break;
+	case 4:
+		*val = readl(addr + reg);
+		break;
+	}
+	err = 0;
+err:
+	rcu_read_unlock();
+	return err;
+}
+
+int raw_pci_write(unsigned int seg, unsigned int bus,
+		unsigned int devfn, int reg, int len, u32 val)
+{
+	struct pci_mmcfg_region *mcfg;
+	void __iomem *addr;
+	int err = -EINVAL;
+
+	rcu_read_lock();
+	mcfg = pci_mmconfig_lookup(seg, bus);
+	if (!mcfg || !mcfg->virt)
+		goto err;
+
+	addr = mcfg->virt + PCI_MMCFG_OFFSET(bus, devfn);
+	switch (len) {
+	case 1:
+		writeb(val, addr + reg);
+		break;
+	case 2:
+		writew(val, addr + reg);
+		break;
+	case 4:
+		writel(val, addr + reg);
+		break;
+	}
+	err = 0;
+err:
+	rcu_read_unlock();
+	return err;
+}
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v6 5/5] PCI: ACPI: Add a generic ACPI based host controller
@ 2016-01-14  7:32   ` Jayachandran C
  0 siblings, 0 replies; 31+ messages in thread
From: Jayachandran C @ 2016-01-14  7:32 UTC (permalink / raw)
  To: linux-arm-kernel

Add a simple ACPI based PCI host controller. This is done by
providing a simple implementation for pci_acpi_scan_root().

The pci_mmcfg_list handling is done by the ACPI code, we
keep a reference to the pci_mmcfg_region which is already
mapped.

Provide implementations of raw_pci_read and raw_pci_write based
on the pci_mmcfg_list

This is enabled only for ARM64 now.

Signed-off-by: Jayachandran C <jchandra@broadcom.com>
---
 drivers/acpi/Kconfig         |   8 ++
 drivers/acpi/Makefile        |   1 +
 drivers/acpi/pci_host_acpi.c | 186 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 195 insertions(+)
 create mode 100644 drivers/acpi/pci_host_acpi.c

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 5eef4cb..c5738be 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -283,6 +283,14 @@ config ACPI_NUMA
 	depends on (X86 || IA64)
 	default y if IA64_GENERIC || IA64_SGI_SN2
 
+config ACPI_PCI_HOST_GENERIC
+	bool "Generic ACPI PCI host controller"
+	depends on ARM64 && ACPI
+	help
+	  Say Y here if you want to support a simple generic ACPI PCI host
+	  controller.
+
+
 config ACPI_CUSTOM_DSDT_FILE
 	string "Custom DSDT Table file to include"
 	default ""
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 3f65311..9fffdf2 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -41,6 +41,7 @@ acpi-y				+= ec.o
 acpi-$(CONFIG_ACPI_DOCK)	+= dock.o
 acpi-y				+= pci_root.o pci_link.o pci_irq.o
 acpi-$(CONFIG_PCI_MMCONFIG)	+= pci_mcfg.o
+acpi-$(CONFIG_ACPI_PCI_HOST_GENERIC) += pci_host_acpi.o
 acpi-y				+= acpi_lpss.o acpi_apd.o
 acpi-y				+= acpi_platform.o
 acpi-y				+= acpi_pnp.o
diff --git a/drivers/acpi/pci_host_acpi.c b/drivers/acpi/pci_host_acpi.c
new file mode 100644
index 0000000..9dbdd81
--- /dev/null
+++ b/drivers/acpi/pci_host_acpi.c
@@ -0,0 +1,186 @@
+/*
+ * Generic PCI host controller driver for ACPI based systems
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (c) 2015 Broadcom Corporation
+ *
+ * Based on drivers/pci/host/pci-host-generic.c
+ * Copyright (C) 2014 ARM Limited
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/acpi.h>
+#include <linux/pci-acpi.h>
+#include <linux/sfi_acpi.h>
+#include <linux/slab.h>
+
+#define PREFIX			"pci-host-acpi:"
+
+/* sysdata pointer is ->root_info */
+struct gen_acpi_root_info {
+	struct acpi_pci_root_info	common;
+	struct pci_mmcfg_region		*mcfg;
+	bool				mcfg_added;
+};
+
+/* find mapping of a MCFG area */
+static void __iomem *gen_acpi_map_cfg_bus(struct pci_bus *bus,
+			unsigned int devfn, int where)
+{
+	struct gen_acpi_root_info *pci = bus->sysdata;
+	struct pci_mmcfg_region *mcfg = pci->mcfg;
+
+	if (bus->number < mcfg->start_bus || bus->number > mcfg->end_bus)
+		return NULL;
+
+	return mcfg->virt +
+		PCI_MMCFG_OFFSET(bus->number - mcfg->start_bus, devfn) +
+		where;
+}
+
+static struct pci_ops gen_acpi_pci_ops = {
+	.map_bus	= gen_acpi_map_cfg_bus,
+	.read		= pci_generic_config_read,
+	.write		= pci_generic_config_write,
+};
+
+/* Insert the ECFG area for a root bus */
+static int pci_acpi_root_init_info(struct acpi_pci_root_info *ci)
+{
+	struct gen_acpi_root_info *info;
+	struct acpi_pci_root *root = ci->root;
+	struct device *dev = &ci->bridge->dev;
+	int err;
+
+	info = container_of(ci, struct gen_acpi_root_info, common);
+	err = pci_mmconfig_insert(dev, root->segment, root->secondary.start,
+			root->secondary.end, root->mcfg_addr);
+	if (err && err != -EEXIST)
+		return err;
+
+	info->mcfg = pci_mmconfig_lookup(root->segment, root->secondary.start);
+	WARN_ON(info->mcfg == NULL);
+	info->mcfg_added = (err == -EEXIST);
+	return 0;
+}
+
+static void pci_acpi_root_release_info(struct acpi_pci_root_info *ci)
+{
+	struct gen_acpi_root_info *info;
+	struct acpi_pci_root *root = ci->root;
+
+	info = container_of(ci, struct gen_acpi_root_info, common);
+	if (info->mcfg_added)
+		pci_mmconfig_delete(root->segment, root->secondary.start,
+					root->secondary.end);
+	info->mcfg = NULL;
+}
+
+static struct acpi_pci_root_ops pci_acpi_root_ops = {
+	.pci_ops = &gen_acpi_pci_ops,
+	.init_info = pci_acpi_root_init_info,
+	.release_info = pci_acpi_root_release_info,
+};
+
+struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
+{
+	struct acpi_device *device = root->device;
+	struct gen_acpi_root_info *ri;
+	struct pci_bus *bus, *child;
+
+	/* allocate acpi_info/sysdata */
+	ri = devm_kzalloc(&device->dev, sizeof(*ri), GFP_KERNEL);
+	if (!ri) {
+		dev_err(&device->dev,
+			"pci_bus %04x:%02x: ignored (out of memory)\n",
+			root->segment, (int)root->secondary.start);
+		return NULL;
+	}
+
+	bus =  acpi_pci_root_create(root, &pci_acpi_root_ops,
+					&ri->common, ri);
+	if (!bus) {
+		dev_err(&device->dev, "Scanning rootbus failed");
+		return NULL;
+	}
+
+	pci_bus_size_bridges(bus);
+	pci_bus_assign_resources(bus);
+	list_for_each_entry(child, &bus->children, node)
+		pcie_bus_configure_settings(child);
+
+	return bus;
+}
+
+int raw_pci_read(unsigned int seg, unsigned int bus,
+		  unsigned int devfn, int reg, int len, u32 *val)
+{
+	struct pci_mmcfg_region *mcfg;
+	void __iomem *addr;
+	int err = -EINVAL;
+
+	rcu_read_lock();
+	mcfg = pci_mmconfig_lookup(seg, bus);
+	if (!mcfg || !mcfg->virt)
+		goto err;
+
+	addr = mcfg->virt + PCI_MMCFG_OFFSET(bus, devfn);
+	switch (len) {
+	case 1:
+		*val = readb(addr + reg);
+		break;
+	case 2:
+		*val = readw(addr + reg);
+		break;
+	case 4:
+		*val = readl(addr + reg);
+		break;
+	}
+	err = 0;
+err:
+	rcu_read_unlock();
+	return err;
+}
+
+int raw_pci_write(unsigned int seg, unsigned int bus,
+		unsigned int devfn, int reg, int len, u32 val)
+{
+	struct pci_mmcfg_region *mcfg;
+	void __iomem *addr;
+	int err = -EINVAL;
+
+	rcu_read_lock();
+	mcfg = pci_mmconfig_lookup(seg, bus);
+	if (!mcfg || !mcfg->virt)
+		goto err;
+
+	addr = mcfg->virt + PCI_MMCFG_OFFSET(bus, devfn);
+	switch (len) {
+	case 1:
+		writeb(val, addr + reg);
+		break;
+	case 2:
+		writew(val, addr + reg);
+		break;
+	case 4:
+		writel(val, addr + reg);
+		break;
+	}
+	err = 0;
+err:
+	rcu_read_unlock();
+	return err;
+}
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v6 5/5] PCI: ACPI: Add a generic ACPI based host controller
  2016-01-14  7:32 ` Jayachandran C
                   ` (9 preceding siblings ...)
  (?)
@ 2016-01-14  7:32 ` Jayachandran C
  -1 siblings, 0 replies; 31+ messages in thread
From: Jayachandran C @ 2016-01-14  7:32 UTC (permalink / raw)
  To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann,
	linux-arm-kernel, Rafael J. Wysocki
  Cc: Tomasz Nowicki, Lorenzo Pieralisi, Jayachandran C, xen-devel

Add a simple ACPI based PCI host controller. This is done by
providing a simple implementation for pci_acpi_scan_root().

The pci_mmcfg_list handling is done by the ACPI code, we
keep a reference to the pci_mmcfg_region which is already
mapped.

Provide implementations of raw_pci_read and raw_pci_write based
on the pci_mmcfg_list

This is enabled only for ARM64 now.

Signed-off-by: Jayachandran C <jchandra@broadcom.com>
---
 drivers/acpi/Kconfig         |   8 ++
 drivers/acpi/Makefile        |   1 +
 drivers/acpi/pci_host_acpi.c | 186 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 195 insertions(+)
 create mode 100644 drivers/acpi/pci_host_acpi.c

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 5eef4cb..c5738be 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -283,6 +283,14 @@ config ACPI_NUMA
 	depends on (X86 || IA64)
 	default y if IA64_GENERIC || IA64_SGI_SN2
 
+config ACPI_PCI_HOST_GENERIC
+	bool "Generic ACPI PCI host controller"
+	depends on ARM64 && ACPI
+	help
+	  Say Y here if you want to support a simple generic ACPI PCI host
+	  controller.
+
+
 config ACPI_CUSTOM_DSDT_FILE
 	string "Custom DSDT Table file to include"
 	default ""
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 3f65311..9fffdf2 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -41,6 +41,7 @@ acpi-y				+= ec.o
 acpi-$(CONFIG_ACPI_DOCK)	+= dock.o
 acpi-y				+= pci_root.o pci_link.o pci_irq.o
 acpi-$(CONFIG_PCI_MMCONFIG)	+= pci_mcfg.o
+acpi-$(CONFIG_ACPI_PCI_HOST_GENERIC) += pci_host_acpi.o
 acpi-y				+= acpi_lpss.o acpi_apd.o
 acpi-y				+= acpi_platform.o
 acpi-y				+= acpi_pnp.o
diff --git a/drivers/acpi/pci_host_acpi.c b/drivers/acpi/pci_host_acpi.c
new file mode 100644
index 0000000..9dbdd81
--- /dev/null
+++ b/drivers/acpi/pci_host_acpi.c
@@ -0,0 +1,186 @@
+/*
+ * Generic PCI host controller driver for ACPI based systems
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (c) 2015 Broadcom Corporation
+ *
+ * Based on drivers/pci/host/pci-host-generic.c
+ * Copyright (C) 2014 ARM Limited
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/acpi.h>
+#include <linux/pci-acpi.h>
+#include <linux/sfi_acpi.h>
+#include <linux/slab.h>
+
+#define PREFIX			"pci-host-acpi:"
+
+/* sysdata pointer is ->root_info */
+struct gen_acpi_root_info {
+	struct acpi_pci_root_info	common;
+	struct pci_mmcfg_region		*mcfg;
+	bool				mcfg_added;
+};
+
+/* find mapping of a MCFG area */
+static void __iomem *gen_acpi_map_cfg_bus(struct pci_bus *bus,
+			unsigned int devfn, int where)
+{
+	struct gen_acpi_root_info *pci = bus->sysdata;
+	struct pci_mmcfg_region *mcfg = pci->mcfg;
+
+	if (bus->number < mcfg->start_bus || bus->number > mcfg->end_bus)
+		return NULL;
+
+	return mcfg->virt +
+		PCI_MMCFG_OFFSET(bus->number - mcfg->start_bus, devfn) +
+		where;
+}
+
+static struct pci_ops gen_acpi_pci_ops = {
+	.map_bus	= gen_acpi_map_cfg_bus,
+	.read		= pci_generic_config_read,
+	.write		= pci_generic_config_write,
+};
+
+/* Insert the ECFG area for a root bus */
+static int pci_acpi_root_init_info(struct acpi_pci_root_info *ci)
+{
+	struct gen_acpi_root_info *info;
+	struct acpi_pci_root *root = ci->root;
+	struct device *dev = &ci->bridge->dev;
+	int err;
+
+	info = container_of(ci, struct gen_acpi_root_info, common);
+	err = pci_mmconfig_insert(dev, root->segment, root->secondary.start,
+			root->secondary.end, root->mcfg_addr);
+	if (err && err != -EEXIST)
+		return err;
+
+	info->mcfg = pci_mmconfig_lookup(root->segment, root->secondary.start);
+	WARN_ON(info->mcfg == NULL);
+	info->mcfg_added = (err == -EEXIST);
+	return 0;
+}
+
+static void pci_acpi_root_release_info(struct acpi_pci_root_info *ci)
+{
+	struct gen_acpi_root_info *info;
+	struct acpi_pci_root *root = ci->root;
+
+	info = container_of(ci, struct gen_acpi_root_info, common);
+	if (info->mcfg_added)
+		pci_mmconfig_delete(root->segment, root->secondary.start,
+					root->secondary.end);
+	info->mcfg = NULL;
+}
+
+static struct acpi_pci_root_ops pci_acpi_root_ops = {
+	.pci_ops = &gen_acpi_pci_ops,
+	.init_info = pci_acpi_root_init_info,
+	.release_info = pci_acpi_root_release_info,
+};
+
+struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
+{
+	struct acpi_device *device = root->device;
+	struct gen_acpi_root_info *ri;
+	struct pci_bus *bus, *child;
+
+	/* allocate acpi_info/sysdata */
+	ri = devm_kzalloc(&device->dev, sizeof(*ri), GFP_KERNEL);
+	if (!ri) {
+		dev_err(&device->dev,
+			"pci_bus %04x:%02x: ignored (out of memory)\n",
+			root->segment, (int)root->secondary.start);
+		return NULL;
+	}
+
+	bus =  acpi_pci_root_create(root, &pci_acpi_root_ops,
+					&ri->common, ri);
+	if (!bus) {
+		dev_err(&device->dev, "Scanning rootbus failed");
+		return NULL;
+	}
+
+	pci_bus_size_bridges(bus);
+	pci_bus_assign_resources(bus);
+	list_for_each_entry(child, &bus->children, node)
+		pcie_bus_configure_settings(child);
+
+	return bus;
+}
+
+int raw_pci_read(unsigned int seg, unsigned int bus,
+		  unsigned int devfn, int reg, int len, u32 *val)
+{
+	struct pci_mmcfg_region *mcfg;
+	void __iomem *addr;
+	int err = -EINVAL;
+
+	rcu_read_lock();
+	mcfg = pci_mmconfig_lookup(seg, bus);
+	if (!mcfg || !mcfg->virt)
+		goto err;
+
+	addr = mcfg->virt + PCI_MMCFG_OFFSET(bus, devfn);
+	switch (len) {
+	case 1:
+		*val = readb(addr + reg);
+		break;
+	case 2:
+		*val = readw(addr + reg);
+		break;
+	case 4:
+		*val = readl(addr + reg);
+		break;
+	}
+	err = 0;
+err:
+	rcu_read_unlock();
+	return err;
+}
+
+int raw_pci_write(unsigned int seg, unsigned int bus,
+		unsigned int devfn, int reg, int len, u32 val)
+{
+	struct pci_mmcfg_region *mcfg;
+	void __iomem *addr;
+	int err = -EINVAL;
+
+	rcu_read_lock();
+	mcfg = pci_mmconfig_lookup(seg, bus);
+	if (!mcfg || !mcfg->virt)
+		goto err;
+
+	addr = mcfg->virt + PCI_MMCFG_OFFSET(bus, devfn);
+	switch (len) {
+	case 1:
+		writeb(val, addr + reg);
+		break;
+	case 2:
+		writew(val, addr + reg);
+		break;
+	case 4:
+		writel(val, addr + reg);
+		break;
+	}
+	err = 0;
+err:
+	rcu_read_unlock();
+	return err;
+}
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* Re: [PATCH v6 1/5] APCI: MCFG: Move mmcfg_list management to drivers/acpi
  2016-01-14  7:32   ` Jayachandran C
@ 2016-01-14  8:48     ` Dennis Chen
  -1 siblings, 0 replies; 31+ messages in thread
From: Dennis Chen @ 2016-01-14  8:48 UTC (permalink / raw)
  To: Jayachandran C
  Cc: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann,
	linux-arm-kernel, Rafael J. Wysocki, Lorenzo Pieralisi,
	Tomasz Nowicki, xen-devel

On Thu, Jan 14, 2016 at 3:32 PM, Jayachandran C <jchandra@broadcom.com> wrote:
> Move pci_mmcfg_list handling to a drivers/acpi/pci_mcfg.c. This is
> to share the API and code with ARM64 later. The corresponding
> declarations are moved from asm/pci_x86.h to linux/pci-acpi.h
>
> As a part of this we introduce three functions that can be
> implemented by the arch code: pci_mmconfig_map_resource() to map a
> mcfg entry, pci_mmconfig_unmap_resource to do the corresponding
> unmap and pci_mmconfig_enabled to see if the arch setup of
> mcfg entries was successful. We also provide weak implementations
> of these, which will be used from ARM64. On x86, we retain the
> old logic by providing platform specific implementation.
>
> This patch is purely rearranging code, it should not have any
> impact on the logic of MCFG parsing or list handling.
>
> Signed-off-by: Jayachandran C <jchandra@broadcom.com>
> ---
>  arch/x86/include/asm/pci_x86.h |  24 +---
>  arch/x86/pci/mmconfig-shared.c | 269 +++++------------------------------
>  arch/x86/pci/mmconfig_32.c     |   1 +
>  arch/x86/pci/mmconfig_64.c     |   1 +
>  arch/x86/pci/numachip.c        |   1 +
>  drivers/acpi/Makefile          |   1 +
>  drivers/acpi/pci_mcfg.c        | 312 +++++++++++++++++++++++++++++++++++++++++
>  drivers/xen/pci.c              |   5 +-
>  include/linux/pci-acpi.h       |  33 +++++
>  9 files changed, 386 insertions(+), 261 deletions(-)
>  create mode 100644 drivers/acpi/pci_mcfg.c
>
> diff --git a/arch/x86/include/asm/pci_x86.h b/arch/x86/include/asm/pci_x86.h
> index fa1195d..83d74cb 100644
> --- a/arch/x86/include/asm/pci_x86.h
> +++ b/arch/x86/include/asm/pci_x86.h
> @@ -122,33 +122,11 @@ extern int pci_legacy_init(void);
>  extern void pcibios_fixup_irqs(void);
>
>  /* pci-mmconfig.c */
> -
> -/* "PCI MMCONFIG %04x [bus %02x-%02x]" */
> -#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2)
> -
> -struct pci_mmcfg_region {
> -       struct list_head list;
> -       struct resource res;
> -       u64 address;
> -       char __iomem *virt;
> -       u16 segment;
> -       u8 start_bus;
> -       u8 end_bus;
> -       char name[PCI_MMCFG_RESOURCE_NAME_LEN];
> -};
> -
> +struct pci_mmcfg_region;
>  extern int __init pci_mmcfg_arch_init(void);
>  extern void __init pci_mmcfg_arch_free(void);
>  extern int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg);
>  extern void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg);
> -extern int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
> -                              phys_addr_t addr);
> -extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end);
> -extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus);
> -
> -extern struct list_head pci_mmcfg_list;
> -
> -#define PCI_MMCFG_BUS_OFFSET(bus)      ((bus) << 20)
>
>  /*
>   * AMD Fam10h CPUs are buggy, and cannot access MMIO config space
> diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c
> index dd30b7e..626710b 100644
> --- a/arch/x86/pci/mmconfig-shared.c
> +++ b/arch/x86/pci/mmconfig-shared.c
> @@ -12,13 +12,12 @@
>
>  #include <linux/pci.h>
>  #include <linux/init.h>
> -#include <linux/sfi_acpi.h>
>  #include <linux/bitmap.h>
> -#include <linux/dmi.h>
>  #include <linux/slab.h>
>  #include <linux/mutex.h>
>  #include <linux/rculist.h>
>  #include <asm/e820.h>
> +#include <linux/pci-acpi.h>
>  #include <asm/pci_x86.h>
>  #include <asm/acpi.h>
>
> @@ -27,9 +26,6 @@
>  /* Indicate if the mmcfg resources have been placed into the resource table. */
>  static bool pci_mmcfg_running_state;
>  static bool pci_mmcfg_arch_init_failed;
> -static DEFINE_MUTEX(pci_mmcfg_lock);
> -
> -LIST_HEAD(pci_mmcfg_list);
>
>  static void __init pci_mmconfig_remove(struct pci_mmcfg_region *cfg)
>  {
> @@ -48,83 +44,6 @@ static void __init free_all_mmcfg(void)
>                 pci_mmconfig_remove(cfg);
>  }
>
> -static void list_add_sorted(struct pci_mmcfg_region *new)
> -{
> -       struct pci_mmcfg_region *cfg;
> -
> -       /* keep list sorted by segment and starting bus number */
> -       list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) {
> -               if (cfg->segment > new->segment ||
> -                   (cfg->segment == new->segment &&
> -                    cfg->start_bus >= new->start_bus)) {
> -                       list_add_tail_rcu(&new->list, &cfg->list);
> -                       return;
> -               }
> -       }
> -       list_add_tail_rcu(&new->list, &pci_mmcfg_list);
> -}
> -
> -static struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start,
> -                                                  int end, u64 addr)
> -{
> -       struct pci_mmcfg_region *new;
> -       struct resource *res;
> -
> -       if (addr == 0)
> -               return NULL;
> -
> -       new = kzalloc(sizeof(*new), GFP_KERNEL);
> -       if (!new)
> -               return NULL;
> -
> -       new->address = addr;
> -       new->segment = segment;
> -       new->start_bus = start;
> -       new->end_bus = end;
> -
> -       res = &new->res;
> -       res->start = addr + PCI_MMCFG_BUS_OFFSET(start);
> -       res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1;
> -       res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
> -       snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN,
> -                "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end);
> -       res->name = new->name;
> -
> -       return new;
> -}

Hmm, long long story about this refactor of pci code under x86 folder.
I think everyone who as long as has the brain in his head knows these
are the common code, not arch specific. But those kind of patches
repeats again and again and again in the mail list while our
maintainers are selective blind to this.

> -
> -static struct pci_mmcfg_region *__init pci_mmconfig_add(int segment, int start,
> -                                                       int end, u64 addr)
> -{
> -       struct pci_mmcfg_region *new;
> -
> -       new = pci_mmconfig_alloc(segment, start, end, addr);
> -       if (new) {
> -               mutex_lock(&pci_mmcfg_lock);
> -               list_add_sorted(new);
> -               mutex_unlock(&pci_mmcfg_lock);
> -
> -               pr_info(PREFIX
> -                      "MMCONFIG for domain %04x [bus %02x-%02x] at %pR "
> -                      "(base %#lx)\n",
> -                      segment, start, end, &new->res, (unsigned long)addr);
> -       }
> -
> -       return new;
> -}
> -
> -struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus)
> -{
> -       struct pci_mmcfg_region *cfg;
> -
> -       list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
> -               if (cfg->segment == segment &&
> -                   cfg->start_bus <= bus && bus <= cfg->end_bus)
> -                       return cfg;
> -
> -       return NULL;
> -}
> -
>  static const char *__init pci_mmcfg_e7520(void)
>  {
>         u32 win;
> @@ -543,73 +462,6 @@ static void __init pci_mmcfg_reject_broken(int early)
>         }
>  }
>
> -static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg,
> -                                       struct acpi_mcfg_allocation *cfg)
> -{
> -       int year;
> -
> -       if (cfg->address < 0xFFFFFFFF)
> -               return 0;
> -
> -       if (!strncmp(mcfg->header.oem_id, "SGI", 3))
> -               return 0;
> -
> -       if (mcfg->header.revision >= 1) {
> -               if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) &&
> -                   year >= 2010)
> -                       return 0;
> -       }
> -
> -       pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx "
> -              "is above 4GB, ignored\n", cfg->pci_segment,
> -              cfg->start_bus_number, cfg->end_bus_number, cfg->address);
> -       return -EINVAL;
> -}
> -
> -static int __init pci_parse_mcfg(struct acpi_table_header *header)
> -{
> -       struct acpi_table_mcfg *mcfg;
> -       struct acpi_mcfg_allocation *cfg_table, *cfg;
> -       unsigned long i;
> -       int entries;
> -
> -       if (!header)
> -               return -EINVAL;
> -
> -       mcfg = (struct acpi_table_mcfg *)header;
> -
> -       /* how many config structures do we have */
> -       free_all_mmcfg();
> -       entries = 0;
> -       i = header->length - sizeof(struct acpi_table_mcfg);
> -       while (i >= sizeof(struct acpi_mcfg_allocation)) {
> -               entries++;
> -               i -= sizeof(struct acpi_mcfg_allocation);
> -       }
> -       if (entries == 0) {
> -               pr_err(PREFIX "MMCONFIG has no entries\n");
> -               return -ENODEV;
> -       }
> -
> -       cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1];
> -       for (i = 0; i < entries; i++) {
> -               cfg = &cfg_table[i];
> -               if (acpi_mcfg_check_entry(mcfg, cfg)) {
> -                       free_all_mmcfg();
> -                       return -ENODEV;
> -               }
> -
> -               if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number,
> -                                  cfg->end_bus_number, cfg->address) == NULL) {
> -                       pr_warn(PREFIX "no memory for MCFG entries\n");
> -                       free_all_mmcfg();
> -                       return -ENOMEM;
> -               }
> -       }
> -
> -       return 0;
> -}
> -
>  #ifdef CONFIG_ACPI_APEI
>  extern int (*arch_apei_filter_addr)(int (*func)(__u64 start, __u64 size,
>                                      void *data), void *data);
> @@ -662,13 +514,20 @@ static void __init __pci_mmcfg_init(int early)
>
>  static int __initdata known_bridge;
>
> +static void __init pci_mmcfg_list_setup(void)
> +{
> +       free_all_mmcfg();
> +       if (pci_mmconfig_parse_table())
> +               free_all_mmcfg();
> +}
> +
>  void __init pci_mmcfg_early_init(void)
>  {
>         if (pci_probe & PCI_PROBE_MMCONF) {
>                 if (pci_mmcfg_check_hostbridge())
>                         known_bridge = 1;
>                 else
> -                       acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
> +                       pci_mmcfg_list_setup();
>                 __pci_mmcfg_init(1);
>
>                 set_apei_filter();
> @@ -686,7 +545,7 @@ void __init pci_mmcfg_late_init(void)
>
>         /* MMCONFIG hasn't been enabled yet, try again */
>         if (pci_probe & PCI_PROBE_MASK & ~PCI_PROBE_MMCONF) {
> -               acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
> +               pci_mmcfg_list_setup();
>                 __pci_mmcfg_init(0);
>         }
>  }
> @@ -720,99 +579,41 @@ static int __init pci_mmcfg_late_insert_resources(void)
>   */
>  late_initcall(pci_mmcfg_late_insert_resources);
>
> -/* Add MMCFG information for host bridges */
> -int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
> -                       phys_addr_t addr)
> +int pci_mmconfig_map_resource(struct device *dev, struct pci_mmcfg_region *cfg)
>  {
> -       int rc;
> -       struct resource *tmp = NULL;
> -       struct pci_mmcfg_region *cfg;
> +       struct resource *tmp;
>
> -       if (!(pci_probe & PCI_PROBE_MMCONF) || pci_mmcfg_arch_init_failed)
> -               return -ENODEV;
> -
> -       if (start > end)
> -               return -EINVAL;
> -
> -       mutex_lock(&pci_mmcfg_lock);
> -       cfg = pci_mmconfig_lookup(seg, start);
> -       if (cfg) {
> -               if (cfg->end_bus < end)
> -                       dev_info(dev, FW_INFO
> -                                "MMCONFIG for "
> -                                "domain %04x [bus %02x-%02x] "
> -                                "only partially covers this bridge\n",
> -                                 cfg->segment, cfg->start_bus, cfg->end_bus);
> -               mutex_unlock(&pci_mmcfg_lock);
> -               return -EEXIST;
> -       }
> -
> -       if (!addr) {
> -               mutex_unlock(&pci_mmcfg_lock);
> -               return -EINVAL;
> -       }
> -
> -       rc = -EBUSY;
> -       cfg = pci_mmconfig_alloc(seg, start, end, addr);
> -       if (cfg == NULL) {
> -               dev_warn(dev, "fail to add MMCONFIG (out of memory)\n");
> -               rc = -ENOMEM;
> -       } else if (!pci_mmcfg_check_reserved(dev, cfg, 0)) {
> +       if (!pci_mmcfg_check_reserved(dev, cfg, 0)) {
>                 dev_warn(dev, FW_BUG "MMCONFIG %pR isn't reserved\n",
>                          &cfg->res);
> -       } else {
> -               /* Insert resource if it's not in boot stage */
> -               if (pci_mmcfg_running_state)
> -                       tmp = insert_resource_conflict(&iomem_resource,
> -                                                      &cfg->res);
> -
> +               return -EBUSY;
> +       }
> +       /* Insert resource if it's not in boot stage */
> +       if (pci_mmcfg_running_state) {
> +               tmp = insert_resource_conflict(&iomem_resource,
> +                                              &cfg->res);
>                 if (tmp) {
> -                       dev_warn(dev,
> -                                "MMCONFIG %pR conflicts with "
> -                                "%s %pR\n",
> -                                &cfg->res, tmp->name, tmp);
> -               } else if (pci_mmcfg_arch_map(cfg)) {
> -                       dev_warn(dev, "fail to map MMCONFIG %pR.\n",
> -                                &cfg->res);
> -               } else {
> -                       list_add_sorted(cfg);
> -                       dev_info(dev, "MMCONFIG at %pR (base %#lx)\n",
> -                                &cfg->res, (unsigned long)addr);
> -                       cfg = NULL;
> -                       rc = 0;
> +                       dev_warn(dev, "MMCONFIG %pR conflicts with %s %pR\n",
> +                               &cfg->res, tmp->name, tmp);
> +                       return -EBUSY;
>                 }
>         }
> -
> -       if (cfg) {
> -               if (cfg->res.parent)
> -                       release_resource(&cfg->res);
> -               kfree(cfg);
> +       if (pci_mmcfg_arch_map(cfg)) {
> +               dev_warn(dev, "fail to map MMCONFIG %pR.\n", &cfg->res);
> +               return -EBUSY;
>         }
> -
> -       mutex_unlock(&pci_mmcfg_lock);
> -
> -       return rc;
> +       return 0;
>  }
>
> -/* Delete MMCFG information for host bridges */
> -int pci_mmconfig_delete(u16 seg, u8 start, u8 end)
> +void pci_mmconfig_unmap_resource(struct pci_mmcfg_region *cfg)
>  {
> -       struct pci_mmcfg_region *cfg;
> -
> -       mutex_lock(&pci_mmcfg_lock);
> -       list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
> -               if (cfg->segment == seg && cfg->start_bus == start &&
> -                   cfg->end_bus == end) {
> -                       list_del_rcu(&cfg->list);
> -                       synchronize_rcu();
> -                       pci_mmcfg_arch_unmap(cfg);
> -                       if (cfg->res.parent)
> -                               release_resource(&cfg->res);
> -                       mutex_unlock(&pci_mmcfg_lock);
> -                       kfree(cfg);
> -                       return 0;
> -               }
> -       mutex_unlock(&pci_mmcfg_lock);
> +       pci_mmcfg_arch_unmap(cfg);
> +       if (cfg->res.parent)
> +               release_resource(&cfg->res);
> +       cfg->res.parent = NULL;
> +}
>
> -       return -ENOENT;
> +int pci_mmconfig_enabled(void)
> +{
> +       return (pci_probe & PCI_PROBE_MMCONF) && !pci_mmcfg_arch_init_failed;
>  }
> diff --git a/arch/x86/pci/mmconfig_32.c b/arch/x86/pci/mmconfig_32.c
> index 43984bc..38a37f8 100644
> --- a/arch/x86/pci/mmconfig_32.c
> +++ b/arch/x86/pci/mmconfig_32.c
> @@ -12,6 +12,7 @@
>  #include <linux/pci.h>
>  #include <linux/init.h>
>  #include <linux/rcupdate.h>
> +#include <linux/pci-acpi.h>
>  #include <asm/e820.h>
>  #include <asm/pci_x86.h>
>
> diff --git a/arch/x86/pci/mmconfig_64.c b/arch/x86/pci/mmconfig_64.c
> index bea5249..29253ec 100644
> --- a/arch/x86/pci/mmconfig_64.c
> +++ b/arch/x86/pci/mmconfig_64.c
> @@ -10,6 +10,7 @@
>  #include <linux/acpi.h>
>  #include <linux/bitmap.h>
>  #include <linux/rcupdate.h>
> +#include <linux/pci-acpi.h>
>  #include <asm/e820.h>
>  #include <asm/pci_x86.h>
>
> diff --git a/arch/x86/pci/numachip.c b/arch/x86/pci/numachip.c
> index 2e565e6..c181eeb 100644
> --- a/arch/x86/pci/numachip.c
> +++ b/arch/x86/pci/numachip.c
> @@ -14,6 +14,7 @@
>   */
>
>  #include <linux/pci.h>
> +#include <linux/pci-acpi.h>
>  #include <asm/pci_x86.h>
>
>  static u8 limit __read_mostly;
> diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
> index 675eaf3..3f65311 100644
> --- a/drivers/acpi/Makefile
> +++ b/drivers/acpi/Makefile
> @@ -40,6 +40,7 @@ acpi-$(CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC) += processor_pdc.o
>  acpi-y                         += ec.o
>  acpi-$(CONFIG_ACPI_DOCK)       += dock.o
>  acpi-y                         += pci_root.o pci_link.o pci_irq.o
> +acpi-$(CONFIG_PCI_MMCONFIG)    += pci_mcfg.o
>  acpi-y                         += acpi_lpss.o acpi_apd.o
>  acpi-y                         += acpi_platform.o
>  acpi-y                         += acpi_pnp.o
> diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c
> new file mode 100644
> index 0000000..ea84365
> --- /dev/null
> +++ b/drivers/acpi/pci_mcfg.c
> @@ -0,0 +1,312 @@
> +/*
> + * pci_mcfg.c
> + *
> + * Common code to maintain the MCFG areas and mappings
> + *
> + * This has been extracted from arch/x86/pci/mmconfig-shared.c
> + * and moved here so that other architectures can use this code.
> + */
> +
> +#include <linux/pci.h>
> +#include <linux/init.h>
> +#include <linux/dmi.h>
> +#include <linux/pci-acpi.h>
> +#include <linux/sfi_acpi.h>
> +#include <linux/slab.h>
> +#include <linux/mutex.h>
> +#include <linux/rculist.h>
> +
> +#define PREFIX "ACPI: "
> +
> +static DEFINE_MUTEX(pci_mmcfg_lock);
> +LIST_HEAD(pci_mmcfg_list);
> +
> +static void list_add_sorted(struct pci_mmcfg_region *new)
> +{
> +       struct pci_mmcfg_region *cfg;
> +
> +       /* keep list sorted by segment and starting bus number */
> +       list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) {
> +               if (cfg->segment > new->segment ||
> +                   (cfg->segment == new->segment &&
> +                    cfg->start_bus >= new->start_bus)) {
> +                       list_add_tail_rcu(&new->list, &cfg->list);
> +                       return;
> +               }
> +       }
> +       list_add_tail_rcu(&new->list, &pci_mmcfg_list);
> +}
> +
> +static struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start,
> +                                                  int end, u64 addr)
> +{
> +       struct pci_mmcfg_region *new;
> +       struct resource *res;
> +
> +       if (addr == 0)
> +               return NULL;
> +
> +       new = kzalloc(sizeof(*new), GFP_KERNEL);
> +       if (!new)
> +               return NULL;
> +
> +       new->address = addr;
> +       new->segment = segment;
> +       new->start_bus = start;
> +       new->end_bus = end;
> +
> +       res = &new->res;
> +       res->start = addr + PCI_MMCFG_BUS_OFFSET(start);
> +       res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1;
> +       res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
> +       snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN,
> +                "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end);
> +       res->name = new->name;
> +
> +       return new;
> +}
> +
> +struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start,
> +                                                       int end, u64 addr)
> +{
> +       struct pci_mmcfg_region *new;
> +
> +       new = pci_mmconfig_alloc(segment, start, end, addr);
> +       if (new) {
> +               mutex_lock(&pci_mmcfg_lock);
> +               list_add_sorted(new);
> +               mutex_unlock(&pci_mmcfg_lock);
> +
> +               pr_info(PREFIX
> +                      "MMCONFIG for domain %04x [bus %02x-%02x] at %pR "
> +                      "(base %#lx)\n",
> +                      segment, start, end, &new->res, (unsigned long)addr);
> +       }
> +
> +       return new;
> +}
> +
> +struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus)
> +{
> +       struct pci_mmcfg_region *cfg;
> +
> +       list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
> +               if (cfg->segment == segment &&
> +                   cfg->start_bus <= bus && bus <= cfg->end_bus)
> +                       return cfg;
> +
> +       return NULL;
> +}
> +
> +/*
> + * Map a pci_mmcfg_region, can be overrriden by arch
> + */
> +int __weak pci_mmconfig_map_resource(struct device *dev,
> +       struct pci_mmcfg_region *mcfg)
> +{
> +       struct resource *tmp;
> +       void __iomem *vaddr;
> +
> +       tmp = insert_resource_conflict(&iomem_resource, &mcfg->res);
> +       if (tmp) {
> +               dev_warn(dev, "MMCONFIG %pR conflicts with %s %pR\n",
> +                       &mcfg->res, tmp->name, tmp);
> +               return -EBUSY;
> +       }
> +
> +       vaddr =  ioremap(mcfg->res.start, resource_size(&mcfg->res));
> +       if (!vaddr) {
> +               release_resource(&mcfg->res);
> +               return -ENOMEM;
> +       }
> +
> +       mcfg->virt = vaddr;
> +       return 0;
> +}
> +
> +/*
> + * Unmap a pci_mmcfg_region, can be overrriden by arch
> + */
> +void __weak pci_mmconfig_unmap_resource(struct pci_mmcfg_region *mcfg)
> +{
> +       if (mcfg->virt) {
> +               iounmap(mcfg->virt);
> +               mcfg->virt = NULL;
> +       }
> +       if (mcfg->res.parent) {
> +               release_resource(&mcfg->res);
> +               mcfg->res.parent = NULL;
> +       }
> +}
> +
> +/*
> + * check if the mmconfig is enabled and configured
> + */
> +int __weak pci_mmconfig_enabled(void)
> +{
> +       return 1;
> +}
> +
> +/* Add MMCFG information for host bridges */
> +int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
> +                       phys_addr_t addr)
> +{
> +       struct pci_mmcfg_region *cfg;
> +       int rc;
> +
> +       if (!pci_mmconfig_enabled())
> +               return -ENODEV;
> +       if (start > end)
> +               return -EINVAL;
> +
> +       mutex_lock(&pci_mmcfg_lock);
> +       cfg = pci_mmconfig_lookup(seg, start);
> +       if (cfg) {
> +               if (cfg->end_bus < end)
> +                       dev_info(dev, FW_INFO
> +                                "MMCONFIG for "
> +                                "domain %04x [bus %02x-%02x] "
> +                                "only partially covers this bridge\n",
> +                                 cfg->segment, cfg->start_bus, cfg->end_bus);
> +               rc = -EEXIST;
> +               goto err;
> +       }
> +
> +       if (!addr) {
> +               rc = -EINVAL;
> +               goto err;
> +       }
> +
> +       cfg = pci_mmconfig_alloc(seg, start, end, addr);
> +       if (cfg == NULL) {
> +               dev_warn(dev, "fail to add MMCONFIG (out of memory)\n");
> +               rc = -ENOMEM;
> +               goto err;
> +       }
> +       rc = pci_mmconfig_map_resource(dev, cfg);
> +       if (!rc) {
> +               list_add_sorted(cfg);
> +               dev_info(dev, "MMCONFIG at %pR (base %#lx)\n",
> +                                &cfg->res, (unsigned long)addr);
> +               return 0;
> +       } else {
> +               if (cfg->res.parent)
> +                       release_resource(&cfg->res);
> +               kfree(cfg);
> +       }
> +
> +err:
> +       mutex_unlock(&pci_mmcfg_lock);
> +       return rc;
> +}
> +
> +/* Delete MMCFG information for host bridges */
> +int pci_mmconfig_delete(u16 seg, u8 start, u8 end)
> +{
> +       struct pci_mmcfg_region *cfg;
> +
> +       mutex_lock(&pci_mmcfg_lock);
> +       list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
> +               if (cfg->segment == seg && cfg->start_bus == start &&
> +                   cfg->end_bus == end) {
> +                       list_del_rcu(&cfg->list);
> +                       synchronize_rcu();
> +                       pci_mmconfig_unmap_resource(cfg);
> +                       mutex_unlock(&pci_mmcfg_lock);
> +                       kfree(cfg);
> +                       return 0;
> +               }
> +       mutex_unlock(&pci_mmcfg_lock);
> +
> +       return -ENOENT;
> +}
> +
> +static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg,
> +                                       struct acpi_mcfg_allocation *cfg)
> +{
> +       int year;
> +
> +       if (!config_enabled(CONFIG_X86))
> +               return 0;
> +
> +       if (cfg->address < 0xFFFFFFFF)
> +               return 0;
> +
> +       if (!strncmp(mcfg->header.oem_id, "SGI", 3))
> +               return 0;
> +
> +       if (mcfg->header.revision >= 1) {
> +               if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) &&
> +                   year >= 2010)
> +                       return 0;
> +       }
> +
> +       pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx "
> +              "is above 4GB, ignored\n", cfg->pci_segment,
> +              cfg->start_bus_number, cfg->end_bus_number, cfg->address);
> +       return -EINVAL;
> +}
> +
> +static int __init pci_parse_mcfg(struct acpi_table_header *header)
> +{
> +       struct acpi_table_mcfg *mcfg;
> +       struct acpi_mcfg_allocation *cfg_table, *cfg;
> +       unsigned long i;
> +       int entries;
> +
> +       if (!header)
> +               return -EINVAL;
> +
> +       mcfg = (struct acpi_table_mcfg *)header;
> +
> +       /* how many config structures do we have */
> +       entries = 0;
> +       i = header->length - sizeof(struct acpi_table_mcfg);
> +       while (i >= sizeof(struct acpi_mcfg_allocation)) {
> +               entries++;
> +               i -= sizeof(struct acpi_mcfg_allocation);
> +       }
> +       if (entries == 0) {
> +               pr_err(PREFIX "MMCONFIG has no entries\n");
> +               return -ENODEV;
> +       }
> +
> +       cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1];
> +       for (i = 0; i < entries; i++) {
> +               cfg = &cfg_table[i];
> +               if (acpi_mcfg_check_entry(mcfg, cfg))
> +                       return -ENODEV;
> +
> +               if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number,
> +                                  cfg->end_bus_number, cfg->address) == NULL) {
> +                       pr_warn(PREFIX "no memory for MCFG entries\n");
> +                       return -ENOMEM;
> +               }
> +       }
> +
> +       return 0;
> +}
> +
> +int __init pci_mmconfig_parse_table(void)
> +{
> +       return acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
> +}
> +
> +void __weak __init pci_mmcfg_late_init(void)
> +{
> +       int err, n = 0;
> +       struct pci_mmcfg_region *cfg;
> +
> +       err = pci_mmconfig_parse_table();
> +       if (err) {
> +               pr_err(PREFIX " Failed to parse MCFG (%d)\n", err);
> +               return;
> +       }
> +
> +       list_for_each_entry(cfg, &pci_mmcfg_list, list) {
> +               pci_mmconfig_map_resource(NULL, cfg);
> +               n++;
> +       }
> +
> +       pr_info(PREFIX " MCFG table loaded %d entries\n", n);
> +}
> diff --git a/drivers/xen/pci.c b/drivers/xen/pci.c
> index 7494dbe..97aa9d3 100644
> --- a/drivers/xen/pci.c
> +++ b/drivers/xen/pci.c
> @@ -27,9 +27,6 @@
>  #include <asm/xen/hypervisor.h>
>  #include <asm/xen/hypercall.h>
>  #include "../pci/pci.h"
> -#ifdef CONFIG_PCI_MMCONFIG
> -#include <asm/pci_x86.h>
> -#endif
>
>  static bool __read_mostly pci_seg_supported = true;
>
> @@ -221,7 +218,7 @@ static int __init xen_mcfg_late(void)
>         if (!xen_initial_domain())
>                 return 0;
>
> -       if ((pci_probe & PCI_PROBE_MMCONF) == 0)
> +       if (!pci_mmconfig_enabled())
>                 return 0;
>
>         if (list_empty(&pci_mmcfg_list))
> diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h
> index 89ab057..e9450ef 100644
> --- a/include/linux/pci-acpi.h
> +++ b/include/linux/pci-acpi.h
> @@ -106,6 +106,39 @@ extern const u8 pci_acpi_dsm_uuid[];
>  #define RESET_DELAY_DSM                0x08
>  #define FUNCTION_DELAY_DSM     0x09
>
> +/* common API to maintain list of MCFG regions */
> +/* "PCI MMCONFIG %04x [bus %02x-%02x]" */
> +#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2)
> +
> +struct pci_mmcfg_region {
> +       struct list_head list;
> +       struct resource res;
> +       u64 address;
> +       char __iomem *virt;
> +       u16 segment;
> +       u8 start_bus;
> +       u8 end_bus;
> +       char name[PCI_MMCFG_RESOURCE_NAME_LEN];
> +};
> +
> +extern int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
> +                              phys_addr_t addr);
> +extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end);
> +
> +extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus);
> +extern struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start,
> +                                                       int end, u64 addr);
> +extern int pci_mmconfig_map_resource(struct device *dev,
> +       struct pci_mmcfg_region *mcfg);
> +extern void pci_mmconfig_unmap_resource(struct pci_mmcfg_region *mcfg);
> +extern int pci_mmconfig_enabled(void);
> +extern int __init pci_mmconfig_parse_table(void);
> +
> +extern struct list_head pci_mmcfg_list;
> +
> +#define PCI_MMCFG_BUS_OFFSET(bus)      ((bus) << 20)
> +#define PCI_MMCFG_OFFSET(bus, devfn)   ((bus) << 20 | (devfn) << 12)
> +
>  #else  /* CONFIG_ACPI */
>  static inline void acpi_pci_add_bus(struct pci_bus *bus) { }
>  static inline void acpi_pci_remove_bus(struct pci_bus *bus) { }
> --
> 1.9.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-pci" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH v6 1/5] APCI: MCFG: Move mmcfg_list management to drivers/acpi
@ 2016-01-14  8:48     ` Dennis Chen
  0 siblings, 0 replies; 31+ messages in thread
From: Dennis Chen @ 2016-01-14  8:48 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Jan 14, 2016 at 3:32 PM, Jayachandran C <jchandra@broadcom.com> wrote:
> Move pci_mmcfg_list handling to a drivers/acpi/pci_mcfg.c. This is
> to share the API and code with ARM64 later. The corresponding
> declarations are moved from asm/pci_x86.h to linux/pci-acpi.h
>
> As a part of this we introduce three functions that can be
> implemented by the arch code: pci_mmconfig_map_resource() to map a
> mcfg entry, pci_mmconfig_unmap_resource to do the corresponding
> unmap and pci_mmconfig_enabled to see if the arch setup of
> mcfg entries was successful. We also provide weak implementations
> of these, which will be used from ARM64. On x86, we retain the
> old logic by providing platform specific implementation.
>
> This patch is purely rearranging code, it should not have any
> impact on the logic of MCFG parsing or list handling.
>
> Signed-off-by: Jayachandran C <jchandra@broadcom.com>
> ---
>  arch/x86/include/asm/pci_x86.h |  24 +---
>  arch/x86/pci/mmconfig-shared.c | 269 +++++------------------------------
>  arch/x86/pci/mmconfig_32.c     |   1 +
>  arch/x86/pci/mmconfig_64.c     |   1 +
>  arch/x86/pci/numachip.c        |   1 +
>  drivers/acpi/Makefile          |   1 +
>  drivers/acpi/pci_mcfg.c        | 312 +++++++++++++++++++++++++++++++++++++++++
>  drivers/xen/pci.c              |   5 +-
>  include/linux/pci-acpi.h       |  33 +++++
>  9 files changed, 386 insertions(+), 261 deletions(-)
>  create mode 100644 drivers/acpi/pci_mcfg.c
>
> diff --git a/arch/x86/include/asm/pci_x86.h b/arch/x86/include/asm/pci_x86.h
> index fa1195d..83d74cb 100644
> --- a/arch/x86/include/asm/pci_x86.h
> +++ b/arch/x86/include/asm/pci_x86.h
> @@ -122,33 +122,11 @@ extern int pci_legacy_init(void);
>  extern void pcibios_fixup_irqs(void);
>
>  /* pci-mmconfig.c */
> -
> -/* "PCI MMCONFIG %04x [bus %02x-%02x]" */
> -#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2)
> -
> -struct pci_mmcfg_region {
> -       struct list_head list;
> -       struct resource res;
> -       u64 address;
> -       char __iomem *virt;
> -       u16 segment;
> -       u8 start_bus;
> -       u8 end_bus;
> -       char name[PCI_MMCFG_RESOURCE_NAME_LEN];
> -};
> -
> +struct pci_mmcfg_region;
>  extern int __init pci_mmcfg_arch_init(void);
>  extern void __init pci_mmcfg_arch_free(void);
>  extern int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg);
>  extern void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg);
> -extern int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
> -                              phys_addr_t addr);
> -extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end);
> -extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus);
> -
> -extern struct list_head pci_mmcfg_list;
> -
> -#define PCI_MMCFG_BUS_OFFSET(bus)      ((bus) << 20)
>
>  /*
>   * AMD Fam10h CPUs are buggy, and cannot access MMIO config space
> diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c
> index dd30b7e..626710b 100644
> --- a/arch/x86/pci/mmconfig-shared.c
> +++ b/arch/x86/pci/mmconfig-shared.c
> @@ -12,13 +12,12 @@
>
>  #include <linux/pci.h>
>  #include <linux/init.h>
> -#include <linux/sfi_acpi.h>
>  #include <linux/bitmap.h>
> -#include <linux/dmi.h>
>  #include <linux/slab.h>
>  #include <linux/mutex.h>
>  #include <linux/rculist.h>
>  #include <asm/e820.h>
> +#include <linux/pci-acpi.h>
>  #include <asm/pci_x86.h>
>  #include <asm/acpi.h>
>
> @@ -27,9 +26,6 @@
>  /* Indicate if the mmcfg resources have been placed into the resource table. */
>  static bool pci_mmcfg_running_state;
>  static bool pci_mmcfg_arch_init_failed;
> -static DEFINE_MUTEX(pci_mmcfg_lock);
> -
> -LIST_HEAD(pci_mmcfg_list);
>
>  static void __init pci_mmconfig_remove(struct pci_mmcfg_region *cfg)
>  {
> @@ -48,83 +44,6 @@ static void __init free_all_mmcfg(void)
>                 pci_mmconfig_remove(cfg);
>  }
>
> -static void list_add_sorted(struct pci_mmcfg_region *new)
> -{
> -       struct pci_mmcfg_region *cfg;
> -
> -       /* keep list sorted by segment and starting bus number */
> -       list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) {
> -               if (cfg->segment > new->segment ||
> -                   (cfg->segment == new->segment &&
> -                    cfg->start_bus >= new->start_bus)) {
> -                       list_add_tail_rcu(&new->list, &cfg->list);
> -                       return;
> -               }
> -       }
> -       list_add_tail_rcu(&new->list, &pci_mmcfg_list);
> -}
> -
> -static struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start,
> -                                                  int end, u64 addr)
> -{
> -       struct pci_mmcfg_region *new;
> -       struct resource *res;
> -
> -       if (addr == 0)
> -               return NULL;
> -
> -       new = kzalloc(sizeof(*new), GFP_KERNEL);
> -       if (!new)
> -               return NULL;
> -
> -       new->address = addr;
> -       new->segment = segment;
> -       new->start_bus = start;
> -       new->end_bus = end;
> -
> -       res = &new->res;
> -       res->start = addr + PCI_MMCFG_BUS_OFFSET(start);
> -       res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1;
> -       res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
> -       snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN,
> -                "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end);
> -       res->name = new->name;
> -
> -       return new;
> -}

Hmm, long long story about this refactor of pci code under x86 folder.
I think everyone who as long as has the brain in his head knows these
are the common code, not arch specific. But those kind of patches
repeats again and again and again in the mail list while our
maintainers are selective blind to this.

> -
> -static struct pci_mmcfg_region *__init pci_mmconfig_add(int segment, int start,
> -                                                       int end, u64 addr)
> -{
> -       struct pci_mmcfg_region *new;
> -
> -       new = pci_mmconfig_alloc(segment, start, end, addr);
> -       if (new) {
> -               mutex_lock(&pci_mmcfg_lock);
> -               list_add_sorted(new);
> -               mutex_unlock(&pci_mmcfg_lock);
> -
> -               pr_info(PREFIX
> -                      "MMCONFIG for domain %04x [bus %02x-%02x] at %pR "
> -                      "(base %#lx)\n",
> -                      segment, start, end, &new->res, (unsigned long)addr);
> -       }
> -
> -       return new;
> -}
> -
> -struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus)
> -{
> -       struct pci_mmcfg_region *cfg;
> -
> -       list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
> -               if (cfg->segment == segment &&
> -                   cfg->start_bus <= bus && bus <= cfg->end_bus)
> -                       return cfg;
> -
> -       return NULL;
> -}
> -
>  static const char *__init pci_mmcfg_e7520(void)
>  {
>         u32 win;
> @@ -543,73 +462,6 @@ static void __init pci_mmcfg_reject_broken(int early)
>         }
>  }
>
> -static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg,
> -                                       struct acpi_mcfg_allocation *cfg)
> -{
> -       int year;
> -
> -       if (cfg->address < 0xFFFFFFFF)
> -               return 0;
> -
> -       if (!strncmp(mcfg->header.oem_id, "SGI", 3))
> -               return 0;
> -
> -       if (mcfg->header.revision >= 1) {
> -               if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) &&
> -                   year >= 2010)
> -                       return 0;
> -       }
> -
> -       pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx "
> -              "is above 4GB, ignored\n", cfg->pci_segment,
> -              cfg->start_bus_number, cfg->end_bus_number, cfg->address);
> -       return -EINVAL;
> -}
> -
> -static int __init pci_parse_mcfg(struct acpi_table_header *header)
> -{
> -       struct acpi_table_mcfg *mcfg;
> -       struct acpi_mcfg_allocation *cfg_table, *cfg;
> -       unsigned long i;
> -       int entries;
> -
> -       if (!header)
> -               return -EINVAL;
> -
> -       mcfg = (struct acpi_table_mcfg *)header;
> -
> -       /* how many config structures do we have */
> -       free_all_mmcfg();
> -       entries = 0;
> -       i = header->length - sizeof(struct acpi_table_mcfg);
> -       while (i >= sizeof(struct acpi_mcfg_allocation)) {
> -               entries++;
> -               i -= sizeof(struct acpi_mcfg_allocation);
> -       }
> -       if (entries == 0) {
> -               pr_err(PREFIX "MMCONFIG has no entries\n");
> -               return -ENODEV;
> -       }
> -
> -       cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1];
> -       for (i = 0; i < entries; i++) {
> -               cfg = &cfg_table[i];
> -               if (acpi_mcfg_check_entry(mcfg, cfg)) {
> -                       free_all_mmcfg();
> -                       return -ENODEV;
> -               }
> -
> -               if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number,
> -                                  cfg->end_bus_number, cfg->address) == NULL) {
> -                       pr_warn(PREFIX "no memory for MCFG entries\n");
> -                       free_all_mmcfg();
> -                       return -ENOMEM;
> -               }
> -       }
> -
> -       return 0;
> -}
> -
>  #ifdef CONFIG_ACPI_APEI
>  extern int (*arch_apei_filter_addr)(int (*func)(__u64 start, __u64 size,
>                                      void *data), void *data);
> @@ -662,13 +514,20 @@ static void __init __pci_mmcfg_init(int early)
>
>  static int __initdata known_bridge;
>
> +static void __init pci_mmcfg_list_setup(void)
> +{
> +       free_all_mmcfg();
> +       if (pci_mmconfig_parse_table())
> +               free_all_mmcfg();
> +}
> +
>  void __init pci_mmcfg_early_init(void)
>  {
>         if (pci_probe & PCI_PROBE_MMCONF) {
>                 if (pci_mmcfg_check_hostbridge())
>                         known_bridge = 1;
>                 else
> -                       acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
> +                       pci_mmcfg_list_setup();
>                 __pci_mmcfg_init(1);
>
>                 set_apei_filter();
> @@ -686,7 +545,7 @@ void __init pci_mmcfg_late_init(void)
>
>         /* MMCONFIG hasn't been enabled yet, try again */
>         if (pci_probe & PCI_PROBE_MASK & ~PCI_PROBE_MMCONF) {
> -               acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
> +               pci_mmcfg_list_setup();
>                 __pci_mmcfg_init(0);
>         }
>  }
> @@ -720,99 +579,41 @@ static int __init pci_mmcfg_late_insert_resources(void)
>   */
>  late_initcall(pci_mmcfg_late_insert_resources);
>
> -/* Add MMCFG information for host bridges */
> -int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
> -                       phys_addr_t addr)
> +int pci_mmconfig_map_resource(struct device *dev, struct pci_mmcfg_region *cfg)
>  {
> -       int rc;
> -       struct resource *tmp = NULL;
> -       struct pci_mmcfg_region *cfg;
> +       struct resource *tmp;
>
> -       if (!(pci_probe & PCI_PROBE_MMCONF) || pci_mmcfg_arch_init_failed)
> -               return -ENODEV;
> -
> -       if (start > end)
> -               return -EINVAL;
> -
> -       mutex_lock(&pci_mmcfg_lock);
> -       cfg = pci_mmconfig_lookup(seg, start);
> -       if (cfg) {
> -               if (cfg->end_bus < end)
> -                       dev_info(dev, FW_INFO
> -                                "MMCONFIG for "
> -                                "domain %04x [bus %02x-%02x] "
> -                                "only partially covers this bridge\n",
> -                                 cfg->segment, cfg->start_bus, cfg->end_bus);
> -               mutex_unlock(&pci_mmcfg_lock);
> -               return -EEXIST;
> -       }
> -
> -       if (!addr) {
> -               mutex_unlock(&pci_mmcfg_lock);
> -               return -EINVAL;
> -       }
> -
> -       rc = -EBUSY;
> -       cfg = pci_mmconfig_alloc(seg, start, end, addr);
> -       if (cfg == NULL) {
> -               dev_warn(dev, "fail to add MMCONFIG (out of memory)\n");
> -               rc = -ENOMEM;
> -       } else if (!pci_mmcfg_check_reserved(dev, cfg, 0)) {
> +       if (!pci_mmcfg_check_reserved(dev, cfg, 0)) {
>                 dev_warn(dev, FW_BUG "MMCONFIG %pR isn't reserved\n",
>                          &cfg->res);
> -       } else {
> -               /* Insert resource if it's not in boot stage */
> -               if (pci_mmcfg_running_state)
> -                       tmp = insert_resource_conflict(&iomem_resource,
> -                                                      &cfg->res);
> -
> +               return -EBUSY;
> +       }
> +       /* Insert resource if it's not in boot stage */
> +       if (pci_mmcfg_running_state) {
> +               tmp = insert_resource_conflict(&iomem_resource,
> +                                              &cfg->res);
>                 if (tmp) {
> -                       dev_warn(dev,
> -                                "MMCONFIG %pR conflicts with "
> -                                "%s %pR\n",
> -                                &cfg->res, tmp->name, tmp);
> -               } else if (pci_mmcfg_arch_map(cfg)) {
> -                       dev_warn(dev, "fail to map MMCONFIG %pR.\n",
> -                                &cfg->res);
> -               } else {
> -                       list_add_sorted(cfg);
> -                       dev_info(dev, "MMCONFIG at %pR (base %#lx)\n",
> -                                &cfg->res, (unsigned long)addr);
> -                       cfg = NULL;
> -                       rc = 0;
> +                       dev_warn(dev, "MMCONFIG %pR conflicts with %s %pR\n",
> +                               &cfg->res, tmp->name, tmp);
> +                       return -EBUSY;
>                 }
>         }
> -
> -       if (cfg) {
> -               if (cfg->res.parent)
> -                       release_resource(&cfg->res);
> -               kfree(cfg);
> +       if (pci_mmcfg_arch_map(cfg)) {
> +               dev_warn(dev, "fail to map MMCONFIG %pR.\n", &cfg->res);
> +               return -EBUSY;
>         }
> -
> -       mutex_unlock(&pci_mmcfg_lock);
> -
> -       return rc;
> +       return 0;
>  }
>
> -/* Delete MMCFG information for host bridges */
> -int pci_mmconfig_delete(u16 seg, u8 start, u8 end)
> +void pci_mmconfig_unmap_resource(struct pci_mmcfg_region *cfg)
>  {
> -       struct pci_mmcfg_region *cfg;
> -
> -       mutex_lock(&pci_mmcfg_lock);
> -       list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
> -               if (cfg->segment == seg && cfg->start_bus == start &&
> -                   cfg->end_bus == end) {
> -                       list_del_rcu(&cfg->list);
> -                       synchronize_rcu();
> -                       pci_mmcfg_arch_unmap(cfg);
> -                       if (cfg->res.parent)
> -                               release_resource(&cfg->res);
> -                       mutex_unlock(&pci_mmcfg_lock);
> -                       kfree(cfg);
> -                       return 0;
> -               }
> -       mutex_unlock(&pci_mmcfg_lock);
> +       pci_mmcfg_arch_unmap(cfg);
> +       if (cfg->res.parent)
> +               release_resource(&cfg->res);
> +       cfg->res.parent = NULL;
> +}
>
> -       return -ENOENT;
> +int pci_mmconfig_enabled(void)
> +{
> +       return (pci_probe & PCI_PROBE_MMCONF) && !pci_mmcfg_arch_init_failed;
>  }
> diff --git a/arch/x86/pci/mmconfig_32.c b/arch/x86/pci/mmconfig_32.c
> index 43984bc..38a37f8 100644
> --- a/arch/x86/pci/mmconfig_32.c
> +++ b/arch/x86/pci/mmconfig_32.c
> @@ -12,6 +12,7 @@
>  #include <linux/pci.h>
>  #include <linux/init.h>
>  #include <linux/rcupdate.h>
> +#include <linux/pci-acpi.h>
>  #include <asm/e820.h>
>  #include <asm/pci_x86.h>
>
> diff --git a/arch/x86/pci/mmconfig_64.c b/arch/x86/pci/mmconfig_64.c
> index bea5249..29253ec 100644
> --- a/arch/x86/pci/mmconfig_64.c
> +++ b/arch/x86/pci/mmconfig_64.c
> @@ -10,6 +10,7 @@
>  #include <linux/acpi.h>
>  #include <linux/bitmap.h>
>  #include <linux/rcupdate.h>
> +#include <linux/pci-acpi.h>
>  #include <asm/e820.h>
>  #include <asm/pci_x86.h>
>
> diff --git a/arch/x86/pci/numachip.c b/arch/x86/pci/numachip.c
> index 2e565e6..c181eeb 100644
> --- a/arch/x86/pci/numachip.c
> +++ b/arch/x86/pci/numachip.c
> @@ -14,6 +14,7 @@
>   */
>
>  #include <linux/pci.h>
> +#include <linux/pci-acpi.h>
>  #include <asm/pci_x86.h>
>
>  static u8 limit __read_mostly;
> diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
> index 675eaf3..3f65311 100644
> --- a/drivers/acpi/Makefile
> +++ b/drivers/acpi/Makefile
> @@ -40,6 +40,7 @@ acpi-$(CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC) += processor_pdc.o
>  acpi-y                         += ec.o
>  acpi-$(CONFIG_ACPI_DOCK)       += dock.o
>  acpi-y                         += pci_root.o pci_link.o pci_irq.o
> +acpi-$(CONFIG_PCI_MMCONFIG)    += pci_mcfg.o
>  acpi-y                         += acpi_lpss.o acpi_apd.o
>  acpi-y                         += acpi_platform.o
>  acpi-y                         += acpi_pnp.o
> diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c
> new file mode 100644
> index 0000000..ea84365
> --- /dev/null
> +++ b/drivers/acpi/pci_mcfg.c
> @@ -0,0 +1,312 @@
> +/*
> + * pci_mcfg.c
> + *
> + * Common code to maintain the MCFG areas and mappings
> + *
> + * This has been extracted from arch/x86/pci/mmconfig-shared.c
> + * and moved here so that other architectures can use this code.
> + */
> +
> +#include <linux/pci.h>
> +#include <linux/init.h>
> +#include <linux/dmi.h>
> +#include <linux/pci-acpi.h>
> +#include <linux/sfi_acpi.h>
> +#include <linux/slab.h>
> +#include <linux/mutex.h>
> +#include <linux/rculist.h>
> +
> +#define PREFIX "ACPI: "
> +
> +static DEFINE_MUTEX(pci_mmcfg_lock);
> +LIST_HEAD(pci_mmcfg_list);
> +
> +static void list_add_sorted(struct pci_mmcfg_region *new)
> +{
> +       struct pci_mmcfg_region *cfg;
> +
> +       /* keep list sorted by segment and starting bus number */
> +       list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) {
> +               if (cfg->segment > new->segment ||
> +                   (cfg->segment == new->segment &&
> +                    cfg->start_bus >= new->start_bus)) {
> +                       list_add_tail_rcu(&new->list, &cfg->list);
> +                       return;
> +               }
> +       }
> +       list_add_tail_rcu(&new->list, &pci_mmcfg_list);
> +}
> +
> +static struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start,
> +                                                  int end, u64 addr)
> +{
> +       struct pci_mmcfg_region *new;
> +       struct resource *res;
> +
> +       if (addr == 0)
> +               return NULL;
> +
> +       new = kzalloc(sizeof(*new), GFP_KERNEL);
> +       if (!new)
> +               return NULL;
> +
> +       new->address = addr;
> +       new->segment = segment;
> +       new->start_bus = start;
> +       new->end_bus = end;
> +
> +       res = &new->res;
> +       res->start = addr + PCI_MMCFG_BUS_OFFSET(start);
> +       res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1;
> +       res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
> +       snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN,
> +                "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end);
> +       res->name = new->name;
> +
> +       return new;
> +}
> +
> +struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start,
> +                                                       int end, u64 addr)
> +{
> +       struct pci_mmcfg_region *new;
> +
> +       new = pci_mmconfig_alloc(segment, start, end, addr);
> +       if (new) {
> +               mutex_lock(&pci_mmcfg_lock);
> +               list_add_sorted(new);
> +               mutex_unlock(&pci_mmcfg_lock);
> +
> +               pr_info(PREFIX
> +                      "MMCONFIG for domain %04x [bus %02x-%02x] at %pR "
> +                      "(base %#lx)\n",
> +                      segment, start, end, &new->res, (unsigned long)addr);
> +       }
> +
> +       return new;
> +}
> +
> +struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus)
> +{
> +       struct pci_mmcfg_region *cfg;
> +
> +       list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
> +               if (cfg->segment == segment &&
> +                   cfg->start_bus <= bus && bus <= cfg->end_bus)
> +                       return cfg;
> +
> +       return NULL;
> +}
> +
> +/*
> + * Map a pci_mmcfg_region, can be overrriden by arch
> + */
> +int __weak pci_mmconfig_map_resource(struct device *dev,
> +       struct pci_mmcfg_region *mcfg)
> +{
> +       struct resource *tmp;
> +       void __iomem *vaddr;
> +
> +       tmp = insert_resource_conflict(&iomem_resource, &mcfg->res);
> +       if (tmp) {
> +               dev_warn(dev, "MMCONFIG %pR conflicts with %s %pR\n",
> +                       &mcfg->res, tmp->name, tmp);
> +               return -EBUSY;
> +       }
> +
> +       vaddr =  ioremap(mcfg->res.start, resource_size(&mcfg->res));
> +       if (!vaddr) {
> +               release_resource(&mcfg->res);
> +               return -ENOMEM;
> +       }
> +
> +       mcfg->virt = vaddr;
> +       return 0;
> +}
> +
> +/*
> + * Unmap a pci_mmcfg_region, can be overrriden by arch
> + */
> +void __weak pci_mmconfig_unmap_resource(struct pci_mmcfg_region *mcfg)
> +{
> +       if (mcfg->virt) {
> +               iounmap(mcfg->virt);
> +               mcfg->virt = NULL;
> +       }
> +       if (mcfg->res.parent) {
> +               release_resource(&mcfg->res);
> +               mcfg->res.parent = NULL;
> +       }
> +}
> +
> +/*
> + * check if the mmconfig is enabled and configured
> + */
> +int __weak pci_mmconfig_enabled(void)
> +{
> +       return 1;
> +}
> +
> +/* Add MMCFG information for host bridges */
> +int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
> +                       phys_addr_t addr)
> +{
> +       struct pci_mmcfg_region *cfg;
> +       int rc;
> +
> +       if (!pci_mmconfig_enabled())
> +               return -ENODEV;
> +       if (start > end)
> +               return -EINVAL;
> +
> +       mutex_lock(&pci_mmcfg_lock);
> +       cfg = pci_mmconfig_lookup(seg, start);
> +       if (cfg) {
> +               if (cfg->end_bus < end)
> +                       dev_info(dev, FW_INFO
> +                                "MMCONFIG for "
> +                                "domain %04x [bus %02x-%02x] "
> +                                "only partially covers this bridge\n",
> +                                 cfg->segment, cfg->start_bus, cfg->end_bus);
> +               rc = -EEXIST;
> +               goto err;
> +       }
> +
> +       if (!addr) {
> +               rc = -EINVAL;
> +               goto err;
> +       }
> +
> +       cfg = pci_mmconfig_alloc(seg, start, end, addr);
> +       if (cfg == NULL) {
> +               dev_warn(dev, "fail to add MMCONFIG (out of memory)\n");
> +               rc = -ENOMEM;
> +               goto err;
> +       }
> +       rc = pci_mmconfig_map_resource(dev, cfg);
> +       if (!rc) {
> +               list_add_sorted(cfg);
> +               dev_info(dev, "MMCONFIG at %pR (base %#lx)\n",
> +                                &cfg->res, (unsigned long)addr);
> +               return 0;
> +       } else {
> +               if (cfg->res.parent)
> +                       release_resource(&cfg->res);
> +               kfree(cfg);
> +       }
> +
> +err:
> +       mutex_unlock(&pci_mmcfg_lock);
> +       return rc;
> +}
> +
> +/* Delete MMCFG information for host bridges */
> +int pci_mmconfig_delete(u16 seg, u8 start, u8 end)
> +{
> +       struct pci_mmcfg_region *cfg;
> +
> +       mutex_lock(&pci_mmcfg_lock);
> +       list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
> +               if (cfg->segment == seg && cfg->start_bus == start &&
> +                   cfg->end_bus == end) {
> +                       list_del_rcu(&cfg->list);
> +                       synchronize_rcu();
> +                       pci_mmconfig_unmap_resource(cfg);
> +                       mutex_unlock(&pci_mmcfg_lock);
> +                       kfree(cfg);
> +                       return 0;
> +               }
> +       mutex_unlock(&pci_mmcfg_lock);
> +
> +       return -ENOENT;
> +}
> +
> +static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg,
> +                                       struct acpi_mcfg_allocation *cfg)
> +{
> +       int year;
> +
> +       if (!config_enabled(CONFIG_X86))
> +               return 0;
> +
> +       if (cfg->address < 0xFFFFFFFF)
> +               return 0;
> +
> +       if (!strncmp(mcfg->header.oem_id, "SGI", 3))
> +               return 0;
> +
> +       if (mcfg->header.revision >= 1) {
> +               if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) &&
> +                   year >= 2010)
> +                       return 0;
> +       }
> +
> +       pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx "
> +              "is above 4GB, ignored\n", cfg->pci_segment,
> +              cfg->start_bus_number, cfg->end_bus_number, cfg->address);
> +       return -EINVAL;
> +}
> +
> +static int __init pci_parse_mcfg(struct acpi_table_header *header)
> +{
> +       struct acpi_table_mcfg *mcfg;
> +       struct acpi_mcfg_allocation *cfg_table, *cfg;
> +       unsigned long i;
> +       int entries;
> +
> +       if (!header)
> +               return -EINVAL;
> +
> +       mcfg = (struct acpi_table_mcfg *)header;
> +
> +       /* how many config structures do we have */
> +       entries = 0;
> +       i = header->length - sizeof(struct acpi_table_mcfg);
> +       while (i >= sizeof(struct acpi_mcfg_allocation)) {
> +               entries++;
> +               i -= sizeof(struct acpi_mcfg_allocation);
> +       }
> +       if (entries == 0) {
> +               pr_err(PREFIX "MMCONFIG has no entries\n");
> +               return -ENODEV;
> +       }
> +
> +       cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1];
> +       for (i = 0; i < entries; i++) {
> +               cfg = &cfg_table[i];
> +               if (acpi_mcfg_check_entry(mcfg, cfg))
> +                       return -ENODEV;
> +
> +               if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number,
> +                                  cfg->end_bus_number, cfg->address) == NULL) {
> +                       pr_warn(PREFIX "no memory for MCFG entries\n");
> +                       return -ENOMEM;
> +               }
> +       }
> +
> +       return 0;
> +}
> +
> +int __init pci_mmconfig_parse_table(void)
> +{
> +       return acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
> +}
> +
> +void __weak __init pci_mmcfg_late_init(void)
> +{
> +       int err, n = 0;
> +       struct pci_mmcfg_region *cfg;
> +
> +       err = pci_mmconfig_parse_table();
> +       if (err) {
> +               pr_err(PREFIX " Failed to parse MCFG (%d)\n", err);
> +               return;
> +       }
> +
> +       list_for_each_entry(cfg, &pci_mmcfg_list, list) {
> +               pci_mmconfig_map_resource(NULL, cfg);
> +               n++;
> +       }
> +
> +       pr_info(PREFIX " MCFG table loaded %d entries\n", n);
> +}
> diff --git a/drivers/xen/pci.c b/drivers/xen/pci.c
> index 7494dbe..97aa9d3 100644
> --- a/drivers/xen/pci.c
> +++ b/drivers/xen/pci.c
> @@ -27,9 +27,6 @@
>  #include <asm/xen/hypervisor.h>
>  #include <asm/xen/hypercall.h>
>  #include "../pci/pci.h"
> -#ifdef CONFIG_PCI_MMCONFIG
> -#include <asm/pci_x86.h>
> -#endif
>
>  static bool __read_mostly pci_seg_supported = true;
>
> @@ -221,7 +218,7 @@ static int __init xen_mcfg_late(void)
>         if (!xen_initial_domain())
>                 return 0;
>
> -       if ((pci_probe & PCI_PROBE_MMCONF) == 0)
> +       if (!pci_mmconfig_enabled())
>                 return 0;
>
>         if (list_empty(&pci_mmcfg_list))
> diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h
> index 89ab057..e9450ef 100644
> --- a/include/linux/pci-acpi.h
> +++ b/include/linux/pci-acpi.h
> @@ -106,6 +106,39 @@ extern const u8 pci_acpi_dsm_uuid[];
>  #define RESET_DELAY_DSM                0x08
>  #define FUNCTION_DELAY_DSM     0x09
>
> +/* common API to maintain list of MCFG regions */
> +/* "PCI MMCONFIG %04x [bus %02x-%02x]" */
> +#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2)
> +
> +struct pci_mmcfg_region {
> +       struct list_head list;
> +       struct resource res;
> +       u64 address;
> +       char __iomem *virt;
> +       u16 segment;
> +       u8 start_bus;
> +       u8 end_bus;
> +       char name[PCI_MMCFG_RESOURCE_NAME_LEN];
> +};
> +
> +extern int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
> +                              phys_addr_t addr);
> +extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end);
> +
> +extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus);
> +extern struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start,
> +                                                       int end, u64 addr);
> +extern int pci_mmconfig_map_resource(struct device *dev,
> +       struct pci_mmcfg_region *mcfg);
> +extern void pci_mmconfig_unmap_resource(struct pci_mmcfg_region *mcfg);
> +extern int pci_mmconfig_enabled(void);
> +extern int __init pci_mmconfig_parse_table(void);
> +
> +extern struct list_head pci_mmcfg_list;
> +
> +#define PCI_MMCFG_BUS_OFFSET(bus)      ((bus) << 20)
> +#define PCI_MMCFG_OFFSET(bus, devfn)   ((bus) << 20 | (devfn) << 12)
> +
>  #else  /* CONFIG_ACPI */
>  static inline void acpi_pci_add_bus(struct pci_bus *bus) { }
>  static inline void acpi_pci_remove_bus(struct pci_bus *bus) { }
> --
> 1.9.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-pci" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v6 1/5] APCI: MCFG: Move mmcfg_list management to drivers/acpi
  2016-01-14  7:32   ` Jayachandran C
                     ` (2 preceding siblings ...)
  (?)
@ 2016-01-14  8:48   ` Dennis Chen
  -1 siblings, 0 replies; 31+ messages in thread
From: Dennis Chen @ 2016-01-14  8:48 UTC (permalink / raw)
  To: Jayachandran C
  Cc: Lorenzo Pieralisi, Arnd Bergmann, linux-pci, Rafael J. Wysocki,
	linux-acpi, Tomasz Nowicki, Bjorn Helgaas, xen-devel,
	linux-arm-kernel

On Thu, Jan 14, 2016 at 3:32 PM, Jayachandran C <jchandra@broadcom.com> wrote:
> Move pci_mmcfg_list handling to a drivers/acpi/pci_mcfg.c. This is
> to share the API and code with ARM64 later. The corresponding
> declarations are moved from asm/pci_x86.h to linux/pci-acpi.h
>
> As a part of this we introduce three functions that can be
> implemented by the arch code: pci_mmconfig_map_resource() to map a
> mcfg entry, pci_mmconfig_unmap_resource to do the corresponding
> unmap and pci_mmconfig_enabled to see if the arch setup of
> mcfg entries was successful. We also provide weak implementations
> of these, which will be used from ARM64. On x86, we retain the
> old logic by providing platform specific implementation.
>
> This patch is purely rearranging code, it should not have any
> impact on the logic of MCFG parsing or list handling.
>
> Signed-off-by: Jayachandran C <jchandra@broadcom.com>
> ---
>  arch/x86/include/asm/pci_x86.h |  24 +---
>  arch/x86/pci/mmconfig-shared.c | 269 +++++------------------------------
>  arch/x86/pci/mmconfig_32.c     |   1 +
>  arch/x86/pci/mmconfig_64.c     |   1 +
>  arch/x86/pci/numachip.c        |   1 +
>  drivers/acpi/Makefile          |   1 +
>  drivers/acpi/pci_mcfg.c        | 312 +++++++++++++++++++++++++++++++++++++++++
>  drivers/xen/pci.c              |   5 +-
>  include/linux/pci-acpi.h       |  33 +++++
>  9 files changed, 386 insertions(+), 261 deletions(-)
>  create mode 100644 drivers/acpi/pci_mcfg.c
>
> diff --git a/arch/x86/include/asm/pci_x86.h b/arch/x86/include/asm/pci_x86.h
> index fa1195d..83d74cb 100644
> --- a/arch/x86/include/asm/pci_x86.h
> +++ b/arch/x86/include/asm/pci_x86.h
> @@ -122,33 +122,11 @@ extern int pci_legacy_init(void);
>  extern void pcibios_fixup_irqs(void);
>
>  /* pci-mmconfig.c */
> -
> -/* "PCI MMCONFIG %04x [bus %02x-%02x]" */
> -#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2)
> -
> -struct pci_mmcfg_region {
> -       struct list_head list;
> -       struct resource res;
> -       u64 address;
> -       char __iomem *virt;
> -       u16 segment;
> -       u8 start_bus;
> -       u8 end_bus;
> -       char name[PCI_MMCFG_RESOURCE_NAME_LEN];
> -};
> -
> +struct pci_mmcfg_region;
>  extern int __init pci_mmcfg_arch_init(void);
>  extern void __init pci_mmcfg_arch_free(void);
>  extern int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg);
>  extern void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg);
> -extern int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
> -                              phys_addr_t addr);
> -extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end);
> -extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus);
> -
> -extern struct list_head pci_mmcfg_list;
> -
> -#define PCI_MMCFG_BUS_OFFSET(bus)      ((bus) << 20)
>
>  /*
>   * AMD Fam10h CPUs are buggy, and cannot access MMIO config space
> diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c
> index dd30b7e..626710b 100644
> --- a/arch/x86/pci/mmconfig-shared.c
> +++ b/arch/x86/pci/mmconfig-shared.c
> @@ -12,13 +12,12 @@
>
>  #include <linux/pci.h>
>  #include <linux/init.h>
> -#include <linux/sfi_acpi.h>
>  #include <linux/bitmap.h>
> -#include <linux/dmi.h>
>  #include <linux/slab.h>
>  #include <linux/mutex.h>
>  #include <linux/rculist.h>
>  #include <asm/e820.h>
> +#include <linux/pci-acpi.h>
>  #include <asm/pci_x86.h>
>  #include <asm/acpi.h>
>
> @@ -27,9 +26,6 @@
>  /* Indicate if the mmcfg resources have been placed into the resource table. */
>  static bool pci_mmcfg_running_state;
>  static bool pci_mmcfg_arch_init_failed;
> -static DEFINE_MUTEX(pci_mmcfg_lock);
> -
> -LIST_HEAD(pci_mmcfg_list);
>
>  static void __init pci_mmconfig_remove(struct pci_mmcfg_region *cfg)
>  {
> @@ -48,83 +44,6 @@ static void __init free_all_mmcfg(void)
>                 pci_mmconfig_remove(cfg);
>  }
>
> -static void list_add_sorted(struct pci_mmcfg_region *new)
> -{
> -       struct pci_mmcfg_region *cfg;
> -
> -       /* keep list sorted by segment and starting bus number */
> -       list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) {
> -               if (cfg->segment > new->segment ||
> -                   (cfg->segment == new->segment &&
> -                    cfg->start_bus >= new->start_bus)) {
> -                       list_add_tail_rcu(&new->list, &cfg->list);
> -                       return;
> -               }
> -       }
> -       list_add_tail_rcu(&new->list, &pci_mmcfg_list);
> -}
> -
> -static struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start,
> -                                                  int end, u64 addr)
> -{
> -       struct pci_mmcfg_region *new;
> -       struct resource *res;
> -
> -       if (addr == 0)
> -               return NULL;
> -
> -       new = kzalloc(sizeof(*new), GFP_KERNEL);
> -       if (!new)
> -               return NULL;
> -
> -       new->address = addr;
> -       new->segment = segment;
> -       new->start_bus = start;
> -       new->end_bus = end;
> -
> -       res = &new->res;
> -       res->start = addr + PCI_MMCFG_BUS_OFFSET(start);
> -       res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1;
> -       res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
> -       snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN,
> -                "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end);
> -       res->name = new->name;
> -
> -       return new;
> -}

Hmm, long long story about this refactor of pci code under x86 folder.
I think everyone who as long as has the brain in his head knows these
are the common code, not arch specific. But those kind of patches
repeats again and again and again in the mail list while our
maintainers are selective blind to this.

> -
> -static struct pci_mmcfg_region *__init pci_mmconfig_add(int segment, int start,
> -                                                       int end, u64 addr)
> -{
> -       struct pci_mmcfg_region *new;
> -
> -       new = pci_mmconfig_alloc(segment, start, end, addr);
> -       if (new) {
> -               mutex_lock(&pci_mmcfg_lock);
> -               list_add_sorted(new);
> -               mutex_unlock(&pci_mmcfg_lock);
> -
> -               pr_info(PREFIX
> -                      "MMCONFIG for domain %04x [bus %02x-%02x] at %pR "
> -                      "(base %#lx)\n",
> -                      segment, start, end, &new->res, (unsigned long)addr);
> -       }
> -
> -       return new;
> -}
> -
> -struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus)
> -{
> -       struct pci_mmcfg_region *cfg;
> -
> -       list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
> -               if (cfg->segment == segment &&
> -                   cfg->start_bus <= bus && bus <= cfg->end_bus)
> -                       return cfg;
> -
> -       return NULL;
> -}
> -
>  static const char *__init pci_mmcfg_e7520(void)
>  {
>         u32 win;
> @@ -543,73 +462,6 @@ static void __init pci_mmcfg_reject_broken(int early)
>         }
>  }
>
> -static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg,
> -                                       struct acpi_mcfg_allocation *cfg)
> -{
> -       int year;
> -
> -       if (cfg->address < 0xFFFFFFFF)
> -               return 0;
> -
> -       if (!strncmp(mcfg->header.oem_id, "SGI", 3))
> -               return 0;
> -
> -       if (mcfg->header.revision >= 1) {
> -               if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) &&
> -                   year >= 2010)
> -                       return 0;
> -       }
> -
> -       pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx "
> -              "is above 4GB, ignored\n", cfg->pci_segment,
> -              cfg->start_bus_number, cfg->end_bus_number, cfg->address);
> -       return -EINVAL;
> -}
> -
> -static int __init pci_parse_mcfg(struct acpi_table_header *header)
> -{
> -       struct acpi_table_mcfg *mcfg;
> -       struct acpi_mcfg_allocation *cfg_table, *cfg;
> -       unsigned long i;
> -       int entries;
> -
> -       if (!header)
> -               return -EINVAL;
> -
> -       mcfg = (struct acpi_table_mcfg *)header;
> -
> -       /* how many config structures do we have */
> -       free_all_mmcfg();
> -       entries = 0;
> -       i = header->length - sizeof(struct acpi_table_mcfg);
> -       while (i >= sizeof(struct acpi_mcfg_allocation)) {
> -               entries++;
> -               i -= sizeof(struct acpi_mcfg_allocation);
> -       }
> -       if (entries == 0) {
> -               pr_err(PREFIX "MMCONFIG has no entries\n");
> -               return -ENODEV;
> -       }
> -
> -       cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1];
> -       for (i = 0; i < entries; i++) {
> -               cfg = &cfg_table[i];
> -               if (acpi_mcfg_check_entry(mcfg, cfg)) {
> -                       free_all_mmcfg();
> -                       return -ENODEV;
> -               }
> -
> -               if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number,
> -                                  cfg->end_bus_number, cfg->address) == NULL) {
> -                       pr_warn(PREFIX "no memory for MCFG entries\n");
> -                       free_all_mmcfg();
> -                       return -ENOMEM;
> -               }
> -       }
> -
> -       return 0;
> -}
> -
>  #ifdef CONFIG_ACPI_APEI
>  extern int (*arch_apei_filter_addr)(int (*func)(__u64 start, __u64 size,
>                                      void *data), void *data);
> @@ -662,13 +514,20 @@ static void __init __pci_mmcfg_init(int early)
>
>  static int __initdata known_bridge;
>
> +static void __init pci_mmcfg_list_setup(void)
> +{
> +       free_all_mmcfg();
> +       if (pci_mmconfig_parse_table())
> +               free_all_mmcfg();
> +}
> +
>  void __init pci_mmcfg_early_init(void)
>  {
>         if (pci_probe & PCI_PROBE_MMCONF) {
>                 if (pci_mmcfg_check_hostbridge())
>                         known_bridge = 1;
>                 else
> -                       acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
> +                       pci_mmcfg_list_setup();
>                 __pci_mmcfg_init(1);
>
>                 set_apei_filter();
> @@ -686,7 +545,7 @@ void __init pci_mmcfg_late_init(void)
>
>         /* MMCONFIG hasn't been enabled yet, try again */
>         if (pci_probe & PCI_PROBE_MASK & ~PCI_PROBE_MMCONF) {
> -               acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
> +               pci_mmcfg_list_setup();
>                 __pci_mmcfg_init(0);
>         }
>  }
> @@ -720,99 +579,41 @@ static int __init pci_mmcfg_late_insert_resources(void)
>   */
>  late_initcall(pci_mmcfg_late_insert_resources);
>
> -/* Add MMCFG information for host bridges */
> -int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
> -                       phys_addr_t addr)
> +int pci_mmconfig_map_resource(struct device *dev, struct pci_mmcfg_region *cfg)
>  {
> -       int rc;
> -       struct resource *tmp = NULL;
> -       struct pci_mmcfg_region *cfg;
> +       struct resource *tmp;
>
> -       if (!(pci_probe & PCI_PROBE_MMCONF) || pci_mmcfg_arch_init_failed)
> -               return -ENODEV;
> -
> -       if (start > end)
> -               return -EINVAL;
> -
> -       mutex_lock(&pci_mmcfg_lock);
> -       cfg = pci_mmconfig_lookup(seg, start);
> -       if (cfg) {
> -               if (cfg->end_bus < end)
> -                       dev_info(dev, FW_INFO
> -                                "MMCONFIG for "
> -                                "domain %04x [bus %02x-%02x] "
> -                                "only partially covers this bridge\n",
> -                                 cfg->segment, cfg->start_bus, cfg->end_bus);
> -               mutex_unlock(&pci_mmcfg_lock);
> -               return -EEXIST;
> -       }
> -
> -       if (!addr) {
> -               mutex_unlock(&pci_mmcfg_lock);
> -               return -EINVAL;
> -       }
> -
> -       rc = -EBUSY;
> -       cfg = pci_mmconfig_alloc(seg, start, end, addr);
> -       if (cfg == NULL) {
> -               dev_warn(dev, "fail to add MMCONFIG (out of memory)\n");
> -               rc = -ENOMEM;
> -       } else if (!pci_mmcfg_check_reserved(dev, cfg, 0)) {
> +       if (!pci_mmcfg_check_reserved(dev, cfg, 0)) {
>                 dev_warn(dev, FW_BUG "MMCONFIG %pR isn't reserved\n",
>                          &cfg->res);
> -       } else {
> -               /* Insert resource if it's not in boot stage */
> -               if (pci_mmcfg_running_state)
> -                       tmp = insert_resource_conflict(&iomem_resource,
> -                                                      &cfg->res);
> -
> +               return -EBUSY;
> +       }
> +       /* Insert resource if it's not in boot stage */
> +       if (pci_mmcfg_running_state) {
> +               tmp = insert_resource_conflict(&iomem_resource,
> +                                              &cfg->res);
>                 if (tmp) {
> -                       dev_warn(dev,
> -                                "MMCONFIG %pR conflicts with "
> -                                "%s %pR\n",
> -                                &cfg->res, tmp->name, tmp);
> -               } else if (pci_mmcfg_arch_map(cfg)) {
> -                       dev_warn(dev, "fail to map MMCONFIG %pR.\n",
> -                                &cfg->res);
> -               } else {
> -                       list_add_sorted(cfg);
> -                       dev_info(dev, "MMCONFIG at %pR (base %#lx)\n",
> -                                &cfg->res, (unsigned long)addr);
> -                       cfg = NULL;
> -                       rc = 0;
> +                       dev_warn(dev, "MMCONFIG %pR conflicts with %s %pR\n",
> +                               &cfg->res, tmp->name, tmp);
> +                       return -EBUSY;
>                 }
>         }
> -
> -       if (cfg) {
> -               if (cfg->res.parent)
> -                       release_resource(&cfg->res);
> -               kfree(cfg);
> +       if (pci_mmcfg_arch_map(cfg)) {
> +               dev_warn(dev, "fail to map MMCONFIG %pR.\n", &cfg->res);
> +               return -EBUSY;
>         }
> -
> -       mutex_unlock(&pci_mmcfg_lock);
> -
> -       return rc;
> +       return 0;
>  }
>
> -/* Delete MMCFG information for host bridges */
> -int pci_mmconfig_delete(u16 seg, u8 start, u8 end)
> +void pci_mmconfig_unmap_resource(struct pci_mmcfg_region *cfg)
>  {
> -       struct pci_mmcfg_region *cfg;
> -
> -       mutex_lock(&pci_mmcfg_lock);
> -       list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
> -               if (cfg->segment == seg && cfg->start_bus == start &&
> -                   cfg->end_bus == end) {
> -                       list_del_rcu(&cfg->list);
> -                       synchronize_rcu();
> -                       pci_mmcfg_arch_unmap(cfg);
> -                       if (cfg->res.parent)
> -                               release_resource(&cfg->res);
> -                       mutex_unlock(&pci_mmcfg_lock);
> -                       kfree(cfg);
> -                       return 0;
> -               }
> -       mutex_unlock(&pci_mmcfg_lock);
> +       pci_mmcfg_arch_unmap(cfg);
> +       if (cfg->res.parent)
> +               release_resource(&cfg->res);
> +       cfg->res.parent = NULL;
> +}
>
> -       return -ENOENT;
> +int pci_mmconfig_enabled(void)
> +{
> +       return (pci_probe & PCI_PROBE_MMCONF) && !pci_mmcfg_arch_init_failed;
>  }
> diff --git a/arch/x86/pci/mmconfig_32.c b/arch/x86/pci/mmconfig_32.c
> index 43984bc..38a37f8 100644
> --- a/arch/x86/pci/mmconfig_32.c
> +++ b/arch/x86/pci/mmconfig_32.c
> @@ -12,6 +12,7 @@
>  #include <linux/pci.h>
>  #include <linux/init.h>
>  #include <linux/rcupdate.h>
> +#include <linux/pci-acpi.h>
>  #include <asm/e820.h>
>  #include <asm/pci_x86.h>
>
> diff --git a/arch/x86/pci/mmconfig_64.c b/arch/x86/pci/mmconfig_64.c
> index bea5249..29253ec 100644
> --- a/arch/x86/pci/mmconfig_64.c
> +++ b/arch/x86/pci/mmconfig_64.c
> @@ -10,6 +10,7 @@
>  #include <linux/acpi.h>
>  #include <linux/bitmap.h>
>  #include <linux/rcupdate.h>
> +#include <linux/pci-acpi.h>
>  #include <asm/e820.h>
>  #include <asm/pci_x86.h>
>
> diff --git a/arch/x86/pci/numachip.c b/arch/x86/pci/numachip.c
> index 2e565e6..c181eeb 100644
> --- a/arch/x86/pci/numachip.c
> +++ b/arch/x86/pci/numachip.c
> @@ -14,6 +14,7 @@
>   */
>
>  #include <linux/pci.h>
> +#include <linux/pci-acpi.h>
>  #include <asm/pci_x86.h>
>
>  static u8 limit __read_mostly;
> diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
> index 675eaf3..3f65311 100644
> --- a/drivers/acpi/Makefile
> +++ b/drivers/acpi/Makefile
> @@ -40,6 +40,7 @@ acpi-$(CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC) += processor_pdc.o
>  acpi-y                         += ec.o
>  acpi-$(CONFIG_ACPI_DOCK)       += dock.o
>  acpi-y                         += pci_root.o pci_link.o pci_irq.o
> +acpi-$(CONFIG_PCI_MMCONFIG)    += pci_mcfg.o
>  acpi-y                         += acpi_lpss.o acpi_apd.o
>  acpi-y                         += acpi_platform.o
>  acpi-y                         += acpi_pnp.o
> diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c
> new file mode 100644
> index 0000000..ea84365
> --- /dev/null
> +++ b/drivers/acpi/pci_mcfg.c
> @@ -0,0 +1,312 @@
> +/*
> + * pci_mcfg.c
> + *
> + * Common code to maintain the MCFG areas and mappings
> + *
> + * This has been extracted from arch/x86/pci/mmconfig-shared.c
> + * and moved here so that other architectures can use this code.
> + */
> +
> +#include <linux/pci.h>
> +#include <linux/init.h>
> +#include <linux/dmi.h>
> +#include <linux/pci-acpi.h>
> +#include <linux/sfi_acpi.h>
> +#include <linux/slab.h>
> +#include <linux/mutex.h>
> +#include <linux/rculist.h>
> +
> +#define PREFIX "ACPI: "
> +
> +static DEFINE_MUTEX(pci_mmcfg_lock);
> +LIST_HEAD(pci_mmcfg_list);
> +
> +static void list_add_sorted(struct pci_mmcfg_region *new)
> +{
> +       struct pci_mmcfg_region *cfg;
> +
> +       /* keep list sorted by segment and starting bus number */
> +       list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) {
> +               if (cfg->segment > new->segment ||
> +                   (cfg->segment == new->segment &&
> +                    cfg->start_bus >= new->start_bus)) {
> +                       list_add_tail_rcu(&new->list, &cfg->list);
> +                       return;
> +               }
> +       }
> +       list_add_tail_rcu(&new->list, &pci_mmcfg_list);
> +}
> +
> +static struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start,
> +                                                  int end, u64 addr)
> +{
> +       struct pci_mmcfg_region *new;
> +       struct resource *res;
> +
> +       if (addr == 0)
> +               return NULL;
> +
> +       new = kzalloc(sizeof(*new), GFP_KERNEL);
> +       if (!new)
> +               return NULL;
> +
> +       new->address = addr;
> +       new->segment = segment;
> +       new->start_bus = start;
> +       new->end_bus = end;
> +
> +       res = &new->res;
> +       res->start = addr + PCI_MMCFG_BUS_OFFSET(start);
> +       res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1;
> +       res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
> +       snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN,
> +                "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end);
> +       res->name = new->name;
> +
> +       return new;
> +}
> +
> +struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start,
> +                                                       int end, u64 addr)
> +{
> +       struct pci_mmcfg_region *new;
> +
> +       new = pci_mmconfig_alloc(segment, start, end, addr);
> +       if (new) {
> +               mutex_lock(&pci_mmcfg_lock);
> +               list_add_sorted(new);
> +               mutex_unlock(&pci_mmcfg_lock);
> +
> +               pr_info(PREFIX
> +                      "MMCONFIG for domain %04x [bus %02x-%02x] at %pR "
> +                      "(base %#lx)\n",
> +                      segment, start, end, &new->res, (unsigned long)addr);
> +       }
> +
> +       return new;
> +}
> +
> +struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus)
> +{
> +       struct pci_mmcfg_region *cfg;
> +
> +       list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
> +               if (cfg->segment == segment &&
> +                   cfg->start_bus <= bus && bus <= cfg->end_bus)
> +                       return cfg;
> +
> +       return NULL;
> +}
> +
> +/*
> + * Map a pci_mmcfg_region, can be overrriden by arch
> + */
> +int __weak pci_mmconfig_map_resource(struct device *dev,
> +       struct pci_mmcfg_region *mcfg)
> +{
> +       struct resource *tmp;
> +       void __iomem *vaddr;
> +
> +       tmp = insert_resource_conflict(&iomem_resource, &mcfg->res);
> +       if (tmp) {
> +               dev_warn(dev, "MMCONFIG %pR conflicts with %s %pR\n",
> +                       &mcfg->res, tmp->name, tmp);
> +               return -EBUSY;
> +       }
> +
> +       vaddr =  ioremap(mcfg->res.start, resource_size(&mcfg->res));
> +       if (!vaddr) {
> +               release_resource(&mcfg->res);
> +               return -ENOMEM;
> +       }
> +
> +       mcfg->virt = vaddr;
> +       return 0;
> +}
> +
> +/*
> + * Unmap a pci_mmcfg_region, can be overrriden by arch
> + */
> +void __weak pci_mmconfig_unmap_resource(struct pci_mmcfg_region *mcfg)
> +{
> +       if (mcfg->virt) {
> +               iounmap(mcfg->virt);
> +               mcfg->virt = NULL;
> +       }
> +       if (mcfg->res.parent) {
> +               release_resource(&mcfg->res);
> +               mcfg->res.parent = NULL;
> +       }
> +}
> +
> +/*
> + * check if the mmconfig is enabled and configured
> + */
> +int __weak pci_mmconfig_enabled(void)
> +{
> +       return 1;
> +}
> +
> +/* Add MMCFG information for host bridges */
> +int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
> +                       phys_addr_t addr)
> +{
> +       struct pci_mmcfg_region *cfg;
> +       int rc;
> +
> +       if (!pci_mmconfig_enabled())
> +               return -ENODEV;
> +       if (start > end)
> +               return -EINVAL;
> +
> +       mutex_lock(&pci_mmcfg_lock);
> +       cfg = pci_mmconfig_lookup(seg, start);
> +       if (cfg) {
> +               if (cfg->end_bus < end)
> +                       dev_info(dev, FW_INFO
> +                                "MMCONFIG for "
> +                                "domain %04x [bus %02x-%02x] "
> +                                "only partially covers this bridge\n",
> +                                 cfg->segment, cfg->start_bus, cfg->end_bus);
> +               rc = -EEXIST;
> +               goto err;
> +       }
> +
> +       if (!addr) {
> +               rc = -EINVAL;
> +               goto err;
> +       }
> +
> +       cfg = pci_mmconfig_alloc(seg, start, end, addr);
> +       if (cfg == NULL) {
> +               dev_warn(dev, "fail to add MMCONFIG (out of memory)\n");
> +               rc = -ENOMEM;
> +               goto err;
> +       }
> +       rc = pci_mmconfig_map_resource(dev, cfg);
> +       if (!rc) {
> +               list_add_sorted(cfg);
> +               dev_info(dev, "MMCONFIG at %pR (base %#lx)\n",
> +                                &cfg->res, (unsigned long)addr);
> +               return 0;
> +       } else {
> +               if (cfg->res.parent)
> +                       release_resource(&cfg->res);
> +               kfree(cfg);
> +       }
> +
> +err:
> +       mutex_unlock(&pci_mmcfg_lock);
> +       return rc;
> +}
> +
> +/* Delete MMCFG information for host bridges */
> +int pci_mmconfig_delete(u16 seg, u8 start, u8 end)
> +{
> +       struct pci_mmcfg_region *cfg;
> +
> +       mutex_lock(&pci_mmcfg_lock);
> +       list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
> +               if (cfg->segment == seg && cfg->start_bus == start &&
> +                   cfg->end_bus == end) {
> +                       list_del_rcu(&cfg->list);
> +                       synchronize_rcu();
> +                       pci_mmconfig_unmap_resource(cfg);
> +                       mutex_unlock(&pci_mmcfg_lock);
> +                       kfree(cfg);
> +                       return 0;
> +               }
> +       mutex_unlock(&pci_mmcfg_lock);
> +
> +       return -ENOENT;
> +}
> +
> +static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg,
> +                                       struct acpi_mcfg_allocation *cfg)
> +{
> +       int year;
> +
> +       if (!config_enabled(CONFIG_X86))
> +               return 0;
> +
> +       if (cfg->address < 0xFFFFFFFF)
> +               return 0;
> +
> +       if (!strncmp(mcfg->header.oem_id, "SGI", 3))
> +               return 0;
> +
> +       if (mcfg->header.revision >= 1) {
> +               if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) &&
> +                   year >= 2010)
> +                       return 0;
> +       }
> +
> +       pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx "
> +              "is above 4GB, ignored\n", cfg->pci_segment,
> +              cfg->start_bus_number, cfg->end_bus_number, cfg->address);
> +       return -EINVAL;
> +}
> +
> +static int __init pci_parse_mcfg(struct acpi_table_header *header)
> +{
> +       struct acpi_table_mcfg *mcfg;
> +       struct acpi_mcfg_allocation *cfg_table, *cfg;
> +       unsigned long i;
> +       int entries;
> +
> +       if (!header)
> +               return -EINVAL;
> +
> +       mcfg = (struct acpi_table_mcfg *)header;
> +
> +       /* how many config structures do we have */
> +       entries = 0;
> +       i = header->length - sizeof(struct acpi_table_mcfg);
> +       while (i >= sizeof(struct acpi_mcfg_allocation)) {
> +               entries++;
> +               i -= sizeof(struct acpi_mcfg_allocation);
> +       }
> +       if (entries == 0) {
> +               pr_err(PREFIX "MMCONFIG has no entries\n");
> +               return -ENODEV;
> +       }
> +
> +       cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1];
> +       for (i = 0; i < entries; i++) {
> +               cfg = &cfg_table[i];
> +               if (acpi_mcfg_check_entry(mcfg, cfg))
> +                       return -ENODEV;
> +
> +               if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number,
> +                                  cfg->end_bus_number, cfg->address) == NULL) {
> +                       pr_warn(PREFIX "no memory for MCFG entries\n");
> +                       return -ENOMEM;
> +               }
> +       }
> +
> +       return 0;
> +}
> +
> +int __init pci_mmconfig_parse_table(void)
> +{
> +       return acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
> +}
> +
> +void __weak __init pci_mmcfg_late_init(void)
> +{
> +       int err, n = 0;
> +       struct pci_mmcfg_region *cfg;
> +
> +       err = pci_mmconfig_parse_table();
> +       if (err) {
> +               pr_err(PREFIX " Failed to parse MCFG (%d)\n", err);
> +               return;
> +       }
> +
> +       list_for_each_entry(cfg, &pci_mmcfg_list, list) {
> +               pci_mmconfig_map_resource(NULL, cfg);
> +               n++;
> +       }
> +
> +       pr_info(PREFIX " MCFG table loaded %d entries\n", n);
> +}
> diff --git a/drivers/xen/pci.c b/drivers/xen/pci.c
> index 7494dbe..97aa9d3 100644
> --- a/drivers/xen/pci.c
> +++ b/drivers/xen/pci.c
> @@ -27,9 +27,6 @@
>  #include <asm/xen/hypervisor.h>
>  #include <asm/xen/hypercall.h>
>  #include "../pci/pci.h"
> -#ifdef CONFIG_PCI_MMCONFIG
> -#include <asm/pci_x86.h>
> -#endif
>
>  static bool __read_mostly pci_seg_supported = true;
>
> @@ -221,7 +218,7 @@ static int __init xen_mcfg_late(void)
>         if (!xen_initial_domain())
>                 return 0;
>
> -       if ((pci_probe & PCI_PROBE_MMCONF) == 0)
> +       if (!pci_mmconfig_enabled())
>                 return 0;
>
>         if (list_empty(&pci_mmcfg_list))
> diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h
> index 89ab057..e9450ef 100644
> --- a/include/linux/pci-acpi.h
> +++ b/include/linux/pci-acpi.h
> @@ -106,6 +106,39 @@ extern const u8 pci_acpi_dsm_uuid[];
>  #define RESET_DELAY_DSM                0x08
>  #define FUNCTION_DELAY_DSM     0x09
>
> +/* common API to maintain list of MCFG regions */
> +/* "PCI MMCONFIG %04x [bus %02x-%02x]" */
> +#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2)
> +
> +struct pci_mmcfg_region {
> +       struct list_head list;
> +       struct resource res;
> +       u64 address;
> +       char __iomem *virt;
> +       u16 segment;
> +       u8 start_bus;
> +       u8 end_bus;
> +       char name[PCI_MMCFG_RESOURCE_NAME_LEN];
> +};
> +
> +extern int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
> +                              phys_addr_t addr);
> +extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end);
> +
> +extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus);
> +extern struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start,
> +                                                       int end, u64 addr);
> +extern int pci_mmconfig_map_resource(struct device *dev,
> +       struct pci_mmcfg_region *mcfg);
> +extern void pci_mmconfig_unmap_resource(struct pci_mmcfg_region *mcfg);
> +extern int pci_mmconfig_enabled(void);
> +extern int __init pci_mmconfig_parse_table(void);
> +
> +extern struct list_head pci_mmcfg_list;
> +
> +#define PCI_MMCFG_BUS_OFFSET(bus)      ((bus) << 20)
> +#define PCI_MMCFG_OFFSET(bus, devfn)   ((bus) << 20 | (devfn) << 12)
> +
>  #else  /* CONFIG_ACPI */
>  static inline void acpi_pci_add_bus(struct pci_bus *bus) { }
>  static inline void acpi_pci_remove_bus(struct pci_bus *bus) { }
> --
> 1.9.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-pci" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Xen-devel] [PATCH v6 1/5] APCI: MCFG: Move mmcfg_list management to drivers/acpi
  2016-01-14  7:32   ` Jayachandran C
  (?)
@ 2016-01-14 10:33     ` David Vrabel
  -1 siblings, 0 replies; 31+ messages in thread
From: David Vrabel @ 2016-01-14 10:33 UTC (permalink / raw)
  To: Jayachandran C, linux-pci, Bjorn Helgaas, linux-acpi,
	Arnd Bergmann, linux-arm-kernel, Rafael J. Wysocki
  Cc: Tomasz Nowicki, Lorenzo Pieralisi, xen-devel

On 14/01/16 07:32, Jayachandran C wrote:
> Move pci_mmcfg_list handling to a drivers/acpi/pci_mcfg.c. This is
> to share the API and code with ARM64 later. The corresponding
> declarations are moved from asm/pci_x86.h to linux/pci-acpi.h
[...]
> --- a/drivers/xen/pci.c
> +++ b/drivers/xen/pci.c
> @@ -27,9 +27,6 @@
>  #include <asm/xen/hypervisor.h>
>  #include <asm/xen/hypercall.h>
>  #include "../pci/pci.h"
> -#ifdef CONFIG_PCI_MMCONFIG
> -#include <asm/pci_x86.h>
> -#endif
>  
>  static bool __read_mostly pci_seg_supported = true;
>  
> @@ -221,7 +218,7 @@ static int __init xen_mcfg_late(void)
>  	if (!xen_initial_domain())
>  		return 0;
>  
> -	if ((pci_probe & PCI_PROBE_MMCONF) == 0)
> +	if (!pci_mmconfig_enabled())
>  		return 0;
>  
>  	if (list_empty(&pci_mmcfg_list))

Xen parts:

Acked-by: David Vrabel <david.vrabel@citrix.com>

David

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Xen-devel] [PATCH v6 1/5] APCI: MCFG: Move mmcfg_list management to drivers/acpi
@ 2016-01-14 10:33     ` David Vrabel
  0 siblings, 0 replies; 31+ messages in thread
From: David Vrabel @ 2016-01-14 10:33 UTC (permalink / raw)
  To: Jayachandran C, linux-pci, Bjorn Helgaas, linux-acpi,
	Arnd Bergmann, linux-arm-kernel, Rafael J. Wysocki
  Cc: Tomasz Nowicki, Lorenzo Pieralisi, xen-devel

On 14/01/16 07:32, Jayachandran C wrote:
> Move pci_mmcfg_list handling to a drivers/acpi/pci_mcfg.c. This is
> to share the API and code with ARM64 later. The corresponding
> declarations are moved from asm/pci_x86.h to linux/pci-acpi.h
[...]
> --- a/drivers/xen/pci.c
> +++ b/drivers/xen/pci.c
> @@ -27,9 +27,6 @@
>  #include <asm/xen/hypervisor.h>
>  #include <asm/xen/hypercall.h>
>  #include "../pci/pci.h"
> -#ifdef CONFIG_PCI_MMCONFIG
> -#include <asm/pci_x86.h>
> -#endif
>  
>  static bool __read_mostly pci_seg_supported = true;
>  
> @@ -221,7 +218,7 @@ static int __init xen_mcfg_late(void)
>  	if (!xen_initial_domain())
>  		return 0;
>  
> -	if ((pci_probe & PCI_PROBE_MMCONF) == 0)
> +	if (!pci_mmconfig_enabled())
>  		return 0;
>  
>  	if (list_empty(&pci_mmcfg_list))

Xen parts:

Acked-by: David Vrabel <david.vrabel@citrix.com>

David

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [Xen-devel] [PATCH v6 1/5] APCI: MCFG: Move mmcfg_list management to drivers/acpi
@ 2016-01-14 10:33     ` David Vrabel
  0 siblings, 0 replies; 31+ messages in thread
From: David Vrabel @ 2016-01-14 10:33 UTC (permalink / raw)
  To: linux-arm-kernel

On 14/01/16 07:32, Jayachandran C wrote:
> Move pci_mmcfg_list handling to a drivers/acpi/pci_mcfg.c. This is
> to share the API and code with ARM64 later. The corresponding
> declarations are moved from asm/pci_x86.h to linux/pci-acpi.h
[...]
> --- a/drivers/xen/pci.c
> +++ b/drivers/xen/pci.c
> @@ -27,9 +27,6 @@
>  #include <asm/xen/hypervisor.h>
>  #include <asm/xen/hypercall.h>
>  #include "../pci/pci.h"
> -#ifdef CONFIG_PCI_MMCONFIG
> -#include <asm/pci_x86.h>
> -#endif
>  
>  static bool __read_mostly pci_seg_supported = true;
>  
> @@ -221,7 +218,7 @@ static int __init xen_mcfg_late(void)
>  	if (!xen_initial_domain())
>  		return 0;
>  
> -	if ((pci_probe & PCI_PROBE_MMCONF) == 0)
> +	if (!pci_mmconfig_enabled())
>  		return 0;
>  
>  	if (list_empty(&pci_mmcfg_list))

Xen parts:

Acked-by: David Vrabel <david.vrabel@citrix.com>

David

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v6 1/5] APCI: MCFG: Move mmcfg_list management to drivers/acpi
  2016-01-14  7:32   ` Jayachandran C
                     ` (4 preceding siblings ...)
  (?)
@ 2016-01-14 10:33   ` David Vrabel
  -1 siblings, 0 replies; 31+ messages in thread
From: David Vrabel @ 2016-01-14 10:33 UTC (permalink / raw)
  To: Jayachandran C, linux-pci, Bjorn Helgaas, linux-acpi,
	Arnd Bergmann, linux-arm-kernel, Rafael J. Wysocki
  Cc: Tomasz Nowicki, Lorenzo Pieralisi, xen-devel

On 14/01/16 07:32, Jayachandran C wrote:
> Move pci_mmcfg_list handling to a drivers/acpi/pci_mcfg.c. This is
> to share the API and code with ARM64 later. The corresponding
> declarations are moved from asm/pci_x86.h to linux/pci-acpi.h
[...]
> --- a/drivers/xen/pci.c
> +++ b/drivers/xen/pci.c
> @@ -27,9 +27,6 @@
>  #include <asm/xen/hypervisor.h>
>  #include <asm/xen/hypercall.h>
>  #include "../pci/pci.h"
> -#ifdef CONFIG_PCI_MMCONFIG
> -#include <asm/pci_x86.h>
> -#endif
>  
>  static bool __read_mostly pci_seg_supported = true;
>  
> @@ -221,7 +218,7 @@ static int __init xen_mcfg_late(void)
>  	if (!xen_initial_domain())
>  		return 0;
>  
> -	if ((pci_probe & PCI_PROBE_MMCONF) == 0)
> +	if (!pci_mmconfig_enabled())
>  		return 0;
>  
>  	if (list_empty(&pci_mmcfg_list))

Xen parts:

Acked-by: David Vrabel <david.vrabel@citrix.com>

David

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH v6 0/5] ACPI based PCI support for arm64
@ 2016-01-14  7:32 Jayachandran C
  0 siblings, 0 replies; 31+ messages in thread
From: Jayachandran C @ 2016-01-14  7:32 UTC (permalink / raw)
  To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann,
	linux-arm-kernel, Rafael J. Wysocki
  Cc: Tomasz Nowicki, Lorenzo Pieralisi, Jayachandran C, xen-devel

This patchset provides a generic ACPI based PCI host controller
implementation and uses it on arm64.

The first patch moves the common code to handle MCFG ACPI table from
arch/x86 to drivers/acpi/pci_mcfg.c. The last patch in the patchset
provides the generic implementation of an ACPI based PCI host
controller with a new file drivers/acpi/pci_host_acpi.c. The other
patches are to fix up arm64 and ACPI code to work with these two
patches.

The pci host controller implementation keeps a reference to
pci_mmcfg_region entry so that config space access is done with a
simple mapping and generic PCI config read/write. There is also an
implementation of raw_pci_read/raw_pci_write provided by walking the
pci_mmcfg_list

The patchset is against 4.4, but it can be applied to pci/next
with a very minor fixup.  This is tested with arm64 QEMU and OVMF
and on x86 with qemu.

More testing or comments are welcome. The idea is to provide a
simpler alternative for ACPI PCI support in arm64, any feedback
from PCI or ACPI maintainers would be appreciated on this front.

Thanks,
JC.

v5->v6:
 - fix arm64 with xen compile issue reported by kbuild bot

v4->v5:
 - Fix arm build issue reported by the kbuild bot
 - Use config_enabled instead of ifdef in pci_mcfg.c

v3->v4:
 - Handle suggestions from Arnd Bergmann <arnd@arndb.de>
    * move the implementation of host controller to drivers/acpi
      under config option CONFIG_ACPI_PCI_HOST_GENERIC
    * remove unnecessary arch hooks (this done as much as possible
      without affecting the scope of the patch, will need another
      separate patchset for the rest).
    * remove pcibios_assign_resources fs_initcall that was added
 - fixup domain_nr assignment code, and move ACPI companion set
   to PCI code.
 - Call map resource correctly in setup code
 - fix kbuild robot report
 - More testing

v2->v3:
 - Move maintenance of the pci_mmcfg_list to drivers/acpi/pci_mcfg.c
   without changing x86 logic
 - use the pci_mmcfg_list in ARM64 implementation
 - provide raw_pci_read/raw_pci_write

v1->v2:
 - use CONFIG_PCI_MMCONFIG on arm64, provide a weak implementation
   of pci_mmcfg_late_init for arm64.
 - The real implementation of pci_mmcfg_late_init is in pci-host-acpi.c
   and it will save the MCFG table entries to an array. Earlier this
   was done with an arch_init call
 - remove unneeded pci_bus_add_devices call and fix MCFG saving code
 - Added a patch to ACPI pci_root.c to handle arm64 PCI IO resources

Jayachandran C (5):
  APCI: MCFG: Move mmcfg_list management to drivers/acpi
  PCI: Handle ACPI companion and domain number
  ACPI: PCI: Support platforms that need pci_remap_iospace
  arm64: pci: Add ACPI support
  PCI: ACPI: Add a generic ACPI based host controller

 arch/arm64/Kconfig             |   3 +
 arch/arm64/kernel/pci.c        |  34 ++++-
 arch/x86/include/asm/pci_x86.h |  24 +---
 arch/x86/pci/mmconfig-shared.c | 269 +++++------------------------------
 arch/x86/pci/mmconfig_32.c     |   1 +
 arch/x86/pci/mmconfig_64.c     |   1 +
 arch/x86/pci/numachip.c        |   1 +
 drivers/acpi/Kconfig           |   8 ++
 drivers/acpi/Makefile          |   2 +
 drivers/acpi/pci_host_acpi.c   | 186 ++++++++++++++++++++++++
 drivers/acpi/pci_mcfg.c        | 312 +++++++++++++++++++++++++++++++++++++++++
 drivers/acpi/pci_root.c        |  62 +++++++-
 drivers/acpi/resource.c        |   2 +
 drivers/pci/pci.c              |  15 +-
 drivers/pci/probe.c            |   2 +
 drivers/xen/pci.c              |   5 +-
 include/linux/pci-acpi.h       |  59 ++++++++
 17 files changed, 717 insertions(+), 269 deletions(-)
 create mode 100644 drivers/acpi/pci_host_acpi.c
 create mode 100644 drivers/acpi/pci_mcfg.c

-- 
1.9.1

^ permalink raw reply	[flat|nested] 31+ messages in thread

end of thread, other threads:[~2016-01-14 10:42 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-01-14  7:32 [PATCH v6 0/5] ACPI based PCI support for arm64 Jayachandran C
2016-01-14  7:32 ` Jayachandran C
2016-01-14  7:32 ` Jayachandran C
2016-01-14  7:32 ` [PATCH v6 1/5] APCI: MCFG: Move mmcfg_list management to drivers/acpi Jayachandran C
2016-01-14  7:32 ` Jayachandran C
2016-01-14  7:32   ` Jayachandran C
2016-01-14  7:32   ` Jayachandran C
2016-01-14  8:48   ` Dennis Chen
2016-01-14  8:48     ` Dennis Chen
2016-01-14  8:48   ` Dennis Chen
2016-01-14 10:33   ` [Xen-devel] " David Vrabel
2016-01-14 10:33     ` David Vrabel
2016-01-14 10:33     ` David Vrabel
2016-01-14 10:33   ` David Vrabel
2016-01-14  7:32 ` [PATCH v6 2/5] PCI: Handle ACPI companion and domain number Jayachandran C
2016-01-14  7:32   ` Jayachandran C
2016-01-14  7:32   ` Jayachandran C
2016-01-14  7:32 ` Jayachandran C
2016-01-14  7:32 ` [PATCH v6 3/5] ACPI: PCI: Support platforms that need pci_remap_iospace Jayachandran C
2016-01-14  7:32 ` Jayachandran C
2016-01-14  7:32   ` Jayachandran C
2016-01-14  7:32   ` Jayachandran C
2016-01-14  7:32 ` [PATCH v6 4/5] arm64: pci: Add ACPI support Jayachandran C
2016-01-14  7:32   ` Jayachandran C
2016-01-14  7:32   ` Jayachandran C
2016-01-14  7:32 ` Jayachandran C
2016-01-14  7:32 ` [PATCH v6 5/5] PCI: ACPI: Add a generic ACPI based host controller Jayachandran C
2016-01-14  7:32 ` Jayachandran C
2016-01-14  7:32   ` Jayachandran C
2016-01-14  7:32   ` Jayachandran C
2016-01-14  7:32 [PATCH v6 0/5] ACPI based PCI support for arm64 Jayachandran C

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.