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 44 static u32 hisi_cpa_pmu_get_counter_offset(int idx) 45 { 46 return (CPA_CNT0_LOWER + idx * CPA_REG_OFFSET); 47 } 48 49 static 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 55 static 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 61 static 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 84 static 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 93 static 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 102 static 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 111 static 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 120 static 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 131 static 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 142 static 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 153 static 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 164 static u32 hisi_cpa_pmu_get_int_status(struct hisi_pmu *cpa_pmu) 165 { 166 return readl(cpa_pmu->base + CPA_INT_STATUS); 167 } 168 169 static 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 174 static const struct acpi_device_id hisi_cpa_pmu_acpi_match[] = { 175 { "HISI0281", }, 176 {} 177 }; 178 MODULE_DEVICE_TABLE(acpi, hisi_cpa_pmu_acpi_match); 179 180 static 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 206 static struct attribute *hisi_cpa_pmu_format_attr[] = { 207 HISI_PMU_FORMAT_ATTR(event, "config:0-15"), 208 NULL 209 }; 210 211 static const struct attribute_group hisi_cpa_pmu_format_group = { 212 .name = "format", 213 .attrs = hisi_cpa_pmu_format_attr, 214 }; 215 216 static 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 225 static const struct attribute_group hisi_cpa_pmu_events_group = { 226 .name = "events", 227 .attrs = hisi_cpa_pmu_events_attr, 228 }; 229 230 static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL); 231 232 static struct attribute *hisi_cpa_pmu_cpumask_attrs[] = { 233 &dev_attr_cpumask.attr, 234 NULL 235 }; 236 237 static const struct attribute_group hisi_cpa_pmu_cpumask_attr_group = { 238 .attrs = hisi_cpa_pmu_cpumask_attrs, 239 }; 240 241 static struct device_attribute hisi_cpa_pmu_identifier_attr = 242 __ATTR(identifier, 0444, hisi_uncore_pmu_identifier_attr_show, NULL); 243 244 static struct attribute *hisi_cpa_pmu_identifier_attrs[] = { 245 &hisi_cpa_pmu_identifier_attr.attr, 246 NULL 247 }; 248 249 static const struct attribute_group hisi_cpa_pmu_identifier_group = { 250 .attrs = hisi_cpa_pmu_identifier_attrs, 251 }; 252 253 static 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 261 static 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 276 static 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 300 static 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 hisi_pmu_init(cpa_pmu, name, THIS_MODULE); 320 321 /* Power Management should be disabled before using CPA PMU. */ 322 hisi_cpa_pmu_disable_pm(cpa_pmu); 323 ret = cpuhp_state_add_instance(CPUHP_AP_PERF_ARM_HISI_CPA_ONLINE, 324 &cpa_pmu->node); 325 if (ret) { 326 dev_err(&pdev->dev, "Error %d registering hotplug\n", ret); 327 hisi_cpa_pmu_enable_pm(cpa_pmu); 328 return ret; 329 } 330 331 ret = perf_pmu_register(&cpa_pmu->pmu, name, -1); 332 if (ret) { 333 dev_err(cpa_pmu->dev, "PMU register failed\n"); 334 cpuhp_state_remove_instance_nocalls( 335 CPUHP_AP_PERF_ARM_HISI_CPA_ONLINE, &cpa_pmu->node); 336 hisi_cpa_pmu_enable_pm(cpa_pmu); 337 return ret; 338 } 339 340 platform_set_drvdata(pdev, cpa_pmu); 341 return ret; 342 } 343 344 static int hisi_cpa_pmu_remove(struct platform_device *pdev) 345 { 346 struct hisi_pmu *cpa_pmu = platform_get_drvdata(pdev); 347 348 perf_pmu_unregister(&cpa_pmu->pmu); 349 cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_HISI_CPA_ONLINE, 350 &cpa_pmu->node); 351 hisi_cpa_pmu_enable_pm(cpa_pmu); 352 return 0; 353 } 354 355 static struct platform_driver hisi_cpa_pmu_driver = { 356 .driver = { 357 .name = "hisi_cpa_pmu", 358 .acpi_match_table = ACPI_PTR(hisi_cpa_pmu_acpi_match), 359 .suppress_bind_attrs = true, 360 }, 361 .probe = hisi_cpa_pmu_probe, 362 .remove = hisi_cpa_pmu_remove, 363 }; 364 365 static int __init hisi_cpa_pmu_module_init(void) 366 { 367 int ret; 368 369 ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_HISI_CPA_ONLINE, 370 "AP_PERF_ARM_HISI_CPA_ONLINE", 371 hisi_uncore_pmu_online_cpu, 372 hisi_uncore_pmu_offline_cpu); 373 if (ret) { 374 pr_err("setup hotplug failed: %d\n", ret); 375 return ret; 376 } 377 378 ret = platform_driver_register(&hisi_cpa_pmu_driver); 379 if (ret) 380 cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_CPA_ONLINE); 381 382 return ret; 383 } 384 module_init(hisi_cpa_pmu_module_init); 385 386 static void __exit hisi_cpa_pmu_module_exit(void) 387 { 388 platform_driver_unregister(&hisi_cpa_pmu_driver); 389 cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_CPA_ONLINE); 390 } 391 module_exit(hisi_cpa_pmu_module_exit); 392 393 MODULE_DESCRIPTION("HiSilicon SoC CPA PMU driver"); 394 MODULE_LICENSE("GPL v2"); 395 MODULE_AUTHOR("Qi Liu <liuqi115@huawei.com>"); 396