root/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. hisi_l3c_pmu_get_counter_offset
  2. hisi_l3c_pmu_read_counter
  3. hisi_l3c_pmu_write_counter
  4. hisi_l3c_pmu_write_evtype
  5. hisi_l3c_pmu_start_counters
  6. hisi_l3c_pmu_stop_counters
  7. hisi_l3c_pmu_enable_counter
  8. hisi_l3c_pmu_disable_counter
  9. hisi_l3c_pmu_enable_counter_int
  10. hisi_l3c_pmu_disable_counter_int
  11. hisi_l3c_pmu_isr
  12. hisi_l3c_pmu_init_irq
  13. hisi_l3c_pmu_init_data
  14. hisi_l3c_pmu_dev_probe
  15. hisi_l3c_pmu_probe
  16. hisi_l3c_pmu_remove
  17. hisi_l3c_pmu_module_init
  18. hisi_l3c_pmu_module_exit

   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/platform_device.h>
  18 #include <linux/smp.h>
  19 
  20 #include "hisi_uncore_pmu.h"
  21 
  22 /* L3C register definition */
  23 #define L3C_PERF_CTRL           0x0408
  24 #define L3C_INT_MASK            0x0800
  25 #define L3C_INT_STATUS          0x0808
  26 #define L3C_INT_CLEAR           0x080c
  27 #define L3C_EVENT_CTRL          0x1c00
  28 #define L3C_EVENT_TYPE0         0x1d00
  29 /*
  30  * Each counter is 48-bits and [48:63] are reserved
  31  * which are Read-As-Zero and Writes-Ignored.
  32  */
  33 #define L3C_CNTR0_LOWER         0x1e00
  34 
  35 /* L3C has 8-counters */
  36 #define L3C_NR_COUNTERS         0x8
  37 
  38 #define L3C_PERF_CTRL_EN        0x20000
  39 #define L3C_EVTYPE_NONE         0xff
  40 
  41 /*
  42  * Select the counter register offset using the counter index
  43  */
  44 static u32 hisi_l3c_pmu_get_counter_offset(int cntr_idx)
  45 {
  46         return (L3C_CNTR0_LOWER + (cntr_idx * 8));
  47 }
  48 
  49 static u64 hisi_l3c_pmu_read_counter(struct hisi_pmu *l3c_pmu,
  50                                      struct hw_perf_event *hwc)
  51 {
  52         u32 idx = hwc->idx;
  53 
  54         if (!hisi_uncore_pmu_counter_valid(l3c_pmu, idx)) {
  55                 dev_err(l3c_pmu->dev, "Unsupported event index:%d!\n", idx);
  56                 return 0;
  57         }
  58 
  59         /* Read 64-bits and the upper 16 bits are RAZ */
  60         return readq(l3c_pmu->base + hisi_l3c_pmu_get_counter_offset(idx));
  61 }
  62 
  63 static void hisi_l3c_pmu_write_counter(struct hisi_pmu *l3c_pmu,
  64                                        struct hw_perf_event *hwc, u64 val)
  65 {
  66         u32 idx = hwc->idx;
  67 
  68         if (!hisi_uncore_pmu_counter_valid(l3c_pmu, idx)) {
  69                 dev_err(l3c_pmu->dev, "Unsupported event index:%d!\n", idx);
  70                 return;
  71         }
  72 
  73         /* Write 64-bits and the upper 16 bits are WI */
  74         writeq(val, l3c_pmu->base + hisi_l3c_pmu_get_counter_offset(idx));
  75 }
  76 
  77 static void hisi_l3c_pmu_write_evtype(struct hisi_pmu *l3c_pmu, int idx,
  78                                       u32 type)
  79 {
  80         u32 reg, reg_idx, shift, val;
  81 
  82         /*
  83          * Select the appropriate event select register(L3C_EVENT_TYPE0/1).
  84          * There are 2 event select registers for the 8 hardware counters.
  85          * Event code is 8-bits and for the former 4 hardware counters,
  86          * L3C_EVENT_TYPE0 is chosen. For the latter 4 hardware counters,
  87          * L3C_EVENT_TYPE1 is chosen.
  88          */
  89         reg = L3C_EVENT_TYPE0 + (idx / 4) * 4;
  90         reg_idx = idx % 4;
  91         shift = 8 * reg_idx;
  92 
  93         /* Write event code to L3C_EVENT_TYPEx Register */
  94         val = readl(l3c_pmu->base + reg);
  95         val &= ~(L3C_EVTYPE_NONE << shift);
  96         val |= (type << shift);
  97         writel(val, l3c_pmu->base + reg);
  98 }
  99 
 100 static void hisi_l3c_pmu_start_counters(struct hisi_pmu *l3c_pmu)
 101 {
 102         u32 val;
 103 
 104         /*
 105          * Set perf_enable bit in L3C_PERF_CTRL register to start counting
 106          * for all enabled counters.
 107          */
 108         val = readl(l3c_pmu->base + L3C_PERF_CTRL);
 109         val |= L3C_PERF_CTRL_EN;
 110         writel(val, l3c_pmu->base + L3C_PERF_CTRL);
 111 }
 112 
 113 static void hisi_l3c_pmu_stop_counters(struct hisi_pmu *l3c_pmu)
 114 {
 115         u32 val;
 116 
 117         /*
 118          * Clear perf_enable bit in L3C_PERF_CTRL register to stop counting
 119          * for all enabled counters.
 120          */
 121         val = readl(l3c_pmu->base + L3C_PERF_CTRL);
 122         val &= ~(L3C_PERF_CTRL_EN);
 123         writel(val, l3c_pmu->base + L3C_PERF_CTRL);
 124 }
 125 
 126 static void hisi_l3c_pmu_enable_counter(struct hisi_pmu *l3c_pmu,
 127                                         struct hw_perf_event *hwc)
 128 {
 129         u32 val;
 130 
 131         /* Enable counter index in L3C_EVENT_CTRL register */
 132         val = readl(l3c_pmu->base + L3C_EVENT_CTRL);
 133         val |= (1 << hwc->idx);
 134         writel(val, l3c_pmu->base + L3C_EVENT_CTRL);
 135 }
 136 
 137 static void hisi_l3c_pmu_disable_counter(struct hisi_pmu *l3c_pmu,
 138                                          struct hw_perf_event *hwc)
 139 {
 140         u32 val;
 141 
 142         /* Clear counter index in L3C_EVENT_CTRL register */
 143         val = readl(l3c_pmu->base + L3C_EVENT_CTRL);
 144         val &= ~(1 << hwc->idx);
 145         writel(val, l3c_pmu->base + L3C_EVENT_CTRL);
 146 }
 147 
 148 static void hisi_l3c_pmu_enable_counter_int(struct hisi_pmu *l3c_pmu,
 149                                             struct hw_perf_event *hwc)
 150 {
 151         u32 val;
 152 
 153         val = readl(l3c_pmu->base + L3C_INT_MASK);
 154         /* Write 0 to enable interrupt */
 155         val &= ~(1 << hwc->idx);
 156         writel(val, l3c_pmu->base + L3C_INT_MASK);
 157 }
 158 
 159 static void hisi_l3c_pmu_disable_counter_int(struct hisi_pmu *l3c_pmu,
 160                                              struct hw_perf_event *hwc)
 161 {
 162         u32 val;
 163 
 164         val = readl(l3c_pmu->base + L3C_INT_MASK);
 165         /* Write 1 to mask interrupt */
 166         val |= (1 << hwc->idx);
 167         writel(val, l3c_pmu->base + L3C_INT_MASK);
 168 }
 169 
 170 static irqreturn_t hisi_l3c_pmu_isr(int irq, void *dev_id)
 171 {
 172         struct hisi_pmu *l3c_pmu = dev_id;
 173         struct perf_event *event;
 174         unsigned long overflown;
 175         int idx;
 176 
 177         /* Read L3C_INT_STATUS register */
 178         overflown = readl(l3c_pmu->base + L3C_INT_STATUS);
 179         if (!overflown)
 180                 return IRQ_NONE;
 181 
 182         /*
 183          * Find the counter index which overflowed if the bit was set
 184          * and handle it.
 185          */
 186         for_each_set_bit(idx, &overflown, L3C_NR_COUNTERS) {
 187                 /* Write 1 to clear the IRQ status flag */
 188                 writel((1 << idx), l3c_pmu->base + L3C_INT_CLEAR);
 189 
 190                 /* Get the corresponding event struct */
 191                 event = l3c_pmu->pmu_events.hw_events[idx];
 192                 if (!event)
 193                         continue;
 194 
 195                 hisi_uncore_pmu_event_update(event);
 196                 hisi_uncore_pmu_set_event_period(event);
 197         }
 198 
 199         return IRQ_HANDLED;
 200 }
 201 
 202 static int hisi_l3c_pmu_init_irq(struct hisi_pmu *l3c_pmu,
 203                                  struct platform_device *pdev)
 204 {
 205         int irq, ret;
 206 
 207         /* Read and init IRQ */
 208         irq = platform_get_irq(pdev, 0);
 209         if (irq < 0)
 210                 return irq;
 211 
 212         ret = devm_request_irq(&pdev->dev, irq, hisi_l3c_pmu_isr,
 213                                IRQF_NOBALANCING | IRQF_NO_THREAD,
 214                                dev_name(&pdev->dev), l3c_pmu);
 215         if (ret < 0) {
 216                 dev_err(&pdev->dev,
 217                         "Fail to request IRQ:%d ret:%d\n", irq, ret);
 218                 return ret;
 219         }
 220 
 221         l3c_pmu->irq = irq;
 222 
 223         return 0;
 224 }
 225 
 226 static const struct acpi_device_id hisi_l3c_pmu_acpi_match[] = {
 227         { "HISI0213", },
 228         {},
 229 };
 230 MODULE_DEVICE_TABLE(acpi, hisi_l3c_pmu_acpi_match);
 231 
 232 static int hisi_l3c_pmu_init_data(struct platform_device *pdev,
 233                                   struct hisi_pmu *l3c_pmu)
 234 {
 235         unsigned long long id;
 236         struct resource *res;
 237         acpi_status status;
 238 
 239         status = acpi_evaluate_integer(ACPI_HANDLE(&pdev->dev),
 240                                        "_UID", NULL, &id);
 241         if (ACPI_FAILURE(status))
 242                 return -EINVAL;
 243 
 244         l3c_pmu->index_id = id;
 245 
 246         /*
 247          * Use the SCCL_ID and CCL_ID to identify the L3C PMU, while
 248          * SCCL_ID is in MPIDR[aff2] and CCL_ID is in MPIDR[aff1].
 249          */
 250         if (device_property_read_u32(&pdev->dev, "hisilicon,scl-id",
 251                                      &l3c_pmu->sccl_id)) {
 252                 dev_err(&pdev->dev, "Can not read l3c sccl-id!\n");
 253                 return -EINVAL;
 254         }
 255 
 256         if (device_property_read_u32(&pdev->dev, "hisilicon,ccl-id",
 257                                      &l3c_pmu->ccl_id)) {
 258                 dev_err(&pdev->dev, "Can not read l3c ccl-id!\n");
 259                 return -EINVAL;
 260         }
 261 
 262         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 263         l3c_pmu->base = devm_ioremap_resource(&pdev->dev, res);
 264         if (IS_ERR(l3c_pmu->base)) {
 265                 dev_err(&pdev->dev, "ioremap failed for l3c_pmu resource\n");
 266                 return PTR_ERR(l3c_pmu->base);
 267         }
 268 
 269         return 0;
 270 }
 271 
 272 static struct attribute *hisi_l3c_pmu_format_attr[] = {
 273         HISI_PMU_FORMAT_ATTR(event, "config:0-7"),
 274         NULL,
 275 };
 276 
 277 static const struct attribute_group hisi_l3c_pmu_format_group = {
 278         .name = "format",
 279         .attrs = hisi_l3c_pmu_format_attr,
 280 };
 281 
 282 static struct attribute *hisi_l3c_pmu_events_attr[] = {
 283         HISI_PMU_EVENT_ATTR(rd_cpipe,           0x00),
 284         HISI_PMU_EVENT_ATTR(wr_cpipe,           0x01),
 285         HISI_PMU_EVENT_ATTR(rd_hit_cpipe,       0x02),
 286         HISI_PMU_EVENT_ATTR(wr_hit_cpipe,       0x03),
 287         HISI_PMU_EVENT_ATTR(victim_num,         0x04),
 288         HISI_PMU_EVENT_ATTR(rd_spipe,           0x20),
 289         HISI_PMU_EVENT_ATTR(wr_spipe,           0x21),
 290         HISI_PMU_EVENT_ATTR(rd_hit_spipe,       0x22),
 291         HISI_PMU_EVENT_ATTR(wr_hit_spipe,       0x23),
 292         HISI_PMU_EVENT_ATTR(back_invalid,       0x29),
 293         HISI_PMU_EVENT_ATTR(retry_cpu,          0x40),
 294         HISI_PMU_EVENT_ATTR(retry_ring,         0x41),
 295         HISI_PMU_EVENT_ATTR(prefetch_drop,      0x42),
 296         NULL,
 297 };
 298 
 299 static const struct attribute_group hisi_l3c_pmu_events_group = {
 300         .name = "events",
 301         .attrs = hisi_l3c_pmu_events_attr,
 302 };
 303 
 304 static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL);
 305 
 306 static struct attribute *hisi_l3c_pmu_cpumask_attrs[] = {
 307         &dev_attr_cpumask.attr,
 308         NULL,
 309 };
 310 
 311 static const struct attribute_group hisi_l3c_pmu_cpumask_attr_group = {
 312         .attrs = hisi_l3c_pmu_cpumask_attrs,
 313 };
 314 
 315 static const struct attribute_group *hisi_l3c_pmu_attr_groups[] = {
 316         &hisi_l3c_pmu_format_group,
 317         &hisi_l3c_pmu_events_group,
 318         &hisi_l3c_pmu_cpumask_attr_group,
 319         NULL,
 320 };
 321 
 322 static const struct hisi_uncore_ops hisi_uncore_l3c_ops = {
 323         .write_evtype           = hisi_l3c_pmu_write_evtype,
 324         .get_event_idx          = hisi_uncore_pmu_get_event_idx,
 325         .start_counters         = hisi_l3c_pmu_start_counters,
 326         .stop_counters          = hisi_l3c_pmu_stop_counters,
 327         .enable_counter         = hisi_l3c_pmu_enable_counter,
 328         .disable_counter        = hisi_l3c_pmu_disable_counter,
 329         .enable_counter_int     = hisi_l3c_pmu_enable_counter_int,
 330         .disable_counter_int    = hisi_l3c_pmu_disable_counter_int,
 331         .write_counter          = hisi_l3c_pmu_write_counter,
 332         .read_counter           = hisi_l3c_pmu_read_counter,
 333 };
 334 
 335 static int hisi_l3c_pmu_dev_probe(struct platform_device *pdev,
 336                                   struct hisi_pmu *l3c_pmu)
 337 {
 338         int ret;
 339 
 340         ret = hisi_l3c_pmu_init_data(pdev, l3c_pmu);
 341         if (ret)
 342                 return ret;
 343 
 344         ret = hisi_l3c_pmu_init_irq(l3c_pmu, pdev);
 345         if (ret)
 346                 return ret;
 347 
 348         l3c_pmu->num_counters = L3C_NR_COUNTERS;
 349         l3c_pmu->counter_bits = 48;
 350         l3c_pmu->ops = &hisi_uncore_l3c_ops;
 351         l3c_pmu->dev = &pdev->dev;
 352         l3c_pmu->on_cpu = -1;
 353         l3c_pmu->check_event = 0x59;
 354 
 355         return 0;
 356 }
 357 
 358 static int hisi_l3c_pmu_probe(struct platform_device *pdev)
 359 {
 360         struct hisi_pmu *l3c_pmu;
 361         char *name;
 362         int ret;
 363 
 364         l3c_pmu = devm_kzalloc(&pdev->dev, sizeof(*l3c_pmu), GFP_KERNEL);
 365         if (!l3c_pmu)
 366                 return -ENOMEM;
 367 
 368         platform_set_drvdata(pdev, l3c_pmu);
 369 
 370         ret = hisi_l3c_pmu_dev_probe(pdev, l3c_pmu);
 371         if (ret)
 372                 return ret;
 373 
 374         ret = cpuhp_state_add_instance(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE,
 375                                        &l3c_pmu->node);
 376         if (ret) {
 377                 dev_err(&pdev->dev, "Error %d registering hotplug\n", ret);
 378                 return ret;
 379         }
 380 
 381         name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sccl%u_l3c%u",
 382                               l3c_pmu->sccl_id, l3c_pmu->index_id);
 383         l3c_pmu->pmu = (struct pmu) {
 384                 .name           = name,
 385                 .task_ctx_nr    = perf_invalid_context,
 386                 .event_init     = hisi_uncore_pmu_event_init,
 387                 .pmu_enable     = hisi_uncore_pmu_enable,
 388                 .pmu_disable    = hisi_uncore_pmu_disable,
 389                 .add            = hisi_uncore_pmu_add,
 390                 .del            = hisi_uncore_pmu_del,
 391                 .start          = hisi_uncore_pmu_start,
 392                 .stop           = hisi_uncore_pmu_stop,
 393                 .read           = hisi_uncore_pmu_read,
 394                 .attr_groups    = hisi_l3c_pmu_attr_groups,
 395                 .capabilities   = PERF_PMU_CAP_NO_EXCLUDE,
 396         };
 397 
 398         ret = perf_pmu_register(&l3c_pmu->pmu, name, -1);
 399         if (ret) {
 400                 dev_err(l3c_pmu->dev, "L3C PMU register failed!\n");
 401                 cpuhp_state_remove_instance(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE,
 402                                             &l3c_pmu->node);
 403         }
 404 
 405         return ret;
 406 }
 407 
 408 static int hisi_l3c_pmu_remove(struct platform_device *pdev)
 409 {
 410         struct hisi_pmu *l3c_pmu = platform_get_drvdata(pdev);
 411 
 412         perf_pmu_unregister(&l3c_pmu->pmu);
 413         cpuhp_state_remove_instance(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE,
 414                                     &l3c_pmu->node);
 415 
 416         return 0;
 417 }
 418 
 419 static struct platform_driver hisi_l3c_pmu_driver = {
 420         .driver = {
 421                 .name = "hisi_l3c_pmu",
 422                 .acpi_match_table = ACPI_PTR(hisi_l3c_pmu_acpi_match),
 423         },
 424         .probe = hisi_l3c_pmu_probe,
 425         .remove = hisi_l3c_pmu_remove,
 426 };
 427 
 428 static int __init hisi_l3c_pmu_module_init(void)
 429 {
 430         int ret;
 431 
 432         ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE,
 433                                       "AP_PERF_ARM_HISI_L3_ONLINE",
 434                                       hisi_uncore_pmu_online_cpu,
 435                                       hisi_uncore_pmu_offline_cpu);
 436         if (ret) {
 437                 pr_err("L3C PMU: Error setup hotplug, ret = %d\n", ret);
 438                 return ret;
 439         }
 440 
 441         ret = platform_driver_register(&hisi_l3c_pmu_driver);
 442         if (ret)
 443                 cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE);
 444 
 445         return ret;
 446 }
 447 module_init(hisi_l3c_pmu_module_init);
 448 
 449 static void __exit hisi_l3c_pmu_module_exit(void)
 450 {
 451         platform_driver_unregister(&hisi_l3c_pmu_driver);
 452         cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE);
 453 }
 454 module_exit(hisi_l3c_pmu_module_exit);
 455 
 456 MODULE_DESCRIPTION("HiSilicon SoC L3C uncore PMU driver");
 457 MODULE_LICENSE("GPL v2");
 458 MODULE_AUTHOR("Anurup M <anurup.m@huawei.com>");
 459 MODULE_AUTHOR("Shaokun Zhang <zhangshaokun@hisilicon.com>");

/* [<][>][^][v][top][bottom][index][help] */