linux/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c
<<
>>
Prefs
   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>");
 638