@@ -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:
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(+)