1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * HiSilicon SoC L3C uncore Hardware event counters support |
4 | * |
5 | * Copyright (C) 2017 HiSilicon Limited |
6 | * Author: Anurup M <anurup.m@huawei.com> |
7 | * Shaokun Zhang <zhangshaokun@hisilicon.com> |
8 | * |
9 | * This code is based on the uncore PMUs like arm-cci and arm-ccn. |
10 | */ |
11 | #include <linux/acpi.h> |
12 | #include <linux/bug.h> |
13 | #include <linux/cpuhotplug.h> |
14 | #include <linux/interrupt.h> |
15 | #include <linux/irq.h> |
16 | #include <linux/list.h> |
17 | #include <linux/smp.h> |
18 | |
19 | #include "hisi_uncore_pmu.h" |
20 | |
21 | /* L3C register definition */ |
22 | #define L3C_PERF_CTRL 0x0408 |
23 | #define L3C_INT_MASK 0x0800 |
24 | #define L3C_INT_STATUS 0x0808 |
25 | #define L3C_INT_CLEAR 0x080c |
26 | #define L3C_CORE_CTRL 0x1b04 |
27 | #define L3C_TRACETAG_CTRL 0x1b20 |
28 | #define L3C_DATSRC_TYPE 0x1b48 |
29 | #define L3C_DATSRC_CTRL 0x1bf0 |
30 | #define L3C_EVENT_CTRL 0x1c00 |
31 | #define L3C_VERSION 0x1cf0 |
32 | #define L3C_EVENT_TYPE0 0x1d00 |
33 | /* |
34 | * If the HW version only supports a 48-bit counter, then |
35 | * bits [63:48] are reserved, which are Read-As-Zero and |
36 | * Writes-Ignored. |
37 | */ |
38 | #define L3C_CNTR0_LOWER 0x1e00 |
39 | |
40 | /* L3C has 8-counters */ |
41 | #define L3C_NR_COUNTERS 0x8 |
42 | |
43 | #define L3C_PERF_CTRL_EN 0x10000 |
44 | #define L3C_TRACETAG_EN BIT(31) |
45 | #define L3C_TRACETAG_REQ_SHIFT 7 |
46 | #define L3C_TRACETAG_MARK_EN BIT(0) |
47 | #define L3C_TRACETAG_REQ_EN (L3C_TRACETAG_MARK_EN | BIT(2)) |
48 | #define L3C_TRACETAG_CORE_EN (L3C_TRACETAG_MARK_EN | BIT(3)) |
49 | #define L3C_CORE_EN BIT(20) |
50 | #define L3C_COER_NONE 0x0 |
51 | #define L3C_DATSRC_MASK 0xFF |
52 | #define L3C_DATSRC_SKT_EN BIT(23) |
53 | #define L3C_DATSRC_NONE 0x0 |
54 | #define L3C_EVTYPE_NONE 0xff |
55 | #define L3C_V1_NR_EVENTS 0x59 |
56 | #define L3C_V2_NR_EVENTS 0xFF |
57 | |
58 | HISI_PMU_EVENT_ATTR_EXTRACTOR(tt_core, config1, 7, 0); |
59 | HISI_PMU_EVENT_ATTR_EXTRACTOR(tt_req, config1, 10, 8); |
60 | HISI_PMU_EVENT_ATTR_EXTRACTOR(datasrc_cfg, config1, 15, 11); |
61 | HISI_PMU_EVENT_ATTR_EXTRACTOR(datasrc_skt, config1, 16, 16); |
62 | |
63 | static void hisi_l3c_pmu_config_req_tracetag(struct perf_event *event) |
64 | { |
65 | struct hisi_pmu *l3c_pmu = to_hisi_pmu(event->pmu); |
66 | u32 tt_req = hisi_get_tt_req(event); |
67 | |
68 | if (tt_req) { |
69 | u32 val; |
70 | |
71 | /* Set request-type for tracetag */ |
72 | val = readl(addr: l3c_pmu->base + L3C_TRACETAG_CTRL); |
73 | val |= tt_req << L3C_TRACETAG_REQ_SHIFT; |
74 | val |= L3C_TRACETAG_REQ_EN; |
75 | writel(val, addr: l3c_pmu->base + L3C_TRACETAG_CTRL); |
76 | |
77 | /* Enable request-tracetag statistics */ |
78 | val = readl(addr: l3c_pmu->base + L3C_PERF_CTRL); |
79 | val |= L3C_TRACETAG_EN; |
80 | writel(val, addr: l3c_pmu->base + L3C_PERF_CTRL); |
81 | } |
82 | } |
83 | |
84 | static void hisi_l3c_pmu_clear_req_tracetag(struct perf_event *event) |
85 | { |
86 | struct hisi_pmu *l3c_pmu = to_hisi_pmu(event->pmu); |
87 | u32 tt_req = hisi_get_tt_req(event); |
88 | |
89 | if (tt_req) { |
90 | u32 val; |
91 | |
92 | /* Clear request-type */ |
93 | val = readl(addr: l3c_pmu->base + L3C_TRACETAG_CTRL); |
94 | val &= ~(tt_req << L3C_TRACETAG_REQ_SHIFT); |
95 | val &= ~L3C_TRACETAG_REQ_EN; |
96 | writel(val, addr: l3c_pmu->base + L3C_TRACETAG_CTRL); |
97 | |
98 | /* Disable request-tracetag statistics */ |
99 | val = readl(addr: l3c_pmu->base + L3C_PERF_CTRL); |
100 | val &= ~L3C_TRACETAG_EN; |
101 | writel(val, addr: l3c_pmu->base + L3C_PERF_CTRL); |
102 | } |
103 | } |
104 | |
105 | static void hisi_l3c_pmu_write_ds(struct perf_event *event, u32 ds_cfg) |
106 | { |
107 | struct hisi_pmu *l3c_pmu = to_hisi_pmu(event->pmu); |
108 | struct hw_perf_event *hwc = &event->hw; |
109 | u32 reg, reg_idx, shift, val; |
110 | int idx = hwc->idx; |
111 | |
112 | /* |
113 | * Select the appropriate datasource register(L3C_DATSRC_TYPE0/1). |
114 | * There are 2 datasource ctrl register for the 8 hardware counters. |
115 | * Datasrc is 8-bits and for the former 4 hardware counters, |
116 | * L3C_DATSRC_TYPE0 is chosen. For the latter 4 hardware counters, |
117 | * L3C_DATSRC_TYPE1 is chosen. |
118 | */ |
119 | reg = L3C_DATSRC_TYPE + (idx / 4) * 4; |
120 | reg_idx = idx % 4; |
121 | shift = 8 * reg_idx; |
122 | |
123 | val = readl(addr: l3c_pmu->base + reg); |
124 | val &= ~(L3C_DATSRC_MASK << shift); |
125 | val |= ds_cfg << shift; |
126 | writel(val, addr: l3c_pmu->base + reg); |
127 | } |
128 | |
129 | static void hisi_l3c_pmu_config_ds(struct perf_event *event) |
130 | { |
131 | struct hisi_pmu *l3c_pmu = to_hisi_pmu(event->pmu); |
132 | u32 ds_cfg = hisi_get_datasrc_cfg(event); |
133 | u32 ds_skt = hisi_get_datasrc_skt(event); |
134 | |
135 | if (ds_cfg) |
136 | hisi_l3c_pmu_write_ds(event, ds_cfg); |
137 | |
138 | if (ds_skt) { |
139 | u32 val; |
140 | |
141 | val = readl(addr: l3c_pmu->base + L3C_DATSRC_CTRL); |
142 | val |= L3C_DATSRC_SKT_EN; |
143 | writel(val, addr: l3c_pmu->base + L3C_DATSRC_CTRL); |
144 | } |
145 | } |
146 | |
147 | static void hisi_l3c_pmu_clear_ds(struct perf_event *event) |
148 | { |
149 | struct hisi_pmu *l3c_pmu = to_hisi_pmu(event->pmu); |
150 | u32 ds_cfg = hisi_get_datasrc_cfg(event); |
151 | u32 ds_skt = hisi_get_datasrc_skt(event); |
152 | |
153 | if (ds_cfg) |
154 | hisi_l3c_pmu_write_ds(event, L3C_DATSRC_NONE); |
155 | |
156 | if (ds_skt) { |
157 | u32 val; |
158 | |
159 | val = readl(addr: l3c_pmu->base + L3C_DATSRC_CTRL); |
160 | val &= ~L3C_DATSRC_SKT_EN; |
161 | writel(val, addr: l3c_pmu->base + L3C_DATSRC_CTRL); |
162 | } |
163 | } |
164 | |
165 | static void hisi_l3c_pmu_config_core_tracetag(struct perf_event *event) |
166 | { |
167 | struct hisi_pmu *l3c_pmu = to_hisi_pmu(event->pmu); |
168 | u32 core = hisi_get_tt_core(event); |
169 | |
170 | if (core) { |
171 | u32 val; |
172 | |
173 | /* Config and enable core information */ |
174 | writel(val: core, addr: l3c_pmu->base + L3C_CORE_CTRL); |
175 | val = readl(addr: l3c_pmu->base + L3C_PERF_CTRL); |
176 | val |= L3C_CORE_EN; |
177 | writel(val, addr: l3c_pmu->base + L3C_PERF_CTRL); |
178 | |
179 | /* Enable core-tracetag statistics */ |
180 | val = readl(addr: l3c_pmu->base + L3C_TRACETAG_CTRL); |
181 | val |= L3C_TRACETAG_CORE_EN; |
182 | writel(val, addr: l3c_pmu->base + L3C_TRACETAG_CTRL); |
183 | } |
184 | } |
185 | |
186 | static void hisi_l3c_pmu_clear_core_tracetag(struct perf_event *event) |
187 | { |
188 | struct hisi_pmu *l3c_pmu = to_hisi_pmu(event->pmu); |
189 | u32 core = hisi_get_tt_core(event); |
190 | |
191 | if (core) { |
192 | u32 val; |
193 | |
194 | /* Clear core information */ |
195 | writel(L3C_COER_NONE, addr: l3c_pmu->base + L3C_CORE_CTRL); |
196 | val = readl(addr: l3c_pmu->base + L3C_PERF_CTRL); |
197 | val &= ~L3C_CORE_EN; |
198 | writel(val, addr: l3c_pmu->base + L3C_PERF_CTRL); |
199 | |
200 | /* Disable core-tracetag statistics */ |
201 | val = readl(addr: l3c_pmu->base + L3C_TRACETAG_CTRL); |
202 | val &= ~L3C_TRACETAG_CORE_EN; |
203 | writel(val, addr: l3c_pmu->base + L3C_TRACETAG_CTRL); |
204 | } |
205 | } |
206 | |
207 | static void hisi_l3c_pmu_enable_filter(struct perf_event *event) |
208 | { |
209 | if (event->attr.config1 != 0x0) { |
210 | hisi_l3c_pmu_config_req_tracetag(event); |
211 | hisi_l3c_pmu_config_core_tracetag(event); |
212 | hisi_l3c_pmu_config_ds(event); |
213 | } |
214 | } |
215 | |
216 | static void hisi_l3c_pmu_disable_filter(struct perf_event *event) |
217 | { |
218 | if (event->attr.config1 != 0x0) { |
219 | hisi_l3c_pmu_clear_ds(event); |
220 | hisi_l3c_pmu_clear_core_tracetag(event); |
221 | hisi_l3c_pmu_clear_req_tracetag(event); |
222 | } |
223 | } |
224 | |
225 | /* |
226 | * Select the counter register offset using the counter index |
227 | */ |
228 | static u32 hisi_l3c_pmu_get_counter_offset(int cntr_idx) |
229 | { |
230 | return (L3C_CNTR0_LOWER + (cntr_idx * 8)); |
231 | } |
232 | |
233 | static u64 hisi_l3c_pmu_read_counter(struct hisi_pmu *l3c_pmu, |
234 | struct hw_perf_event *hwc) |
235 | { |
236 | return readq(addr: l3c_pmu->base + hisi_l3c_pmu_get_counter_offset(cntr_idx: hwc->idx)); |
237 | } |
238 | |
239 | static void hisi_l3c_pmu_write_counter(struct hisi_pmu *l3c_pmu, |
240 | struct hw_perf_event *hwc, u64 val) |
241 | { |
242 | writeq(val, addr: l3c_pmu->base + hisi_l3c_pmu_get_counter_offset(cntr_idx: hwc->idx)); |
243 | } |
244 | |
245 | static void hisi_l3c_pmu_write_evtype(struct hisi_pmu *l3c_pmu, int idx, |
246 | u32 type) |
247 | { |
248 | u32 reg, reg_idx, shift, val; |
249 | |
250 | /* |
251 | * Select the appropriate event select register(L3C_EVENT_TYPE0/1). |
252 | * There are 2 event select registers for the 8 hardware counters. |
253 | * Event code is 8-bits and for the former 4 hardware counters, |
254 | * L3C_EVENT_TYPE0 is chosen. For the latter 4 hardware counters, |
255 | * L3C_EVENT_TYPE1 is chosen. |
256 | */ |
257 | reg = L3C_EVENT_TYPE0 + (idx / 4) * 4; |
258 | reg_idx = idx % 4; |
259 | shift = 8 * reg_idx; |
260 | |
261 | /* Write event code to L3C_EVENT_TYPEx Register */ |
262 | val = readl(addr: l3c_pmu->base + reg); |
263 | val &= ~(L3C_EVTYPE_NONE << shift); |
264 | val |= (type << shift); |
265 | writel(val, addr: l3c_pmu->base + reg); |
266 | } |
267 | |
268 | static void hisi_l3c_pmu_start_counters(struct hisi_pmu *l3c_pmu) |
269 | { |
270 | u32 val; |
271 | |
272 | /* |
273 | * Set perf_enable bit in L3C_PERF_CTRL register to start counting |
274 | * for all enabled counters. |
275 | */ |
276 | val = readl(addr: l3c_pmu->base + L3C_PERF_CTRL); |
277 | val |= L3C_PERF_CTRL_EN; |
278 | writel(val, addr: l3c_pmu->base + L3C_PERF_CTRL); |
279 | } |
280 | |
281 | static void hisi_l3c_pmu_stop_counters(struct hisi_pmu *l3c_pmu) |
282 | { |
283 | u32 val; |
284 | |
285 | /* |
286 | * Clear perf_enable bit in L3C_PERF_CTRL register to stop counting |
287 | * for all enabled counters. |
288 | */ |
289 | val = readl(addr: l3c_pmu->base + L3C_PERF_CTRL); |
290 | val &= ~(L3C_PERF_CTRL_EN); |
291 | writel(val, addr: l3c_pmu->base + L3C_PERF_CTRL); |
292 | } |
293 | |
294 | static void hisi_l3c_pmu_enable_counter(struct hisi_pmu *l3c_pmu, |
295 | struct hw_perf_event *hwc) |
296 | { |
297 | u32 val; |
298 | |
299 | /* Enable counter index in L3C_EVENT_CTRL register */ |
300 | val = readl(addr: l3c_pmu->base + L3C_EVENT_CTRL); |
301 | val |= (1 << hwc->idx); |
302 | writel(val, addr: l3c_pmu->base + L3C_EVENT_CTRL); |
303 | } |
304 | |
305 | static void hisi_l3c_pmu_disable_counter(struct hisi_pmu *l3c_pmu, |
306 | struct hw_perf_event *hwc) |
307 | { |
308 | u32 val; |
309 | |
310 | /* Clear counter index in L3C_EVENT_CTRL register */ |
311 | val = readl(addr: l3c_pmu->base + L3C_EVENT_CTRL); |
312 | val &= ~(1 << hwc->idx); |
313 | writel(val, addr: l3c_pmu->base + L3C_EVENT_CTRL); |
314 | } |
315 | |
316 | static void hisi_l3c_pmu_enable_counter_int(struct hisi_pmu *l3c_pmu, |
317 | struct hw_perf_event *hwc) |
318 | { |
319 | u32 val; |
320 | |
321 | val = readl(addr: l3c_pmu->base + L3C_INT_MASK); |
322 | /* Write 0 to enable interrupt */ |
323 | val &= ~(1 << hwc->idx); |
324 | writel(val, addr: l3c_pmu->base + L3C_INT_MASK); |
325 | } |
326 | |
327 | static void hisi_l3c_pmu_disable_counter_int(struct hisi_pmu *l3c_pmu, |
328 | struct hw_perf_event *hwc) |
329 | { |
330 | u32 val; |
331 | |
332 | val = readl(addr: l3c_pmu->base + L3C_INT_MASK); |
333 | /* Write 1 to mask interrupt */ |
334 | val |= (1 << hwc->idx); |
335 | writel(val, addr: l3c_pmu->base + L3C_INT_MASK); |
336 | } |
337 | |
338 | static u32 hisi_l3c_pmu_get_int_status(struct hisi_pmu *l3c_pmu) |
339 | { |
340 | return readl(addr: l3c_pmu->base + L3C_INT_STATUS); |
341 | } |
342 | |
343 | static void hisi_l3c_pmu_clear_int_status(struct hisi_pmu *l3c_pmu, int idx) |
344 | { |
345 | writel(val: 1 << idx, addr: l3c_pmu->base + L3C_INT_CLEAR); |
346 | } |
347 | |
348 | static const struct acpi_device_id hisi_l3c_pmu_acpi_match[] = { |
349 | { "HISI0213" , }, |
350 | { "HISI0214" , }, |
351 | {} |
352 | }; |
353 | MODULE_DEVICE_TABLE(acpi, hisi_l3c_pmu_acpi_match); |
354 | |
355 | static int hisi_l3c_pmu_init_data(struct platform_device *pdev, |
356 | struct hisi_pmu *l3c_pmu) |
357 | { |
358 | /* |
359 | * Use the SCCL_ID and CCL_ID to identify the L3C PMU, while |
360 | * SCCL_ID is in MPIDR[aff2] and CCL_ID is in MPIDR[aff1]. |
361 | */ |
362 | if (device_property_read_u32(dev: &pdev->dev, propname: "hisilicon,scl-id" , |
363 | val: &l3c_pmu->sccl_id)) { |
364 | dev_err(&pdev->dev, "Can not read l3c sccl-id!\n" ); |
365 | return -EINVAL; |
366 | } |
367 | |
368 | if (device_property_read_u32(dev: &pdev->dev, propname: "hisilicon,ccl-id" , |
369 | val: &l3c_pmu->ccl_id)) { |
370 | dev_err(&pdev->dev, "Can not read l3c ccl-id!\n" ); |
371 | return -EINVAL; |
372 | } |
373 | |
374 | l3c_pmu->base = devm_platform_ioremap_resource(pdev, index: 0); |
375 | if (IS_ERR(ptr: l3c_pmu->base)) { |
376 | dev_err(&pdev->dev, "ioremap failed for l3c_pmu resource\n" ); |
377 | return PTR_ERR(ptr: l3c_pmu->base); |
378 | } |
379 | |
380 | l3c_pmu->identifier = readl(addr: l3c_pmu->base + L3C_VERSION); |
381 | |
382 | return 0; |
383 | } |
384 | |
385 | static struct attribute *hisi_l3c_pmu_v1_format_attr[] = { |
386 | HISI_PMU_FORMAT_ATTR(event, "config:0-7" ), |
387 | NULL, |
388 | }; |
389 | |
390 | static const struct attribute_group hisi_l3c_pmu_v1_format_group = { |
391 | .name = "format" , |
392 | .attrs = hisi_l3c_pmu_v1_format_attr, |
393 | }; |
394 | |
395 | static struct attribute *hisi_l3c_pmu_v2_format_attr[] = { |
396 | HISI_PMU_FORMAT_ATTR(event, "config:0-7" ), |
397 | HISI_PMU_FORMAT_ATTR(tt_core, "config1:0-7" ), |
398 | HISI_PMU_FORMAT_ATTR(tt_req, "config1:8-10" ), |
399 | HISI_PMU_FORMAT_ATTR(datasrc_cfg, "config1:11-15" ), |
400 | HISI_PMU_FORMAT_ATTR(datasrc_skt, "config1:16" ), |
401 | NULL |
402 | }; |
403 | |
404 | static const struct attribute_group hisi_l3c_pmu_v2_format_group = { |
405 | .name = "format" , |
406 | .attrs = hisi_l3c_pmu_v2_format_attr, |
407 | }; |
408 | |
409 | static struct attribute *hisi_l3c_pmu_v1_events_attr[] = { |
410 | HISI_PMU_EVENT_ATTR(rd_cpipe, 0x00), |
411 | HISI_PMU_EVENT_ATTR(wr_cpipe, 0x01), |
412 | HISI_PMU_EVENT_ATTR(rd_hit_cpipe, 0x02), |
413 | HISI_PMU_EVENT_ATTR(wr_hit_cpipe, 0x03), |
414 | HISI_PMU_EVENT_ATTR(victim_num, 0x04), |
415 | HISI_PMU_EVENT_ATTR(rd_spipe, 0x20), |
416 | HISI_PMU_EVENT_ATTR(wr_spipe, 0x21), |
417 | HISI_PMU_EVENT_ATTR(rd_hit_spipe, 0x22), |
418 | HISI_PMU_EVENT_ATTR(wr_hit_spipe, 0x23), |
419 | HISI_PMU_EVENT_ATTR(back_invalid, 0x29), |
420 | HISI_PMU_EVENT_ATTR(retry_cpu, 0x40), |
421 | HISI_PMU_EVENT_ATTR(retry_ring, 0x41), |
422 | HISI_PMU_EVENT_ATTR(prefetch_drop, 0x42), |
423 | NULL, |
424 | }; |
425 | |
426 | static const struct attribute_group hisi_l3c_pmu_v1_events_group = { |
427 | .name = "events" , |
428 | .attrs = hisi_l3c_pmu_v1_events_attr, |
429 | }; |
430 | |
431 | static struct attribute *hisi_l3c_pmu_v2_events_attr[] = { |
432 | HISI_PMU_EVENT_ATTR(l3c_hit, 0x48), |
433 | HISI_PMU_EVENT_ATTR(cycles, 0x7f), |
434 | HISI_PMU_EVENT_ATTR(l3c_ref, 0xb8), |
435 | HISI_PMU_EVENT_ATTR(dat_access, 0xb9), |
436 | NULL |
437 | }; |
438 | |
439 | static const struct attribute_group hisi_l3c_pmu_v2_events_group = { |
440 | .name = "events" , |
441 | .attrs = hisi_l3c_pmu_v2_events_attr, |
442 | }; |
443 | |
444 | static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL); |
445 | |
446 | static struct attribute *hisi_l3c_pmu_cpumask_attrs[] = { |
447 | &dev_attr_cpumask.attr, |
448 | NULL, |
449 | }; |
450 | |
451 | static const struct attribute_group hisi_l3c_pmu_cpumask_attr_group = { |
452 | .attrs = hisi_l3c_pmu_cpumask_attrs, |
453 | }; |
454 | |
455 | static struct device_attribute hisi_l3c_pmu_identifier_attr = |
456 | __ATTR(identifier, 0444, hisi_uncore_pmu_identifier_attr_show, NULL); |
457 | |
458 | static struct attribute *hisi_l3c_pmu_identifier_attrs[] = { |
459 | &hisi_l3c_pmu_identifier_attr.attr, |
460 | NULL |
461 | }; |
462 | |
463 | static const struct attribute_group hisi_l3c_pmu_identifier_group = { |
464 | .attrs = hisi_l3c_pmu_identifier_attrs, |
465 | }; |
466 | |
467 | static const struct attribute_group *hisi_l3c_pmu_v1_attr_groups[] = { |
468 | &hisi_l3c_pmu_v1_format_group, |
469 | &hisi_l3c_pmu_v1_events_group, |
470 | &hisi_l3c_pmu_cpumask_attr_group, |
471 | &hisi_l3c_pmu_identifier_group, |
472 | NULL, |
473 | }; |
474 | |
475 | static const struct attribute_group *hisi_l3c_pmu_v2_attr_groups[] = { |
476 | &hisi_l3c_pmu_v2_format_group, |
477 | &hisi_l3c_pmu_v2_events_group, |
478 | &hisi_l3c_pmu_cpumask_attr_group, |
479 | &hisi_l3c_pmu_identifier_group, |
480 | NULL |
481 | }; |
482 | |
483 | static const struct hisi_uncore_ops hisi_uncore_l3c_ops = { |
484 | .write_evtype = hisi_l3c_pmu_write_evtype, |
485 | .get_event_idx = hisi_uncore_pmu_get_event_idx, |
486 | .start_counters = hisi_l3c_pmu_start_counters, |
487 | .stop_counters = hisi_l3c_pmu_stop_counters, |
488 | .enable_counter = hisi_l3c_pmu_enable_counter, |
489 | .disable_counter = hisi_l3c_pmu_disable_counter, |
490 | .enable_counter_int = hisi_l3c_pmu_enable_counter_int, |
491 | .disable_counter_int = hisi_l3c_pmu_disable_counter_int, |
492 | .write_counter = hisi_l3c_pmu_write_counter, |
493 | .read_counter = hisi_l3c_pmu_read_counter, |
494 | .get_int_status = hisi_l3c_pmu_get_int_status, |
495 | .clear_int_status = hisi_l3c_pmu_clear_int_status, |
496 | .enable_filter = hisi_l3c_pmu_enable_filter, |
497 | .disable_filter = hisi_l3c_pmu_disable_filter, |
498 | }; |
499 | |
500 | static int hisi_l3c_pmu_dev_probe(struct platform_device *pdev, |
501 | struct hisi_pmu *l3c_pmu) |
502 | { |
503 | int ret; |
504 | |
505 | ret = hisi_l3c_pmu_init_data(pdev, l3c_pmu); |
506 | if (ret) |
507 | return ret; |
508 | |
509 | ret = hisi_uncore_pmu_init_irq(hisi_pmu: l3c_pmu, pdev); |
510 | if (ret) |
511 | return ret; |
512 | |
513 | if (l3c_pmu->identifier >= HISI_PMU_V2) { |
514 | l3c_pmu->counter_bits = 64; |
515 | l3c_pmu->check_event = L3C_V2_NR_EVENTS; |
516 | l3c_pmu->pmu_events.attr_groups = hisi_l3c_pmu_v2_attr_groups; |
517 | } else { |
518 | l3c_pmu->counter_bits = 48; |
519 | l3c_pmu->check_event = L3C_V1_NR_EVENTS; |
520 | l3c_pmu->pmu_events.attr_groups = hisi_l3c_pmu_v1_attr_groups; |
521 | } |
522 | |
523 | l3c_pmu->num_counters = L3C_NR_COUNTERS; |
524 | l3c_pmu->ops = &hisi_uncore_l3c_ops; |
525 | l3c_pmu->dev = &pdev->dev; |
526 | l3c_pmu->on_cpu = -1; |
527 | |
528 | return 0; |
529 | } |
530 | |
531 | static int hisi_l3c_pmu_probe(struct platform_device *pdev) |
532 | { |
533 | struct hisi_pmu *l3c_pmu; |
534 | char *name; |
535 | int ret; |
536 | |
537 | l3c_pmu = devm_kzalloc(dev: &pdev->dev, size: sizeof(*l3c_pmu), GFP_KERNEL); |
538 | if (!l3c_pmu) |
539 | return -ENOMEM; |
540 | |
541 | platform_set_drvdata(pdev, data: l3c_pmu); |
542 | |
543 | ret = hisi_l3c_pmu_dev_probe(pdev, l3c_pmu); |
544 | if (ret) |
545 | return ret; |
546 | |
547 | name = devm_kasprintf(dev: &pdev->dev, GFP_KERNEL, fmt: "hisi_sccl%u_l3c%u" , |
548 | l3c_pmu->sccl_id, l3c_pmu->ccl_id); |
549 | if (!name) |
550 | return -ENOMEM; |
551 | |
552 | ret = cpuhp_state_add_instance(state: CPUHP_AP_PERF_ARM_HISI_L3_ONLINE, |
553 | node: &l3c_pmu->node); |
554 | if (ret) { |
555 | dev_err(&pdev->dev, "Error %d registering hotplug\n" , ret); |
556 | return ret; |
557 | } |
558 | |
559 | hisi_pmu_init(hisi_pmu: l3c_pmu, THIS_MODULE); |
560 | |
561 | ret = perf_pmu_register(pmu: &l3c_pmu->pmu, name, type: -1); |
562 | if (ret) { |
563 | dev_err(l3c_pmu->dev, "L3C PMU register failed!\n" ); |
564 | cpuhp_state_remove_instance_nocalls( |
565 | state: CPUHP_AP_PERF_ARM_HISI_L3_ONLINE, node: &l3c_pmu->node); |
566 | } |
567 | |
568 | return ret; |
569 | } |
570 | |
571 | static int hisi_l3c_pmu_remove(struct platform_device *pdev) |
572 | { |
573 | struct hisi_pmu *l3c_pmu = platform_get_drvdata(pdev); |
574 | |
575 | perf_pmu_unregister(pmu: &l3c_pmu->pmu); |
576 | cpuhp_state_remove_instance_nocalls(state: CPUHP_AP_PERF_ARM_HISI_L3_ONLINE, |
577 | node: &l3c_pmu->node); |
578 | return 0; |
579 | } |
580 | |
581 | static struct platform_driver hisi_l3c_pmu_driver = { |
582 | .driver = { |
583 | .name = "hisi_l3c_pmu" , |
584 | .acpi_match_table = ACPI_PTR(hisi_l3c_pmu_acpi_match), |
585 | .suppress_bind_attrs = true, |
586 | }, |
587 | .probe = hisi_l3c_pmu_probe, |
588 | .remove = hisi_l3c_pmu_remove, |
589 | }; |
590 | |
591 | static int __init hisi_l3c_pmu_module_init(void) |
592 | { |
593 | int ret; |
594 | |
595 | ret = cpuhp_setup_state_multi(state: CPUHP_AP_PERF_ARM_HISI_L3_ONLINE, |
596 | name: "AP_PERF_ARM_HISI_L3_ONLINE" , |
597 | startup: hisi_uncore_pmu_online_cpu, |
598 | teardown: hisi_uncore_pmu_offline_cpu); |
599 | if (ret) { |
600 | pr_err("L3C PMU: Error setup hotplug, ret = %d\n" , ret); |
601 | return ret; |
602 | } |
603 | |
604 | ret = platform_driver_register(&hisi_l3c_pmu_driver); |
605 | if (ret) |
606 | cpuhp_remove_multi_state(state: CPUHP_AP_PERF_ARM_HISI_L3_ONLINE); |
607 | |
608 | return ret; |
609 | } |
610 | module_init(hisi_l3c_pmu_module_init); |
611 | |
612 | static void __exit hisi_l3c_pmu_module_exit(void) |
613 | { |
614 | platform_driver_unregister(&hisi_l3c_pmu_driver); |
615 | cpuhp_remove_multi_state(state: CPUHP_AP_PERF_ARM_HISI_L3_ONLINE); |
616 | } |
617 | module_exit(hisi_l3c_pmu_module_exit); |
618 | |
619 | MODULE_DESCRIPTION("HiSilicon SoC L3C uncore PMU driver" ); |
620 | MODULE_LICENSE("GPL v2" ); |
621 | MODULE_AUTHOR("Anurup M <anurup.m@huawei.com>" ); |
622 | MODULE_AUTHOR("Shaokun Zhang <zhangshaokun@hisilicon.com>" ); |
623 | |