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 
hisi_l3c_pmu_config_req_tracetag(struct perf_event * event)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(l3c_pmu->base + L3C_TRACETAG_CTRL);
73 		val |= tt_req << L3C_TRACETAG_REQ_SHIFT;
74 		val |= L3C_TRACETAG_REQ_EN;
75 		writel(val, l3c_pmu->base + L3C_TRACETAG_CTRL);
76 
77 		/* Enable request-tracetag statistics */
78 		val = readl(l3c_pmu->base + L3C_PERF_CTRL);
79 		val |= L3C_TRACETAG_EN;
80 		writel(val, l3c_pmu->base + L3C_PERF_CTRL);
81 	}
82 }
83 
hisi_l3c_pmu_clear_req_tracetag(struct perf_event * event)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(l3c_pmu->base + L3C_TRACETAG_CTRL);
94 		val &= ~(tt_req << L3C_TRACETAG_REQ_SHIFT);
95 		val &= ~L3C_TRACETAG_REQ_EN;
96 		writel(val, l3c_pmu->base + L3C_TRACETAG_CTRL);
97 
98 		/* Disable request-tracetag statistics */
99 		val = readl(l3c_pmu->base + L3C_PERF_CTRL);
100 		val &= ~L3C_TRACETAG_EN;
101 		writel(val, l3c_pmu->base + L3C_PERF_CTRL);
102 	}
103 }
104 
hisi_l3c_pmu_write_ds(struct perf_event * event,u32 ds_cfg)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(l3c_pmu->base + reg);
124 	val &= ~(L3C_DATSRC_MASK << shift);
125 	val |= ds_cfg << shift;
126 	writel(val, l3c_pmu->base + reg);
127 }
128 
hisi_l3c_pmu_config_ds(struct perf_event * event)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(l3c_pmu->base + L3C_DATSRC_CTRL);
142 		val |= L3C_DATSRC_SKT_EN;
143 		writel(val, l3c_pmu->base + L3C_DATSRC_CTRL);
144 	}
145 }
146 
hisi_l3c_pmu_clear_ds(struct perf_event * event)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(l3c_pmu->base + L3C_DATSRC_CTRL);
160 		val &= ~L3C_DATSRC_SKT_EN;
161 		writel(val, l3c_pmu->base + L3C_DATSRC_CTRL);
162 	}
163 }
164 
hisi_l3c_pmu_config_core_tracetag(struct perf_event * event)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(core, l3c_pmu->base + L3C_CORE_CTRL);
175 		val = readl(l3c_pmu->base + L3C_PERF_CTRL);
176 		val |= L3C_CORE_EN;
177 		writel(val, l3c_pmu->base + L3C_PERF_CTRL);
178 
179 		/* Enable core-tracetag statistics */
180 		val = readl(l3c_pmu->base + L3C_TRACETAG_CTRL);
181 		val |= L3C_TRACETAG_CORE_EN;
182 		writel(val, l3c_pmu->base + L3C_TRACETAG_CTRL);
183 	}
184 }
185 
hisi_l3c_pmu_clear_core_tracetag(struct perf_event * event)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, l3c_pmu->base + L3C_CORE_CTRL);
196 		val = readl(l3c_pmu->base + L3C_PERF_CTRL);
197 		val &= ~L3C_CORE_EN;
198 		writel(val, l3c_pmu->base + L3C_PERF_CTRL);
199 
200 		/* Disable core-tracetag statistics */
201 		val = readl(l3c_pmu->base + L3C_TRACETAG_CTRL);
202 		val &= ~L3C_TRACETAG_CORE_EN;
203 		writel(val, l3c_pmu->base + L3C_TRACETAG_CTRL);
204 	}
205 }
206 
hisi_l3c_pmu_enable_filter(struct perf_event * event)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 
hisi_l3c_pmu_disable_filter(struct perf_event * event)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  */
hisi_l3c_pmu_get_counter_offset(int cntr_idx)228 static u32 hisi_l3c_pmu_get_counter_offset(int cntr_idx)
229 {
230 	return (L3C_CNTR0_LOWER + (cntr_idx * 8));
231 }
232 
hisi_l3c_pmu_read_counter(struct hisi_pmu * l3c_pmu,struct hw_perf_event * hwc)233 static u64 hisi_l3c_pmu_read_counter(struct hisi_pmu *l3c_pmu,
234 				     struct hw_perf_event *hwc)
235 {
236 	return readq(l3c_pmu->base + hisi_l3c_pmu_get_counter_offset(hwc->idx));
237 }
238 
hisi_l3c_pmu_write_counter(struct hisi_pmu * l3c_pmu,struct hw_perf_event * hwc,u64 val)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, l3c_pmu->base + hisi_l3c_pmu_get_counter_offset(hwc->idx));
243 }
244 
hisi_l3c_pmu_write_evtype(struct hisi_pmu * l3c_pmu,int idx,u32 type)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(l3c_pmu->base + reg);
263 	val &= ~(L3C_EVTYPE_NONE << shift);
264 	val |= (type << shift);
265 	writel(val, l3c_pmu->base + reg);
266 }
267 
hisi_l3c_pmu_start_counters(struct hisi_pmu * l3c_pmu)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(l3c_pmu->base + L3C_PERF_CTRL);
277 	val |= L3C_PERF_CTRL_EN;
278 	writel(val, l3c_pmu->base + L3C_PERF_CTRL);
279 }
280 
hisi_l3c_pmu_stop_counters(struct hisi_pmu * l3c_pmu)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(l3c_pmu->base + L3C_PERF_CTRL);
290 	val &= ~(L3C_PERF_CTRL_EN);
291 	writel(val, l3c_pmu->base + L3C_PERF_CTRL);
292 }
293 
hisi_l3c_pmu_enable_counter(struct hisi_pmu * l3c_pmu,struct hw_perf_event * hwc)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(l3c_pmu->base + L3C_EVENT_CTRL);
301 	val |= (1 << hwc->idx);
302 	writel(val, l3c_pmu->base + L3C_EVENT_CTRL);
303 }
304 
hisi_l3c_pmu_disable_counter(struct hisi_pmu * l3c_pmu,struct hw_perf_event * hwc)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(l3c_pmu->base + L3C_EVENT_CTRL);
312 	val &= ~(1 << hwc->idx);
313 	writel(val, l3c_pmu->base + L3C_EVENT_CTRL);
314 }
315 
hisi_l3c_pmu_enable_counter_int(struct hisi_pmu * l3c_pmu,struct hw_perf_event * hwc)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(l3c_pmu->base + L3C_INT_MASK);
322 	/* Write 0 to enable interrupt */
323 	val &= ~(1 << hwc->idx);
324 	writel(val, l3c_pmu->base + L3C_INT_MASK);
325 }
326 
hisi_l3c_pmu_disable_counter_int(struct hisi_pmu * l3c_pmu,struct hw_perf_event * hwc)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(l3c_pmu->base + L3C_INT_MASK);
333 	/* Write 1 to mask interrupt */
334 	val |= (1 << hwc->idx);
335 	writel(val, l3c_pmu->base + L3C_INT_MASK);
336 }
337 
hisi_l3c_pmu_get_int_status(struct hisi_pmu * l3c_pmu)338 static u32 hisi_l3c_pmu_get_int_status(struct hisi_pmu *l3c_pmu)
339 {
340 	return readl(l3c_pmu->base + L3C_INT_STATUS);
341 }
342 
hisi_l3c_pmu_clear_int_status(struct hisi_pmu * l3c_pmu,int idx)343 static void hisi_l3c_pmu_clear_int_status(struct hisi_pmu *l3c_pmu, int idx)
344 {
345 	writel(1 << idx, 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 
hisi_l3c_pmu_init_data(struct platform_device * pdev,struct hisi_pmu * l3c_pmu)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(&pdev->dev, "hisilicon,scl-id",
363 				     &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(&pdev->dev, "hisilicon,ccl-id",
369 				     &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, 0);
375 	if (IS_ERR(l3c_pmu->base)) {
376 		dev_err(&pdev->dev, "ioremap failed for l3c_pmu resource\n");
377 		return PTR_ERR(l3c_pmu->base);
378 	}
379 
380 	l3c_pmu->identifier = readl(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 
hisi_l3c_pmu_dev_probe(struct platform_device * pdev,struct hisi_pmu * l3c_pmu)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(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 
hisi_l3c_pmu_probe(struct platform_device * pdev)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(&pdev->dev, sizeof(*l3c_pmu), GFP_KERNEL);
538 	if (!l3c_pmu)
539 		return -ENOMEM;
540 
541 	platform_set_drvdata(pdev, l3c_pmu);
542 
543 	ret = hisi_l3c_pmu_dev_probe(pdev, l3c_pmu);
544 	if (ret)
545 		return ret;
546 
547 	ret = cpuhp_state_add_instance(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE,
548 				       &l3c_pmu->node);
549 	if (ret) {
550 		dev_err(&pdev->dev, "Error %d registering hotplug\n", ret);
551 		return ret;
552 	}
553 
554 	/*
555 	 * CCL_ID is used to identify the L3C in the same SCCL which was
556 	 * used _UID by mistake.
557 	 */
558 	name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sccl%u_l3c%u",
559 			      l3c_pmu->sccl_id, l3c_pmu->ccl_id);
560 	l3c_pmu->pmu = (struct pmu) {
561 		.name		= name,
562 		.module		= THIS_MODULE,
563 		.task_ctx_nr	= perf_invalid_context,
564 		.event_init	= hisi_uncore_pmu_event_init,
565 		.pmu_enable	= hisi_uncore_pmu_enable,
566 		.pmu_disable	= hisi_uncore_pmu_disable,
567 		.add		= hisi_uncore_pmu_add,
568 		.del		= hisi_uncore_pmu_del,
569 		.start		= hisi_uncore_pmu_start,
570 		.stop		= hisi_uncore_pmu_stop,
571 		.read		= hisi_uncore_pmu_read,
572 		.attr_groups	= l3c_pmu->pmu_events.attr_groups,
573 		.capabilities	= PERF_PMU_CAP_NO_EXCLUDE,
574 	};
575 
576 	ret = perf_pmu_register(&l3c_pmu->pmu, name, -1);
577 	if (ret) {
578 		dev_err(l3c_pmu->dev, "L3C PMU register failed!\n");
579 		cpuhp_state_remove_instance_nocalls(
580 			CPUHP_AP_PERF_ARM_HISI_L3_ONLINE, &l3c_pmu->node);
581 	}
582 
583 	return ret;
584 }
585 
hisi_l3c_pmu_remove(struct platform_device * pdev)586 static int hisi_l3c_pmu_remove(struct platform_device *pdev)
587 {
588 	struct hisi_pmu *l3c_pmu = platform_get_drvdata(pdev);
589 
590 	perf_pmu_unregister(&l3c_pmu->pmu);
591 	cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE,
592 					    &l3c_pmu->node);
593 	return 0;
594 }
595 
596 static struct platform_driver hisi_l3c_pmu_driver = {
597 	.driver = {
598 		.name = "hisi_l3c_pmu",
599 		.acpi_match_table = ACPI_PTR(hisi_l3c_pmu_acpi_match),
600 		.suppress_bind_attrs = true,
601 	},
602 	.probe = hisi_l3c_pmu_probe,
603 	.remove = hisi_l3c_pmu_remove,
604 };
605 
hisi_l3c_pmu_module_init(void)606 static int __init hisi_l3c_pmu_module_init(void)
607 {
608 	int ret;
609 
610 	ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE,
611 				      "AP_PERF_ARM_HISI_L3_ONLINE",
612 				      hisi_uncore_pmu_online_cpu,
613 				      hisi_uncore_pmu_offline_cpu);
614 	if (ret) {
615 		pr_err("L3C PMU: Error setup hotplug, ret = %d\n", ret);
616 		return ret;
617 	}
618 
619 	ret = platform_driver_register(&hisi_l3c_pmu_driver);
620 	if (ret)
621 		cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE);
622 
623 	return ret;
624 }
625 module_init(hisi_l3c_pmu_module_init);
626 
hisi_l3c_pmu_module_exit(void)627 static void __exit hisi_l3c_pmu_module_exit(void)
628 {
629 	platform_driver_unregister(&hisi_l3c_pmu_driver);
630 	cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE);
631 }
632 module_exit(hisi_l3c_pmu_module_exit);
633 
634 MODULE_DESCRIPTION("HiSilicon SoC L3C uncore PMU driver");
635 MODULE_LICENSE("GPL v2");
636 MODULE_AUTHOR("Anurup M <anurup.m@huawei.com>");
637 MODULE_AUTHOR("Shaokun Zhang <zhangshaokun@hisilicon.com>");
638