GNU Linux-libre 4.19.304-gnu1
[releases.git] / drivers / perf / hisilicon / hisi_uncore_l3c_pmu.c
1 /*
2  * HiSilicon SoC L3C uncore Hardware event counters support
3  *
4  * Copyright (C) 2017 Hisilicon Limited
5  * Author: Anurup M <anurup.m@huawei.com>
6  *         Shaokun Zhang <zhangshaokun@hisilicon.com>
7  *
8  * This code is based on the uncore PMUs like arm-cci and arm-ccn.
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License version 2 as
12  * published by the Free Software Foundation.
13  */
14 #include <linux/acpi.h>
15 #include <linux/bug.h>
16 #include <linux/cpuhotplug.h>
17 #include <linux/interrupt.h>
18 #include <linux/irq.h>
19 #include <linux/list.h>
20 #include <linux/platform_device.h>
21 #include <linux/smp.h>
22
23 #include "hisi_uncore_pmu.h"
24
25 /* L3C register definition */
26 #define L3C_PERF_CTRL           0x0408
27 #define L3C_INT_MASK            0x0800
28 #define L3C_INT_STATUS          0x0808
29 #define L3C_INT_CLEAR           0x080c
30 #define L3C_EVENT_CTRL          0x1c00
31 #define L3C_EVENT_TYPE0         0x1d00
32 /*
33  * Each counter is 48-bits and [48:63] are reserved
34  * which are Read-As-Zero and Writes-Ignored.
35  */
36 #define L3C_CNTR0_LOWER         0x1e00
37
38 /* L3C has 8-counters */
39 #define L3C_NR_COUNTERS         0x8
40
41 #define L3C_PERF_CTRL_EN        0x10000
42 #define L3C_EVTYPE_NONE         0xff
43
44 /*
45  * Select the counter register offset using the counter index
46  */
47 static u32 hisi_l3c_pmu_get_counter_offset(int cntr_idx)
48 {
49         return (L3C_CNTR0_LOWER + (cntr_idx * 8));
50 }
51
52 static u64 hisi_l3c_pmu_read_counter(struct hisi_pmu *l3c_pmu,
53                                      struct hw_perf_event *hwc)
54 {
55         u32 idx = hwc->idx;
56
57         if (!hisi_uncore_pmu_counter_valid(l3c_pmu, idx)) {
58                 dev_err(l3c_pmu->dev, "Unsupported event index:%d!\n", idx);
59                 return 0;
60         }
61
62         /* Read 64-bits and the upper 16 bits are RAZ */
63         return readq(l3c_pmu->base + hisi_l3c_pmu_get_counter_offset(idx));
64 }
65
66 static void hisi_l3c_pmu_write_counter(struct hisi_pmu *l3c_pmu,
67                                        struct hw_perf_event *hwc, u64 val)
68 {
69         u32 idx = hwc->idx;
70
71         if (!hisi_uncore_pmu_counter_valid(l3c_pmu, idx)) {
72                 dev_err(l3c_pmu->dev, "Unsupported event index:%d!\n", idx);
73                 return;
74         }
75
76         /* Write 64-bits and the upper 16 bits are WI */
77         writeq(val, l3c_pmu->base + hisi_l3c_pmu_get_counter_offset(idx));
78 }
79
80 static void hisi_l3c_pmu_write_evtype(struct hisi_pmu *l3c_pmu, int idx,
81                                       u32 type)
82 {
83         u32 reg, reg_idx, shift, val;
84
85         /*
86          * Select the appropriate event select register(L3C_EVENT_TYPE0/1).
87          * There are 2 event select registers for the 8 hardware counters.
88          * Event code is 8-bits and for the former 4 hardware counters,
89          * L3C_EVENT_TYPE0 is chosen. For the latter 4 hardware counters,
90          * L3C_EVENT_TYPE1 is chosen.
91          */
92         reg = L3C_EVENT_TYPE0 + (idx / 4) * 4;
93         reg_idx = idx % 4;
94         shift = 8 * reg_idx;
95
96         /* Write event code to L3C_EVENT_TYPEx Register */
97         val = readl(l3c_pmu->base + reg);
98         val &= ~(L3C_EVTYPE_NONE << shift);
99         val |= (type << shift);
100         writel(val, l3c_pmu->base + reg);
101 }
102
103 static void hisi_l3c_pmu_start_counters(struct hisi_pmu *l3c_pmu)
104 {
105         u32 val;
106
107         /*
108          * Set perf_enable bit in L3C_PERF_CTRL register to start counting
109          * for all enabled counters.
110          */
111         val = readl(l3c_pmu->base + L3C_PERF_CTRL);
112         val |= L3C_PERF_CTRL_EN;
113         writel(val, l3c_pmu->base + L3C_PERF_CTRL);
114 }
115
116 static void hisi_l3c_pmu_stop_counters(struct hisi_pmu *l3c_pmu)
117 {
118         u32 val;
119
120         /*
121          * Clear perf_enable bit in L3C_PERF_CTRL register to stop counting
122          * for all enabled counters.
123          */
124         val = readl(l3c_pmu->base + L3C_PERF_CTRL);
125         val &= ~(L3C_PERF_CTRL_EN);
126         writel(val, l3c_pmu->base + L3C_PERF_CTRL);
127 }
128
129 static void hisi_l3c_pmu_enable_counter(struct hisi_pmu *l3c_pmu,
130                                         struct hw_perf_event *hwc)
131 {
132         u32 val;
133
134         /* Enable counter index in L3C_EVENT_CTRL register */
135         val = readl(l3c_pmu->base + L3C_EVENT_CTRL);
136         val |= (1 << hwc->idx);
137         writel(val, l3c_pmu->base + L3C_EVENT_CTRL);
138 }
139
140 static void hisi_l3c_pmu_disable_counter(struct hisi_pmu *l3c_pmu,
141                                          struct hw_perf_event *hwc)
142 {
143         u32 val;
144
145         /* Clear counter index in L3C_EVENT_CTRL register */
146         val = readl(l3c_pmu->base + L3C_EVENT_CTRL);
147         val &= ~(1 << hwc->idx);
148         writel(val, l3c_pmu->base + L3C_EVENT_CTRL);
149 }
150
151 static void hisi_l3c_pmu_enable_counter_int(struct hisi_pmu *l3c_pmu,
152                                             struct hw_perf_event *hwc)
153 {
154         u32 val;
155
156         val = readl(l3c_pmu->base + L3C_INT_MASK);
157         /* Write 0 to enable interrupt */
158         val &= ~(1 << hwc->idx);
159         writel(val, l3c_pmu->base + L3C_INT_MASK);
160 }
161
162 static void hisi_l3c_pmu_disable_counter_int(struct hisi_pmu *l3c_pmu,
163                                              struct hw_perf_event *hwc)
164 {
165         u32 val;
166
167         val = readl(l3c_pmu->base + L3C_INT_MASK);
168         /* Write 1 to mask interrupt */
169         val |= (1 << hwc->idx);
170         writel(val, l3c_pmu->base + L3C_INT_MASK);
171 }
172
173 static irqreturn_t hisi_l3c_pmu_isr(int irq, void *dev_id)
174 {
175         struct hisi_pmu *l3c_pmu = dev_id;
176         struct perf_event *event;
177         unsigned long overflown;
178         int idx;
179
180         /* Read L3C_INT_STATUS register */
181         overflown = readl(l3c_pmu->base + L3C_INT_STATUS);
182         if (!overflown)
183                 return IRQ_NONE;
184
185         /*
186          * Find the counter index which overflowed if the bit was set
187          * and handle it.
188          */
189         for_each_set_bit(idx, &overflown, L3C_NR_COUNTERS) {
190                 /* Write 1 to clear the IRQ status flag */
191                 writel((1 << idx), l3c_pmu->base + L3C_INT_CLEAR);
192
193                 /* Get the corresponding event struct */
194                 event = l3c_pmu->pmu_events.hw_events[idx];
195                 if (!event)
196                         continue;
197
198                 hisi_uncore_pmu_event_update(event);
199                 hisi_uncore_pmu_set_event_period(event);
200         }
201
202         return IRQ_HANDLED;
203 }
204
205 static int hisi_l3c_pmu_init_irq(struct hisi_pmu *l3c_pmu,
206                                  struct platform_device *pdev)
207 {
208         int irq, ret;
209
210         /* Read and init IRQ */
211         irq = platform_get_irq(pdev, 0);
212         if (irq < 0) {
213                 dev_err(&pdev->dev, "L3C PMU get irq fail; irq:%d\n", irq);
214                 return irq;
215         }
216
217         ret = devm_request_irq(&pdev->dev, irq, hisi_l3c_pmu_isr,
218                                IRQF_NOBALANCING | IRQF_NO_THREAD,
219                                dev_name(&pdev->dev), l3c_pmu);
220         if (ret < 0) {
221                 dev_err(&pdev->dev,
222                         "Fail to request IRQ:%d ret:%d\n", irq, ret);
223                 return ret;
224         }
225
226         l3c_pmu->irq = irq;
227
228         return 0;
229 }
230
231 static const struct acpi_device_id hisi_l3c_pmu_acpi_match[] = {
232         { "HISI0213", },
233         {},
234 };
235 MODULE_DEVICE_TABLE(acpi, hisi_l3c_pmu_acpi_match);
236
237 static int hisi_l3c_pmu_init_data(struct platform_device *pdev,
238                                   struct hisi_pmu *l3c_pmu)
239 {
240         unsigned long long id;
241         struct resource *res;
242         acpi_status status;
243
244         status = acpi_evaluate_integer(ACPI_HANDLE(&pdev->dev),
245                                        "_UID", NULL, &id);
246         if (ACPI_FAILURE(status))
247                 return -EINVAL;
248
249         l3c_pmu->index_id = id;
250
251         /*
252          * Use the SCCL_ID and CCL_ID to identify the L3C PMU, while
253          * SCCL_ID is in MPIDR[aff2] and CCL_ID is in MPIDR[aff1].
254          */
255         if (device_property_read_u32(&pdev->dev, "hisilicon,scl-id",
256                                      &l3c_pmu->sccl_id)) {
257                 dev_err(&pdev->dev, "Can not read l3c sccl-id!\n");
258                 return -EINVAL;
259         }
260
261         if (device_property_read_u32(&pdev->dev, "hisilicon,ccl-id",
262                                      &l3c_pmu->ccl_id)) {
263                 dev_err(&pdev->dev, "Can not read l3c ccl-id!\n");
264                 return -EINVAL;
265         }
266
267         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
268         l3c_pmu->base = devm_ioremap_resource(&pdev->dev, res);
269         if (IS_ERR(l3c_pmu->base)) {
270                 dev_err(&pdev->dev, "ioremap failed for l3c_pmu resource\n");
271                 return PTR_ERR(l3c_pmu->base);
272         }
273
274         return 0;
275 }
276
277 static struct attribute *hisi_l3c_pmu_format_attr[] = {
278         HISI_PMU_FORMAT_ATTR(event, "config:0-7"),
279         NULL,
280 };
281
282 static const struct attribute_group hisi_l3c_pmu_format_group = {
283         .name = "format",
284         .attrs = hisi_l3c_pmu_format_attr,
285 };
286
287 static struct attribute *hisi_l3c_pmu_events_attr[] = {
288         HISI_PMU_EVENT_ATTR(rd_cpipe,           0x00),
289         HISI_PMU_EVENT_ATTR(wr_cpipe,           0x01),
290         HISI_PMU_EVENT_ATTR(rd_hit_cpipe,       0x02),
291         HISI_PMU_EVENT_ATTR(wr_hit_cpipe,       0x03),
292         HISI_PMU_EVENT_ATTR(victim_num,         0x04),
293         HISI_PMU_EVENT_ATTR(rd_spipe,           0x20),
294         HISI_PMU_EVENT_ATTR(wr_spipe,           0x21),
295         HISI_PMU_EVENT_ATTR(rd_hit_spipe,       0x22),
296         HISI_PMU_EVENT_ATTR(wr_hit_spipe,       0x23),
297         HISI_PMU_EVENT_ATTR(back_invalid,       0x29),
298         HISI_PMU_EVENT_ATTR(retry_cpu,          0x40),
299         HISI_PMU_EVENT_ATTR(retry_ring,         0x41),
300         HISI_PMU_EVENT_ATTR(prefetch_drop,      0x42),
301         NULL,
302 };
303
304 static const struct attribute_group hisi_l3c_pmu_events_group = {
305         .name = "events",
306         .attrs = hisi_l3c_pmu_events_attr,
307 };
308
309 static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL);
310
311 static struct attribute *hisi_l3c_pmu_cpumask_attrs[] = {
312         &dev_attr_cpumask.attr,
313         NULL,
314 };
315
316 static const struct attribute_group hisi_l3c_pmu_cpumask_attr_group = {
317         .attrs = hisi_l3c_pmu_cpumask_attrs,
318 };
319
320 static const struct attribute_group *hisi_l3c_pmu_attr_groups[] = {
321         &hisi_l3c_pmu_format_group,
322         &hisi_l3c_pmu_events_group,
323         &hisi_l3c_pmu_cpumask_attr_group,
324         NULL,
325 };
326
327 static const struct hisi_uncore_ops hisi_uncore_l3c_ops = {
328         .write_evtype           = hisi_l3c_pmu_write_evtype,
329         .get_event_idx          = hisi_uncore_pmu_get_event_idx,
330         .start_counters         = hisi_l3c_pmu_start_counters,
331         .stop_counters          = hisi_l3c_pmu_stop_counters,
332         .enable_counter         = hisi_l3c_pmu_enable_counter,
333         .disable_counter        = hisi_l3c_pmu_disable_counter,
334         .enable_counter_int     = hisi_l3c_pmu_enable_counter_int,
335         .disable_counter_int    = hisi_l3c_pmu_disable_counter_int,
336         .write_counter          = hisi_l3c_pmu_write_counter,
337         .read_counter           = hisi_l3c_pmu_read_counter,
338 };
339
340 static int hisi_l3c_pmu_dev_probe(struct platform_device *pdev,
341                                   struct hisi_pmu *l3c_pmu)
342 {
343         int ret;
344
345         ret = hisi_l3c_pmu_init_data(pdev, l3c_pmu);
346         if (ret)
347                 return ret;
348
349         ret = hisi_l3c_pmu_init_irq(l3c_pmu, pdev);
350         if (ret)
351                 return ret;
352
353         l3c_pmu->num_counters = L3C_NR_COUNTERS;
354         l3c_pmu->counter_bits = 48;
355         l3c_pmu->ops = &hisi_uncore_l3c_ops;
356         l3c_pmu->dev = &pdev->dev;
357         l3c_pmu->on_cpu = -1;
358         l3c_pmu->check_event = 0x59;
359
360         return 0;
361 }
362
363 static int hisi_l3c_pmu_probe(struct platform_device *pdev)
364 {
365         struct hisi_pmu *l3c_pmu;
366         char *name;
367         int ret;
368
369         l3c_pmu = devm_kzalloc(&pdev->dev, sizeof(*l3c_pmu), GFP_KERNEL);
370         if (!l3c_pmu)
371                 return -ENOMEM;
372
373         platform_set_drvdata(pdev, l3c_pmu);
374
375         ret = hisi_l3c_pmu_dev_probe(pdev, l3c_pmu);
376         if (ret)
377                 return ret;
378
379         ret = cpuhp_state_add_instance(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE,
380                                        &l3c_pmu->node);
381         if (ret) {
382                 dev_err(&pdev->dev, "Error %d registering hotplug\n", ret);
383                 return ret;
384         }
385
386         name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sccl%u_l3c%u",
387                               l3c_pmu->sccl_id, l3c_pmu->index_id);
388         l3c_pmu->pmu = (struct pmu) {
389                 .name           = name,
390                 .task_ctx_nr    = perf_invalid_context,
391                 .event_init     = hisi_uncore_pmu_event_init,
392                 .pmu_enable     = hisi_uncore_pmu_enable,
393                 .pmu_disable    = hisi_uncore_pmu_disable,
394                 .add            = hisi_uncore_pmu_add,
395                 .del            = hisi_uncore_pmu_del,
396                 .start          = hisi_uncore_pmu_start,
397                 .stop           = hisi_uncore_pmu_stop,
398                 .read           = hisi_uncore_pmu_read,
399                 .attr_groups    = hisi_l3c_pmu_attr_groups,
400         };
401
402         ret = perf_pmu_register(&l3c_pmu->pmu, name, -1);
403         if (ret) {
404                 dev_err(l3c_pmu->dev, "L3C PMU register failed!\n");
405                 cpuhp_state_remove_instance(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE,
406                                             &l3c_pmu->node);
407         }
408
409         return ret;
410 }
411
412 static int hisi_l3c_pmu_remove(struct platform_device *pdev)
413 {
414         struct hisi_pmu *l3c_pmu = platform_get_drvdata(pdev);
415
416         perf_pmu_unregister(&l3c_pmu->pmu);
417         cpuhp_state_remove_instance(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE,
418                                     &l3c_pmu->node);
419
420         return 0;
421 }
422
423 static struct platform_driver hisi_l3c_pmu_driver = {
424         .driver = {
425                 .name = "hisi_l3c_pmu",
426                 .acpi_match_table = ACPI_PTR(hisi_l3c_pmu_acpi_match),
427         },
428         .probe = hisi_l3c_pmu_probe,
429         .remove = hisi_l3c_pmu_remove,
430 };
431
432 static int __init hisi_l3c_pmu_module_init(void)
433 {
434         int ret;
435
436         ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE,
437                                       "AP_PERF_ARM_HISI_L3_ONLINE",
438                                       hisi_uncore_pmu_online_cpu,
439                                       hisi_uncore_pmu_offline_cpu);
440         if (ret) {
441                 pr_err("L3C PMU: Error setup hotplug, ret = %d\n", ret);
442                 return ret;
443         }
444
445         ret = platform_driver_register(&hisi_l3c_pmu_driver);
446         if (ret)
447                 cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE);
448
449         return ret;
450 }
451 module_init(hisi_l3c_pmu_module_init);
452
453 static void __exit hisi_l3c_pmu_module_exit(void)
454 {
455         platform_driver_unregister(&hisi_l3c_pmu_driver);
456         cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE);
457 }
458 module_exit(hisi_l3c_pmu_module_exit);
459
460 MODULE_DESCRIPTION("HiSilicon SoC L3C uncore PMU driver");
461 MODULE_LICENSE("GPL v2");
462 MODULE_AUTHOR("Anurup M <anurup.m@huawei.com>");
463 MODULE_AUTHOR("Shaokun Zhang <zhangshaokun@hisilicon.com>");