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 dev_err(&pdev->dev, "L3C PMU get irq fail; irq:%d\n", irq); 211 return irq; 212 } 213 214 ret = devm_request_irq(&pdev->dev, irq, hisi_l3c_pmu_isr, 215 IRQF_NOBALANCING | IRQF_NO_THREAD, 216 dev_name(&pdev->dev), l3c_pmu); 217 if (ret < 0) { 218 dev_err(&pdev->dev, 219 "Fail to request IRQ:%d ret:%d\n", irq, ret); 220 return ret; 221 } 222 223 l3c_pmu->irq = irq; 224 225 return 0; 226 } 227 228 static const struct acpi_device_id hisi_l3c_pmu_acpi_match[] = { 229 { "HISI0213", }, 230 {}, 231 }; 232 MODULE_DEVICE_TABLE(acpi, hisi_l3c_pmu_acpi_match); 233 234 static int hisi_l3c_pmu_init_data(struct platform_device *pdev, 235 struct hisi_pmu *l3c_pmu) 236 { 237 unsigned long long id; 238 struct resource *res; 239 acpi_status status; 240 241 status = acpi_evaluate_integer(ACPI_HANDLE(&pdev->dev), 242 "_UID", NULL, &id); 243 if (ACPI_FAILURE(status)) 244 return -EINVAL; 245 246 l3c_pmu->index_id = id; 247 248 /* 249 * Use the SCCL_ID and CCL_ID to identify the L3C PMU, while 250 * SCCL_ID is in MPIDR[aff2] and CCL_ID is in MPIDR[aff1]. 251 */ 252 if (device_property_read_u32(&pdev->dev, "hisilicon,scl-id", 253 &l3c_pmu->sccl_id)) { 254 dev_err(&pdev->dev, "Can not read l3c sccl-id!\n"); 255 return -EINVAL; 256 } 257 258 if (device_property_read_u32(&pdev->dev, "hisilicon,ccl-id", 259 &l3c_pmu->ccl_id)) { 260 dev_err(&pdev->dev, "Can not read l3c ccl-id!\n"); 261 return -EINVAL; 262 } 263 264 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 265 l3c_pmu->base = devm_ioremap_resource(&pdev->dev, res); 266 if (IS_ERR(l3c_pmu->base)) { 267 dev_err(&pdev->dev, "ioremap failed for l3c_pmu resource\n"); 268 return PTR_ERR(l3c_pmu->base); 269 } 270 271 return 0; 272 } 273 274 static struct attribute *hisi_l3c_pmu_format_attr[] = { 275 HISI_PMU_FORMAT_ATTR(event, "config:0-7"), 276 NULL, 277 }; 278 279 static const struct attribute_group hisi_l3c_pmu_format_group = { 280 .name = "format", 281 .attrs = hisi_l3c_pmu_format_attr, 282 }; 283 284 static struct attribute *hisi_l3c_pmu_events_attr[] = { 285 HISI_PMU_EVENT_ATTR(rd_cpipe, 0x00), 286 HISI_PMU_EVENT_ATTR(wr_cpipe, 0x01), 287 HISI_PMU_EVENT_ATTR(rd_hit_cpipe, 0x02), 288 HISI_PMU_EVENT_ATTR(wr_hit_cpipe, 0x03), 289 HISI_PMU_EVENT_ATTR(victim_num, 0x04), 290 HISI_PMU_EVENT_ATTR(rd_spipe, 0x20), 291 HISI_PMU_EVENT_ATTR(wr_spipe, 0x21), 292 HISI_PMU_EVENT_ATTR(rd_hit_spipe, 0x22), 293 HISI_PMU_EVENT_ATTR(wr_hit_spipe, 0x23), 294 HISI_PMU_EVENT_ATTR(back_invalid, 0x29), 295 HISI_PMU_EVENT_ATTR(retry_cpu, 0x40), 296 HISI_PMU_EVENT_ATTR(retry_ring, 0x41), 297 HISI_PMU_EVENT_ATTR(prefetch_drop, 0x42), 298 NULL, 299 }; 300 301 static const struct attribute_group hisi_l3c_pmu_events_group = { 302 .name = "events", 303 .attrs = hisi_l3c_pmu_events_attr, 304 }; 305 306 static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL); 307 308 static struct attribute *hisi_l3c_pmu_cpumask_attrs[] = { 309 &dev_attr_cpumask.attr, 310 NULL, 311 }; 312 313 static const struct attribute_group hisi_l3c_pmu_cpumask_attr_group = { 314 .attrs = hisi_l3c_pmu_cpumask_attrs, 315 }; 316 317 static const struct attribute_group *hisi_l3c_pmu_attr_groups[] = { 318 &hisi_l3c_pmu_format_group, 319 &hisi_l3c_pmu_events_group, 320 &hisi_l3c_pmu_cpumask_attr_group, 321 NULL, 322 }; 323 324 static const struct hisi_uncore_ops hisi_uncore_l3c_ops = { 325 .write_evtype = hisi_l3c_pmu_write_evtype, 326 .get_event_idx = hisi_uncore_pmu_get_event_idx, 327 .start_counters = hisi_l3c_pmu_start_counters, 328 .stop_counters = hisi_l3c_pmu_stop_counters, 329 .enable_counter = hisi_l3c_pmu_enable_counter, 330 .disable_counter = hisi_l3c_pmu_disable_counter, 331 .enable_counter_int = hisi_l3c_pmu_enable_counter_int, 332 .disable_counter_int = hisi_l3c_pmu_disable_counter_int, 333 .write_counter = hisi_l3c_pmu_write_counter, 334 .read_counter = hisi_l3c_pmu_read_counter, 335 }; 336 337 static int hisi_l3c_pmu_dev_probe(struct platform_device *pdev, 338 struct hisi_pmu *l3c_pmu) 339 { 340 int ret; 341 342 ret = hisi_l3c_pmu_init_data(pdev, l3c_pmu); 343 if (ret) 344 return ret; 345 346 ret = hisi_l3c_pmu_init_irq(l3c_pmu, pdev); 347 if (ret) 348 return ret; 349 350 l3c_pmu->num_counters = L3C_NR_COUNTERS; 351 l3c_pmu->counter_bits = 48; 352 l3c_pmu->ops = &hisi_uncore_l3c_ops; 353 l3c_pmu->dev = &pdev->dev; 354 l3c_pmu->on_cpu = -1; 355 l3c_pmu->check_event = 0x59; 356 357 return 0; 358 } 359 360 static int hisi_l3c_pmu_probe(struct platform_device *pdev) 361 { 362 struct hisi_pmu *l3c_pmu; 363 char *name; 364 int ret; 365 366 l3c_pmu = devm_kzalloc(&pdev->dev, sizeof(*l3c_pmu), GFP_KERNEL); 367 if (!l3c_pmu) 368 return -ENOMEM; 369 370 platform_set_drvdata(pdev, l3c_pmu); 371 372 ret = hisi_l3c_pmu_dev_probe(pdev, l3c_pmu); 373 if (ret) 374 return ret; 375 376 ret = cpuhp_state_add_instance(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE, 377 &l3c_pmu->node); 378 if (ret) { 379 dev_err(&pdev->dev, "Error %d registering hotplug\n", ret); 380 return ret; 381 } 382 383 name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sccl%u_l3c%u", 384 l3c_pmu->sccl_id, l3c_pmu->index_id); 385 l3c_pmu->pmu = (struct pmu) { 386 .name = name, 387 .task_ctx_nr = perf_invalid_context, 388 .event_init = hisi_uncore_pmu_event_init, 389 .pmu_enable = hisi_uncore_pmu_enable, 390 .pmu_disable = hisi_uncore_pmu_disable, 391 .add = hisi_uncore_pmu_add, 392 .del = hisi_uncore_pmu_del, 393 .start = hisi_uncore_pmu_start, 394 .stop = hisi_uncore_pmu_stop, 395 .read = hisi_uncore_pmu_read, 396 .attr_groups = hisi_l3c_pmu_attr_groups, 397 .capabilities = PERF_PMU_CAP_NO_EXCLUDE, 398 }; 399 400 ret = perf_pmu_register(&l3c_pmu->pmu, name, -1); 401 if (ret) { 402 dev_err(l3c_pmu->dev, "L3C PMU register failed!\n"); 403 cpuhp_state_remove_instance(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE, 404 &l3c_pmu->node); 405 } 406 407 return ret; 408 } 409 410 static int hisi_l3c_pmu_remove(struct platform_device *pdev) 411 { 412 struct hisi_pmu *l3c_pmu = platform_get_drvdata(pdev); 413 414 perf_pmu_unregister(&l3c_pmu->pmu); 415 cpuhp_state_remove_instance(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE, 416 &l3c_pmu->node); 417 418 return 0; 419 } 420 421 static struct platform_driver hisi_l3c_pmu_driver = { 422 .driver = { 423 .name = "hisi_l3c_pmu", 424 .acpi_match_table = ACPI_PTR(hisi_l3c_pmu_acpi_match), 425 }, 426 .probe = hisi_l3c_pmu_probe, 427 .remove = hisi_l3c_pmu_remove, 428 }; 429 430 static int __init hisi_l3c_pmu_module_init(void) 431 { 432 int ret; 433 434 ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE, 435 "AP_PERF_ARM_HISI_L3_ONLINE", 436 hisi_uncore_pmu_online_cpu, 437 hisi_uncore_pmu_offline_cpu); 438 if (ret) { 439 pr_err("L3C PMU: Error setup hotplug, ret = %d\n", ret); 440 return ret; 441 } 442 443 ret = platform_driver_register(&hisi_l3c_pmu_driver); 444 if (ret) 445 cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE); 446 447 return ret; 448 } 449 module_init(hisi_l3c_pmu_module_init); 450 451 static void __exit hisi_l3c_pmu_module_exit(void) 452 { 453 platform_driver_unregister(&hisi_l3c_pmu_driver); 454 cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE); 455 } 456 module_exit(hisi_l3c_pmu_module_exit); 457 458 MODULE_DESCRIPTION("HiSilicon SoC L3C uncore PMU driver"); 459 MODULE_LICENSE("GPL v2"); 460 MODULE_AUTHOR("Anurup M <anurup.m@huawei.com>"); 461 MODULE_AUTHOR("Shaokun Zhang <zhangshaokun@hisilicon.com>"); 462