1 // SPDX-License-Identifier: GPL-2.0
3 * Marvell PEM(PCIe RC) Performance Monitor Driver
5 * Copyright (C) 2024 Marvell.
8 #include <linux/acpi.h>
9 #include <linux/init.h>
11 #include <linux/module.h>
12 #include <linux/perf_event.h>
13 #include <linux/platform_device.h>
16 * Each of these events maps to a free running 64 bit counter
17 * with no event control, but can be reset.
50 static u64 eventid_to_offset_table
[] = {
54 [IB_TLP_DWORDS_NPR
] = 0x100,
55 [IB_TLP_DWORDS_PR
] = 0x108,
56 [IB_TLP_DWORDS_CPL
] = 0x110,
57 [IB_INFLIGHT
] = 0x200,
59 [IB_REQ_NO_RO_NCB
] = 0x400,
60 [IB_REQ_NO_RO_EBUS
] = 0x408,
64 [OB_TLP_DWORDS_NPR
] = 0x600,
65 [OB_TLP_DWORDS_PR
] = 0x608,
66 [OB_TLP_DWORDS_CPL
] = 0x610,
67 [OB_INFLIGHT
] = 0x700,
69 [OB_MERGES_NPR
] = 0x900,
70 [OB_MERGES_PR
] = 0x908,
71 [OB_MERGES_CPL
] = 0x910,
73 [ATS_TRANS_LATENCY
] = 0x2D20,
75 [ATS_PRI_LATENCY
] = 0x2D30,
77 [ATS_INV_LATENCY
] = 0x2D40,
85 struct hlist_node node
;
88 #define to_pem_pmu(p) container_of(p, struct pem_pmu, pmu)
90 static int eventid_to_offset(int eventid
)
92 return eventid_to_offset_table
[eventid
];
96 static ssize_t
pem_pmu_event_show(struct device
*dev
,
97 struct device_attribute
*attr
,
100 struct perf_pmu_events_attr
*pmu_attr
;
102 pmu_attr
= container_of(attr
, struct perf_pmu_events_attr
, attr
);
103 return sysfs_emit(page
, "event=0x%02llx\n", pmu_attr
->id
);
106 #define PEM_EVENT_ATTR(_name, _id) \
107 (&((struct perf_pmu_events_attr[]) { \
108 { .attr = __ATTR(_name, 0444, pem_pmu_event_show, NULL), \
112 static struct attribute
*pem_perf_events_attrs
[] = {
113 PEM_EVENT_ATTR(ib_tlp_npr
, IB_TLP_NPR
),
114 PEM_EVENT_ATTR(ib_tlp_pr
, IB_TLP_PR
),
115 PEM_EVENT_ATTR(ib_tlp_cpl_partid
, IB_TLP_CPL
),
116 PEM_EVENT_ATTR(ib_tlp_dwords_npr
, IB_TLP_DWORDS_NPR
),
117 PEM_EVENT_ATTR(ib_tlp_dwords_pr
, IB_TLP_DWORDS_PR
),
118 PEM_EVENT_ATTR(ib_tlp_dwords_cpl_partid
, IB_TLP_DWORDS_CPL
),
119 PEM_EVENT_ATTR(ib_inflight
, IB_INFLIGHT
),
120 PEM_EVENT_ATTR(ib_reads
, IB_READS
),
121 PEM_EVENT_ATTR(ib_req_no_ro_ncb
, IB_REQ_NO_RO_NCB
),
122 PEM_EVENT_ATTR(ib_req_no_ro_ebus
, IB_REQ_NO_RO_EBUS
),
123 PEM_EVENT_ATTR(ob_tlp_npr_partid
, OB_TLP_NPR
),
124 PEM_EVENT_ATTR(ob_tlp_pr_partid
, OB_TLP_PR
),
125 PEM_EVENT_ATTR(ob_tlp_cpl_partid
, OB_TLP_CPL
),
126 PEM_EVENT_ATTR(ob_tlp_dwords_npr_partid
, OB_TLP_DWORDS_NPR
),
127 PEM_EVENT_ATTR(ob_tlp_dwords_pr_partid
, OB_TLP_DWORDS_PR
),
128 PEM_EVENT_ATTR(ob_tlp_dwords_cpl_partid
, OB_TLP_DWORDS_CPL
),
129 PEM_EVENT_ATTR(ob_inflight_partid
, OB_INFLIGHT
),
130 PEM_EVENT_ATTR(ob_reads_partid
, OB_READS
),
131 PEM_EVENT_ATTR(ob_merges_npr_partid
, OB_MERGES_NPR
),
132 PEM_EVENT_ATTR(ob_merges_pr_partid
, OB_MERGES_PR
),
133 PEM_EVENT_ATTR(ob_merges_cpl_partid
, OB_MERGES_CPL
),
134 PEM_EVENT_ATTR(ats_trans
, ATS_TRANS
),
135 PEM_EVENT_ATTR(ats_trans_latency
, ATS_TRANS_LATENCY
),
136 PEM_EVENT_ATTR(ats_pri
, ATS_PRI
),
137 PEM_EVENT_ATTR(ats_pri_latency
, ATS_PRI_LATENCY
),
138 PEM_EVENT_ATTR(ats_inv
, ATS_INV
),
139 PEM_EVENT_ATTR(ats_inv_latency
, ATS_INV_LATENCY
),
143 static struct attribute_group pem_perf_events_attr_group
= {
145 .attrs
= pem_perf_events_attrs
,
148 PMU_FORMAT_ATTR(event
, "config:0-5");
150 static struct attribute
*pem_perf_format_attrs
[] = {
151 &format_attr_event
.attr
,
155 static struct attribute_group pem_perf_format_attr_group
= {
157 .attrs
= pem_perf_format_attrs
,
161 static ssize_t
pem_perf_cpumask_show(struct device
*dev
,
162 struct device_attribute
*attr
,
165 struct pem_pmu
*pmu
= dev_get_drvdata(dev
);
167 return cpumap_print_to_pagebuf(true, buf
, cpumask_of(pmu
->cpu
));
170 static struct device_attribute pem_perf_cpumask_attr
=
171 __ATTR(cpumask
, 0444, pem_perf_cpumask_show
, NULL
);
173 static struct attribute
*pem_perf_cpumask_attrs
[] = {
174 &pem_perf_cpumask_attr
.attr
,
178 static struct attribute_group pem_perf_cpumask_attr_group
= {
179 .attrs
= pem_perf_cpumask_attrs
,
182 static const struct attribute_group
*pem_perf_attr_groups
[] = {
183 &pem_perf_events_attr_group
,
184 &pem_perf_cpumask_attr_group
,
185 &pem_perf_format_attr_group
,
189 static int pem_perf_event_init(struct perf_event
*event
)
191 struct pem_pmu
*pmu
= to_pem_pmu(event
->pmu
);
192 struct hw_perf_event
*hwc
= &event
->hw
;
193 struct perf_event
*sibling
;
195 if (event
->attr
.type
!= event
->pmu
->type
)
198 if (event
->attr
.config
>= PEM_EVENTIDS_MAX
)
201 if (is_sampling_event(event
) ||
202 event
->attach_state
& PERF_ATTACH_TASK
) {
209 /* We must NOT create groups containing mixed PMUs */
210 if (event
->group_leader
->pmu
!= event
->pmu
&&
211 !is_software_event(event
->group_leader
))
214 for_each_sibling_event(sibling
, event
->group_leader
) {
215 if (sibling
->pmu
!= event
->pmu
&&
216 !is_software_event(sibling
))
220 * Set ownership of event to one CPU, same event can not be observed
221 * on multiple cpus at same time.
223 event
->cpu
= pmu
->cpu
;
228 static u64
pem_perf_read_counter(struct pem_pmu
*pmu
,
229 struct perf_event
*event
, int eventid
)
231 return readq_relaxed(pmu
->base
+ eventid_to_offset(eventid
));
234 static void pem_perf_event_update(struct perf_event
*event
)
236 struct pem_pmu
*pmu
= to_pem_pmu(event
->pmu
);
237 struct hw_perf_event
*hwc
= &event
->hw
;
238 u64 prev_count
, new_count
;
241 prev_count
= local64_read(&hwc
->prev_count
);
242 new_count
= pem_perf_read_counter(pmu
, event
, hwc
->idx
);
243 } while (local64_xchg(&hwc
->prev_count
, new_count
) != prev_count
);
245 local64_add((new_count
- prev_count
), &event
->count
);
248 static void pem_perf_event_start(struct perf_event
*event
, int flags
)
250 struct pem_pmu
*pmu
= to_pem_pmu(event
->pmu
);
251 struct hw_perf_event
*hwc
= &event
->hw
;
252 int eventid
= hwc
->idx
;
255 * All counters are free-running and associated with
256 * a fixed event to track in Hardware
258 local64_set(&hwc
->prev_count
,
259 pem_perf_read_counter(pmu
, event
, eventid
));
264 static int pem_perf_event_add(struct perf_event
*event
, int flags
)
266 struct hw_perf_event
*hwc
= &event
->hw
;
268 hwc
->idx
= event
->attr
.config
;
269 if (WARN_ON_ONCE(hwc
->idx
>= PEM_EVENTIDS_MAX
))
271 hwc
->state
|= PERF_HES_STOPPED
;
273 if (flags
& PERF_EF_START
)
274 pem_perf_event_start(event
, flags
);
279 static void pem_perf_event_stop(struct perf_event
*event
, int flags
)
281 struct hw_perf_event
*hwc
= &event
->hw
;
283 if (flags
& PERF_EF_UPDATE
)
284 pem_perf_event_update(event
);
286 hwc
->state
|= PERF_HES_STOPPED
;
289 static void pem_perf_event_del(struct perf_event
*event
, int flags
)
291 struct hw_perf_event
*hwc
= &event
->hw
;
293 pem_perf_event_stop(event
, PERF_EF_UPDATE
);
297 static int pem_pmu_offline_cpu(unsigned int cpu
, struct hlist_node
*node
)
299 struct pem_pmu
*pmu
= hlist_entry_safe(node
, struct pem_pmu
, node
);
305 target
= cpumask_any_but(cpu_online_mask
, cpu
);
306 if (target
>= nr_cpu_ids
)
309 perf_pmu_migrate_context(&pmu
->pmu
, cpu
, target
);
314 static int pem_perf_probe(struct platform_device
*pdev
)
316 struct pem_pmu
*pem_pmu
;
317 struct resource
*res
;
322 pem_pmu
= devm_kzalloc(&pdev
->dev
, sizeof(*pem_pmu
), GFP_KERNEL
);
326 pem_pmu
->dev
= &pdev
->dev
;
327 platform_set_drvdata(pdev
, pem_pmu
);
329 base
= devm_platform_get_and_ioremap_resource(pdev
, 0, &res
);
331 return PTR_ERR(base
);
333 pem_pmu
->base
= base
;
335 pem_pmu
->pmu
= (struct pmu
) {
336 .module
= THIS_MODULE
,
337 .capabilities
= PERF_PMU_CAP_NO_EXCLUDE
,
338 .task_ctx_nr
= perf_invalid_context
,
339 .attr_groups
= pem_perf_attr_groups
,
340 .event_init
= pem_perf_event_init
,
341 .add
= pem_perf_event_add
,
342 .del
= pem_perf_event_del
,
343 .start
= pem_perf_event_start
,
344 .stop
= pem_perf_event_stop
,
345 .read
= pem_perf_event_update
,
348 /* Choose this cpu to collect perf data */
349 pem_pmu
->cpu
= raw_smp_processor_id();
351 name
= devm_kasprintf(pem_pmu
->dev
, GFP_KERNEL
, "mrvl_pcie_rc_pmu_%llx",
356 cpuhp_state_add_instance_nocalls(CPUHP_AP_PERF_ARM_MRVL_PEM_ONLINE
,
359 ret
= perf_pmu_register(&pem_pmu
->pmu
, name
, -1);
365 cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_MRVL_PEM_ONLINE
,
370 static void pem_perf_remove(struct platform_device
*pdev
)
372 struct pem_pmu
*pem_pmu
= platform_get_drvdata(pdev
);
374 cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_MRVL_PEM_ONLINE
,
377 perf_pmu_unregister(&pem_pmu
->pmu
);
381 static const struct acpi_device_id pem_pmu_acpi_match
[] = {
385 MODULE_DEVICE_TABLE(acpi
, pem_pmu_acpi_match
);
388 static struct platform_driver pem_pmu_driver
= {
391 .acpi_match_table
= ACPI_PTR(pem_pmu_acpi_match
),
392 .suppress_bind_attrs
= true,
394 .probe
= pem_perf_probe
,
395 .remove
= pem_perf_remove
,
398 static int __init
pem_pmu_init(void)
402 ret
= cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_MRVL_PEM_ONLINE
,
403 "perf/marvell/pem:online", NULL
,
404 pem_pmu_offline_cpu
);
408 ret
= platform_driver_register(&pem_pmu_driver
);
410 cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_MRVL_PEM_ONLINE
);
414 static void __exit
pem_pmu_exit(void)
416 platform_driver_unregister(&pem_pmu_driver
);
417 cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_MRVL_PEM_ONLINE
);
420 module_init(pem_pmu_init
);
421 module_exit(pem_pmu_exit
);
423 MODULE_DESCRIPTION("Marvell PEM Perf driver");
424 MODULE_AUTHOR("Gowthami Thiagarajan <gthiagarajan@marvell.com>");
425 MODULE_LICENSE("GPL");