FILE
hisi_uncore_hha_pmu.c
File preview with syntax highlighting.
linux-6.19.14/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * HiSilicon SoC HHA uncore Hardware event counters support 4 * 5 * Copyright (C) 2017 HiSilicon Limited 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. 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/* HHA register definition */ 22#define HHA_INT_MASK 0x0804 23#define HHA_INT_STATUS 0x0808 24#define HHA_INT_CLEAR 0x080C 25#define HHA_VERSION 0x1cf0 26#define HHA_PERF_CTRL 0x1E00 27#define HHA_EVENT_CTRL 0x1E04 28#define HHA_SRCID_CTRL 0x1E08 29#define HHA_DATSRC_CTRL 0x1BF0 30#define HHA_EVENT_TYPE0 0x1E80 31/* 32 * If the HW version only supports a 48-bit counter, then 33 * bits [63:48] are reserved, which are Read-As-Zero and 34 * Writes-Ignored. 35 */ 36#define HHA_CNT0_LOWER 0x1F00 37 38/* HHA PMU v1 has 16 counters and v2 only has 8 counters */ 39#define HHA_V1_NR_COUNTERS 0x10 40#define HHA_V2_NR_COUNTERS 0x8 41 42#define HHA_PERF_CTRL_EN 0x1 43#define HHA_TRACETAG_EN BIT(31) 44#define HHA_SRCID_EN BIT(2) 45#define HHA_SRCID_CMD_SHIFT 6 46#define HHA_SRCID_MSK_SHIFT 20 47#define HHA_SRCID_CMD GENMASK(16, 6) 48#define HHA_SRCID_MSK GENMASK(30, 20) 49#define HHA_DATSRC_SKT_EN BIT(23) 50#define HHA_EVTYPE_MASK GENMASK(7, 0) 51#define HHA_V1_NR_EVENT 0x65 52#define HHA_V2_NR_EVENT 0xFF 53 54HISI_PMU_EVENT_ATTR_EXTRACTOR(srcid_cmd, config1, 10, 0); 55HISI_PMU_EVENT_ATTR_EXTRACTOR(srcid_msk, config1, 21, 11); 56HISI_PMU_EVENT_ATTR_EXTRACTOR(tracetag_en, config1, 22, 22); 57HISI_PMU_EVENT_ATTR_EXTRACTOR(datasrc_skt, config1, 23, 23); 58 59static void hisi_hha_pmu_enable_tracetag(struct perf_event *event) 60{ 61 struct hisi_pmu *hha_pmu = to_hisi_pmu(event->pmu); 62 u32 tt_en = hisi_get_tracetag_en(event); 63 64 if (tt_en) { 65 u32 val; 66 67 val = readl(hha_pmu->base + HHA_SRCID_CTRL); 68 val |= HHA_TRACETAG_EN; 69 writel(val, hha_pmu->base + HHA_SRCID_CTRL); 70 } 71} 72 73static void hisi_hha_pmu_clear_tracetag(struct perf_event *event) 74{ 75 struct hisi_pmu *hha_pmu = to_hisi_pmu(event->pmu); 76 u32 val; 77 78 val = readl(hha_pmu->base + HHA_SRCID_CTRL); 79 val &= ~HHA_TRACETAG_EN; 80 writel(val, hha_pmu->base + HHA_SRCID_CTRL); 81} 82 83static void hisi_hha_pmu_config_ds(struct perf_event *event) 84{ 85 struct hisi_pmu *hha_pmu = to_hisi_pmu(event->pmu); 86 u32 ds_skt = hisi_get_datasrc_skt(event); 87 88 if (ds_skt) { 89 u32 val; 90 91 val = readl(hha_pmu->base + HHA_DATSRC_CTRL); 92 val |= HHA_DATSRC_SKT_EN; 93 writel(val, hha_pmu->base + HHA_DATSRC_CTRL); 94 } 95} 96 97static void hisi_hha_pmu_clear_ds(struct perf_event *event) 98{ 99 struct hisi_pmu *hha_pmu = to_hisi_pmu(event->pmu); 100 u32 ds_skt = hisi_get_datasrc_skt(event); 101 102 if (ds_skt) { 103 u32 val; 104 105 val = readl(hha_pmu->base + HHA_DATSRC_CTRL); 106 val &= ~HHA_DATSRC_SKT_EN; 107 writel(val, hha_pmu->base + HHA_DATSRC_CTRL); 108 } 109} 110 111static void hisi_hha_pmu_config_srcid(struct perf_event *event) 112{ 113 struct hisi_pmu *hha_pmu = to_hisi_pmu(event->pmu); 114 u32 cmd = hisi_get_srcid_cmd(event); 115 116 if (cmd) { 117 u32 val, msk; 118 119 msk = hisi_get_srcid_msk(event); 120 val = readl(hha_pmu->base + HHA_SRCID_CTRL); 121 val |= HHA_SRCID_EN | (cmd << HHA_SRCID_CMD_SHIFT) | 122 (msk << HHA_SRCID_MSK_SHIFT); 123 writel(val, hha_pmu->base + HHA_SRCID_CTRL); 124 } 125} 126 127static void hisi_hha_pmu_disable_srcid(struct perf_event *event) 128{ 129 struct hisi_pmu *hha_pmu = to_hisi_pmu(event->pmu); 130 u32 cmd = hisi_get_srcid_cmd(event); 131 132 if (cmd) { 133 u32 val; 134 135 val = readl(hha_pmu->base + HHA_SRCID_CTRL); 136 val &= ~(HHA_SRCID_EN | HHA_SRCID_MSK | HHA_SRCID_CMD); 137 writel(val, hha_pmu->base + HHA_SRCID_CTRL); 138 } 139} 140 141static void hisi_hha_pmu_enable_filter(struct perf_event *event) 142{ 143 if (event->attr.config1 != 0x0) { 144 hisi_hha_pmu_enable_tracetag(event); 145 hisi_hha_pmu_config_ds(event); 146 hisi_hha_pmu_config_srcid(event); 147 } 148} 149 150static void hisi_hha_pmu_disable_filter(struct perf_event *event) 151{ 152 if (event->attr.config1 != 0x0) { 153 hisi_hha_pmu_disable_srcid(event); 154 hisi_hha_pmu_clear_ds(event); 155 hisi_hha_pmu_clear_tracetag(event); 156 } 157} 158 159/* 160 * Select the counter register offset using the counter index 161 * each counter is 48-bits. 162 */ 163static u32 hisi_hha_pmu_get_counter_offset(int cntr_idx) 164{ 165 return (HHA_CNT0_LOWER + (cntr_idx * 8)); 166} 167 168static u64 hisi_hha_pmu_read_counter(struct hisi_pmu *hha_pmu, 169 struct hw_perf_event *hwc) 170{ 171 /* Read 64 bits and like L3C, top 16 bits are RAZ */ 172 return readq(hha_pmu->base + hisi_hha_pmu_get_counter_offset(hwc->idx)); 173} 174 175static void hisi_hha_pmu_write_counter(struct hisi_pmu *hha_pmu, 176 struct hw_perf_event *hwc, u64 val) 177{ 178 /* Write 64 bits and like L3C, top 16 bits are WI */ 179 writeq(val, hha_pmu->base + hisi_hha_pmu_get_counter_offset(hwc->idx)); 180} 181 182static void hisi_hha_pmu_write_evtype(struct hisi_pmu *hha_pmu, int idx, 183 u32 type) 184{ 185 u32 reg, reg_idx, shift, val; 186 187 /* 188 * Select the appropriate event select register(HHA_EVENT_TYPEx). 189 * There are 4 event select registers for the 16 hardware counters. 190 * Event code is 8-bits and for the first 4 hardware counters, 191 * HHA_EVENT_TYPE0 is chosen. For the next 4 hardware counters, 192 * HHA_EVENT_TYPE1 is chosen and so on. 193 */ 194 reg = HHA_EVENT_TYPE0 + 4 * (idx / 4); 195 reg_idx = idx % 4; 196 shift = 8 * reg_idx; 197 198 /* Write event code to HHA_EVENT_TYPEx register */ 199 val = readl(hha_pmu->base + reg); 200 val &= ~(HHA_EVTYPE_MASK << shift); 201 val |= (type << shift); 202 writel(val, hha_pmu->base + reg); 203} 204 205static void hisi_hha_pmu_start_counters(struct hisi_pmu *hha_pmu) 206{ 207 u32 val; 208 209 /* 210 * Set perf_enable bit in HHA_PERF_CTRL to start event 211 * counting for all enabled counters. 212 */ 213 val = readl(hha_pmu->base + HHA_PERF_CTRL); 214 val |= HHA_PERF_CTRL_EN; 215 writel(val, hha_pmu->base + HHA_PERF_CTRL); 216} 217 218static void hisi_hha_pmu_stop_counters(struct hisi_pmu *hha_pmu) 219{ 220 u32 val; 221 222 /* 223 * Clear perf_enable bit in HHA_PERF_CTRL to stop event 224 * counting for all enabled counters. 225 */ 226 val = readl(hha_pmu->base + HHA_PERF_CTRL); 227 val &= ~(HHA_PERF_CTRL_EN); 228 writel(val, hha_pmu->base + HHA_PERF_CTRL); 229} 230 231static void hisi_hha_pmu_enable_counter(struct hisi_pmu *hha_pmu, 232 struct hw_perf_event *hwc) 233{ 234 u32 val; 235 236 /* Enable counter index in HHA_EVENT_CTRL register */ 237 val = readl(hha_pmu->base + HHA_EVENT_CTRL); 238 val |= (1 << hwc->idx); 239 writel(val, hha_pmu->base + HHA_EVENT_CTRL); 240} 241 242static void hisi_hha_pmu_disable_counter(struct hisi_pmu *hha_pmu, 243 struct hw_perf_event *hwc) 244{ 245 u32 val; 246 247 /* Clear counter index in HHA_EVENT_CTRL register */ 248 val = readl(hha_pmu->base + HHA_EVENT_CTRL); 249 val &= ~(1 << hwc->idx); 250 writel(val, hha_pmu->base + HHA_EVENT_CTRL); 251} 252 253static void hisi_hha_pmu_enable_counter_int(struct hisi_pmu *hha_pmu, 254 struct hw_perf_event *hwc) 255{ 256 u32 val; 257 258 /* Write 0 to enable interrupt */ 259 val = readl(hha_pmu->base + HHA_INT_MASK); 260 val &= ~(1 << hwc->idx); 261 writel(val, hha_pmu->base + HHA_INT_MASK); 262} 263 264static void hisi_hha_pmu_disable_counter_int(struct hisi_pmu *hha_pmu, 265 struct hw_perf_event *hwc) 266{ 267 u32 val; 268 269 /* Write 1 to mask interrupt */ 270 val = readl(hha_pmu->base + HHA_INT_MASK); 271 val |= (1 << hwc->idx); 272 writel(val, hha_pmu->base + HHA_INT_MASK); 273} 274 275static u32 hisi_hha_pmu_get_int_status(struct hisi_pmu *hha_pmu) 276{ 277 return readl(hha_pmu->base + HHA_INT_STATUS); 278} 279 280static void hisi_hha_pmu_clear_int_status(struct hisi_pmu *hha_pmu, int idx) 281{ 282 writel(1 << idx, hha_pmu->base + HHA_INT_CLEAR); 283} 284 285static const struct acpi_device_id hisi_hha_pmu_acpi_match[] = { 286 { "HISI0243", }, 287 { "HISI0244", }, 288 {} 289}; 290MODULE_DEVICE_TABLE(acpi, hisi_hha_pmu_acpi_match); 291 292static int hisi_hha_pmu_init_data(struct platform_device *pdev, 293 struct hisi_pmu *hha_pmu) 294{ 295 unsigned long long id; 296 acpi_status status; 297 298 hisi_uncore_pmu_init_topology(hha_pmu, &pdev->dev); 299 300 /* 301 * Use SCCL_ID and UID to identify the HHA PMU, while 302 * SCCL_ID is in MPIDR[aff2]. 303 */ 304 if (hha_pmu->topo.sccl_id < 0) { 305 dev_err(&pdev->dev, "Can not read hha sccl-id!\n"); 306 return -EINVAL; 307 } 308 309 /* 310 * Early versions of BIOS support _UID by mistake, so we support 311 * both "hisilicon, idx-id" as preference, if available. 312 */ 313 if (hha_pmu->topo.index_id < 0) { 314 status = acpi_evaluate_integer(ACPI_HANDLE(&pdev->dev), 315 "_UID", NULL, &id); 316 if (ACPI_FAILURE(status)) { 317 dev_err(&pdev->dev, "Cannot read idx-id!\n"); 318 return -EINVAL; 319 } 320 321 hha_pmu->topo.index_id = id; 322 } 323 324 hha_pmu->base = devm_platform_ioremap_resource(pdev, 0); 325 if (IS_ERR(hha_pmu->base)) { 326 dev_err(&pdev->dev, "ioremap failed for hha_pmu resource\n"); 327 return PTR_ERR(hha_pmu->base); 328 } 329 330 hha_pmu->identifier = readl(hha_pmu->base + HHA_VERSION); 331 332 return 0; 333} 334 335static struct attribute *hisi_hha_pmu_v1_format_attr[] = { 336 HISI_PMU_FORMAT_ATTR(event, "config:0-7"), 337 NULL, 338}; 339 340static const struct attribute_group hisi_hha_pmu_v1_format_group = { 341 .name = "format", 342 .attrs = hisi_hha_pmu_v1_format_attr, 343}; 344 345static struct attribute *hisi_hha_pmu_v2_format_attr[] = { 346 HISI_PMU_FORMAT_ATTR(event, "config:0-7"), 347 HISI_PMU_FORMAT_ATTR(srcid_cmd, "config1:0-10"), 348 HISI_PMU_FORMAT_ATTR(srcid_msk, "config1:11-21"), 349 HISI_PMU_FORMAT_ATTR(tracetag_en, "config1:22"), 350 HISI_PMU_FORMAT_ATTR(datasrc_skt, "config1:23"), 351 NULL 352}; 353 354static const struct attribute_group hisi_hha_pmu_v2_format_group = { 355 .name = "format", 356 .attrs = hisi_hha_pmu_v2_format_attr, 357}; 358 359static struct attribute *hisi_hha_pmu_v1_events_attr[] = { 360 HISI_PMU_EVENT_ATTR(rx_ops_num, 0x00), 361 HISI_PMU_EVENT_ATTR(rx_outer, 0x01), 362 HISI_PMU_EVENT_ATTR(rx_sccl, 0x02), 363 HISI_PMU_EVENT_ATTR(rx_ccix, 0x03), 364 HISI_PMU_EVENT_ATTR(rx_wbi, 0x04), 365 HISI_PMU_EVENT_ATTR(rx_wbip, 0x05), 366 HISI_PMU_EVENT_ATTR(rx_wtistash, 0x11), 367 HISI_PMU_EVENT_ATTR(rd_ddr_64b, 0x1c), 368 HISI_PMU_EVENT_ATTR(wr_ddr_64b, 0x1d), 369 HISI_PMU_EVENT_ATTR(rd_ddr_128b, 0x1e), 370 HISI_PMU_EVENT_ATTR(wr_ddr_128b, 0x1f), 371 HISI_PMU_EVENT_ATTR(spill_num, 0x20), 372 HISI_PMU_EVENT_ATTR(spill_success, 0x21), 373 HISI_PMU_EVENT_ATTR(bi_num, 0x23), 374 HISI_PMU_EVENT_ATTR(mediated_num, 0x32), 375 HISI_PMU_EVENT_ATTR(tx_snp_num, 0x33), 376 HISI_PMU_EVENT_ATTR(tx_snp_outer, 0x34), 377 HISI_PMU_EVENT_ATTR(tx_snp_ccix, 0x35), 378 HISI_PMU_EVENT_ATTR(rx_snprspdata, 0x38), 379 HISI_PMU_EVENT_ATTR(rx_snprsp_outer, 0x3c), 380 HISI_PMU_EVENT_ATTR(sdir-lookup, 0x40), 381 HISI_PMU_EVENT_ATTR(edir-lookup, 0x41), 382 HISI_PMU_EVENT_ATTR(sdir-hit, 0x42), 383 HISI_PMU_EVENT_ATTR(edir-hit, 0x43), 384 HISI_PMU_EVENT_ATTR(sdir-home-migrate, 0x4c), 385 HISI_PMU_EVENT_ATTR(edir-home-migrate, 0x4d), 386 NULL, 387}; 388 389static const struct attribute_group hisi_hha_pmu_v1_events_group = { 390 .name = "events", 391 .attrs = hisi_hha_pmu_v1_events_attr, 392}; 393 394static struct attribute *hisi_hha_pmu_v2_events_attr[] = { 395 HISI_PMU_EVENT_ATTR(rx_ops_num, 0x00), 396 HISI_PMU_EVENT_ATTR(rx_outer, 0x01), 397 HISI_PMU_EVENT_ATTR(rx_sccl, 0x02), 398 HISI_PMU_EVENT_ATTR(hha_retry, 0x2e), 399 HISI_PMU_EVENT_ATTR(cycles, 0x55), 400 NULL 401}; 402 403static const struct attribute_group hisi_hha_pmu_v2_events_group = { 404 .name = "events", 405 .attrs = hisi_hha_pmu_v2_events_attr, 406}; 407 408static const struct attribute_group *hisi_hha_pmu_v1_attr_groups[] = { 409 &hisi_hha_pmu_v1_format_group, 410 &hisi_hha_pmu_v1_events_group, 411 &hisi_pmu_cpumask_attr_group, 412 &hisi_pmu_identifier_group, 413 NULL, 414}; 415 416static const struct attribute_group *hisi_hha_pmu_v2_attr_groups[] = { 417 &hisi_hha_pmu_v2_format_group, 418 &hisi_hha_pmu_v2_events_group, 419 &hisi_pmu_cpumask_attr_group, 420 &hisi_pmu_identifier_group, 421 NULL 422}; 423 424static const struct hisi_uncore_ops hisi_uncore_hha_ops = { 425 .write_evtype = hisi_hha_pmu_write_evtype, 426 .get_event_idx = hisi_uncore_pmu_get_event_idx, 427 .start_counters = hisi_hha_pmu_start_counters, 428 .stop_counters = hisi_hha_pmu_stop_counters, 429 .enable_counter = hisi_hha_pmu_enable_counter, 430 .disable_counter = hisi_hha_pmu_disable_counter, 431 .enable_counter_int = hisi_hha_pmu_enable_counter_int, 432 .disable_counter_int = hisi_hha_pmu_disable_counter_int, 433 .write_counter = hisi_hha_pmu_write_counter, 434 .read_counter = hisi_hha_pmu_read_counter, 435 .get_int_status = hisi_hha_pmu_get_int_status, 436 .clear_int_status = hisi_hha_pmu_clear_int_status, 437 .enable_filter = hisi_hha_pmu_enable_filter, 438 .disable_filter = hisi_hha_pmu_disable_filter, 439}; 440 441static int hisi_hha_pmu_dev_probe(struct platform_device *pdev, 442 struct hisi_pmu *hha_pmu) 443{ 444 int ret; 445 446 ret = hisi_hha_pmu_init_data(pdev, hha_pmu); 447 if (ret) 448 return ret; 449 450 ret = hisi_uncore_pmu_init_irq(hha_pmu, pdev); 451 if (ret) 452 return ret; 453 454 if (hha_pmu->identifier >= HISI_PMU_V2) { 455 hha_pmu->counter_bits = 64; 456 hha_pmu->check_event = HHA_V2_NR_EVENT; 457 hha_pmu->pmu_events.attr_groups = hisi_hha_pmu_v2_attr_groups; 458 hha_pmu->num_counters = HHA_V2_NR_COUNTERS; 459 } else { 460 hha_pmu->counter_bits = 48; 461 hha_pmu->check_event = HHA_V1_NR_EVENT; 462 hha_pmu->pmu_events.attr_groups = hisi_hha_pmu_v1_attr_groups; 463 hha_pmu->num_counters = HHA_V1_NR_COUNTERS; 464 } 465 hha_pmu->ops = &hisi_uncore_hha_ops; 466 hha_pmu->dev = &pdev->dev; 467 hha_pmu->on_cpu = -1; 468 469 return 0; 470} 471 472static int hisi_hha_pmu_probe(struct platform_device *pdev) 473{ 474 struct hisi_pmu *hha_pmu; 475 char *name; 476 int ret; 477 478 hha_pmu = devm_kzalloc(&pdev->dev, sizeof(*hha_pmu), GFP_KERNEL); 479 if (!hha_pmu) 480 return -ENOMEM; 481 482 platform_set_drvdata(pdev, hha_pmu); 483 484 ret = hisi_hha_pmu_dev_probe(pdev, hha_pmu); 485 if (ret) 486 return ret; 487 488 name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sccl%d_hha%d", 489 hha_pmu->topo.sccl_id, hha_pmu->topo.index_id); 490 if (!name) 491 return -ENOMEM; 492 493 ret = cpuhp_state_add_instance(CPUHP_AP_PERF_ARM_HISI_HHA_ONLINE, 494 &hha_pmu->node); 495 if (ret) { 496 dev_err(&pdev->dev, "Error %d registering hotplug\n", ret); 497 return ret; 498 } 499 500 hisi_pmu_init(hha_pmu, THIS_MODULE); 501 502 ret = perf_pmu_register(&hha_pmu->pmu, name, -1); 503 if (ret) { 504 dev_err(hha_pmu->dev, "HHA PMU register failed!\n"); 505 cpuhp_state_remove_instance_nocalls( 506 CPUHP_AP_PERF_ARM_HISI_HHA_ONLINE, &hha_pmu->node); 507 } 508 509 return ret; 510} 511 512static void hisi_hha_pmu_remove(struct platform_device *pdev) 513{ 514 struct hisi_pmu *hha_pmu = platform_get_drvdata(pdev); 515 516 perf_pmu_unregister(&hha_pmu->pmu); 517 cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_HISI_HHA_ONLINE, 518 &hha_pmu->node); 519} 520 521static struct platform_driver hisi_hha_pmu_driver = { 522 .driver = { 523 .name = "hisi_hha_pmu", 524 .acpi_match_table = ACPI_PTR(hisi_hha_pmu_acpi_match), 525 .suppress_bind_attrs = true, 526 }, 527 .probe = hisi_hha_pmu_probe, 528 .remove = hisi_hha_pmu_remove, 529}; 530 531static int __init hisi_hha_pmu_module_init(void) 532{ 533 int ret; 534 535 ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_HISI_HHA_ONLINE, 536 "AP_PERF_ARM_HISI_HHA_ONLINE", 537 hisi_uncore_pmu_online_cpu, 538 hisi_uncore_pmu_offline_cpu); 539 if (ret) { 540 pr_err("HHA PMU: Error setup hotplug, ret = %d;\n", ret); 541 return ret; 542 } 543 544 ret = platform_driver_register(&hisi_hha_pmu_driver); 545 if (ret) 546 cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_HHA_ONLINE); 547 548 return ret; 549} 550module_init(hisi_hha_pmu_module_init); 551 552static void __exit hisi_hha_pmu_module_exit(void) 553{ 554 platform_driver_unregister(&hisi_hha_pmu_driver); 555 cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_HHA_ONLINE); 556} 557module_exit(hisi_hha_pmu_module_exit); 558 559MODULE_IMPORT_NS("HISI_PMU"); 560MODULE_DESCRIPTION("HiSilicon SoC HHA uncore PMU driver"); 561MODULE_LICENSE("GPL v2"); 562MODULE_AUTHOR("Shaokun Zhang <zhangshaokun@hisilicon.com>"); 563MODULE_AUTHOR("Anurup M <anurup.m@huawei.com>");
~/
Read-only file preview.