[PATCH v1] perf/hx_arm_ni: Support uncore ARM NI-700 PMU

Yang Jialong 杨佳龙 jialong.yang at shingroup.cn
Tue Jan 30 18:57:25 PST 2024



在 2024/1/30 16:43, Krzysztof Kozlowski 写道:
> On 30/01/2024 09:17, JiaLong.Yang wrote:
>> This code is based on uncore PMUs arm_smmuv3_pmu and arm-cmn.
>> One ni-700 can have many clock domains. Each of them has only one PMU.
>> Here one PMU corresponds to one 'struct ni_pmu' instance.
>> PMU name will be ni_pmu_N_M, which N means different NI-700s and M means
>> different PMU in one NI-700. If only one NI-700 found in NI-700, name will
>> be ni_pmu_N.
>> Node interface event name will be xxni_N_eventname, such as asni_0_rdreq_any.
>> There are many kinds of type of nodes in one clock domain. Also means that
>> there are many kinds of that in one PMU. So we distinguish them by xxni string.
>> Besides, maybe there are many nodes have same type. So we have number N in
>> event name.
>> By ni_pmu_0_0/asni_0_rdreq_any/, we can pinpoint accurate bus traffic.
>> Example1: perf stat -a -e ni_pmu_0_0/asni_0_rdreq_any/,ni_pmu_0_0/cycles/
>> EXample2: perf stat -a -e ni_pmu_0_0/asni,id=0,event=0x0/
>>
>> Signed-off-by: JiaLong.Yang <jialong.yang at shingroup.cn>
>> ---
>> If I should send Doc*/bindings/perf/*.yaml seperately?
> 
> Checkpatch tells you that, doesn't it?
OK. I will send it seperately.
> 
> Please run scripts/checkpatch.pl and fix reported warnings. Some
> warnings can be ignored, but the code here looks like it needs a fix.
> Feel free to get in touch if the warning is not clear.
OK.
> 
>>
>>   .../bindings/perf/hx,c2000-arm-ni.yaml        |   58 +
>>   .../devicetree/bindings/vendor-prefixes.yaml  |    2 +
>>   MAINTAINERS                                   |    6 +
>>   drivers/perf/Kconfig                          |   10 +
>>   drivers/perf/Makefile                         |    1 +
>>   drivers/perf/hx_arm_ni.c                      | 1308 +++++++++++++++++
>>   6 files changed, 1385 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/perf/hx,c2000-arm-ni.yaml
>>   create mode 100644 drivers/perf/hx_arm_ni.c
>>
>> diff --git a/Documentation/devicetree/bindings/perf/hx,c2000-arm-ni.yaml b/Documentation/devicetree/bindings/perf/hx,c2000-arm-ni.yaml
>> new file mode 100644
>> index 000000000000..1b145ecbfa83
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/perf/hx,c2000-arm-ni.yaml
>> @@ -0,0 +1,58 @@
>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/perf/hx,c2000-arm-ni.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: HX-C2000 NI (Network-on_chip Interconnect) Performance Monitors
>> +
>> +maintainers:
>> +  - Jialong Yang <jialong.yang at shingroup.cn>
>> +
>> +properties:
>> +  compatible:
>> +    enum:
>> +      - hx,c2000-arm-ni
>> +
>> +  reg:
>> +    items:
>> +      - description: Physical address of the base (PERIPHBASE) and
>> +          size of the whole NI configuration address space.
>> +
>> +  interrupts:
>> +    minItems: 1
> 
> Why?
According to hw, one PMU has one interrupt line. So one NI maybe has 
more than one. But actually it depends on hw implementation.
And in C code, I will return error when there is no interrupt.
> 
>> +    items:
>> +      - description: Overflow interrupt for clock domain 0
>> +      - description: Overflow interrupt for clock domain 1
>> +      - description: Overflow interrupt for clock domain 2
>> +    description: Generally, one interrupt line for one PMU. But this also
>> +      support one interrupt line for a NI if merged.
>> +
>> +required:
>> +  - compatible
>> +  - reg
>> +  - interrupts
>> +
>> +if:
>> +  properties:
>> +    compatible:
>> +      contains:
>> +        const: hx,c2000-arm-ni
> 
> Drop entire if. What is the point of it?
This attribute is used to identify different NI in my company's product.
But even if I don't give this attribute, nothing will be wrong in code.
However if I do that, I will couldn't know the relation between sysfs 
file and hardware NI.

I will drop it. It will be as a normal way to identify NIs manually.
If there is only one NI and not give pccs-id, no thing wrong will happen.
> 
>> +then:
>> +  required:
>> +    - pccs-id
>> +
>> +additionalProperties: false
>> +
>> +examples:
>> +  - |
>> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
>> +    #include <dt-bindings/interrupt-controller/irq.h>
>> +
>> +    ni-pmu at 23ff0000 {
> 
> Node names should be generic. See also an explanation and list of
> examples (not exhaustive) in DT specification:
> https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#generic-names-recommendation
OK.
> 
> 
>> +            compatible = "hx,c2000-arm-ni";
>> +	    #address-cells = <1>;
>> +	    #size-cells = <1>;
> 
> Broken indentation.....
> 
> 
> (...)
> 
>> +		ni_pmu->dev = dev;
>> +		ni_pmu->ni = ni;
>> +		ni->ni_pmus[cd_idx] = ni_pmu;
>> +	}
>> +
>> +	devm_kfree(dev, cd_arrays);
>> +
>> +	pr_info("End discovering hardware registers.");
> 
> That's rather useless print. First entire driver must use dev_*. Second,
> drop it, no need for end-of-function prints.
OK.
> 
>> +
>> +	return 0;
>> +}
>> +
>> +static int ni_pmu_probe(struct platform_device *pdev)
>> +{
>> +	int ret, cd_num, idx, irq_num, irq_idx;
>> +	void __iomem *periphbase;
>> +	struct global_ni *ni;
>> +	struct device *dev = &pdev->dev;
>> +	char *name;
>> +	static int id;
>> +	struct ni_pmu *ni_pmu;
>> +
>> +	BUILD_BUG_ON(sizeof(long) == 4);
> 
> I am sorry, but what?
I only want to ensure 64 bit environment. Maybe there are many other way.
I will ensure that in Kconfig.
> 
>> +	BUILD_BUG_ON(sizeof(struct ni_hw_perf_event) >
>> +		     offsetof(struct hw_perf_event, target));
>> +#define NI_PMU_REG_MAP_SIZE 0xE08
>> +	BUILD_BUG_ON(sizeof(struct ni_pmu_reg_map) != NI_PMU_REG_MAP_SIZE);
>> +
>> +	pr_info("Begin probing.");
> 
> NAK, drop. Drop all silly entrance/exit messages. EVERYWHERE.
OK.
> 
>> +
>> +	periphbase = devm_platform_ioremap_resource(pdev, 0);
>> +
> 
> Drop blank line
OK.
> 
>> +	if (IS_ERR(periphbase)) {
>> +		pr_err("%s: ioremap error.", __func__);
> 
> dev_err and you need to print something useful, like error code. Or use
> return dev_err_probe
OK.
> 
>> +		return PTR_ERR(periphbase);
>> +	}
>> +
>> +	cd_num = ni_child_number_total(periphbase, periphbase, NI_CD);
>> +	pr_info("%d clock domains found in NI-700.", cd_num);
> 
> Really, what's with all this printing?
I just want to print how many clock domains.
One clock domain correspond to one PMU.
> 
>> +
>> +	/* Each clock domain contains one PMU. So cd_num == pmu_num. */
>> +	ni = devm_kzalloc(dev,
>> +			  struct_size(ni, ni_pmus, cd_num),
>> +			  GFP_KERNEL);
>> +	if (!ni)
>> +		return -ENOMEM;
>> +
>> +	ni->cd_num = cd_num;
>> +	ni->base = periphbase;
>> +	ni->dev = dev;
>> +	ni->on_cpu = raw_smp_processor_id();
>> +	platform_set_drvdata(pdev, ni);
>> +
>> +	ret = ni_discovery(ni);
>> +	if (ret) {
>> +		pr_err("%s: discovery error.", __func__);
>> +		return ret;
>> +	}
>> +
>> +	irq_num = platform_irq_count(pdev);
>> +	/* Support that one NI with one irq or one clock domain with one irq. */
>> +	if (irq_num < 0 || (irq_num != 1 && irq_num != ni->cd_num)) {
>> +		pr_err("Error in irq number: %d.", irq_num);
>> +		return -EINVAL;
>> +	}
>> +
>> +	if (irq_num != cd_num) {
>> +		pr_warn("Only one IRQ found for all PMU.");
>> +		ret = ni_pmu_irq_setup(ni->ni_pmus[0], 0);
>> +		if (ret)
>> +			return ret;
>> +	}
>> +
>> +	ni->irq_num = irq_num;
>> +
>> +	for (idx = 0, irq_idx = 0; idx < ni->pmu_num; idx++) {
>> +		ni_pmu = ni->ni_pmus[idx];
>> +		ret = ni_pmu_init_attr_groups(ni_pmu);
>> +		if (ret)
>> +			return ret;
>> +
>> +		if (irq_num == cd_num) {
>> +			ret = ni_pmu_irq_setup(ni_pmu, irq_idx++);
>> +			if (ret)
>> +				return ret;
>> +		}
>> +
>> +		ni_pmu_reset(ni_pmu);
>> +
>> +		ni_pmu->pmu = (struct pmu) {
>> +			.module		= THIS_MODULE,
>> +			.task_ctx_nr    = perf_invalid_context,
>> +			.pmu_enable	= ni_pmu_enable,
>> +			.pmu_disable	= ni_pmu_disable,
>> +			.event_init	= ni_pmu_event_init,
>> +			.add		= ni_pmu_event_add,
>> +			.del		= ni_pmu_event_del,
>> +			.start		= ni_pmu_event_start,
>> +			.stop		= ni_pmu_event_stop,
>> +			.read		= ni_pmu_event_read,
>> +			.attr_groups    = ni_pmu->pmu.attr_groups,
>> +			.capabilities	= PERF_PMU_CAP_NO_EXCLUDE | PERF_PMU_CAP_NO_INTERRUPT,
>> +		};
>> +
>> +#ifdef CONFIG_PPC_HX_C2000
>> +		if (of_property_read_u32(pdev->dev.of_node, "pccs-id", &id))
>> +			pr_warn("No found pccs_id in dts ni pmu node.");
>> +#endif
>> +		if (cd_num > 1)
>> +			name = devm_kasprintf(dev, GFP_KERNEL, "ni_pmu_%d_%d", id++, idx);
>> +		else
>> +			name = devm_kasprintf(dev, GFP_KERNEL, "ni_pmu_%d", id++);
>> +
>> +		ret = perf_pmu_register(&ni_pmu->pmu, name, -1);
>> +		if (ret) {
>> +			pr_err("Error %d_%d registering PMU", id - 1, idx);
>> +			return ret;
>> +		}
>> +	}
>> +
>> +	ret = cpuhp_state_add_instance_nocalls(ni_hp_state,
>> +					       &ni->node);
>> +	if (ret)
>> +		return ret;
>> +	pr_info("End probing.");
> 
> No, drop.
> 
>> +
>> +	return 0;
>> +}
>> +
>> +static int ni_pmu_remove(struct platform_device *pdev)
>> +{
>> +	struct global_ni *ni = platform_get_drvdata(pdev);
>> +	int idx;
>> +
>> +	for (idx = 0; idx < ni->pmu_num; idx++)
>> +		perf_pmu_unregister(&ni->ni_pmus[idx]->pmu);
>> +
>> +	cpuhp_remove_multi_state(ni_hp_state);
>> +	return 0;
>> +}
>> +
>> +static const struct of_device_id ni_pmu_of_match[] = {
>> +	{ .compatible = "hx,c2000-arm-ni" },
>> +	{},
>> +};
>> +
>> +static struct platform_driver ni_pmu_driver = {
>> +	.driver = {
>> +		.name = "ni-pmu",
>> +		.of_match_table = of_match_ptr(ni_pmu_of_match),
> 
> Drop of_match_ptr. Leads to warnings.
OK.
> 
> 
> ...
> 
>> +
>> +module_init(ni_pmu_init);
>> +module_exit(ni_pmu_exit);
>> +/* PLATFORM END */
> 
> Useless comment. Please clean your code from debugging or useless markers.
Done.
> 
> 
> 
> Best regards,
> Krzysztof
> 
> 



More information about the linux-arm-kernel mailing list