dt-bindings: soc: bcm: use absolute path to other schema
[linux-2.6-microblaze.git] / drivers / perf / hisilicon / hisi_uncore_sllc_pmu.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * HiSilicon SLLC uncore Hardware event counters support
4  *
5  * Copyright (C) 2020 HiSilicon Limited
6  * Author: Shaokun Zhang <zhangshaokun@hisilicon.com>
7  *
8  * This code is based on the uncore PMUs like arm-cci and arm-ccn.
9  */
10 #include <linux/acpi.h>
11 #include <linux/cpuhotplug.h>
12 #include <linux/interrupt.h>
13 #include <linux/irq.h>
14 #include <linux/list.h>
15 #include <linux/smp.h>
16
17 #include "hisi_uncore_pmu.h"
18
19 /* SLLC register definition */
20 #define SLLC_INT_MASK                   0x0814
21 #define SLLC_INT_STATUS                 0x0818
22 #define SLLC_INT_CLEAR                  0x081c
23 #define SLLC_PERF_CTRL                  0x1c00
24 #define SLLC_SRCID_CTRL                 0x1c04
25 #define SLLC_TGTID_CTRL                 0x1c08
26 #define SLLC_EVENT_CTRL                 0x1c14
27 #define SLLC_EVENT_TYPE0                0x1c18
28 #define SLLC_VERSION                    0x1cf0
29 #define SLLC_EVENT_CNT0_L               0x1d00
30
31 #define SLLC_EVTYPE_MASK                0xff
32 #define SLLC_PERF_CTRL_EN               BIT(0)
33 #define SLLC_FILT_EN                    BIT(1)
34 #define SLLC_TRACETAG_EN                BIT(2)
35 #define SLLC_SRCID_EN                   BIT(4)
36 #define SLLC_SRCID_NONE                 0x0
37 #define SLLC_TGTID_EN                   BIT(5)
38 #define SLLC_TGTID_NONE                 0x0
39 #define SLLC_TGTID_MIN_SHIFT            1
40 #define SLLC_TGTID_MAX_SHIFT            12
41 #define SLLC_SRCID_CMD_SHIFT            1
42 #define SLLC_SRCID_MSK_SHIFT            12
43 #define SLLC_NR_EVENTS                  0x80
44
45 HISI_PMU_EVENT_ATTR_EXTRACTOR(tgtid_min, config1, 10, 0);
46 HISI_PMU_EVENT_ATTR_EXTRACTOR(tgtid_max, config1, 21, 11);
47 HISI_PMU_EVENT_ATTR_EXTRACTOR(srcid_cmd, config1, 32, 22);
48 HISI_PMU_EVENT_ATTR_EXTRACTOR(srcid_msk, config1, 43, 33);
49 HISI_PMU_EVENT_ATTR_EXTRACTOR(tracetag_en, config1, 44, 44);
50
51 static bool tgtid_is_valid(u32 max, u32 min)
52 {
53         return max > 0 && max >= min;
54 }
55
56 static void hisi_sllc_pmu_enable_tracetag(struct perf_event *event)
57 {
58         struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu);
59         u32 tt_en = hisi_get_tracetag_en(event);
60
61         if (tt_en) {
62                 u32 val;
63
64                 val = readl(sllc_pmu->base + SLLC_PERF_CTRL);
65                 val |= SLLC_TRACETAG_EN | SLLC_FILT_EN;
66                 writel(val, sllc_pmu->base + SLLC_PERF_CTRL);
67         }
68 }
69
70 static void hisi_sllc_pmu_disable_tracetag(struct perf_event *event)
71 {
72         struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu);
73         u32 tt_en = hisi_get_tracetag_en(event);
74
75         if (tt_en) {
76                 u32 val;
77
78                 val = readl(sllc_pmu->base + SLLC_PERF_CTRL);
79                 val &= ~(SLLC_TRACETAG_EN | SLLC_FILT_EN);
80                 writel(val, sllc_pmu->base + SLLC_PERF_CTRL);
81         }
82 }
83
84 static void hisi_sllc_pmu_config_tgtid(struct perf_event *event)
85 {
86         struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu);
87         u32 min = hisi_get_tgtid_min(event);
88         u32 max = hisi_get_tgtid_max(event);
89
90         if (tgtid_is_valid(max, min)) {
91                 u32 val = (max << SLLC_TGTID_MAX_SHIFT) | (min << SLLC_TGTID_MIN_SHIFT);
92
93                 writel(val, sllc_pmu->base + SLLC_TGTID_CTRL);
94                 /* Enable the tgtid */
95                 val = readl(sllc_pmu->base + SLLC_PERF_CTRL);
96                 val |= SLLC_TGTID_EN | SLLC_FILT_EN;
97                 writel(val, sllc_pmu->base + SLLC_PERF_CTRL);
98         }
99 }
100
101 static void hisi_sllc_pmu_clear_tgtid(struct perf_event *event)
102 {
103         struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu);
104         u32 min = hisi_get_tgtid_min(event);
105         u32 max = hisi_get_tgtid_max(event);
106
107         if (tgtid_is_valid(max, min)) {
108                 u32 val;
109
110                 writel(SLLC_TGTID_NONE, sllc_pmu->base + SLLC_TGTID_CTRL);
111                 /* Disable the tgtid */
112                 val = readl(sllc_pmu->base + SLLC_PERF_CTRL);
113                 val &= ~(SLLC_TGTID_EN | SLLC_FILT_EN);
114                 writel(val, sllc_pmu->base + SLLC_PERF_CTRL);
115         }
116 }
117
118 static void hisi_sllc_pmu_config_srcid(struct perf_event *event)
119 {
120         struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu);
121         u32 cmd = hisi_get_srcid_cmd(event);
122
123         if (cmd) {
124                 u32 val, msk;
125
126                 msk = hisi_get_srcid_msk(event);
127                 val = (cmd << SLLC_SRCID_CMD_SHIFT) | (msk << SLLC_SRCID_MSK_SHIFT);
128                 writel(val, sllc_pmu->base + SLLC_SRCID_CTRL);
129                 /* Enable the srcid */
130                 val = readl(sllc_pmu->base + SLLC_PERF_CTRL);
131                 val |= SLLC_SRCID_EN | SLLC_FILT_EN;
132                 writel(val, sllc_pmu->base + SLLC_PERF_CTRL);
133         }
134 }
135
136 static void hisi_sllc_pmu_clear_srcid(struct perf_event *event)
137 {
138         struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu);
139         u32 cmd = hisi_get_srcid_cmd(event);
140
141         if (cmd) {
142                 u32 val;
143
144                 writel(SLLC_SRCID_NONE, sllc_pmu->base + SLLC_SRCID_CTRL);
145                 /* Disable the srcid */
146                 val = readl(sllc_pmu->base + SLLC_PERF_CTRL);
147                 val &= ~(SLLC_SRCID_EN | SLLC_FILT_EN);
148                 writel(val, sllc_pmu->base + SLLC_PERF_CTRL);
149         }
150 }
151
152 static void hisi_sllc_pmu_enable_filter(struct perf_event *event)
153 {
154         if (event->attr.config1 != 0x0) {
155                 hisi_sllc_pmu_enable_tracetag(event);
156                 hisi_sllc_pmu_config_srcid(event);
157                 hisi_sllc_pmu_config_tgtid(event);
158         }
159 }
160
161 static void hisi_sllc_pmu_clear_filter(struct perf_event *event)
162 {
163         if (event->attr.config1 != 0x0) {
164                 hisi_sllc_pmu_disable_tracetag(event);
165                 hisi_sllc_pmu_clear_srcid(event);
166                 hisi_sllc_pmu_clear_tgtid(event);
167         }
168 }
169
170 static u32 hisi_sllc_pmu_get_counter_offset(int idx)
171 {
172         return (SLLC_EVENT_CNT0_L + idx * 8);
173 }
174
175 static u64 hisi_sllc_pmu_read_counter(struct hisi_pmu *sllc_pmu,
176                                       struct hw_perf_event *hwc)
177 {
178         return readq(sllc_pmu->base +
179                      hisi_sllc_pmu_get_counter_offset(hwc->idx));
180 }
181
182 static void hisi_sllc_pmu_write_counter(struct hisi_pmu *sllc_pmu,
183                                         struct hw_perf_event *hwc, u64 val)
184 {
185         writeq(val, sllc_pmu->base +
186                hisi_sllc_pmu_get_counter_offset(hwc->idx));
187 }
188
189 static void hisi_sllc_pmu_write_evtype(struct hisi_pmu *sllc_pmu, int idx,
190                                        u32 type)
191 {
192         u32 reg, reg_idx, shift, val;
193
194         /*
195          * Select the appropriate event select register(SLLC_EVENT_TYPE0/1).
196          * There are 2 event select registers for the 8 hardware counters.
197          * Event code is 8-bits and for the former 4 hardware counters,
198          * SLLC_EVENT_TYPE0 is chosen. For the latter 4 hardware counters,
199          * SLLC_EVENT_TYPE1 is chosen.
200          */
201         reg = SLLC_EVENT_TYPE0 + (idx / 4) * 4;
202         reg_idx = idx % 4;
203         shift = 8 * reg_idx;
204
205         /* Write event code to SLLC_EVENT_TYPEx Register */
206         val = readl(sllc_pmu->base + reg);
207         val &= ~(SLLC_EVTYPE_MASK << shift);
208         val |= (type << shift);
209         writel(val, sllc_pmu->base + reg);
210 }
211
212 static void hisi_sllc_pmu_start_counters(struct hisi_pmu *sllc_pmu)
213 {
214         u32 val;
215
216         val = readl(sllc_pmu->base + SLLC_PERF_CTRL);
217         val |= SLLC_PERF_CTRL_EN;
218         writel(val, sllc_pmu->base + SLLC_PERF_CTRL);
219 }
220
221 static void hisi_sllc_pmu_stop_counters(struct hisi_pmu *sllc_pmu)
222 {
223         u32 val;
224
225         val = readl(sllc_pmu->base + SLLC_PERF_CTRL);
226         val &= ~(SLLC_PERF_CTRL_EN);
227         writel(val, sllc_pmu->base + SLLC_PERF_CTRL);
228 }
229
230 static void hisi_sllc_pmu_enable_counter(struct hisi_pmu *sllc_pmu,
231                                          struct hw_perf_event *hwc)
232 {
233         u32 val;
234
235         val = readl(sllc_pmu->base + SLLC_EVENT_CTRL);
236         val |= 1 << hwc->idx;
237         writel(val, sllc_pmu->base + SLLC_EVENT_CTRL);
238 }
239
240 static void hisi_sllc_pmu_disable_counter(struct hisi_pmu *sllc_pmu,
241                                           struct hw_perf_event *hwc)
242 {
243         u32 val;
244
245         val = readl(sllc_pmu->base + SLLC_EVENT_CTRL);
246         val &= ~(1 << hwc->idx);
247         writel(val, sllc_pmu->base + SLLC_EVENT_CTRL);
248 }
249
250 static void hisi_sllc_pmu_enable_counter_int(struct hisi_pmu *sllc_pmu,
251                                              struct hw_perf_event *hwc)
252 {
253         u32 val;
254
255         val = readl(sllc_pmu->base + SLLC_INT_MASK);
256         /* Write 0 to enable interrupt */
257         val &= ~(1 << hwc->idx);
258         writel(val, sllc_pmu->base + SLLC_INT_MASK);
259 }
260
261 static void hisi_sllc_pmu_disable_counter_int(struct hisi_pmu *sllc_pmu,
262                                               struct hw_perf_event *hwc)
263 {
264         u32 val;
265
266         val = readl(sllc_pmu->base + SLLC_INT_MASK);
267         /* Write 1 to mask interrupt */
268         val |= 1 << hwc->idx;
269         writel(val, sllc_pmu->base + SLLC_INT_MASK);
270 }
271
272 static u32 hisi_sllc_pmu_get_int_status(struct hisi_pmu *sllc_pmu)
273 {
274         return readl(sllc_pmu->base + SLLC_INT_STATUS);
275 }
276
277 static void hisi_sllc_pmu_clear_int_status(struct hisi_pmu *sllc_pmu, int idx)
278 {
279         writel(1 << idx, sllc_pmu->base + SLLC_INT_CLEAR);
280 }
281
282 static const struct acpi_device_id hisi_sllc_pmu_acpi_match[] = {
283         { "HISI0263", },
284         {}
285 };
286 MODULE_DEVICE_TABLE(acpi, hisi_sllc_pmu_acpi_match);
287
288 static int hisi_sllc_pmu_init_data(struct platform_device *pdev,
289                                    struct hisi_pmu *sllc_pmu)
290 {
291         /*
292          * Use the SCCL_ID and the index ID to identify the SLLC PMU,
293          * while SCCL_ID is from MPIDR_EL1 by CPU.
294          */
295         if (device_property_read_u32(&pdev->dev, "hisilicon,scl-id",
296                                      &sllc_pmu->sccl_id)) {
297                 dev_err(&pdev->dev, "Cannot read sccl-id!\n");
298                 return -EINVAL;
299         }
300
301         if (device_property_read_u32(&pdev->dev, "hisilicon,idx-id",
302                                      &sllc_pmu->index_id)) {
303                 dev_err(&pdev->dev, "Cannot read idx-id!\n");
304                 return -EINVAL;
305         }
306
307         /* SLLC PMUs only share the same SCCL */
308         sllc_pmu->ccl_id = -1;
309
310         sllc_pmu->base = devm_platform_ioremap_resource(pdev, 0);
311         if (IS_ERR(sllc_pmu->base)) {
312                 dev_err(&pdev->dev, "ioremap failed for sllc_pmu resource.\n");
313                 return PTR_ERR(sllc_pmu->base);
314         }
315
316         sllc_pmu->identifier = readl(sllc_pmu->base + SLLC_VERSION);
317
318         return 0;
319 }
320
321 static struct attribute *hisi_sllc_pmu_v2_format_attr[] = {
322         HISI_PMU_FORMAT_ATTR(event, "config:0-7"),
323         HISI_PMU_FORMAT_ATTR(tgtid_min, "config1:0-10"),
324         HISI_PMU_FORMAT_ATTR(tgtid_max, "config1:11-21"),
325         HISI_PMU_FORMAT_ATTR(srcid_cmd, "config1:22-32"),
326         HISI_PMU_FORMAT_ATTR(srcid_msk, "config1:33-43"),
327         HISI_PMU_FORMAT_ATTR(tracetag_en, "config1:44"),
328         NULL
329 };
330
331 static const struct attribute_group hisi_sllc_pmu_v2_format_group = {
332         .name = "format",
333         .attrs = hisi_sllc_pmu_v2_format_attr,
334 };
335
336 static struct attribute *hisi_sllc_pmu_v2_events_attr[] = {
337         HISI_PMU_EVENT_ATTR(rx_req,             0x30),
338         HISI_PMU_EVENT_ATTR(rx_data,            0x31),
339         HISI_PMU_EVENT_ATTR(tx_req,             0x34),
340         HISI_PMU_EVENT_ATTR(tx_data,            0x35),
341         HISI_PMU_EVENT_ATTR(cycles,             0x09),
342         NULL
343 };
344
345 static const struct attribute_group hisi_sllc_pmu_v2_events_group = {
346         .name = "events",
347         .attrs = hisi_sllc_pmu_v2_events_attr,
348 };
349
350 static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL);
351
352 static struct attribute *hisi_sllc_pmu_cpumask_attrs[] = {
353         &dev_attr_cpumask.attr,
354         NULL
355 };
356
357 static const struct attribute_group hisi_sllc_pmu_cpumask_attr_group = {
358         .attrs = hisi_sllc_pmu_cpumask_attrs,
359 };
360
361 static struct device_attribute hisi_sllc_pmu_identifier_attr =
362         __ATTR(identifier, 0444, hisi_uncore_pmu_identifier_attr_show, NULL);
363
364 static struct attribute *hisi_sllc_pmu_identifier_attrs[] = {
365         &hisi_sllc_pmu_identifier_attr.attr,
366         NULL
367 };
368
369 static const struct attribute_group hisi_sllc_pmu_identifier_group = {
370         .attrs = hisi_sllc_pmu_identifier_attrs,
371 };
372
373 static const struct attribute_group *hisi_sllc_pmu_v2_attr_groups[] = {
374         &hisi_sllc_pmu_v2_format_group,
375         &hisi_sllc_pmu_v2_events_group,
376         &hisi_sllc_pmu_cpumask_attr_group,
377         &hisi_sllc_pmu_identifier_group,
378         NULL
379 };
380
381 static const struct hisi_uncore_ops hisi_uncore_sllc_ops = {
382         .write_evtype           = hisi_sllc_pmu_write_evtype,
383         .get_event_idx          = hisi_uncore_pmu_get_event_idx,
384         .start_counters         = hisi_sllc_pmu_start_counters,
385         .stop_counters          = hisi_sllc_pmu_stop_counters,
386         .enable_counter         = hisi_sllc_pmu_enable_counter,
387         .disable_counter        = hisi_sllc_pmu_disable_counter,
388         .enable_counter_int     = hisi_sllc_pmu_enable_counter_int,
389         .disable_counter_int    = hisi_sllc_pmu_disable_counter_int,
390         .write_counter          = hisi_sllc_pmu_write_counter,
391         .read_counter           = hisi_sllc_pmu_read_counter,
392         .get_int_status         = hisi_sllc_pmu_get_int_status,
393         .clear_int_status       = hisi_sllc_pmu_clear_int_status,
394         .enable_filter          = hisi_sllc_pmu_enable_filter,
395         .disable_filter         = hisi_sllc_pmu_clear_filter,
396 };
397
398 static int hisi_sllc_pmu_dev_probe(struct platform_device *pdev,
399                                    struct hisi_pmu *sllc_pmu)
400 {
401         int ret;
402
403         ret = hisi_sllc_pmu_init_data(pdev, sllc_pmu);
404         if (ret)
405                 return ret;
406
407         ret = hisi_uncore_pmu_init_irq(sllc_pmu, pdev);
408         if (ret)
409                 return ret;
410
411         sllc_pmu->pmu_events.attr_groups = hisi_sllc_pmu_v2_attr_groups;
412         sllc_pmu->ops = &hisi_uncore_sllc_ops;
413         sllc_pmu->check_event = SLLC_NR_EVENTS;
414         sllc_pmu->counter_bits = 64;
415         sllc_pmu->num_counters = 8;
416         sllc_pmu->dev = &pdev->dev;
417         sllc_pmu->on_cpu = -1;
418
419         return 0;
420 }
421
422 static int hisi_sllc_pmu_probe(struct platform_device *pdev)
423 {
424         struct hisi_pmu *sllc_pmu;
425         char *name;
426         int ret;
427
428         sllc_pmu = devm_kzalloc(&pdev->dev, sizeof(*sllc_pmu), GFP_KERNEL);
429         if (!sllc_pmu)
430                 return -ENOMEM;
431
432         ret = hisi_sllc_pmu_dev_probe(pdev, sllc_pmu);
433         if (ret)
434                 return ret;
435
436         name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sccl%u_sllc%u",
437                               sllc_pmu->sccl_id, sllc_pmu->index_id);
438         if (!name)
439                 return -ENOMEM;
440
441         ret = cpuhp_state_add_instance(CPUHP_AP_PERF_ARM_HISI_SLLC_ONLINE,
442                                        &sllc_pmu->node);
443         if (ret) {
444                 dev_err(&pdev->dev, "Error %d registering hotplug\n", ret);
445                 return ret;
446         }
447
448         sllc_pmu->pmu = (struct pmu) {
449                 .module         = THIS_MODULE,
450                 .task_ctx_nr    = perf_invalid_context,
451                 .event_init     = hisi_uncore_pmu_event_init,
452                 .pmu_enable     = hisi_uncore_pmu_enable,
453                 .pmu_disable    = hisi_uncore_pmu_disable,
454                 .add            = hisi_uncore_pmu_add,
455                 .del            = hisi_uncore_pmu_del,
456                 .start          = hisi_uncore_pmu_start,
457                 .stop           = hisi_uncore_pmu_stop,
458                 .read           = hisi_uncore_pmu_read,
459                 .attr_groups    = sllc_pmu->pmu_events.attr_groups,
460                 .capabilities   = PERF_PMU_CAP_NO_EXCLUDE,
461         };
462
463         ret = perf_pmu_register(&sllc_pmu->pmu, name, -1);
464         if (ret) {
465                 dev_err(sllc_pmu->dev, "PMU register failed, ret = %d\n", ret);
466                 cpuhp_state_remove_instance(CPUHP_AP_PERF_ARM_HISI_SLLC_ONLINE,
467                                             &sllc_pmu->node);
468                 return ret;
469         }
470
471         platform_set_drvdata(pdev, sllc_pmu);
472
473         return ret;
474 }
475
476 static int hisi_sllc_pmu_remove(struct platform_device *pdev)
477 {
478         struct hisi_pmu *sllc_pmu = platform_get_drvdata(pdev);
479
480         perf_pmu_unregister(&sllc_pmu->pmu);
481         cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_HISI_SLLC_ONLINE,
482                                             &sllc_pmu->node);
483         return 0;
484 }
485
486 static struct platform_driver hisi_sllc_pmu_driver = {
487         .driver = {
488                 .name = "hisi_sllc_pmu",
489                 .acpi_match_table = hisi_sllc_pmu_acpi_match,
490                 .suppress_bind_attrs = true,
491         },
492         .probe = hisi_sllc_pmu_probe,
493         .remove = hisi_sllc_pmu_remove,
494 };
495
496 static int __init hisi_sllc_pmu_module_init(void)
497 {
498         int ret;
499
500         ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_HISI_SLLC_ONLINE,
501                                       "AP_PERF_ARM_HISI_SLLC_ONLINE",
502                                       hisi_uncore_pmu_online_cpu,
503                                       hisi_uncore_pmu_offline_cpu);
504         if (ret) {
505                 pr_err("SLLC PMU: cpuhp state setup failed, ret = %d\n", ret);
506                 return ret;
507         }
508
509         ret = platform_driver_register(&hisi_sllc_pmu_driver);
510         if (ret)
511                 cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_SLLC_ONLINE);
512
513         return ret;
514 }
515 module_init(hisi_sllc_pmu_module_init);
516
517 static void __exit hisi_sllc_pmu_module_exit(void)
518 {
519         platform_driver_unregister(&hisi_sllc_pmu_driver);
520         cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_SLLC_ONLINE);
521 }
522 module_exit(hisi_sllc_pmu_module_exit);
523
524 MODULE_DESCRIPTION("HiSilicon SLLC uncore PMU driver");
525 MODULE_LICENSE("GPL v2");
526 MODULE_AUTHOR("Shaokun Zhang <zhangshaokun@hisilicon.com>");
527 MODULE_AUTHOR("Qi Liu <liuqi115@huawei.com>");