Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
904dcf03 SZ |
2 | /* |
3 | * HiSilicon SoC DDRC uncore Hardware event counters support | |
4 | * | |
2db52237 | 5 | * Copyright (C) 2017 HiSilicon Limited |
904dcf03 SZ |
6 | * Author: Shaokun Zhang <zhangshaokun@hisilicon.com> |
7 | * Anurup M <anurup.m@huawei.com> | |
8 | * | |
9 | * This code is based on the uncore PMUs like arm-cci and arm-ccn. | |
904dcf03 SZ |
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> | |
904dcf03 SZ |
17 | #include <linux/smp.h> |
18 | ||
19 | #include "hisi_uncore_pmu.h" | |
20 | ||
cce03e70 | 21 | /* DDRC register definition in v1 */ |
904dcf03 SZ |
22 | #define DDRC_PERF_CTRL 0x010 |
23 | #define DDRC_FLUX_WR 0x380 | |
24 | #define DDRC_FLUX_RD 0x384 | |
25 | #define DDRC_FLUX_WCMD 0x388 | |
26 | #define DDRC_FLUX_RCMD 0x38c | |
27 | #define DDRC_PRE_CMD 0x3c0 | |
28 | #define DDRC_ACT_CMD 0x3c4 | |
904dcf03 | 29 | #define DDRC_RNK_CHG 0x3cc |
eb4f5213 | 30 | #define DDRC_RW_CHG 0x3d0 |
904dcf03 SZ |
31 | #define DDRC_EVENT_CTRL 0x6C0 |
32 | #define DDRC_INT_MASK 0x6c8 | |
33 | #define DDRC_INT_STATUS 0x6cc | |
34 | #define DDRC_INT_CLEAR 0x6d0 | |
ac4511c9 | 35 | #define DDRC_VERSION 0x710 |
904dcf03 | 36 | |
cce03e70 SZ |
37 | /* DDRC register definition in v2 */ |
38 | #define DDRC_V2_INT_MASK 0x528 | |
39 | #define DDRC_V2_INT_STATUS 0x52c | |
40 | #define DDRC_V2_INT_CLEAR 0x530 | |
41 | #define DDRC_V2_EVENT_CNT 0xe00 | |
42 | #define DDRC_V2_EVENT_CTRL 0xe70 | |
43 | #define DDRC_V2_EVENT_TYPE 0xe74 | |
44 | #define DDRC_V2_PERF_CTRL 0xeA0 | |
45 | ||
904dcf03 SZ |
46 | /* DDRC has 8-counters */ |
47 | #define DDRC_NR_COUNTERS 0x8 | |
3da582df | 48 | #define DDRC_V1_PERF_CTRL_EN 0x2 |
cce03e70 | 49 | #define DDRC_V2_PERF_CTRL_EN 0x1 |
3da582df | 50 | #define DDRC_V1_NR_EVENTS 0x7 |
cce03e70 | 51 | #define DDRC_V2_NR_EVENTS 0x90 |
904dcf03 SZ |
52 | |
53 | /* | |
cce03e70 | 54 | * For PMU v1, there are eight-events and every event has been mapped |
904dcf03 SZ |
55 | * to fixed-purpose counters which register offset is not consistent. |
56 | * Therefore there is no write event type and we assume that event | |
57 | * code (0 to 7) is equal to counter index in PMU driver. | |
58 | */ | |
59 | #define GET_DDRC_EVENTID(hwc) (hwc->config_base & 0x7) | |
60 | ||
61 | static const u32 ddrc_reg_off[] = { | |
62 | DDRC_FLUX_WR, DDRC_FLUX_RD, DDRC_FLUX_WCMD, DDRC_FLUX_RCMD, | |
eb4f5213 | 63 | DDRC_PRE_CMD, DDRC_ACT_CMD, DDRC_RNK_CHG, DDRC_RW_CHG |
904dcf03 SZ |
64 | }; |
65 | ||
66 | /* | |
67 | * Select the counter register offset using the counter index. | |
3da582df SZ |
68 | * In PMU v1, there are no programmable counter, the count |
69 | * is read form the statistics counter register itself. | |
904dcf03 | 70 | */ |
3da582df | 71 | static u32 hisi_ddrc_pmu_v1_get_counter_offset(int cntr_idx) |
904dcf03 SZ |
72 | { |
73 | return ddrc_reg_off[cntr_idx]; | |
74 | } | |
75 | ||
cce03e70 SZ |
76 | static u32 hisi_ddrc_pmu_v2_get_counter_offset(int cntr_idx) |
77 | { | |
78 | return DDRC_V2_EVENT_CNT + cntr_idx * 8; | |
79 | } | |
80 | ||
3da582df | 81 | static u64 hisi_ddrc_pmu_v1_read_counter(struct hisi_pmu *ddrc_pmu, |
904dcf03 SZ |
82 | struct hw_perf_event *hwc) |
83 | { | |
4e4cb8ca | 84 | return readl(ddrc_pmu->base + |
3da582df | 85 | hisi_ddrc_pmu_v1_get_counter_offset(hwc->idx)); |
904dcf03 SZ |
86 | } |
87 | ||
3da582df | 88 | static void hisi_ddrc_pmu_v1_write_counter(struct hisi_pmu *ddrc_pmu, |
904dcf03 SZ |
89 | struct hw_perf_event *hwc, u64 val) |
90 | { | |
904dcf03 | 91 | writel((u32)val, |
3da582df | 92 | ddrc_pmu->base + hisi_ddrc_pmu_v1_get_counter_offset(hwc->idx)); |
904dcf03 SZ |
93 | } |
94 | ||
cce03e70 SZ |
95 | static u64 hisi_ddrc_pmu_v2_read_counter(struct hisi_pmu *ddrc_pmu, |
96 | struct hw_perf_event *hwc) | |
97 | { | |
98 | return readq(ddrc_pmu->base + | |
99 | hisi_ddrc_pmu_v2_get_counter_offset(hwc->idx)); | |
100 | } | |
101 | ||
102 | static void hisi_ddrc_pmu_v2_write_counter(struct hisi_pmu *ddrc_pmu, | |
103 | struct hw_perf_event *hwc, u64 val) | |
104 | { | |
105 | writeq(val, | |
106 | ddrc_pmu->base + hisi_ddrc_pmu_v2_get_counter_offset(hwc->idx)); | |
107 | } | |
108 | ||
904dcf03 | 109 | /* |
cce03e70 SZ |
110 | * For DDRC PMU v1, event has been mapped to fixed-purpose counter by hardware, |
111 | * so there is no need to write event type, while it is programmable counter in | |
112 | * PMU v2. | |
904dcf03 SZ |
113 | */ |
114 | static void hisi_ddrc_pmu_write_evtype(struct hisi_pmu *hha_pmu, int idx, | |
115 | u32 type) | |
116 | { | |
cce03e70 SZ |
117 | u32 offset; |
118 | ||
119 | if (hha_pmu->identifier >= HISI_PMU_V2) { | |
120 | offset = DDRC_V2_EVENT_TYPE + 4 * idx; | |
121 | writel(type, hha_pmu->base + offset); | |
122 | } | |
904dcf03 SZ |
123 | } |
124 | ||
3da582df | 125 | static void hisi_ddrc_pmu_v1_start_counters(struct hisi_pmu *ddrc_pmu) |
904dcf03 SZ |
126 | { |
127 | u32 val; | |
128 | ||
129 | /* Set perf_enable in DDRC_PERF_CTRL to start event counting */ | |
130 | val = readl(ddrc_pmu->base + DDRC_PERF_CTRL); | |
3da582df | 131 | val |= DDRC_V1_PERF_CTRL_EN; |
904dcf03 SZ |
132 | writel(val, ddrc_pmu->base + DDRC_PERF_CTRL); |
133 | } | |
134 | ||
3da582df | 135 | static void hisi_ddrc_pmu_v1_stop_counters(struct hisi_pmu *ddrc_pmu) |
904dcf03 SZ |
136 | { |
137 | u32 val; | |
138 | ||
139 | /* Clear perf_enable in DDRC_PERF_CTRL to stop event counting */ | |
140 | val = readl(ddrc_pmu->base + DDRC_PERF_CTRL); | |
3da582df | 141 | val &= ~DDRC_V1_PERF_CTRL_EN; |
904dcf03 SZ |
142 | writel(val, ddrc_pmu->base + DDRC_PERF_CTRL); |
143 | } | |
144 | ||
3da582df SZ |
145 | static void hisi_ddrc_pmu_v1_enable_counter(struct hisi_pmu *ddrc_pmu, |
146 | struct hw_perf_event *hwc) | |
904dcf03 SZ |
147 | { |
148 | u32 val; | |
149 | ||
150 | /* Set counter index(event code) in DDRC_EVENT_CTRL register */ | |
151 | val = readl(ddrc_pmu->base + DDRC_EVENT_CTRL); | |
152 | val |= (1 << GET_DDRC_EVENTID(hwc)); | |
153 | writel(val, ddrc_pmu->base + DDRC_EVENT_CTRL); | |
154 | } | |
155 | ||
3da582df SZ |
156 | static void hisi_ddrc_pmu_v1_disable_counter(struct hisi_pmu *ddrc_pmu, |
157 | struct hw_perf_event *hwc) | |
904dcf03 SZ |
158 | { |
159 | u32 val; | |
160 | ||
161 | /* Clear counter index(event code) in DDRC_EVENT_CTRL register */ | |
162 | val = readl(ddrc_pmu->base + DDRC_EVENT_CTRL); | |
163 | val &= ~(1 << GET_DDRC_EVENTID(hwc)); | |
164 | writel(val, ddrc_pmu->base + DDRC_EVENT_CTRL); | |
165 | } | |
166 | ||
3da582df | 167 | static int hisi_ddrc_pmu_v1_get_event_idx(struct perf_event *event) |
904dcf03 SZ |
168 | { |
169 | struct hisi_pmu *ddrc_pmu = to_hisi_pmu(event->pmu); | |
170 | unsigned long *used_mask = ddrc_pmu->pmu_events.used_mask; | |
171 | struct hw_perf_event *hwc = &event->hw; | |
172 | /* For DDRC PMU, we use event code as counter index */ | |
173 | int idx = GET_DDRC_EVENTID(hwc); | |
174 | ||
175 | if (test_bit(idx, used_mask)) | |
176 | return -EAGAIN; | |
177 | ||
178 | set_bit(idx, used_mask); | |
179 | ||
180 | return idx; | |
181 | } | |
182 | ||
cce03e70 SZ |
183 | static int hisi_ddrc_pmu_v2_get_event_idx(struct perf_event *event) |
184 | { | |
185 | return hisi_uncore_pmu_get_event_idx(event); | |
186 | } | |
187 | ||
188 | static void hisi_ddrc_pmu_v2_start_counters(struct hisi_pmu *ddrc_pmu) | |
189 | { | |
190 | u32 val; | |
191 | ||
192 | val = readl(ddrc_pmu->base + DDRC_V2_PERF_CTRL); | |
193 | val |= DDRC_V2_PERF_CTRL_EN; | |
194 | writel(val, ddrc_pmu->base + DDRC_V2_PERF_CTRL); | |
195 | } | |
196 | ||
197 | static void hisi_ddrc_pmu_v2_stop_counters(struct hisi_pmu *ddrc_pmu) | |
198 | { | |
199 | u32 val; | |
200 | ||
201 | val = readl(ddrc_pmu->base + DDRC_V2_PERF_CTRL); | |
202 | val &= ~DDRC_V2_PERF_CTRL_EN; | |
203 | writel(val, ddrc_pmu->base + DDRC_V2_PERF_CTRL); | |
204 | } | |
205 | ||
206 | static void hisi_ddrc_pmu_v2_enable_counter(struct hisi_pmu *ddrc_pmu, | |
207 | struct hw_perf_event *hwc) | |
208 | { | |
209 | u32 val; | |
210 | ||
211 | val = readl(ddrc_pmu->base + DDRC_V2_EVENT_CTRL); | |
212 | val |= 1 << hwc->idx; | |
213 | writel(val, ddrc_pmu->base + DDRC_V2_EVENT_CTRL); | |
214 | } | |
215 | ||
216 | static void hisi_ddrc_pmu_v2_disable_counter(struct hisi_pmu *ddrc_pmu, | |
217 | struct hw_perf_event *hwc) | |
218 | { | |
219 | u32 val; | |
220 | ||
221 | val = readl(ddrc_pmu->base + DDRC_V2_EVENT_CTRL); | |
222 | val &= ~(1 << hwc->idx); | |
223 | writel(val, ddrc_pmu->base + DDRC_V2_EVENT_CTRL); | |
224 | } | |
225 | ||
3da582df SZ |
226 | static void hisi_ddrc_pmu_v1_enable_counter_int(struct hisi_pmu *ddrc_pmu, |
227 | struct hw_perf_event *hwc) | |
904dcf03 SZ |
228 | { |
229 | u32 val; | |
230 | ||
231 | /* Write 0 to enable interrupt */ | |
232 | val = readl(ddrc_pmu->base + DDRC_INT_MASK); | |
cce03e70 | 233 | val &= ~(1 << hwc->idx); |
904dcf03 SZ |
234 | writel(val, ddrc_pmu->base + DDRC_INT_MASK); |
235 | } | |
236 | ||
3da582df SZ |
237 | static void hisi_ddrc_pmu_v1_disable_counter_int(struct hisi_pmu *ddrc_pmu, |
238 | struct hw_perf_event *hwc) | |
904dcf03 SZ |
239 | { |
240 | u32 val; | |
241 | ||
242 | /* Write 1 to mask interrupt */ | |
243 | val = readl(ddrc_pmu->base + DDRC_INT_MASK); | |
cce03e70 | 244 | val |= 1 << hwc->idx; |
904dcf03 SZ |
245 | writel(val, ddrc_pmu->base + DDRC_INT_MASK); |
246 | } | |
247 | ||
cce03e70 SZ |
248 | static void hisi_ddrc_pmu_v2_enable_counter_int(struct hisi_pmu *ddrc_pmu, |
249 | struct hw_perf_event *hwc) | |
250 | { | |
251 | u32 val; | |
252 | ||
253 | val = readl(ddrc_pmu->base + DDRC_V2_INT_MASK); | |
254 | val &= ~(1 << hwc->idx); | |
255 | writel(val, ddrc_pmu->base + DDRC_V2_INT_MASK); | |
256 | } | |
257 | ||
258 | static void hisi_ddrc_pmu_v2_disable_counter_int(struct hisi_pmu *ddrc_pmu, | |
259 | struct hw_perf_event *hwc) | |
260 | { | |
261 | u32 val; | |
262 | ||
263 | val = readl(ddrc_pmu->base + DDRC_V2_INT_MASK); | |
264 | val |= 1 << hwc->idx; | |
265 | writel(val, ddrc_pmu->base + DDRC_V2_INT_MASK); | |
266 | } | |
267 | ||
3da582df | 268 | static u32 hisi_ddrc_pmu_v1_get_int_status(struct hisi_pmu *ddrc_pmu) |
904dcf03 | 269 | { |
baff06c3 | 270 | return readl(ddrc_pmu->base + DDRC_INT_STATUS); |
904dcf03 SZ |
271 | } |
272 | ||
3da582df SZ |
273 | static void hisi_ddrc_pmu_v1_clear_int_status(struct hisi_pmu *ddrc_pmu, |
274 | int idx) | |
904dcf03 | 275 | { |
baff06c3 | 276 | writel(1 << idx, ddrc_pmu->base + DDRC_INT_CLEAR); |
904dcf03 SZ |
277 | } |
278 | ||
cce03e70 SZ |
279 | static u32 hisi_ddrc_pmu_v2_get_int_status(struct hisi_pmu *ddrc_pmu) |
280 | { | |
281 | return readl(ddrc_pmu->base + DDRC_V2_INT_STATUS); | |
282 | } | |
283 | ||
284 | static void hisi_ddrc_pmu_v2_clear_int_status(struct hisi_pmu *ddrc_pmu, | |
285 | int idx) | |
286 | { | |
287 | writel(1 << idx, ddrc_pmu->base + DDRC_V2_INT_CLEAR); | |
288 | } | |
289 | ||
904dcf03 SZ |
290 | static const struct acpi_device_id hisi_ddrc_pmu_acpi_match[] = { |
291 | { "HISI0233", }, | |
cce03e70 SZ |
292 | { "HISI0234", }, |
293 | {} | |
904dcf03 SZ |
294 | }; |
295 | MODULE_DEVICE_TABLE(acpi, hisi_ddrc_pmu_acpi_match); | |
296 | ||
297 | static int hisi_ddrc_pmu_init_data(struct platform_device *pdev, | |
298 | struct hisi_pmu *ddrc_pmu) | |
299 | { | |
904dcf03 SZ |
300 | /* |
301 | * Use the SCCL_ID and DDRC channel ID to identify the | |
302 | * DDRC PMU, while SCCL_ID is in MPIDR[aff2]. | |
303 | */ | |
304 | if (device_property_read_u32(&pdev->dev, "hisilicon,ch-id", | |
305 | &ddrc_pmu->index_id)) { | |
306 | dev_err(&pdev->dev, "Can not read ddrc channel-id!\n"); | |
307 | return -EINVAL; | |
308 | } | |
309 | ||
310 | if (device_property_read_u32(&pdev->dev, "hisilicon,scl-id", | |
311 | &ddrc_pmu->sccl_id)) { | |
312 | dev_err(&pdev->dev, "Can not read ddrc sccl-id!\n"); | |
313 | return -EINVAL; | |
314 | } | |
315 | /* DDRC PMUs only share the same SCCL */ | |
316 | ddrc_pmu->ccl_id = -1; | |
317 | ||
42c184ad | 318 | ddrc_pmu->base = devm_platform_ioremap_resource(pdev, 0); |
904dcf03 SZ |
319 | if (IS_ERR(ddrc_pmu->base)) { |
320 | dev_err(&pdev->dev, "ioremap failed for ddrc_pmu resource\n"); | |
321 | return PTR_ERR(ddrc_pmu->base); | |
322 | } | |
323 | ||
ac4511c9 | 324 | ddrc_pmu->identifier = readl(ddrc_pmu->base + DDRC_VERSION); |
cce03e70 SZ |
325 | if (ddrc_pmu->identifier >= HISI_PMU_V2) { |
326 | if (device_property_read_u32(&pdev->dev, "hisilicon,sub-id", | |
327 | &ddrc_pmu->sub_id)) { | |
328 | dev_err(&pdev->dev, "Can not read sub-id!\n"); | |
329 | return -EINVAL; | |
330 | } | |
331 | } | |
ac4511c9 | 332 | |
904dcf03 SZ |
333 | return 0; |
334 | } | |
335 | ||
3da582df | 336 | static struct attribute *hisi_ddrc_pmu_v1_format_attr[] = { |
904dcf03 SZ |
337 | HISI_PMU_FORMAT_ATTR(event, "config:0-4"), |
338 | NULL, | |
339 | }; | |
340 | ||
3da582df | 341 | static const struct attribute_group hisi_ddrc_pmu_v1_format_group = { |
904dcf03 | 342 | .name = "format", |
3da582df | 343 | .attrs = hisi_ddrc_pmu_v1_format_attr, |
904dcf03 SZ |
344 | }; |
345 | ||
cce03e70 SZ |
346 | static struct attribute *hisi_ddrc_pmu_v2_format_attr[] = { |
347 | HISI_PMU_FORMAT_ATTR(event, "config:0-7"), | |
348 | NULL | |
349 | }; | |
350 | ||
351 | static const struct attribute_group hisi_ddrc_pmu_v2_format_group = { | |
352 | .name = "format", | |
353 | .attrs = hisi_ddrc_pmu_v2_format_attr, | |
354 | }; | |
355 | ||
3da582df | 356 | static struct attribute *hisi_ddrc_pmu_v1_events_attr[] = { |
904dcf03 SZ |
357 | HISI_PMU_EVENT_ATTR(flux_wr, 0x00), |
358 | HISI_PMU_EVENT_ATTR(flux_rd, 0x01), | |
359 | HISI_PMU_EVENT_ATTR(flux_wcmd, 0x02), | |
360 | HISI_PMU_EVENT_ATTR(flux_rcmd, 0x03), | |
361 | HISI_PMU_EVENT_ATTR(pre_cmd, 0x04), | |
362 | HISI_PMU_EVENT_ATTR(act_cmd, 0x05), | |
363 | HISI_PMU_EVENT_ATTR(rnk_chg, 0x06), | |
364 | HISI_PMU_EVENT_ATTR(rw_chg, 0x07), | |
365 | NULL, | |
366 | }; | |
367 | ||
3da582df | 368 | static const struct attribute_group hisi_ddrc_pmu_v1_events_group = { |
904dcf03 | 369 | .name = "events", |
3da582df | 370 | .attrs = hisi_ddrc_pmu_v1_events_attr, |
904dcf03 SZ |
371 | }; |
372 | ||
cce03e70 SZ |
373 | static struct attribute *hisi_ddrc_pmu_v2_events_attr[] = { |
374 | HISI_PMU_EVENT_ATTR(cycles, 0x00), | |
375 | HISI_PMU_EVENT_ATTR(flux_wr, 0x83), | |
376 | HISI_PMU_EVENT_ATTR(flux_rd, 0x84), | |
377 | NULL | |
378 | }; | |
379 | ||
380 | static const struct attribute_group hisi_ddrc_pmu_v2_events_group = { | |
381 | .name = "events", | |
382 | .attrs = hisi_ddrc_pmu_v2_events_attr, | |
383 | }; | |
384 | ||
904dcf03 SZ |
385 | static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL); |
386 | ||
387 | static struct attribute *hisi_ddrc_pmu_cpumask_attrs[] = { | |
388 | &dev_attr_cpumask.attr, | |
389 | NULL, | |
390 | }; | |
391 | ||
392 | static const struct attribute_group hisi_ddrc_pmu_cpumask_attr_group = { | |
393 | .attrs = hisi_ddrc_pmu_cpumask_attrs, | |
394 | }; | |
395 | ||
ac4511c9 JG |
396 | static struct device_attribute hisi_ddrc_pmu_identifier_attr = |
397 | __ATTR(identifier, 0444, hisi_uncore_pmu_identifier_attr_show, NULL); | |
398 | ||
399 | static struct attribute *hisi_ddrc_pmu_identifier_attrs[] = { | |
400 | &hisi_ddrc_pmu_identifier_attr.attr, | |
401 | NULL | |
402 | }; | |
403 | ||
c2c4d5c0 | 404 | static const struct attribute_group hisi_ddrc_pmu_identifier_group = { |
ac4511c9 JG |
405 | .attrs = hisi_ddrc_pmu_identifier_attrs, |
406 | }; | |
407 | ||
3da582df SZ |
408 | static const struct attribute_group *hisi_ddrc_pmu_v1_attr_groups[] = { |
409 | &hisi_ddrc_pmu_v1_format_group, | |
410 | &hisi_ddrc_pmu_v1_events_group, | |
904dcf03 | 411 | &hisi_ddrc_pmu_cpumask_attr_group, |
ac4511c9 | 412 | &hisi_ddrc_pmu_identifier_group, |
904dcf03 SZ |
413 | NULL, |
414 | }; | |
415 | ||
cce03e70 SZ |
416 | static const struct attribute_group *hisi_ddrc_pmu_v2_attr_groups[] = { |
417 | &hisi_ddrc_pmu_v2_format_group, | |
418 | &hisi_ddrc_pmu_v2_events_group, | |
419 | &hisi_ddrc_pmu_cpumask_attr_group, | |
420 | &hisi_ddrc_pmu_identifier_group, | |
421 | NULL | |
422 | }; | |
423 | ||
3da582df | 424 | static const struct hisi_uncore_ops hisi_uncore_ddrc_v1_ops = { |
904dcf03 | 425 | .write_evtype = hisi_ddrc_pmu_write_evtype, |
3da582df SZ |
426 | .get_event_idx = hisi_ddrc_pmu_v1_get_event_idx, |
427 | .start_counters = hisi_ddrc_pmu_v1_start_counters, | |
428 | .stop_counters = hisi_ddrc_pmu_v1_stop_counters, | |
429 | .enable_counter = hisi_ddrc_pmu_v1_enable_counter, | |
430 | .disable_counter = hisi_ddrc_pmu_v1_disable_counter, | |
431 | .enable_counter_int = hisi_ddrc_pmu_v1_enable_counter_int, | |
432 | .disable_counter_int = hisi_ddrc_pmu_v1_disable_counter_int, | |
433 | .write_counter = hisi_ddrc_pmu_v1_write_counter, | |
434 | .read_counter = hisi_ddrc_pmu_v1_read_counter, | |
435 | .get_int_status = hisi_ddrc_pmu_v1_get_int_status, | |
436 | .clear_int_status = hisi_ddrc_pmu_v1_clear_int_status, | |
904dcf03 SZ |
437 | }; |
438 | ||
cce03e70 SZ |
439 | static const struct hisi_uncore_ops hisi_uncore_ddrc_v2_ops = { |
440 | .write_evtype = hisi_ddrc_pmu_write_evtype, | |
441 | .get_event_idx = hisi_ddrc_pmu_v2_get_event_idx, | |
442 | .start_counters = hisi_ddrc_pmu_v2_start_counters, | |
443 | .stop_counters = hisi_ddrc_pmu_v2_stop_counters, | |
444 | .enable_counter = hisi_ddrc_pmu_v2_enable_counter, | |
445 | .disable_counter = hisi_ddrc_pmu_v2_disable_counter, | |
446 | .enable_counter_int = hisi_ddrc_pmu_v2_enable_counter_int, | |
447 | .disable_counter_int = hisi_ddrc_pmu_v2_disable_counter_int, | |
448 | .write_counter = hisi_ddrc_pmu_v2_write_counter, | |
449 | .read_counter = hisi_ddrc_pmu_v2_read_counter, | |
450 | .get_int_status = hisi_ddrc_pmu_v2_get_int_status, | |
451 | .clear_int_status = hisi_ddrc_pmu_v2_clear_int_status, | |
452 | }; | |
453 | ||
904dcf03 SZ |
454 | static int hisi_ddrc_pmu_dev_probe(struct platform_device *pdev, |
455 | struct hisi_pmu *ddrc_pmu) | |
456 | { | |
457 | int ret; | |
458 | ||
459 | ret = hisi_ddrc_pmu_init_data(pdev, ddrc_pmu); | |
460 | if (ret) | |
461 | return ret; | |
462 | ||
baff06c3 | 463 | ret = hisi_uncore_pmu_init_irq(ddrc_pmu, pdev); |
904dcf03 SZ |
464 | if (ret) |
465 | return ret; | |
466 | ||
cce03e70 SZ |
467 | if (ddrc_pmu->identifier >= HISI_PMU_V2) { |
468 | ddrc_pmu->counter_bits = 48; | |
469 | ddrc_pmu->check_event = DDRC_V2_NR_EVENTS; | |
470 | ddrc_pmu->pmu_events.attr_groups = hisi_ddrc_pmu_v2_attr_groups; | |
471 | ddrc_pmu->ops = &hisi_uncore_ddrc_v2_ops; | |
472 | } else { | |
473 | ddrc_pmu->counter_bits = 32; | |
474 | ddrc_pmu->check_event = DDRC_V1_NR_EVENTS; | |
475 | ddrc_pmu->pmu_events.attr_groups = hisi_ddrc_pmu_v1_attr_groups; | |
476 | ddrc_pmu->ops = &hisi_uncore_ddrc_v1_ops; | |
477 | } | |
478 | ||
904dcf03 | 479 | ddrc_pmu->num_counters = DDRC_NR_COUNTERS; |
904dcf03 SZ |
480 | ddrc_pmu->dev = &pdev->dev; |
481 | ddrc_pmu->on_cpu = -1; | |
904dcf03 SZ |
482 | |
483 | return 0; | |
484 | } | |
485 | ||
486 | static int hisi_ddrc_pmu_probe(struct platform_device *pdev) | |
487 | { | |
488 | struct hisi_pmu *ddrc_pmu; | |
489 | char *name; | |
490 | int ret; | |
491 | ||
492 | ddrc_pmu = devm_kzalloc(&pdev->dev, sizeof(*ddrc_pmu), GFP_KERNEL); | |
493 | if (!ddrc_pmu) | |
494 | return -ENOMEM; | |
495 | ||
496 | platform_set_drvdata(pdev, ddrc_pmu); | |
497 | ||
498 | ret = hisi_ddrc_pmu_dev_probe(pdev, ddrc_pmu); | |
499 | if (ret) | |
500 | return ret; | |
501 | ||
502 | ret = cpuhp_state_add_instance(CPUHP_AP_PERF_ARM_HISI_DDRC_ONLINE, | |
503 | &ddrc_pmu->node); | |
504 | if (ret) { | |
505 | dev_err(&pdev->dev, "Error %d registering hotplug;\n", ret); | |
506 | return ret; | |
507 | } | |
508 | ||
cce03e70 SZ |
509 | if (ddrc_pmu->identifier >= HISI_PMU_V2) |
510 | name = devm_kasprintf(&pdev->dev, GFP_KERNEL, | |
511 | "hisi_sccl%u_ddrc%u_%u", | |
512 | ddrc_pmu->sccl_id, ddrc_pmu->index_id, | |
513 | ddrc_pmu->sub_id); | |
514 | else | |
515 | name = devm_kasprintf(&pdev->dev, GFP_KERNEL, | |
516 | "hisi_sccl%u_ddrc%u", ddrc_pmu->sccl_id, | |
517 | ddrc_pmu->index_id); | |
518 | ||
904dcf03 SZ |
519 | ddrc_pmu->pmu = (struct pmu) { |
520 | .name = name, | |
bdc5c744 | 521 | .module = THIS_MODULE, |
904dcf03 SZ |
522 | .task_ctx_nr = perf_invalid_context, |
523 | .event_init = hisi_uncore_pmu_event_init, | |
524 | .pmu_enable = hisi_uncore_pmu_enable, | |
525 | .pmu_disable = hisi_uncore_pmu_disable, | |
526 | .add = hisi_uncore_pmu_add, | |
527 | .del = hisi_uncore_pmu_del, | |
528 | .start = hisi_uncore_pmu_start, | |
529 | .stop = hisi_uncore_pmu_stop, | |
530 | .read = hisi_uncore_pmu_read, | |
cce03e70 | 531 | .attr_groups = ddrc_pmu->pmu_events.attr_groups, |
30656398 | 532 | .capabilities = PERF_PMU_CAP_NO_EXCLUDE, |
904dcf03 SZ |
533 | }; |
534 | ||
535 | ret = perf_pmu_register(&ddrc_pmu->pmu, name, -1); | |
536 | if (ret) { | |
537 | dev_err(ddrc_pmu->dev, "DDRC PMU register failed!\n"); | |
97807325 ZW |
538 | cpuhp_state_remove_instance_nocalls( |
539 | CPUHP_AP_PERF_ARM_HISI_DDRC_ONLINE, &ddrc_pmu->node); | |
904dcf03 SZ |
540 | } |
541 | ||
542 | return ret; | |
543 | } | |
544 | ||
545 | static int hisi_ddrc_pmu_remove(struct platform_device *pdev) | |
546 | { | |
547 | struct hisi_pmu *ddrc_pmu = platform_get_drvdata(pdev); | |
548 | ||
549 | perf_pmu_unregister(&ddrc_pmu->pmu); | |
97807325 ZW |
550 | cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_HISI_DDRC_ONLINE, |
551 | &ddrc_pmu->node); | |
904dcf03 SZ |
552 | return 0; |
553 | } | |
554 | ||
555 | static struct platform_driver hisi_ddrc_pmu_driver = { | |
556 | .driver = { | |
557 | .name = "hisi_ddrc_pmu", | |
558 | .acpi_match_table = ACPI_PTR(hisi_ddrc_pmu_acpi_match), | |
f32ed8eb | 559 | .suppress_bind_attrs = true, |
904dcf03 SZ |
560 | }, |
561 | .probe = hisi_ddrc_pmu_probe, | |
562 | .remove = hisi_ddrc_pmu_remove, | |
563 | }; | |
564 | ||
565 | static int __init hisi_ddrc_pmu_module_init(void) | |
566 | { | |
567 | int ret; | |
568 | ||
569 | ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_HISI_DDRC_ONLINE, | |
570 | "AP_PERF_ARM_HISI_DDRC_ONLINE", | |
571 | hisi_uncore_pmu_online_cpu, | |
572 | hisi_uncore_pmu_offline_cpu); | |
573 | if (ret) { | |
574 | pr_err("DDRC PMU: setup hotplug, ret = %d\n", ret); | |
575 | return ret; | |
576 | } | |
577 | ||
578 | ret = platform_driver_register(&hisi_ddrc_pmu_driver); | |
579 | if (ret) | |
580 | cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_DDRC_ONLINE); | |
581 | ||
582 | return ret; | |
583 | } | |
584 | module_init(hisi_ddrc_pmu_module_init); | |
585 | ||
586 | static void __exit hisi_ddrc_pmu_module_exit(void) | |
587 | { | |
588 | platform_driver_unregister(&hisi_ddrc_pmu_driver); | |
589 | cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_DDRC_ONLINE); | |
590 | ||
591 | } | |
592 | module_exit(hisi_ddrc_pmu_module_exit); | |
593 | ||
594 | MODULE_DESCRIPTION("HiSilicon SoC DDRC uncore PMU driver"); | |
595 | MODULE_LICENSE("GPL v2"); | |
596 | MODULE_AUTHOR("Shaokun Zhang <zhangshaokun@hisilicon.com>"); | |
597 | MODULE_AUTHOR("Anurup M <anurup.m@huawei.com>"); |