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