@@ -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 += cpmu.o
cxl_core-$(CONFIG_TRACING) += trace.o
cxl_core-$(CONFIG_CXL_REGION) += region.o
@@ -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_cpmu_type;
extern struct attribute_group cxl_base_attribute_group;
new file mode 100644
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright(c) 2022 Huawei. All rights reserved. */
+
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/idr.h>
+#include <cxlmem.h>
+#include <cpmu.h>
+#include <cxl.h>
+#include "core.h"
+
+static DEFINE_IDA(cpmu_ida);
+
+static void cxl_cpmu_release(struct device *dev)
+{
+ struct cxl_cpmu *cpmu = container_of(dev, struct cxl_cpmu, dev);
+
+ ida_free(&cpmu_ida, cpmu->id);
+ kfree(cpmu);
+}
+
+const struct device_type cxl_cpmu_type = {
+ .name = "cxl_cpmu",
+ .release = cxl_cpmu_release,
+};
+
+static void remove_dev(void *dev)
+{
+ device_del(dev);
+}
+
+int devm_cxl_cpmu_add(struct device *parent, struct cxl_cpmu_regs *regs, int index)
+{
+ struct cxl_cpmu *cpmu;
+ struct device *dev;
+ int rc;
+
+ cpmu = kzalloc(sizeof(*cpmu), GFP_KERNEL);
+ if (!cpmu)
+ return -ENOMEM;
+
+ rc = ida_alloc(&cpmu_ida, GFP_KERNEL);
+ if (rc < 0) {
+ kfree(cpmu);
+ return rc;
+ }
+ cpmu->id = rc;
+
+ cpmu->base = regs->cpmu;
+ dev = &cpmu->dev;
+ device_initialize(dev);
+ device_set_pm_not_required(dev);
+ dev->parent = parent;
+ dev->bus = &cxl_bus_type;
+ dev->type = &cxl_cpmu_type;
+
+ rc = dev_set_name(dev, "cpmu%d", cpmu->id);
+ 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(&cpmu->dev);
+ return rc;
+}
+EXPORT_SYMBOL_NS_GPL(devm_cxl_cpmu_add, CXL);
+
@@ -57,6 +57,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_cpmu_type)
+ return CXL_DEVICE_CPMU;
return 0;
}
@@ -6,6 +6,7 @@
#include <linux/pci.h>
#include <cxlmem.h>
#include <cxlpci.h>
+#include <cpmu.h>
#include "core.h"
@@ -360,6 +361,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_cpmu_regs(struct pci_dev *pdev, struct cxl_cpmu_regs *regs,
+ struct cxl_register_map *map)
+{
+ struct device *dev = &pdev->dev;
+ resource_size_t phys_addr;
+
+ phys_addr = map->resource;
+ regs->cpmu = devm_cxl_iomap_block(dev, phys_addr, CPMU_REGMAP_SIZE);
+ if (!regs->cpmu)
+ return -ENOMEM;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cxl_map_cpmu_regs, CXL);
+
resource_size_t cxl_rcrb_to_component(struct device *dev,
resource_size_t rcrb,
enum cxl_rcrb which)
new file mode 100644
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2022 Huawei
+ * CXL Specification rev 3.0 Setion 8.2.7 (CPMU Register Interface)
+ */
+#ifndef CXL_CPMU_H
+#define CXL_CPMU_H
+
+#define CPMU_CAP_REG 0x0
+#define CPMU_CAP_NUM_COUNTERS_MSK GENMASK_ULL(4, 0)
+#define CPMU_CAP_COUNTER_WIDTH_MSK GENMASK_ULL(15, 8)
+#define CPMU_CAP_NUM_EVN_CAP_REG_SUP_MSK GENMASK_ULL(24, 20)
+#define CPMU_CAP_FILTERS_SUP_MSK GENMASK_ULL(39, 32)
+#define CPMU_FILTER_HDM BIT(0)
+#define CPMU_FILTER_CHAN_RANK_BANK BIT(1)
+#define CPMU_CAP_MSI_N_MSK GENMASK_ULL(47, 44)
+#define CPMU_CAP_WRITEABLE_WHEN_FROZEN BIT_ULL(48)
+#define CPMU_CAP_FREEZE BIT_ULL(49)
+#define CPMU_CAP_INT BIT_ULL(50)
+#define CPMU_CAP_VERSION_MSK GENMASK_ULL(63, 60)
+
+#define CPMU_OVERFLOW_REG 0x10
+#define CPMU_FREEZE_REG 0x18
+#define CPMU_EVENT_CAP_REG(n) (0x100 + 8 * (n))
+#define CPMU_EVENT_CAP_SUPPORTED_EVENTS_MSK GENMASK_ULL(31, 0)
+#define CPMU_EVENT_CAP_GROUP_ID_MSK GENMASK_ULL(47, 32)
+#define CPMU_EVENT_CAP_VENDOR_ID_MSK GENMASK_ULL(63, 48)
+
+#define CPMU_COUNTER_CFG_REG(n) (0x200 + 8 * (n))
+#define CPMU_COUNTER_CFG_TYPE_MSK GENMASK_ULL(1, 0)
+#define CPMU_COUNTER_CFG_TYPE_FREE_RUN 0
+#define CPMU_COUNTER_CFG_TYPE_FIXED_FUN 1
+#define CPMU_COUNTER_CFG_TYPE_CONFIGURABLE 2
+#define CPMU_COUNTER_CFG_ENABLE BIT_ULL(8)
+#define CPMU_COUNTER_CFG_INT_ON_OVRFLW BIT_ULL(9)
+#define CPMU_COUNTER_CFG_FREEZE_ON_OVRFLW BIT_ULL(10)
+#define CPMU_COUNTER_CFG_EDGE BIT_ULL(11)
+#define CPMU_COUNTER_CFG_INVERT BIT_ULL(12)
+#define CPMU_COUNTER_CFG_THRESHOLD_MSK GENMASK_ULL(23, 16)
+#define CPMU_COUNTER_CFG_EVENTS_MSK GENMASK_ULL(55, 24)
+#define CPMU_COUNTER_CFG_EVENT_GRP_ID_IDX_MSK GENMASK_ULL(63, 59)
+
+#define CPMU_FILTER_CFG_REG(n, f) (0x400 + 4 * ((f) + (n) * 8))
+#define CPMU_FILTER_CFG_VALUE_MSK GENMASK(15, 0)
+
+#define CPMU_COUNTER_REG(n) (0xc00 + 8 * (n))
+
+#define CPMU_REGMAP_SIZE 0xe00 /* Table 8-32 CXL 3.0 specification */
+struct cxl_cpmu {
+ struct device dev;
+ void __iomem *base;
+ int id;
+};
+
+#define to_cxl_cpmu(dev) container_of(dev, struct cxl_cpmu, dev)
+#endif
@@ -209,6 +209,10 @@ struct cxl_regs {
struct_group_tagged(cxl_device_regs, device_regs,
void __iomem *status, *mbox, *memdev;
);
+
+ struct_group_tagged(cxl_cpmu_regs, cpmu_regs,
+ void __iomem *cpmu;
+ );
};
struct cxl_reg_map {
@@ -229,6 +233,10 @@ struct cxl_device_reg_map {
struct cxl_reg_map memdev;
};
+struct cxl_cpmu_reg_map {
+ struct cxl_reg_map cpmu;
+};
+
/**
* 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
+ * @cpmu_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_cpmu_reg_map cpmu_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_cpmu_regs(struct pci_dev *pdev, struct cxl_cpmu_regs *regs,
+ struct cxl_register_map *map);
enum cxl_regloc_type;
int cxl_count_regblock(struct pci_dev *pdev, enum cxl_regloc_type type);
@@ -750,6 +762,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_CPMU 9
#define MODULE_ALIAS_CXL(type) MODULE_ALIAS("cxl:t" __stringify(type) "*")
#define CXL_MODALIAS_FMT "cxl:t%d"
@@ -789,6 +802,7 @@ static inline struct cxl_dax_region *to_cxl_dax_region(struct device *dev)
}
#endif
+int devm_cxl_cpmu_add(struct device *parent, struct cxl_cpmu_regs *regs, int idx);
/*
* Unit test builds overrides this to __weak, find the 'strong' version
* of these symbols in tools/testing/cxl/.
@@ -65,6 +65,7 @@ enum cxl_regloc_type {
CXL_REGLOC_RBI_COMPONENT,
CXL_REGLOC_RBI_VIRT,
CXL_REGLOC_RBI_MEMDEV,
+ CXL_REGLOC_RBI_CPMU,
CXL_REGLOC_RBI_TYPES
};
@@ -704,7 +704,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, cpmu_count;
/*
* Double check the anonymous union trickery in struct cxl_regs
@@ -781,6 +781,29 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (rc)
return rc;
+ cpmu_count = cxl_count_regblock(pdev, CXL_REGLOC_RBI_CPMU);
+ for (i = 0; i < cpmu_count; i++) {
+ struct cxl_cpmu_regs cpmu_regs;
+
+ rc = cxl_find_regblock(pdev, CXL_REGLOC_RBI_CPMU, &map, i);
+ if (rc) {
+ dev_dbg(&pdev->dev, "Could not find CPMU regblock\n");
+ break;
+ }
+
+ rc = cxl_map_cpmu_regs(pdev, &cpmu_regs, &map);
+ if (rc) {
+ dev_dbg(&pdev->dev, "Could not map CPMU regs\n");
+ break;
+ }
+
+ rc = devm_cxl_cpmu_add(cxlds->dev, &cpmu_regs, i);
+ if (rc) {
+ dev_dbg(&pdev->dev, "Could not add CPMU instance\n");
+ break;
+ }
+ }
+
cxlmd = devm_cxl_add_memdev(cxlds);
if (IS_ERR(cxlmd))
return PTR_ERR(cxlmd);