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_VERSION 0x1cf0 29 #define L3C_EVENT_TYPE0 0x1d00 30 /* 31 * Each counter is 48-bits and [48:63] are reserved 32 * which are Read-As-Zero and Writes-Ignored. 33 */ 34 #define L3C_CNTR0_LOWER 0x1e00 35 36 /* L3C has 8-counters */ 37 #define L3C_NR_COUNTERS 0x8 38 39 #define L3C_PERF_CTRL_EN 0x10000 40 #define L3C_EVTYPE_NONE 0xff 41 42 /* 43 * Select the counter register offset using the counter index 44 */ 45 static u32 hisi_l3c_pmu_get_counter_offset(int cntr_idx) 46 { 47 return (L3C_CNTR0_LOWER + (cntr_idx * 8)); 48 } 49 50 static u64 hisi_l3c_pmu_read_counter(struct hisi_pmu *l3c_pmu, 51 struct hw_perf_event *hwc) 52 { 53 /* Read 64-bits and the upper 16 bits are RAZ */ 54 return readq(l3c_pmu->base + hisi_l3c_pmu_get_counter_offset(hwc->idx)); 55 } 56 57 static void hisi_l3c_pmu_write_counter(struct hisi_pmu *l3c_pmu, 58 struct hw_perf_event *hwc, u64 val) 59 { 60 /* Write 64-bits and the upper 16 bits are WI */ 61 writeq(val, l3c_pmu->base + hisi_l3c_pmu_get_counter_offset(hwc->idx)); 62 } 63 64 static void hisi_l3c_pmu_write_evtype(struct hisi_pmu *l3c_pmu, int idx, 65 u32 type) 66 { 67 u32 reg, reg_idx, shift, val; 68 69 /* 70 * Select the appropriate event select register(L3C_EVENT_TYPE0/1). 71 * There are 2 event select registers for the 8 hardware counters. 72 * Event code is 8-bits and for the former 4 hardware counters, 73 * L3C_EVENT_TYPE0 is chosen. For the latter 4 hardware counters, 74 * L3C_EVENT_TYPE1 is chosen. 75 */ 76 reg = L3C_EVENT_TYPE0 + (idx / 4) * 4; 77 reg_idx = idx % 4; 78 shift = 8 * reg_idx; 79 80 /* Write event code to L3C_EVENT_TYPEx Register */ 81 val = readl(l3c_pmu->base + reg); 82 val &= ~(L3C_EVTYPE_NONE << shift); 83 val |= (type << shift); 84 writel(val, l3c_pmu->base + reg); 85 } 86 87 static void hisi_l3c_pmu_start_counters(struct hisi_pmu *l3c_pmu) 88 { 89 u32 val; 90 91 /* 92 * Set perf_enable bit in L3C_PERF_CTRL register to start counting 93 * for all enabled counters. 94 */ 95 val = readl(l3c_pmu->base + L3C_PERF_CTRL); 96 val |= L3C_PERF_CTRL_EN; 97 writel(val, l3c_pmu->base + L3C_PERF_CTRL); 98 } 99 100 static void hisi_l3c_pmu_stop_counters(struct hisi_pmu *l3c_pmu) 101 { 102 u32 val; 103 104 /* 105 * Clear perf_enable bit in L3C_PERF_CTRL register to stop 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_enable_counter(struct hisi_pmu *l3c_pmu, 114 struct hw_perf_event *hwc) 115 { 116 u32 val; 117 118 /* Enable counter index in L3C_EVENT_CTRL register */ 119 val = readl(l3c_pmu->base + L3C_EVENT_CTRL); 120 val |= (1 << hwc->idx); 121 writel(val, l3c_pmu->base + L3C_EVENT_CTRL); 122 } 123 124 static void hisi_l3c_pmu_disable_counter(struct hisi_pmu *l3c_pmu, 125 struct hw_perf_event *hwc) 126 { 127 u32 val; 128 129 /* Clear counter index in L3C_EVENT_CTRL register */ 130 val = readl(l3c_pmu->base + L3C_EVENT_CTRL); 131 val &= ~(1 << hwc->idx); 132 writel(val, l3c_pmu->base + L3C_EVENT_CTRL); 133 } 134 135 static void hisi_l3c_pmu_enable_counter_int(struct hisi_pmu *l3c_pmu, 136 struct hw_perf_event *hwc) 137 { 138 u32 val; 139 140 val = readl(l3c_pmu->base + L3C_INT_MASK); 141 /* Write 0 to enable interrupt */ 142 val &= ~(1 << hwc->idx); 143 writel(val, l3c_pmu->base + L3C_INT_MASK); 144 } 145 146 static void hisi_l3c_pmu_disable_counter_int(struct hisi_pmu *l3c_pmu, 147 struct hw_perf_event *hwc) 148 { 149 u32 val; 150 151 val = readl(l3c_pmu->base + L3C_INT_MASK); 152 /* Write 1 to mask interrupt */ 153 val |= (1 << hwc->idx); 154 writel(val, l3c_pmu->base + L3C_INT_MASK); 155 } 156 157 static irqreturn_t hisi_l3c_pmu_isr(int irq, void *dev_id) 158 { 159 struct hisi_pmu *l3c_pmu = dev_id; 160 struct perf_event *event; 161 unsigned long overflown; 162 int idx; 163 164 /* Read L3C_INT_STATUS register */ 165 overflown = readl(l3c_pmu->base + L3C_INT_STATUS); 166 if (!overflown) 167 return IRQ_NONE; 168 169 /* 170 * Find the counter index which overflowed if the bit was set 171 * and handle it. 172 */ 173 for_each_set_bit(idx, &overflown, L3C_NR_COUNTERS) { 174 /* Write 1 to clear the IRQ status flag */ 175 writel((1 << idx), l3c_pmu->base + L3C_INT_CLEAR); 176 177 /* Get the corresponding event struct */ 178 event = l3c_pmu->pmu_events.hw_events[idx]; 179 if (!event) 180 continue; 181 182 hisi_uncore_pmu_event_update(event); 183 hisi_uncore_pmu_set_event_period(event); 184 } 185 186 return IRQ_HANDLED; 187 } 188 189 static int hisi_l3c_pmu_init_irq(struct hisi_pmu *l3c_pmu, 190 struct platform_device *pdev) 191 { 192 int irq, ret; 193 194 /* Read and init IRQ */ 195 irq = platform_get_irq(pdev, 0); 196 if (irq < 0) 197 return irq; 198 199 ret = devm_request_irq(&pdev->dev, irq, hisi_l3c_pmu_isr, 200 IRQF_NOBALANCING | IRQF_NO_THREAD, 201 dev_name(&pdev->dev), l3c_pmu); 202 if (ret < 0) { 203 dev_err(&pdev->dev, 204 "Fail to request IRQ:%d ret:%d\n", irq, ret); 205 return ret; 206 } 207 208 l3c_pmu->irq = irq; 209 210 return 0; 211 } 212 213 static const struct acpi_device_id hisi_l3c_pmu_acpi_match[] = { 214 { "HISI0213", }, 215 {}, 216 }; 217 MODULE_DEVICE_TABLE(acpi, hisi_l3c_pmu_acpi_match); 218 219 static int hisi_l3c_pmu_init_data(struct platform_device *pdev, 220 struct hisi_pmu *l3c_pmu) 221 { 222 unsigned long long id; 223 acpi_status status; 224 225 status = acpi_evaluate_integer(ACPI_HANDLE(&pdev->dev), 226 "_UID", NULL, &id); 227 if (ACPI_FAILURE(status)) 228 return -EINVAL; 229 230 l3c_pmu->index_id = id; 231 232 /* 233 * Use the SCCL_ID and CCL_ID to identify the L3C PMU, while 234 * SCCL_ID is in MPIDR[aff2] and CCL_ID is in MPIDR[aff1]. 235 */ 236 if (device_property_read_u32(&pdev->dev, "hisilicon,scl-id", 237 &l3c_pmu->sccl_id)) { 238 dev_err(&pdev->dev, "Can not read l3c sccl-id!\n"); 239 return -EINVAL; 240 } 241 242 if (device_property_read_u32(&pdev->dev, "hisilicon,ccl-id", 243 &l3c_pmu->ccl_id)) { 244 dev_err(&pdev->dev, "Can not read l3c ccl-id!\n"); 245 return -EINVAL; 246 } 247 248 l3c_pmu->base = devm_platform_ioremap_resource(pdev, 0); 249 if (IS_ERR(l3c_pmu->base)) { 250 dev_err(&pdev->dev, "ioremap failed for l3c_pmu resource\n"); 251 return PTR_ERR(l3c_pmu->base); 252 } 253 254 l3c_pmu->identifier = readl(l3c_pmu->base + L3C_VERSION); 255 256 return 0; 257 } 258 259 static struct attribute *hisi_l3c_pmu_format_attr[] = { 260 HISI_PMU_FORMAT_ATTR(event, "config:0-7"), 261 NULL, 262 }; 263 264 static const struct attribute_group hisi_l3c_pmu_format_group = { 265 .name = "format", 266 .attrs = hisi_l3c_pmu_format_attr, 267 }; 268 269 static struct attribute *hisi_l3c_pmu_events_attr[] = { 270 HISI_PMU_EVENT_ATTR(rd_cpipe, 0x00), 271 HISI_PMU_EVENT_ATTR(wr_cpipe, 0x01), 272 HISI_PMU_EVENT_ATTR(rd_hit_cpipe, 0x02), 273 HISI_PMU_EVENT_ATTR(wr_hit_cpipe, 0x03), 274 HISI_PMU_EVENT_ATTR(victim_num, 0x04), 275 HISI_PMU_EVENT_ATTR(rd_spipe, 0x20), 276 HISI_PMU_EVENT_ATTR(wr_spipe, 0x21), 277 HISI_PMU_EVENT_ATTR(rd_hit_spipe, 0x22), 278 HISI_PMU_EVENT_ATTR(wr_hit_spipe, 0x23), 279 HISI_PMU_EVENT_ATTR(back_invalid, 0x29), 280 HISI_PMU_EVENT_ATTR(retry_cpu, 0x40), 281 HISI_PMU_EVENT_ATTR(retry_ring, 0x41), 282 HISI_PMU_EVENT_ATTR(prefetch_drop, 0x42), 283 NULL, 284 }; 285 286 static const struct attribute_group hisi_l3c_pmu_events_group = { 287 .name = "events", 288 .attrs = hisi_l3c_pmu_events_attr, 289 }; 290 291 static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL); 292 293 static struct attribute *hisi_l3c_pmu_cpumask_attrs[] = { 294 &dev_attr_cpumask.attr, 295 NULL, 296 }; 297 298 static const struct attribute_group hisi_l3c_pmu_cpumask_attr_group = { 299 .attrs = hisi_l3c_pmu_cpumask_attrs, 300 }; 301 302 static struct device_attribute hisi_l3c_pmu_identifier_attr = 303 __ATTR(identifier, 0444, hisi_uncore_pmu_identifier_attr_show, NULL); 304 305 static struct attribute *hisi_l3c_pmu_identifier_attrs[] = { 306 &hisi_l3c_pmu_identifier_attr.attr, 307 NULL 308 }; 309 310 static const struct attribute_group hisi_l3c_pmu_identifier_group = { 311 .attrs = hisi_l3c_pmu_identifier_attrs, 312 }; 313 314 static const struct attribute_group *hisi_l3c_pmu_attr_groups[] = { 315 &hisi_l3c_pmu_format_group, 316 &hisi_l3c_pmu_events_group, 317 &hisi_l3c_pmu_cpumask_attr_group, 318 &hisi_l3c_pmu_identifier_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 .module = THIS_MODULE, 386 .task_ctx_nr = perf_invalid_context, 387 .event_init = hisi_uncore_pmu_event_init, 388 .pmu_enable = hisi_uncore_pmu_enable, 389 .pmu_disable = hisi_uncore_pmu_disable, 390 .add = hisi_uncore_pmu_add, 391 .del = hisi_uncore_pmu_del, 392 .start = hisi_uncore_pmu_start, 393 .stop = hisi_uncore_pmu_stop, 394 .read = hisi_uncore_pmu_read, 395 .attr_groups = hisi_l3c_pmu_attr_groups, 396 .capabilities = PERF_PMU_CAP_NO_EXCLUDE, 397 }; 398 399 ret = perf_pmu_register(&l3c_pmu->pmu, name, -1); 400 if (ret) { 401 dev_err(l3c_pmu->dev, "L3C PMU register failed!\n"); 402 cpuhp_state_remove_instance_nocalls( 403 CPUHP_AP_PERF_ARM_HISI_L3_ONLINE, &l3c_pmu->node); 404 irq_set_affinity_hint(l3c_pmu->irq, NULL); 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_nocalls(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE, 416 &l3c_pmu->node); 417 irq_set_affinity_hint(l3c_pmu->irq, NULL); 418 419 return 0; 420 } 421 422 static struct platform_driver hisi_l3c_pmu_driver = { 423 .driver = { 424 .name = "hisi_l3c_pmu", 425 .acpi_match_table = ACPI_PTR(hisi_l3c_pmu_acpi_match), 426 .suppress_bind_attrs = true, 427 }, 428 .probe = hisi_l3c_pmu_probe, 429 .remove = hisi_l3c_pmu_remove, 430 }; 431 432 static int __init hisi_l3c_pmu_module_init(void) 433 { 434 int ret; 435 436 ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE, 437 "AP_PERF_ARM_HISI_L3_ONLINE", 438 hisi_uncore_pmu_online_cpu, 439 hisi_uncore_pmu_offline_cpu); 440 if (ret) { 441 pr_err("L3C PMU: Error setup hotplug, ret = %d\n", ret); 442 return ret; 443 } 444 445 ret = platform_driver_register(&hisi_l3c_pmu_driver); 446 if (ret) 447 cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE); 448 449 return ret; 450 } 451 module_init(hisi_l3c_pmu_module_init); 452 453 static void __exit hisi_l3c_pmu_module_exit(void) 454 { 455 platform_driver_unregister(&hisi_l3c_pmu_driver); 456 cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE); 457 } 458 module_exit(hisi_l3c_pmu_module_exit); 459 460 MODULE_DESCRIPTION("HiSilicon SoC L3C uncore PMU driver"); 461 MODULE_LICENSE("GPL v2"); 462 MODULE_AUTHOR("Anurup M <anurup.m@huawei.com>"); 463 MODULE_AUTHOR("Shaokun Zhang <zhangshaokun@hisilicon.com>"); 464