| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * HiSilicon SoC Hardware event counters support |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 6 | 7 | * Shaokun Zhang <zhangshaokun@hisilicon.com> |
|---|
| 7 | 8 | * |
|---|
| 8 | 9 | * 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 | 10 | */ |
|---|
| 14 | 11 | #include <linux/bitmap.h> |
|---|
| 15 | 12 | #include <linux/bitops.h> |
|---|
| .. | .. |
|---|
| 18 | 15 | #include <linux/errno.h> |
|---|
| 19 | 16 | #include <linux/interrupt.h> |
|---|
| 20 | 17 | |
|---|
| 18 | +#include <asm/cputype.h> |
|---|
| 21 | 19 | #include <asm/local64.h> |
|---|
| 22 | 20 | |
|---|
| 23 | 21 | #include "hisi_uncore_pmu.h" |
|---|
| .. | .. |
|---|
| 37 | 35 | |
|---|
| 38 | 36 | return sprintf(buf, "%s\n", (char *)eattr->var); |
|---|
| 39 | 37 | } |
|---|
| 38 | +EXPORT_SYMBOL_GPL(hisi_format_sysfs_show); |
|---|
| 40 | 39 | |
|---|
| 41 | 40 | /* |
|---|
| 42 | 41 | * PMU event attributes |
|---|
| .. | .. |
|---|
| 50 | 49 | |
|---|
| 51 | 50 | return sprintf(page, "config=0x%lx\n", (unsigned long)eattr->var); |
|---|
| 52 | 51 | } |
|---|
| 52 | +EXPORT_SYMBOL_GPL(hisi_event_sysfs_show); |
|---|
| 53 | 53 | |
|---|
| 54 | 54 | /* |
|---|
| 55 | 55 | * sysfs cpumask attributes. For uncore PMU, we only have a single CPU to show |
|---|
| .. | .. |
|---|
| 61 | 61 | |
|---|
| 62 | 62 | return sprintf(buf, "%d\n", hisi_pmu->on_cpu); |
|---|
| 63 | 63 | } |
|---|
| 64 | +EXPORT_SYMBOL_GPL(hisi_cpumask_sysfs_show); |
|---|
| 64 | 65 | |
|---|
| 65 | 66 | static bool hisi_validate_event_group(struct perf_event *event) |
|---|
| 66 | 67 | { |
|---|
| .. | .. |
|---|
| 99 | 100 | { |
|---|
| 100 | 101 | return idx >= 0 && idx < hisi_pmu->num_counters; |
|---|
| 101 | 102 | } |
|---|
| 103 | +EXPORT_SYMBOL_GPL(hisi_uncore_pmu_counter_valid); |
|---|
| 102 | 104 | |
|---|
| 103 | 105 | int hisi_uncore_pmu_get_event_idx(struct perf_event *event) |
|---|
| 104 | 106 | { |
|---|
| .. | .. |
|---|
| 115 | 117 | |
|---|
| 116 | 118 | return idx; |
|---|
| 117 | 119 | } |
|---|
| 120 | +EXPORT_SYMBOL_GPL(hisi_uncore_pmu_get_event_idx); |
|---|
| 118 | 121 | |
|---|
| 119 | 122 | static void hisi_uncore_pmu_clear_event_idx(struct hisi_pmu *hisi_pmu, int idx) |
|---|
| 120 | 123 | { |
|---|
| .. | .. |
|---|
| 141 | 144 | */ |
|---|
| 142 | 145 | if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK) |
|---|
| 143 | 146 | return -EOPNOTSUPP; |
|---|
| 144 | | - |
|---|
| 145 | | - /* counters do not have these bits */ |
|---|
| 146 | | - if (event->attr.exclude_user || |
|---|
| 147 | | - event->attr.exclude_kernel || |
|---|
| 148 | | - event->attr.exclude_host || |
|---|
| 149 | | - event->attr.exclude_guest || |
|---|
| 150 | | - event->attr.exclude_hv || |
|---|
| 151 | | - event->attr.exclude_idle) |
|---|
| 152 | | - return -EINVAL; |
|---|
| 153 | 147 | |
|---|
| 154 | 148 | /* |
|---|
| 155 | 149 | * The uncore counters not specific to any CPU, so cannot |
|---|
| .. | .. |
|---|
| 184 | 178 | |
|---|
| 185 | 179 | return 0; |
|---|
| 186 | 180 | } |
|---|
| 181 | +EXPORT_SYMBOL_GPL(hisi_uncore_pmu_event_init); |
|---|
| 187 | 182 | |
|---|
| 188 | 183 | /* |
|---|
| 189 | 184 | * Set the counter to count the event that we're interested in, |
|---|
| .. | .. |
|---|
| 231 | 226 | /* Write start value to the hardware event counter */ |
|---|
| 232 | 227 | hisi_pmu->ops->write_counter(hisi_pmu, hwc, val); |
|---|
| 233 | 228 | } |
|---|
| 229 | +EXPORT_SYMBOL_GPL(hisi_uncore_pmu_set_event_period); |
|---|
| 234 | 230 | |
|---|
| 235 | 231 | void hisi_uncore_pmu_event_update(struct perf_event *event) |
|---|
| 236 | 232 | { |
|---|
| .. | .. |
|---|
| 251 | 247 | HISI_MAX_PERIOD(hisi_pmu->counter_bits); |
|---|
| 252 | 248 | local64_add(delta, &event->count); |
|---|
| 253 | 249 | } |
|---|
| 250 | +EXPORT_SYMBOL_GPL(hisi_uncore_pmu_event_update); |
|---|
| 254 | 251 | |
|---|
| 255 | 252 | void hisi_uncore_pmu_start(struct perf_event *event, int flags) |
|---|
| 256 | 253 | { |
|---|
| .. | .. |
|---|
| 273 | 270 | hisi_uncore_pmu_enable_event(event); |
|---|
| 274 | 271 | perf_event_update_userpage(event); |
|---|
| 275 | 272 | } |
|---|
| 273 | +EXPORT_SYMBOL_GPL(hisi_uncore_pmu_start); |
|---|
| 276 | 274 | |
|---|
| 277 | 275 | void hisi_uncore_pmu_stop(struct perf_event *event, int flags) |
|---|
| 278 | 276 | { |
|---|
| .. | .. |
|---|
| 289 | 287 | hisi_uncore_pmu_event_update(event); |
|---|
| 290 | 288 | hwc->state |= PERF_HES_UPTODATE; |
|---|
| 291 | 289 | } |
|---|
| 290 | +EXPORT_SYMBOL_GPL(hisi_uncore_pmu_stop); |
|---|
| 292 | 291 | |
|---|
| 293 | 292 | int hisi_uncore_pmu_add(struct perf_event *event, int flags) |
|---|
| 294 | 293 | { |
|---|
| .. | .. |
|---|
| 311 | 310 | |
|---|
| 312 | 311 | return 0; |
|---|
| 313 | 312 | } |
|---|
| 313 | +EXPORT_SYMBOL_GPL(hisi_uncore_pmu_add); |
|---|
| 314 | 314 | |
|---|
| 315 | 315 | void hisi_uncore_pmu_del(struct perf_event *event, int flags) |
|---|
| 316 | 316 | { |
|---|
| .. | .. |
|---|
| 322 | 322 | perf_event_update_userpage(event); |
|---|
| 323 | 323 | hisi_pmu->pmu_events.hw_events[hwc->idx] = NULL; |
|---|
| 324 | 324 | } |
|---|
| 325 | +EXPORT_SYMBOL_GPL(hisi_uncore_pmu_del); |
|---|
| 325 | 326 | |
|---|
| 326 | 327 | void hisi_uncore_pmu_read(struct perf_event *event) |
|---|
| 327 | 328 | { |
|---|
| 328 | 329 | /* Read hardware counter and update the perf counter statistics */ |
|---|
| 329 | 330 | hisi_uncore_pmu_event_update(event); |
|---|
| 330 | 331 | } |
|---|
| 332 | +EXPORT_SYMBOL_GPL(hisi_uncore_pmu_read); |
|---|
| 331 | 333 | |
|---|
| 332 | 334 | void hisi_uncore_pmu_enable(struct pmu *pmu) |
|---|
| 333 | 335 | { |
|---|
| .. | .. |
|---|
| 340 | 342 | |
|---|
| 341 | 343 | hisi_pmu->ops->start_counters(hisi_pmu); |
|---|
| 342 | 344 | } |
|---|
| 345 | +EXPORT_SYMBOL_GPL(hisi_uncore_pmu_enable); |
|---|
| 343 | 346 | |
|---|
| 344 | 347 | void hisi_uncore_pmu_disable(struct pmu *pmu) |
|---|
| 345 | 348 | { |
|---|
| .. | .. |
|---|
| 347 | 350 | |
|---|
| 348 | 351 | hisi_pmu->ops->stop_counters(hisi_pmu); |
|---|
| 349 | 352 | } |
|---|
| 353 | +EXPORT_SYMBOL_GPL(hisi_uncore_pmu_disable); |
|---|
| 354 | + |
|---|
| 350 | 355 | |
|---|
| 351 | 356 | /* |
|---|
| 352 | | - * Read Super CPU cluster and CPU cluster ID from MPIDR_EL1. |
|---|
| 353 | | - * If multi-threading is supported, CCL_ID is the low 3-bits in MPIDR[Aff2] |
|---|
| 354 | | - * and SCCL_ID is the upper 5-bits of Aff2 field; if not, SCCL_ID |
|---|
| 355 | | - * is in MPIDR[Aff2] and CCL_ID is in MPIDR[Aff1]. |
|---|
| 357 | + * The Super CPU Cluster (SCCL) and CPU Cluster (CCL) IDs can be |
|---|
| 358 | + * determined from the MPIDR_EL1, but the encoding varies by CPU: |
|---|
| 359 | + * |
|---|
| 360 | + * - For MT variants of TSV110: |
|---|
| 361 | + * SCCL is Aff2[7:3], CCL is Aff2[2:0] |
|---|
| 362 | + * |
|---|
| 363 | + * - For other MT parts: |
|---|
| 364 | + * SCCL is Aff3[7:0], CCL is Aff2[7:0] |
|---|
| 365 | + * |
|---|
| 366 | + * - For non-MT parts: |
|---|
| 367 | + * SCCL is Aff2[7:0], CCL is Aff1[7:0] |
|---|
| 356 | 368 | */ |
|---|
| 357 | | -static void hisi_read_sccl_and_ccl_id(int *sccl_id, int *ccl_id) |
|---|
| 369 | +static void hisi_read_sccl_and_ccl_id(int *scclp, int *cclp) |
|---|
| 358 | 370 | { |
|---|
| 359 | 371 | u64 mpidr = read_cpuid_mpidr(); |
|---|
| 372 | + int aff3 = MPIDR_AFFINITY_LEVEL(mpidr, 3); |
|---|
| 373 | + int aff2 = MPIDR_AFFINITY_LEVEL(mpidr, 2); |
|---|
| 374 | + int aff1 = MPIDR_AFFINITY_LEVEL(mpidr, 1); |
|---|
| 375 | + bool mt = mpidr & MPIDR_MT_BITMASK; |
|---|
| 376 | + int sccl, ccl; |
|---|
| 360 | 377 | |
|---|
| 361 | | - if (mpidr & MPIDR_MT_BITMASK) { |
|---|
| 362 | | - int aff2 = MPIDR_AFFINITY_LEVEL(mpidr, 2); |
|---|
| 363 | | - |
|---|
| 364 | | - if (sccl_id) |
|---|
| 365 | | - *sccl_id = aff2 >> 3; |
|---|
| 366 | | - if (ccl_id) |
|---|
| 367 | | - *ccl_id = aff2 & 0x7; |
|---|
| 378 | + if (mt && read_cpuid_part_number() == HISI_CPU_PART_TSV110) { |
|---|
| 379 | + sccl = aff2 >> 3; |
|---|
| 380 | + ccl = aff2 & 0x7; |
|---|
| 381 | + } else if (mt) { |
|---|
| 382 | + sccl = aff3; |
|---|
| 383 | + ccl = aff2; |
|---|
| 368 | 384 | } else { |
|---|
| 369 | | - if (sccl_id) |
|---|
| 370 | | - *sccl_id = MPIDR_AFFINITY_LEVEL(mpidr, 2); |
|---|
| 371 | | - if (ccl_id) |
|---|
| 372 | | - *ccl_id = MPIDR_AFFINITY_LEVEL(mpidr, 1); |
|---|
| 385 | + sccl = aff2; |
|---|
| 386 | + ccl = aff1; |
|---|
| 373 | 387 | } |
|---|
| 388 | + |
|---|
| 389 | + if (scclp) |
|---|
| 390 | + *scclp = sccl; |
|---|
| 391 | + if (cclp) |
|---|
| 392 | + *cclp = ccl; |
|---|
| 374 | 393 | } |
|---|
| 375 | 394 | |
|---|
| 376 | 395 | /* |
|---|
| .. | .. |
|---|
| 410 | 429 | hisi_pmu->on_cpu = cpu; |
|---|
| 411 | 430 | |
|---|
| 412 | 431 | /* Overflow interrupt also should use the same CPU */ |
|---|
| 413 | | - WARN_ON(irq_set_affinity(hisi_pmu->irq, cpumask_of(cpu))); |
|---|
| 432 | + WARN_ON(irq_set_affinity_hint(hisi_pmu->irq, cpumask_of(cpu))); |
|---|
| 414 | 433 | |
|---|
| 415 | 434 | return 0; |
|---|
| 416 | 435 | } |
|---|
| 436 | +EXPORT_SYMBOL_GPL(hisi_uncore_pmu_online_cpu); |
|---|
| 417 | 437 | |
|---|
| 418 | 438 | int hisi_uncore_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node) |
|---|
| 419 | 439 | { |
|---|
| .. | .. |
|---|
| 442 | 462 | perf_pmu_migrate_context(&hisi_pmu->pmu, cpu, target); |
|---|
| 443 | 463 | /* Use this CPU for event counting */ |
|---|
| 444 | 464 | hisi_pmu->on_cpu = target; |
|---|
| 445 | | - WARN_ON(irq_set_affinity(hisi_pmu->irq, cpumask_of(target))); |
|---|
| 465 | + WARN_ON(irq_set_affinity_hint(hisi_pmu->irq, cpumask_of(target))); |
|---|
| 446 | 466 | |
|---|
| 447 | 467 | return 0; |
|---|
| 448 | 468 | } |
|---|
| 469 | +EXPORT_SYMBOL_GPL(hisi_uncore_pmu_offline_cpu); |
|---|
| 470 | + |
|---|
| 471 | +MODULE_LICENSE("GPL v2"); |
|---|