diff --git a/drivers/perf/arm-cmn.c b/drivers/perf/arm-cmn.c index 4aee76b5a..54a61276b 100644 --- a/drivers/perf/arm-cmn.c +++ b/drivers/perf/arm-cmn.c @@ -104,7 +104,6 @@ //TODO: Is it worth probing dt_dbg_id.num_pmucntr? #define CMN_DT_NUM_COUNTERS 8 - enum cmn_node_type { CMN_TYPE_INVALID, CMN_TYPE_DVM, @@ -126,6 +125,7 @@ enum cmn_node_type { struct arm_cmn { struct device *dev; void __iomem *base; + void __iomem *dtc_base; u8 mesh_x; u8 mesh_y; @@ -170,6 +170,9 @@ struct arm_cmn_dtc { #define to_cmn_dtc(x) container_of(x, struct arm_cmn_dtc, pmu) +/* Keep track of our dynamic hotplug state */ +static enum cpuhp_state arm_cmn_cpuhp_state; + struct arm_cmn_event_attr { struct device_attribute attr; enum cmn_node_type type; @@ -765,20 +768,28 @@ static irqreturn_t arm_cmn_irq_handler(int irq, void *dev_id) return res; } -static int arm_cmn_init_pmu(struct arm_cmn *cmn, void __iomem *base, int id) +static int arm_cmn_init_pmu(struct arm_cmn *cmn) { struct platform_device *pdev = to_platform_device(cmn->dev); struct arm_cmn_dtc *dtc; + unsigned long long value; + acpi_handle handle; + acpi_status status; const char *name; int irq, err; + if (!cmn->dtc_base) { + dev_err(cmn->dev, "no DTC found\n"); + return -ENODEV; + } + dtc = devm_kzalloc(cmn->dev, sizeof(*dtc), GFP_KERNEL); if (!dtc) return -ENOMEM; - irq = platform_get_irq(pdev, id); + irq = platform_get_irq(pdev, 0); if (irq <= 0) { - dev_err(cmn->dev, "missing IRQ for DTC %d\n", id); + dev_err(cmn->dev, "missing IRQ for DTC\n"); return -EINVAL; } @@ -788,8 +799,10 @@ static int arm_cmn_init_pmu(struct arm_cmn *cmn, void __iomem *base, int id) if (err) return err; + platform_set_drvdata(pdev, dtc); + dtc->cmn = cmn; - dtc->base = base; + dtc->base = cmn->dtc_base; dtc->cpu = get_cpu(); dtc->pmu = (struct pmu) { .attr_groups = arm_cmn_attr_groups, @@ -804,21 +817,35 @@ static int arm_cmn_init_pmu(struct arm_cmn *cmn, void __iomem *base, int id) .read = arm_cmn_event_read, }; - if (id == 0) { - name = "arm_cmn"; + handle = ACPI_HANDLE(cmn->dev); + if (handle) { + status = acpi_evaluate_integer(handle, METHOD_NAME__UID, NULL, + &value); + if (ACPI_FAILURE(status)) { + dev_err(cmn->dev, + "Failed to evaluate _UID (0x%x)\n", status); + return -ENODEV; + } + + name = devm_kasprintf(cmn->dev, GFP_KERNEL, "arm_cmn_%d", + (unsigned int)value); } else { - name = devm_kasprintf(cmn->dev, GFP_KERNEL, "arm_cmn_%d", id); - if (!name) - return -ENOMEM; + /* FIXME: multiple instance if no ACPI? */ + name = "arm_cmn"; } cpuhp_state_add_instance_nocalls(CPUHP_AP_PERF_ARM_CMN_ONLINE, &dtc->cpuhp_node); put_cpu(); - writel_relaxed(CMN_DT_PMCR_PMU_EN, base + CMN_DT_PMCR); + writel_relaxed(CMN_DT_PMCR_PMU_EN, dtc->base + CMN_DT_PMCR); + + err = perf_pmu_register(&dtc->pmu, name, -1); + if (err) + cpuhp_state_remove_instance(arm_cmn_cpuhp_state, + &dtc->cpuhp_node); - return perf_pmu_register(&dtc->pmu, name, -1); + return err; } static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset, int lvl) @@ -884,8 +911,8 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset, int lv "multiple DTCs not supported; events outside domain 0 will not be counted correctly\n"); return 0; } - // FIXME: node_logid is apparently not a nice simple index - return arm_cmn_init_pmu(cmn, region, node_logid); + cmn->dtc_base = region; + return 0; /* These guys have PMU events */ case CMN_TYPE_DVM: case CMN_TYPE_HNI: @@ -895,6 +922,10 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset, int lv case CMN_TYPE_RND: case CMN_TYPE_CXRA: case CMN_TYPE_CXHA: + if (node_type == CMN_TYPE_RND) { + /* RND uses the same event type with RNI */ + node_type = CMN_TYPE_RNI; + } node = &cmn->dns[cmn->num_dns++]; break; /* Nothing to see here */ @@ -1030,7 +1061,6 @@ static int arm_cmn_probe(struct platform_device *pdev) return -ENOMEM; cmn->dev = &pdev->dev; - platform_set_drvdata(pdev, cmn); if (has_acpi_companion(cmn->dev)) rootnode = arm_cmn_acpi_probe(pdev, cmn); @@ -1045,17 +1075,16 @@ static int arm_cmn_probe(struct platform_device *pdev) arm_cmn_mesh_fixup(cmn); - return cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_CMN_ONLINE, - "perf/arm/cmn:online", NULL, - arm_cmn_pmu_offline_cpu); + return arm_cmn_init_pmu(cmn); } static int arm_cmn_remove(struct platform_device *pdev) { - //TODO: What's the neatest way to find the DTCs and clean them up? - - cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_CMN_ONLINE); + struct arm_cmn_dtc *dtc = platform_get_drvdata(pdev); + //TODO: What's the neatest way to find the DTCs and clean them up? + cpuhp_state_remove_instance(arm_cmn_cpuhp_state, + &dtc->cpuhp_node); return 0; } @@ -1082,7 +1111,34 @@ static struct platform_driver arm_cmn_driver = { .probe = arm_cmn_probe, .remove = arm_cmn_remove, }; -module_platform_driver(arm_cmn_driver); + +static int __init arm_cmn_init(void) +{ + int ret; + + ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_CMN_ONLINE, + "perf/arm/cmn:online", NULL, + arm_cmn_pmu_offline_cpu); + if (ret < 0) + return ret; + + arm_cmn_cpuhp_state = ret; + + ret = platform_driver_register(&arm_cmn_driver); + if (ret) + cpuhp_remove_multi_state(arm_cmn_cpuhp_state); + + return ret; +} + +static void __exit arm_cmn_exit(void) +{ + platform_driver_unregister(&arm_cmn_driver); + cpuhp_remove_multi_state(arm_cmn_cpuhp_state); +} + +module_init(arm_cmn_init); +module_exit(arm_cmn_exit); MODULE_AUTHOR("Robin Murphy "); MODULE_DESCRIPTION("Arm CMN-600 PMU driver");