diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig
index 840f87eb4b28..8f43fdec333d 100644
--- a/drivers/pci/pcie/Kconfig
+++ b/drivers/pci/pcie/Kconfig
@@ -156,3 +156,12 @@ config PCIE_EDR
 	  the PCI Firmware Specification r3.2.  Enable this if you want to
 	  support hybrid DPC model which uses both firmware and OS to
 	  implement DPC.
+
+config PCIE_CXL_PMU
+	bool "CXL performance monitoring units on RP and Switch Ports"
+	depends on AUXILIARY_BUS
+	help
+	  CXL root ports, switch upstream and switch downstream ports may
+	  have one or more CXL PMU devices. Enable this option to look
+	  for these and register a device to which the cxl_pmu driver
+	  may bind
\ No newline at end of file
diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile
index 6461aa93fe76..037959bf91bb 100644
--- a/drivers/pci/pcie/Makefile
+++ b/drivers/pci/pcie/Makefile
@@ -13,3 +13,4 @@ obj-$(CONFIG_PCIE_PME)		+= pme.o
 obj-$(CONFIG_PCIE_DPC)		+= dpc.o
 obj-$(CONFIG_PCIE_PTM)		+= ptm.o
 obj-$(CONFIG_PCIE_EDR)		+= edr.o
+obj-$(CONFIG_PCIE_CXL_PMU)	+= cxlpmu.o
diff --git a/drivers/pci/pcie/cxlpmu.c b/drivers/pci/pcie/cxlpmu.c
new file mode 100644
index 000000000000..7716663e567c
--- /dev/null
+++ b/drivers/pci/pcie/cxlpmu.c
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Discovery of presence of CXL PMU instances and the maximum irqnum.
+ * Registers a auxiliary_device to which a driver can bind after the
+ * CXL bus is available and new devices can be added to ti.
+ */
+#include <linux/kernel.h>
+#include <linux/idr.h>
+#include <linux/list.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+
+#include "portdrv.h"
+#include "../../cxl/cxl.h"
+#include "../../cxl/cxlpci.h"
+#include "../../cxl/pmu.h"
+
+static DEFINE_IDA(pcie_cxl_pmu_ida);
+static void cpmu_adev_release(struct device *dev)
+{
+	struct auxiliary_device *adev = to_auxiliary_dev(dev);
+
+	ida_free(&pcie_cxl_pmu_ida, adev->id);
+}
+
+int pcie_cxl_pmu_get_irqs(struct pci_dev *dev, u32 *max_irq,
+			  struct list_head *aux_dev_list)
+{
+	u32 regblocks, regloc_size;
+	int i, regloc, ret;
+	bool found = false;
+
+	regloc = pci_find_dvsec_capability(dev, PCI_VENDOR_ID_CXL,
+					   CXL_DVSEC_REG_LOCATOR);
+	if (!regloc)
+		return -ENODEV;
+
+	pci_read_config_dword(dev, regloc + PCI_DVSEC_HEADER1, &regloc_size);
+	regloc_size = FIELD_GET(PCI_DVSEC_HEADER1_LENGTH_MASK, regloc_size);
+
+	regloc += CXL_DVSEC_REG_LOCATOR_BLOCK1_OFFSET;
+	regblocks = (regloc_size - CXL_DVSEC_REG_LOCATOR_BLOCK1_OFFSET) / 8;
+
+	for (i = 0; i < regblocks; i++, regloc += 8) {
+		u32 reg_lo, reg_hi;
+		u8 reg_type;
+		struct resource *res;
+		void __iomem *base;
+		u64 offset, val;
+		int bar;
+
+		pci_read_config_dword(dev, regloc, &reg_lo);
+		reg_type = FIELD_GET(CXL_DVSEC_REG_LOCATOR_BLOCK_ID_MASK,
+				     reg_lo);
+		if (reg_type != CXL_REGLOC_RBI_PMU)
+			continue;
+
+		found = true;
+		/* Now we need to map just enough to get the irq */
+		bar = FIELD_GET(CXL_DVSEC_REG_LOCATOR_BIR_MASK, reg_lo);
+		pci_read_config_dword(dev, regloc + 4, &reg_hi);
+
+		offset = ((u64) reg_hi << 32) |
+			(reg_lo & CXL_DVSEC_REG_LOCATOR_BLOCK_OFF_LOW_MASK);
+		if (offset > pci_resource_len(dev, bar)) {
+			pci_warn(dev, "CPMU BAR%d: %pr: too small\n",
+				bar, &dev->resource[bar]);
+			continue;
+		}
+		/*
+		 * Map only the CPMU region because other parts are in control
+		 * of the CXL port driver.
+		 */
+		res = request_mem_region(pci_resource_start(dev, bar) + offset,
+					 CXL_PMU_REGMAP_SIZE, NULL);
+		if (!res) {
+			pci_err(dev, "CPMU: could not map\n");
+			continue;
+		}
+
+		base = ioremap(pci_resource_start(dev, bar) + offset,
+		       CXL_PMU_REGMAP_SIZE);
+		if (!base) {
+			pci_err(dev, "CPU: ioremap fail\n");
+			release_mem_region(res->start, resource_size(res));
+			continue;
+		}
+		if (max_irq) {
+			val = readq(base + CXL_PMU_CAP_REG);
+			if (FIELD_GET(CXL_PMU_CAP_INT, val))
+				*max_irq = max(*max_irq,
+					       (u32)FIELD_GET(CXL_PMU_CAP_MSI_N_MSK, val));
+		}
+		iounmap(base);
+		release_mem_region(res->start, resource_size(res));
+
+		if (aux_dev_list) {
+			struct pcie_port_aux_dev *pcie_adev;
+			int id;
+
+			pcie_adev = devm_kzalloc(&dev->dev, sizeof(*pcie_adev),
+						GFP_KERNEL);
+			if (!pcie_adev)
+				return -ENOMEM;
+
+			/* Cleanup handled by release after devm_pcie_port_aux_dev_init() */
+			id = ida_alloc(&pcie_cxl_pmu_ida, GFP_KERNEL);
+			if (id < 0)
+				return -ENOMEM;
+
+			pcie_adev->adev.name = "cpmu";
+			pcie_adev->adev.id = id;
+			pcie_adev->adev.dev.parent = &dev->dev;
+			pcie_adev->adev.dev.release = cpmu_adev_release;
+			pcie_adev->addr = pci_resource_start(dev, bar) + offset;
+			pcie_adev->optional = true;
+
+			ret = devm_pcie_port_aux_dev_init(&dev->dev, pcie_adev);
+			if (ret)
+				return ret;
+
+			list_add(&pcie_adev->node, aux_dev_list);
+		}
+	}
+	if (!found)
+		return -ENODEV;
+
+	return 0;
+}
diff --git a/drivers/pci/pcie/portdrv.c b/drivers/pci/pcie/portdrv.c
index 6314da76de9f..7274ee55a8c3 100644
--- a/drivers/pci/pcie/portdrv.c
+++ b/drivers/pci/pcie/portdrv.c
@@ -59,7 +59,7 @@ static void release_pcie_device(struct device *dev)
 static int pcie_message_numbers(struct pci_dev *dev, int mask,
 				u32 *pme, u32 *aer, u32 *dpc)
 {
-	u32 nvec = 0, pos;
+	u32 nvec = 0, pos, max_cpmu = 0;
 	u16 reg16;
 
 	/*
@@ -100,6 +100,9 @@ static int pcie_message_numbers(struct pci_dev *dev, int mask,
 		}
 	}
 
+	if (!pcie_cxl_pmu_get_irqs(dev, &max_cpmu, NULL))
+		nvec = max(nvec, max_cpmu + 1);
+
 	return nvec;
 }
 
@@ -278,6 +281,12 @@ static int get_port_device_capability(struct pci_dev *dev,
 			services |= PCIE_PORT_SERVICE_BWNOTIF;
 	}
 
+	if (pci_pcie_type(dev) == PCI_EXP_TYPE_UPSTREAM ||
+	    pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM ||
+	    pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT) {
+		pcie_cxl_pmu_get_irqs(dev, NULL, aux_dev_list);
+	}
+
 	return services;
 }
 
diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h
index 243a6c9e9bf1..8fe6fad7b24a 100644
--- a/drivers/pci/pcie/portdrv.h
+++ b/drivers/pci/pcie/portdrv.h
@@ -52,6 +52,17 @@ int pcie_dpc_init(void);
 static inline int pcie_dpc_init(void) { return 0; }
 #endif
 
+struct list_head;
+#ifdef CONFIG_PCIE_CXL_PMU
+int pcie_cxl_pmu_get_irqs(struct pci_dev *dev, u32 *max_irq, struct list_head *aux_dev_list);
+#else
+static inline int pcie_cxl_pmu_get_irqs(struct pci_dev *dev, u32 *max_irq,
+					struct list_head *aux_dev_list)
+{
+	return 0;
+}
+#endif
+
 struct pcie_port_aux_dev {
 	struct auxiliary_device adev;
 	u64 addr;
