diff mbox series

[4/6] iommu/smmu: Create child devices for PMUs

Message ID c75334977e6dd22183763d521c9c479195b4ffb9.1645106346.git.robin.murphy@arm.com (mailing list archive)
State New, archived
Headers show
Series perf: Arm SMMU PMU driver | expand

Commit Message

Robin Murphy Feb. 17, 2022, 2:24 p.m. UTC
Whilst SMMUv3 PMCGs are somewhat free-form, the SMMUv1/v2 PMU has a
well-defined and closely-coupled relationship with the main SMMU
interface. This makes it logical for the SMMU driver to be in charge of
consuming the firmware description and instantiating an abstract PMU
device directly, obviating the need for independent detection methods.

Signed-off-by: Robin Murphy <robin.murphy@arm.com>
---
 drivers/iommu/arm/arm-smmu/arm-smmu.c | 46 +++++++++++++++++++++++++++
 1 file changed, 46 insertions(+)
diff mbox series

Patch

diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu.c b/drivers/iommu/arm/arm-smmu/arm-smmu.c
index 8d6c8106fc1d..58daf81adae7 100644
--- a/drivers/iommu/arm/arm-smmu/arm-smmu.c
+++ b/drivers/iommu/arm/arm-smmu/arm-smmu.c
@@ -2070,6 +2070,45 @@  err_reset_platform_ops: __maybe_unused;
 	return err;
 }
 
+static int arm_smmu_pmu_init(struct arm_smmu_device *smmu,
+			     int irq_offset, int num_irqs)
+{
+	struct platform_device *pmu, *smmu_pdev;
+	int i, ret = -ENOMEM;
+
+	pmu = platform_device_alloc("arm-smmu-pmu", PLATFORM_DEVID_AUTO);
+	if (!pmu)
+		return -ENOMEM;
+
+	pmu->dev.parent = smmu->dev;
+	pmu->dev.platform_data = (__force void *)smmu->base + (3 << smmu->pgshift);
+	pmu->num_resources = num_irqs;
+	pmu->resource = kcalloc(num_irqs, sizeof(*pmu->resource), GFP_KERNEL);
+	if (!pmu->resource)
+		goto error;
+
+	smmu_pdev = to_platform_device(smmu->dev);
+	for (i = 0; i < num_irqs; i++) {
+		ret = platform_get_irq(smmu_pdev, irq_offset + i);
+		if (ret < 0)
+			goto error;
+
+		pmu->resource[i].start = ret;
+		pmu->resource[i].end = ret;
+		pmu->resource[i].flags = IORESOURCE_IRQ;
+	}
+
+	ret = platform_device_add(pmu);
+	if (ret)
+		goto error;
+
+	return 0;
+error:
+	pmu->dev.platform_data = NULL;
+	platform_device_put(pmu);
+	return ret;
+}
+
 static int arm_smmu_device_probe(struct platform_device *pdev)
 {
 	struct resource *res;
@@ -2216,6 +2255,13 @@  static int arm_smmu_device_probe(struct platform_device *pdev)
 			goto err_unregister_device;
 	}
 
+	/* Try the PMU last so we don't have to worry about cleaning it up */
+	if (pmu_irqs) {
+		err = arm_smmu_pmu_init(smmu, global_irqs, pmu_irqs);
+		if (err)
+			dev_warn(dev, "Failed to create PMU device: %d", err);
+	}
+
 	return 0;
 
 err_unregister_device: