cxl/pci: Find and register CXL PMU devices
authorJonathan Cameron <Jonathan.Cameron@huawei.com>
Fri, 26 May 2023 09:58:22 +0000 (10:58 +0100)
committerDan Williams <dan.j.williams@intel.com>
Tue, 30 May 2023 18:20:35 +0000 (11:20 -0700)
CXL PMU devices can be found from entries in the Register
Locator DVSEC.

Reviewed-by: Dan Williams <dan.j.williams@intel.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Link: https://lore.kernel.org/r/20230526095824.16336-4-Jonathan.Cameron@huawei.com
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
drivers/cxl/core/Makefile
drivers/cxl/core/core.h
drivers/cxl/core/pmu.c [new file with mode: 0644]
drivers/cxl/core/port.c
drivers/cxl/core/regs.c
drivers/cxl/cxl.h
drivers/cxl/cxlpci.h
drivers/cxl/pci.c
drivers/cxl/pmu.h [new file with mode: 0644]
tools/testing/cxl/Kbuild

index ca4ae31d8f57fb5bf70d64e78e677b010326e616..1f66b5d4d93556868a5d413b520882f3d94b2f6f 100644 (file)
@@ -12,5 +12,6 @@ cxl_core-y += memdev.o
 cxl_core-y += mbox.o
 cxl_core-y += pci.o
 cxl_core-y += hdm.o
+cxl_core-y += pmu.o
 cxl_core-$(CONFIG_TRACING) += trace.o
 cxl_core-$(CONFIG_CXL_REGION) += region.o
index 27f0968449de61437cd413096f3354c623c37c33..99d4a967eca6bc60393073da24910ae0feaca3b2 100644 (file)
@@ -6,6 +6,7 @@
 
 extern const struct device_type cxl_nvdimm_bridge_type;
 extern const struct device_type cxl_nvdimm_type;
+extern const struct device_type cxl_pmu_type;
 
 extern struct attribute_group cxl_base_attribute_group;
 
diff --git a/drivers/cxl/core/pmu.c b/drivers/cxl/core/pmu.c
new file mode 100644 (file)
index 0000000..7684c84
--- /dev/null
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright(c) 2023 Huawei. All rights reserved. */
+
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/idr.h>
+#include <cxlmem.h>
+#include <pmu.h>
+#include <cxl.h>
+#include "core.h"
+
+static void cxl_pmu_release(struct device *dev)
+{
+       struct cxl_pmu *pmu = to_cxl_pmu(dev);
+
+       kfree(pmu);
+}
+
+const struct device_type cxl_pmu_type = {
+       .name = "cxl_pmu",
+       .release = cxl_pmu_release,
+};
+
+static void remove_dev(void *dev)
+{
+       device_del(dev);
+}
+
+int devm_cxl_pmu_add(struct device *parent, struct cxl_pmu_regs *regs,
+                    int assoc_id, int index, enum cxl_pmu_type type)
+{
+       struct cxl_pmu *pmu;
+       struct device *dev;
+       int rc;
+
+       pmu = kzalloc(sizeof(*pmu), GFP_KERNEL);
+       if (!pmu)
+               return -ENOMEM;
+
+       pmu->assoc_id = assoc_id;
+       pmu->index = index;
+       pmu->type = type;
+       pmu->base = regs->pmu;
+       dev = &pmu->dev;
+       device_initialize(dev);
+       device_set_pm_not_required(dev);
+       dev->parent = parent;
+       dev->bus = &cxl_bus_type;
+       dev->type = &cxl_pmu_type;
+       switch (pmu->type) {
+       case CXL_PMU_MEMDEV:
+               rc = dev_set_name(dev, "pmu_mem%d.%d", assoc_id, index);
+               break;
+       }
+       if (rc)
+               goto err;
+
+       rc = device_add(dev);
+       if (rc)
+               goto err;
+
+       return devm_add_action_or_reset(parent, remove_dev, dev);
+
+err:
+       put_device(&pmu->dev);
+       return rc;
+}
+EXPORT_SYMBOL_NS_GPL(devm_cxl_pmu_add, CXL);
index e7c284c890bc111eeefd19e369e996b4b6966f3f..f8b2b174086e99384d6cde974e5261687f059d59 100644 (file)
@@ -56,6 +56,8 @@ static int cxl_device_id(const struct device *dev)
                return CXL_DEVICE_MEMORY_EXPANDER;
        if (dev->type == CXL_REGION_TYPE())
                return CXL_DEVICE_REGION;
+       if (dev->type == &cxl_pmu_type)
+               return CXL_DEVICE_PMU;
        return 0;
 }
 
index 4b9672db867d554fabe5f5068fc7b2b9b1d308a3..518bc2ad2c1ed95638835c4e6d74436e18adc691 100644 (file)
@@ -6,6 +6,7 @@
 #include <linux/pci.h>
 #include <cxlmem.h>
 #include <cxlpci.h>
+#include <pmu.h>
 
 #include "core.h"
 
@@ -379,6 +380,21 @@ int cxl_count_regblock(struct pci_dev *pdev, enum cxl_regloc_type type)
 }
 EXPORT_SYMBOL_NS_GPL(cxl_count_regblock, CXL);
 
+int cxl_map_pmu_regs(struct pci_dev *pdev, struct cxl_pmu_regs *regs,
+                    struct cxl_register_map *map)
+{
+       struct device *dev = &pdev->dev;
+       resource_size_t phys_addr;
+
+       phys_addr = map->resource;
+       regs->pmu = devm_cxl_iomap_block(dev, phys_addr, CXL_PMU_REGMAP_SIZE);
+       if (!regs->pmu)
+               return -ENOMEM;
+
+       return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cxl_map_pmu_regs, CXL);
+
 resource_size_t cxl_rcrb_to_component(struct device *dev,
                                      resource_size_t rcrb,
                                      enum cxl_rcrb which)
index 1c3d9461de1f00774b66520dce593dfcdd893ee1..496ca9be8f0b69732eafc88ce3faae8a8753b420 100644 (file)
@@ -209,6 +209,10 @@ struct cxl_regs {
        struct_group_tagged(cxl_device_regs, device_regs,
                void __iomem *status, *mbox, *memdev;
        );
+
+       struct_group_tagged(cxl_pmu_regs, pmu_regs,
+               void __iomem *pmu;
+       );
 };
 
 struct cxl_reg_map {
@@ -229,6 +233,10 @@ struct cxl_device_reg_map {
        struct cxl_reg_map memdev;
 };
 
+struct cxl_pmu_reg_map {
+       struct cxl_reg_map pmu;
+};
+
 /**
  * struct cxl_register_map - DVSEC harvested register block mapping parameters
  * @base: virtual base of the register-block-BAR + @block_offset
@@ -237,6 +245,7 @@ struct cxl_device_reg_map {
  * @reg_type: see enum cxl_regloc_type
  * @component_map: cxl_reg_map for component registers
  * @device_map: cxl_reg_maps for device registers
+ * @pmu_map: cxl_reg_maps for CXL Performance Monitoring Units
  */
 struct cxl_register_map {
        void __iomem *base;
@@ -246,6 +255,7 @@ struct cxl_register_map {
        union {
                struct cxl_component_reg_map component_map;
                struct cxl_device_reg_map device_map;
+               struct cxl_pmu_reg_map pmu_map;
        };
 };
 
@@ -258,6 +268,8 @@ int cxl_map_component_regs(struct device *dev, struct cxl_component_regs *regs,
                           unsigned long map_mask);
 int cxl_map_device_regs(struct device *dev, struct cxl_device_regs *regs,
                        struct cxl_register_map *map);
+int cxl_map_pmu_regs(struct pci_dev *pdev, struct cxl_pmu_regs *regs,
+                    struct cxl_register_map *map);
 
 enum cxl_regloc_type;
 int cxl_count_regblock(struct pci_dev *pdev, enum cxl_regloc_type type);
@@ -753,6 +765,7 @@ void cxl_driver_unregister(struct cxl_driver *cxl_drv);
 #define CXL_DEVICE_REGION              6
 #define CXL_DEVICE_PMEM_REGION         7
 #define CXL_DEVICE_DAX_REGION          8
+#define CXL_DEVICE_PMU                 9
 
 #define MODULE_ALIAS_CXL(type) MODULE_ALIAS("cxl:t" __stringify(type) "*")
 #define CXL_MODALIAS_FMT "cxl:t%d"
index 7c02e55b80429a9390d3322407701033bb6c01ce..0fa4799ea316cd802c97feda11fb6e3c88aaa138 100644 (file)
@@ -67,6 +67,7 @@ enum cxl_regloc_type {
        CXL_REGLOC_RBI_COMPONENT,
        CXL_REGLOC_RBI_VIRT,
        CXL_REGLOC_RBI_MEMDEV,
+       CXL_REGLOC_RBI_PMU,
        CXL_REGLOC_RBI_TYPES
 };
 
index 0872f2233ed0c88aa9fec7e9e93935dfa1f0e740..4c3bd20ec7c8a2c1567921f8c79cdd478f5c32f2 100644 (file)
@@ -13,6 +13,7 @@
 #include "cxlmem.h"
 #include "cxlpci.h"
 #include "cxl.h"
+#include "pmu.h"
 
 /**
  * DOC: cxl pci
@@ -657,7 +658,7 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        struct cxl_register_map map;
        struct cxl_memdev *cxlmd;
        struct cxl_dev_state *cxlds;
-       int rc;
+       int i, rc, pmu_count;
 
        /*
         * Double check the anonymous union trickery in struct cxl_regs
@@ -746,6 +747,29 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        if (IS_ERR(cxlmd))
                return PTR_ERR(cxlmd);
 
+       pmu_count = cxl_count_regblock(pdev, CXL_REGLOC_RBI_PMU);
+       for (i = 0; i < pmu_count; i++) {
+               struct cxl_pmu_regs pmu_regs;
+
+               rc = cxl_find_regblock_instance(pdev, CXL_REGLOC_RBI_PMU, &map, i);
+               if (rc) {
+                       dev_dbg(&pdev->dev, "Could not find PMU regblock\n");
+                       break;
+               }
+
+               rc = cxl_map_pmu_regs(pdev, &pmu_regs, &map);
+               if (rc) {
+                       dev_dbg(&pdev->dev, "Could not map PMU regs\n");
+                       break;
+               }
+
+               rc = devm_cxl_pmu_add(cxlds->dev, &pmu_regs, cxlmd->id, i, CXL_PMU_MEMDEV);
+               if (rc) {
+                       dev_dbg(&pdev->dev, "Could not add PMU instance\n");
+                       break;
+               }
+       }
+
        rc = cxl_event_config(host_bridge, cxlds);
        if (rc)
                return rc;
diff --git a/drivers/cxl/pmu.h b/drivers/cxl/pmu.h
new file mode 100644 (file)
index 0000000..b1e9bcd
--- /dev/null
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2023 Huawei
+ * CXL Specification rev 3.0 Setion 8.2.7 (CPMU Register Interface)
+ */
+#ifndef CXL_PMU_H
+#define CXL_PMU_H
+#include <linux/device.h>
+
+enum cxl_pmu_type {
+       CXL_PMU_MEMDEV,
+};
+
+#define CXL_PMU_REGMAP_SIZE 0xe00 /* Table 8-32 CXL 3.0 specification */
+struct cxl_pmu {
+       struct device dev;
+       void __iomem *base;
+       int assoc_id;
+       int index;
+       enum cxl_pmu_type type;
+};
+
+#define to_cxl_pmu(dev) container_of(dev, struct cxl_pmu, dev)
+struct cxl_pmu_regs;
+int devm_cxl_pmu_add(struct device *parent, struct cxl_pmu_regs *regs,
+                    int assoc_id, int idx, enum cxl_pmu_type type);
+
+#endif
index 6f9347ade82cde19fac94be0bea8736192f2ec3b..aa0ca7095a64ccbd2547db4c28cd8cabf9d61c71 100644 (file)
@@ -57,6 +57,7 @@ cxl_core-y += $(CXL_CORE_SRC)/memdev.o
 cxl_core-y += $(CXL_CORE_SRC)/mbox.o
 cxl_core-y += $(CXL_CORE_SRC)/pci.o
 cxl_core-y += $(CXL_CORE_SRC)/hdm.o
+cxl_core-y += $(CXL_CORE_SRC)/pmu.o
 cxl_core-$(CONFIG_TRACING) += $(CXL_CORE_SRC)/trace.o
 cxl_core-$(CONFIG_CXL_REGION) += $(CXL_CORE_SRC)/region.o
 cxl_core-y += config_check.o