hisi_uncore_cpa_pmu.c (10881B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * HiSilicon SoC CPA(Coherency Protocol Agent) hardware event counters support 4 * 5 * Copyright (C) 2022 HiSilicon Limited 6 * Author: Qi Liu <liuqi115@huawei.com> 7 * 8 * This code is based on the uncore PMUs like arm-cci and arm-ccn. 9 */ 10 11#define pr_fmt(fmt) "cpa pmu: " fmt 12#include <linux/acpi.h> 13#include <linux/bug.h> 14#include <linux/cpuhotplug.h> 15#include <linux/interrupt.h> 16#include <linux/irq.h> 17#include <linux/list.h> 18#include <linux/smp.h> 19 20#include "hisi_uncore_pmu.h" 21 22/* CPA register definition */ 23#define CPA_PERF_CTRL 0x1c00 24#define CPA_EVENT_CTRL 0x1c04 25#define CPA_INT_MASK 0x1c70 26#define CPA_INT_STATUS 0x1c78 27#define CPA_INT_CLEAR 0x1c7c 28#define CPA_EVENT_TYPE0 0x1c80 29#define CPA_VERSION 0x1cf0 30#define CPA_CNT0_LOWER 0x1d00 31#define CPA_CFG_REG 0x0534 32 33/* CPA operation command */ 34#define CPA_PERF_CTRL_EN BIT_ULL(0) 35#define CPA_EVTYPE_MASK 0xffUL 36#define CPA_PM_CTRL BIT_ULL(9) 37 38/* CPA has 8-counters */ 39#define CPA_NR_COUNTERS 0x8 40#define CPA_COUNTER_BITS 64 41#define CPA_NR_EVENTS 0xff 42#define CPA_REG_OFFSET 0x8 43 44static u32 hisi_cpa_pmu_get_counter_offset(int idx) 45{ 46 return (CPA_CNT0_LOWER + idx * CPA_REG_OFFSET); 47} 48 49static u64 hisi_cpa_pmu_read_counter(struct hisi_pmu *cpa_pmu, 50 struct hw_perf_event *hwc) 51{ 52 return readq(cpa_pmu->base + hisi_cpa_pmu_get_counter_offset(hwc->idx)); 53} 54 55static void hisi_cpa_pmu_write_counter(struct hisi_pmu *cpa_pmu, 56 struct hw_perf_event *hwc, u64 val) 57{ 58 writeq(val, cpa_pmu->base + hisi_cpa_pmu_get_counter_offset(hwc->idx)); 59} 60 61static void hisi_cpa_pmu_write_evtype(struct hisi_pmu *cpa_pmu, int idx, 62 u32 type) 63{ 64 u32 reg, reg_idx, shift, val; 65 66 /* 67 * Select the appropriate event select register(CPA_EVENT_TYPE0/1). 68 * There are 2 event select registers for the 8 hardware counters. 69 * Event code is 8-bits and for the former 4 hardware counters, 70 * CPA_EVENT_TYPE0 is chosen. For the latter 4 hardware counters, 71 * CPA_EVENT_TYPE1 is chosen. 72 */ 73 reg = CPA_EVENT_TYPE0 + (idx / 4) * 4; 74 reg_idx = idx % 4; 75 shift = CPA_REG_OFFSET * reg_idx; 76 77 /* Write event code to CPA_EVENT_TYPEx Register */ 78 val = readl(cpa_pmu->base + reg); 79 val &= ~(CPA_EVTYPE_MASK << shift); 80 val |= type << shift; 81 writel(val, cpa_pmu->base + reg); 82} 83 84static void hisi_cpa_pmu_start_counters(struct hisi_pmu *cpa_pmu) 85{ 86 u32 val; 87 88 val = readl(cpa_pmu->base + CPA_PERF_CTRL); 89 val |= CPA_PERF_CTRL_EN; 90 writel(val, cpa_pmu->base + CPA_PERF_CTRL); 91} 92 93static void hisi_cpa_pmu_stop_counters(struct hisi_pmu *cpa_pmu) 94{ 95 u32 val; 96 97 val = readl(cpa_pmu->base + CPA_PERF_CTRL); 98 val &= ~(CPA_PERF_CTRL_EN); 99 writel(val, cpa_pmu->base + CPA_PERF_CTRL); 100} 101 102static void hisi_cpa_pmu_disable_pm(struct hisi_pmu *cpa_pmu) 103{ 104 u32 val; 105 106 val = readl(cpa_pmu->base + CPA_CFG_REG); 107 val |= CPA_PM_CTRL; 108 writel(val, cpa_pmu->base + CPA_CFG_REG); 109} 110 111static void hisi_cpa_pmu_enable_pm(struct hisi_pmu *cpa_pmu) 112{ 113 u32 val; 114 115 val = readl(cpa_pmu->base + CPA_CFG_REG); 116 val &= ~(CPA_PM_CTRL); 117 writel(val, cpa_pmu->base + CPA_CFG_REG); 118} 119 120static void hisi_cpa_pmu_enable_counter(struct hisi_pmu *cpa_pmu, 121 struct hw_perf_event *hwc) 122{ 123 u32 val; 124 125 /* Enable counter index in CPA_EVENT_CTRL register */ 126 val = readl(cpa_pmu->base + CPA_EVENT_CTRL); 127 val |= 1 << hwc->idx; 128 writel(val, cpa_pmu->base + CPA_EVENT_CTRL); 129} 130 131static void hisi_cpa_pmu_disable_counter(struct hisi_pmu *cpa_pmu, 132 struct hw_perf_event *hwc) 133{ 134 u32 val; 135 136 /* Clear counter index in CPA_EVENT_CTRL register */ 137 val = readl(cpa_pmu->base + CPA_EVENT_CTRL); 138 val &= ~(1UL << hwc->idx); 139 writel(val, cpa_pmu->base + CPA_EVENT_CTRL); 140} 141 142static void hisi_cpa_pmu_enable_counter_int(struct hisi_pmu *cpa_pmu, 143 struct hw_perf_event *hwc) 144{ 145 u32 val; 146 147 /* Write 0 to enable interrupt */ 148 val = readl(cpa_pmu->base + CPA_INT_MASK); 149 val &= ~(1UL << hwc->idx); 150 writel(val, cpa_pmu->base + CPA_INT_MASK); 151} 152 153static void hisi_cpa_pmu_disable_counter_int(struct hisi_pmu *cpa_pmu, 154 struct hw_perf_event *hwc) 155{ 156 u32 val; 157 158 /* Write 1 to mask interrupt */ 159 val = readl(cpa_pmu->base + CPA_INT_MASK); 160 val |= 1 << hwc->idx; 161 writel(val, cpa_pmu->base + CPA_INT_MASK); 162} 163 164static u32 hisi_cpa_pmu_get_int_status(struct hisi_pmu *cpa_pmu) 165{ 166 return readl(cpa_pmu->base + CPA_INT_STATUS); 167} 168 169static void hisi_cpa_pmu_clear_int_status(struct hisi_pmu *cpa_pmu, int idx) 170{ 171 writel(1 << idx, cpa_pmu->base + CPA_INT_CLEAR); 172} 173 174static const struct acpi_device_id hisi_cpa_pmu_acpi_match[] = { 175 { "HISI0281", }, 176 {} 177}; 178MODULE_DEVICE_TABLE(acpi, hisi_cpa_pmu_acpi_match); 179 180static int hisi_cpa_pmu_init_data(struct platform_device *pdev, 181 struct hisi_pmu *cpa_pmu) 182{ 183 if (device_property_read_u32(&pdev->dev, "hisilicon,scl-id", 184 &cpa_pmu->sicl_id)) { 185 dev_err(&pdev->dev, "Can not read sicl-id\n"); 186 return -EINVAL; 187 } 188 189 if (device_property_read_u32(&pdev->dev, "hisilicon,idx-id", 190 &cpa_pmu->index_id)) { 191 dev_err(&pdev->dev, "Cannot read idx-id\n"); 192 return -EINVAL; 193 } 194 195 cpa_pmu->ccl_id = -1; 196 cpa_pmu->sccl_id = -1; 197 cpa_pmu->base = devm_platform_ioremap_resource(pdev, 0); 198 if (IS_ERR(cpa_pmu->base)) 199 return PTR_ERR(cpa_pmu->base); 200 201 cpa_pmu->identifier = readl(cpa_pmu->base + CPA_VERSION); 202 203 return 0; 204} 205 206static struct attribute *hisi_cpa_pmu_format_attr[] = { 207 HISI_PMU_FORMAT_ATTR(event, "config:0-15"), 208 NULL 209}; 210 211static const struct attribute_group hisi_cpa_pmu_format_group = { 212 .name = "format", 213 .attrs = hisi_cpa_pmu_format_attr, 214}; 215 216static struct attribute *hisi_cpa_pmu_events_attr[] = { 217 HISI_PMU_EVENT_ATTR(cpa_cycles, 0x00), 218 HISI_PMU_EVENT_ATTR(cpa_p1_wr_dat, 0x61), 219 HISI_PMU_EVENT_ATTR(cpa_p1_rd_dat, 0x62), 220 HISI_PMU_EVENT_ATTR(cpa_p0_wr_dat, 0xE1), 221 HISI_PMU_EVENT_ATTR(cpa_p0_rd_dat, 0xE2), 222 NULL 223}; 224 225static const struct attribute_group hisi_cpa_pmu_events_group = { 226 .name = "events", 227 .attrs = hisi_cpa_pmu_events_attr, 228}; 229 230static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL); 231 232static struct attribute *hisi_cpa_pmu_cpumask_attrs[] = { 233 &dev_attr_cpumask.attr, 234 NULL 235}; 236 237static const struct attribute_group hisi_cpa_pmu_cpumask_attr_group = { 238 .attrs = hisi_cpa_pmu_cpumask_attrs, 239}; 240 241static struct device_attribute hisi_cpa_pmu_identifier_attr = 242 __ATTR(identifier, 0444, hisi_uncore_pmu_identifier_attr_show, NULL); 243 244static struct attribute *hisi_cpa_pmu_identifier_attrs[] = { 245 &hisi_cpa_pmu_identifier_attr.attr, 246 NULL 247}; 248 249static const struct attribute_group hisi_cpa_pmu_identifier_group = { 250 .attrs = hisi_cpa_pmu_identifier_attrs, 251}; 252 253static const struct attribute_group *hisi_cpa_pmu_attr_groups[] = { 254 &hisi_cpa_pmu_format_group, 255 &hisi_cpa_pmu_events_group, 256 &hisi_cpa_pmu_cpumask_attr_group, 257 &hisi_cpa_pmu_identifier_group, 258 NULL 259}; 260 261static const struct hisi_uncore_ops hisi_uncore_cpa_pmu_ops = { 262 .write_evtype = hisi_cpa_pmu_write_evtype, 263 .get_event_idx = hisi_uncore_pmu_get_event_idx, 264 .start_counters = hisi_cpa_pmu_start_counters, 265 .stop_counters = hisi_cpa_pmu_stop_counters, 266 .enable_counter = hisi_cpa_pmu_enable_counter, 267 .disable_counter = hisi_cpa_pmu_disable_counter, 268 .enable_counter_int = hisi_cpa_pmu_enable_counter_int, 269 .disable_counter_int = hisi_cpa_pmu_disable_counter_int, 270 .write_counter = hisi_cpa_pmu_write_counter, 271 .read_counter = hisi_cpa_pmu_read_counter, 272 .get_int_status = hisi_cpa_pmu_get_int_status, 273 .clear_int_status = hisi_cpa_pmu_clear_int_status, 274}; 275 276static int hisi_cpa_pmu_dev_probe(struct platform_device *pdev, 277 struct hisi_pmu *cpa_pmu) 278{ 279 int ret; 280 281 ret = hisi_cpa_pmu_init_data(pdev, cpa_pmu); 282 if (ret) 283 return ret; 284 285 ret = hisi_uncore_pmu_init_irq(cpa_pmu, pdev); 286 if (ret) 287 return ret; 288 289 cpa_pmu->counter_bits = CPA_COUNTER_BITS; 290 cpa_pmu->check_event = CPA_NR_EVENTS; 291 cpa_pmu->pmu_events.attr_groups = hisi_cpa_pmu_attr_groups; 292 cpa_pmu->ops = &hisi_uncore_cpa_pmu_ops; 293 cpa_pmu->num_counters = CPA_NR_COUNTERS; 294 cpa_pmu->dev = &pdev->dev; 295 cpa_pmu->on_cpu = -1; 296 297 return 0; 298} 299 300static int hisi_cpa_pmu_probe(struct platform_device *pdev) 301{ 302 struct hisi_pmu *cpa_pmu; 303 char *name; 304 int ret; 305 306 cpa_pmu = devm_kzalloc(&pdev->dev, sizeof(*cpa_pmu), GFP_KERNEL); 307 if (!cpa_pmu) 308 return -ENOMEM; 309 310 ret = hisi_cpa_pmu_dev_probe(pdev, cpa_pmu); 311 if (ret) 312 return ret; 313 314 name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sicl%d_cpa%u", 315 cpa_pmu->sicl_id, cpa_pmu->index_id); 316 if (!name) 317 return -ENOMEM; 318 319 cpa_pmu->pmu = (struct pmu) { 320 .name = name, 321 .module = THIS_MODULE, 322 .task_ctx_nr = perf_invalid_context, 323 .event_init = hisi_uncore_pmu_event_init, 324 .pmu_enable = hisi_uncore_pmu_enable, 325 .pmu_disable = hisi_uncore_pmu_disable, 326 .add = hisi_uncore_pmu_add, 327 .del = hisi_uncore_pmu_del, 328 .start = hisi_uncore_pmu_start, 329 .stop = hisi_uncore_pmu_stop, 330 .read = hisi_uncore_pmu_read, 331 .attr_groups = cpa_pmu->pmu_events.attr_groups, 332 .capabilities = PERF_PMU_CAP_NO_EXCLUDE, 333 }; 334 335 /* Power Management should be disabled before using CPA PMU. */ 336 hisi_cpa_pmu_disable_pm(cpa_pmu); 337 ret = cpuhp_state_add_instance(CPUHP_AP_PERF_ARM_HISI_CPA_ONLINE, 338 &cpa_pmu->node); 339 if (ret) { 340 dev_err(&pdev->dev, "Error %d registering hotplug\n", ret); 341 hisi_cpa_pmu_enable_pm(cpa_pmu); 342 return ret; 343 } 344 345 ret = perf_pmu_register(&cpa_pmu->pmu, name, -1); 346 if (ret) { 347 dev_err(cpa_pmu->dev, "PMU register failed\n"); 348 cpuhp_state_remove_instance_nocalls( 349 CPUHP_AP_PERF_ARM_HISI_CPA_ONLINE, &cpa_pmu->node); 350 hisi_cpa_pmu_enable_pm(cpa_pmu); 351 return ret; 352 } 353 354 platform_set_drvdata(pdev, cpa_pmu); 355 return ret; 356} 357 358static int hisi_cpa_pmu_remove(struct platform_device *pdev) 359{ 360 struct hisi_pmu *cpa_pmu = platform_get_drvdata(pdev); 361 362 perf_pmu_unregister(&cpa_pmu->pmu); 363 cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_HISI_CPA_ONLINE, 364 &cpa_pmu->node); 365 hisi_cpa_pmu_enable_pm(cpa_pmu); 366 return 0; 367} 368 369static struct platform_driver hisi_cpa_pmu_driver = { 370 .driver = { 371 .name = "hisi_cpa_pmu", 372 .acpi_match_table = ACPI_PTR(hisi_cpa_pmu_acpi_match), 373 .suppress_bind_attrs = true, 374 }, 375 .probe = hisi_cpa_pmu_probe, 376 .remove = hisi_cpa_pmu_remove, 377}; 378 379static int __init hisi_cpa_pmu_module_init(void) 380{ 381 int ret; 382 383 ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_HISI_CPA_ONLINE, 384 "AP_PERF_ARM_HISI_CPA_ONLINE", 385 hisi_uncore_pmu_online_cpu, 386 hisi_uncore_pmu_offline_cpu); 387 if (ret) { 388 pr_err("setup hotplug failed: %d\n", ret); 389 return ret; 390 } 391 392 ret = platform_driver_register(&hisi_cpa_pmu_driver); 393 if (ret) 394 cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_CPA_ONLINE); 395 396 return ret; 397} 398module_init(hisi_cpa_pmu_module_init); 399 400static void __exit hisi_cpa_pmu_module_exit(void) 401{ 402 platform_driver_unregister(&hisi_cpa_pmu_driver); 403 cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_CPA_ONLINE); 404} 405module_exit(hisi_cpa_pmu_module_exit); 406 407MODULE_DESCRIPTION("HiSilicon SoC CPA PMU driver"); 408MODULE_LICENSE("GPL v2"); 409MODULE_AUTHOR("Qi Liu <liuqi115@huawei.com>");