hisi_uncore_l3c_pmu.c (17631B)
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 58HISI_PMU_EVENT_ATTR_EXTRACTOR(tt_core, config1, 7, 0); 59HISI_PMU_EVENT_ATTR_EXTRACTOR(tt_req, config1, 10, 8); 60HISI_PMU_EVENT_ATTR_EXTRACTOR(datasrc_cfg, config1, 15, 11); 61HISI_PMU_EVENT_ATTR_EXTRACTOR(datasrc_skt, config1, 16, 16); 62 63static 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 84static 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 105static 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 129static 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 147static 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 165static 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 186static 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 207static 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 216static 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 */ 228static u32 hisi_l3c_pmu_get_counter_offset(int cntr_idx) 229{ 230 return (L3C_CNTR0_LOWER + (cntr_idx * 8)); 231} 232 233static 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 239static 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 245static 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 268static 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 281static 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 294static 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 305static 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 316static 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 327static 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 338static u32 hisi_l3c_pmu_get_int_status(struct hisi_pmu *l3c_pmu) 339{ 340 return readl(l3c_pmu->base + L3C_INT_STATUS); 341} 342 343static 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 348static const struct acpi_device_id hisi_l3c_pmu_acpi_match[] = { 349 { "HISI0213", }, 350 { "HISI0214", }, 351 {} 352}; 353MODULE_DEVICE_TABLE(acpi, hisi_l3c_pmu_acpi_match); 354 355static 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 385static struct attribute *hisi_l3c_pmu_v1_format_attr[] = { 386 HISI_PMU_FORMAT_ATTR(event, "config:0-7"), 387 NULL, 388}; 389 390static const struct attribute_group hisi_l3c_pmu_v1_format_group = { 391 .name = "format", 392 .attrs = hisi_l3c_pmu_v1_format_attr, 393}; 394 395static 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 404static const struct attribute_group hisi_l3c_pmu_v2_format_group = { 405 .name = "format", 406 .attrs = hisi_l3c_pmu_v2_format_attr, 407}; 408 409static 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 426static const struct attribute_group hisi_l3c_pmu_v1_events_group = { 427 .name = "events", 428 .attrs = hisi_l3c_pmu_v1_events_attr, 429}; 430 431static 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 439static const struct attribute_group hisi_l3c_pmu_v2_events_group = { 440 .name = "events", 441 .attrs = hisi_l3c_pmu_v2_events_attr, 442}; 443 444static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL); 445 446static struct attribute *hisi_l3c_pmu_cpumask_attrs[] = { 447 &dev_attr_cpumask.attr, 448 NULL, 449}; 450 451static const struct attribute_group hisi_l3c_pmu_cpumask_attr_group = { 452 .attrs = hisi_l3c_pmu_cpumask_attrs, 453}; 454 455static struct device_attribute hisi_l3c_pmu_identifier_attr = 456 __ATTR(identifier, 0444, hisi_uncore_pmu_identifier_attr_show, NULL); 457 458static struct attribute *hisi_l3c_pmu_identifier_attrs[] = { 459 &hisi_l3c_pmu_identifier_attr.attr, 460 NULL 461}; 462 463static const struct attribute_group hisi_l3c_pmu_identifier_group = { 464 .attrs = hisi_l3c_pmu_identifier_attrs, 465}; 466 467static 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 475static 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 483static 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 500static 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 531static 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 586static 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 596static 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 606static 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} 625module_init(hisi_l3c_pmu_module_init); 626 627static 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} 632module_exit(hisi_l3c_pmu_module_exit); 633 634MODULE_DESCRIPTION("HiSilicon SoC L3C uncore PMU driver"); 635MODULE_LICENSE("GPL v2"); 636MODULE_AUTHOR("Anurup M <anurup.m@huawei.com>"); 637MODULE_AUTHOR("Shaokun Zhang <zhangshaokun@hisilicon.com>");