0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #define pr_fmt(fmt) "cpa pmu: " fmt
0012 #include <linux/acpi.h>
0013 #include <linux/bug.h>
0014 #include <linux/cpuhotplug.h>
0015 #include <linux/interrupt.h>
0016 #include <linux/irq.h>
0017 #include <linux/list.h>
0018 #include <linux/smp.h>
0019
0020 #include "hisi_uncore_pmu.h"
0021
0022
0023 #define CPA_PERF_CTRL 0x1c00
0024 #define CPA_EVENT_CTRL 0x1c04
0025 #define CPA_INT_MASK 0x1c70
0026 #define CPA_INT_STATUS 0x1c78
0027 #define CPA_INT_CLEAR 0x1c7c
0028 #define CPA_EVENT_TYPE0 0x1c80
0029 #define CPA_VERSION 0x1cf0
0030 #define CPA_CNT0_LOWER 0x1d00
0031 #define CPA_CFG_REG 0x0534
0032
0033
0034 #define CPA_PERF_CTRL_EN BIT_ULL(0)
0035 #define CPA_EVTYPE_MASK 0xffUL
0036 #define CPA_PM_CTRL BIT_ULL(9)
0037
0038
0039 #define CPA_NR_COUNTERS 0x8
0040 #define CPA_COUNTER_BITS 64
0041 #define CPA_NR_EVENTS 0xff
0042 #define CPA_REG_OFFSET 0x8
0043
0044 static u32 hisi_cpa_pmu_get_counter_offset(int idx)
0045 {
0046 return (CPA_CNT0_LOWER + idx * CPA_REG_OFFSET);
0047 }
0048
0049 static u64 hisi_cpa_pmu_read_counter(struct hisi_pmu *cpa_pmu,
0050 struct hw_perf_event *hwc)
0051 {
0052 return readq(cpa_pmu->base + hisi_cpa_pmu_get_counter_offset(hwc->idx));
0053 }
0054
0055 static void hisi_cpa_pmu_write_counter(struct hisi_pmu *cpa_pmu,
0056 struct hw_perf_event *hwc, u64 val)
0057 {
0058 writeq(val, cpa_pmu->base + hisi_cpa_pmu_get_counter_offset(hwc->idx));
0059 }
0060
0061 static void hisi_cpa_pmu_write_evtype(struct hisi_pmu *cpa_pmu, int idx,
0062 u32 type)
0063 {
0064 u32 reg, reg_idx, shift, val;
0065
0066
0067
0068
0069
0070
0071
0072
0073 reg = CPA_EVENT_TYPE0 + (idx / 4) * 4;
0074 reg_idx = idx % 4;
0075 shift = CPA_REG_OFFSET * reg_idx;
0076
0077
0078 val = readl(cpa_pmu->base + reg);
0079 val &= ~(CPA_EVTYPE_MASK << shift);
0080 val |= type << shift;
0081 writel(val, cpa_pmu->base + reg);
0082 }
0083
0084 static void hisi_cpa_pmu_start_counters(struct hisi_pmu *cpa_pmu)
0085 {
0086 u32 val;
0087
0088 val = readl(cpa_pmu->base + CPA_PERF_CTRL);
0089 val |= CPA_PERF_CTRL_EN;
0090 writel(val, cpa_pmu->base + CPA_PERF_CTRL);
0091 }
0092
0093 static void hisi_cpa_pmu_stop_counters(struct hisi_pmu *cpa_pmu)
0094 {
0095 u32 val;
0096
0097 val = readl(cpa_pmu->base + CPA_PERF_CTRL);
0098 val &= ~(CPA_PERF_CTRL_EN);
0099 writel(val, cpa_pmu->base + CPA_PERF_CTRL);
0100 }
0101
0102 static void hisi_cpa_pmu_disable_pm(struct hisi_pmu *cpa_pmu)
0103 {
0104 u32 val;
0105
0106 val = readl(cpa_pmu->base + CPA_CFG_REG);
0107 val |= CPA_PM_CTRL;
0108 writel(val, cpa_pmu->base + CPA_CFG_REG);
0109 }
0110
0111 static void hisi_cpa_pmu_enable_pm(struct hisi_pmu *cpa_pmu)
0112 {
0113 u32 val;
0114
0115 val = readl(cpa_pmu->base + CPA_CFG_REG);
0116 val &= ~(CPA_PM_CTRL);
0117 writel(val, cpa_pmu->base + CPA_CFG_REG);
0118 }
0119
0120 static void hisi_cpa_pmu_enable_counter(struct hisi_pmu *cpa_pmu,
0121 struct hw_perf_event *hwc)
0122 {
0123 u32 val;
0124
0125
0126 val = readl(cpa_pmu->base + CPA_EVENT_CTRL);
0127 val |= 1 << hwc->idx;
0128 writel(val, cpa_pmu->base + CPA_EVENT_CTRL);
0129 }
0130
0131 static void hisi_cpa_pmu_disable_counter(struct hisi_pmu *cpa_pmu,
0132 struct hw_perf_event *hwc)
0133 {
0134 u32 val;
0135
0136
0137 val = readl(cpa_pmu->base + CPA_EVENT_CTRL);
0138 val &= ~(1UL << hwc->idx);
0139 writel(val, cpa_pmu->base + CPA_EVENT_CTRL);
0140 }
0141
0142 static void hisi_cpa_pmu_enable_counter_int(struct hisi_pmu *cpa_pmu,
0143 struct hw_perf_event *hwc)
0144 {
0145 u32 val;
0146
0147
0148 val = readl(cpa_pmu->base + CPA_INT_MASK);
0149 val &= ~(1UL << hwc->idx);
0150 writel(val, cpa_pmu->base + CPA_INT_MASK);
0151 }
0152
0153 static void hisi_cpa_pmu_disable_counter_int(struct hisi_pmu *cpa_pmu,
0154 struct hw_perf_event *hwc)
0155 {
0156 u32 val;
0157
0158
0159 val = readl(cpa_pmu->base + CPA_INT_MASK);
0160 val |= 1 << hwc->idx;
0161 writel(val, cpa_pmu->base + CPA_INT_MASK);
0162 }
0163
0164 static u32 hisi_cpa_pmu_get_int_status(struct hisi_pmu *cpa_pmu)
0165 {
0166 return readl(cpa_pmu->base + CPA_INT_STATUS);
0167 }
0168
0169 static void hisi_cpa_pmu_clear_int_status(struct hisi_pmu *cpa_pmu, int idx)
0170 {
0171 writel(1 << idx, cpa_pmu->base + CPA_INT_CLEAR);
0172 }
0173
0174 static const struct acpi_device_id hisi_cpa_pmu_acpi_match[] = {
0175 { "HISI0281", },
0176 {}
0177 };
0178 MODULE_DEVICE_TABLE(acpi, hisi_cpa_pmu_acpi_match);
0179
0180 static int hisi_cpa_pmu_init_data(struct platform_device *pdev,
0181 struct hisi_pmu *cpa_pmu)
0182 {
0183 if (device_property_read_u32(&pdev->dev, "hisilicon,scl-id",
0184 &cpa_pmu->sicl_id)) {
0185 dev_err(&pdev->dev, "Can not read sicl-id\n");
0186 return -EINVAL;
0187 }
0188
0189 if (device_property_read_u32(&pdev->dev, "hisilicon,idx-id",
0190 &cpa_pmu->index_id)) {
0191 dev_err(&pdev->dev, "Cannot read idx-id\n");
0192 return -EINVAL;
0193 }
0194
0195 cpa_pmu->ccl_id = -1;
0196 cpa_pmu->sccl_id = -1;
0197 cpa_pmu->base = devm_platform_ioremap_resource(pdev, 0);
0198 if (IS_ERR(cpa_pmu->base))
0199 return PTR_ERR(cpa_pmu->base);
0200
0201 cpa_pmu->identifier = readl(cpa_pmu->base + CPA_VERSION);
0202
0203 return 0;
0204 }
0205
0206 static struct attribute *hisi_cpa_pmu_format_attr[] = {
0207 HISI_PMU_FORMAT_ATTR(event, "config:0-15"),
0208 NULL
0209 };
0210
0211 static const struct attribute_group hisi_cpa_pmu_format_group = {
0212 .name = "format",
0213 .attrs = hisi_cpa_pmu_format_attr,
0214 };
0215
0216 static struct attribute *hisi_cpa_pmu_events_attr[] = {
0217 HISI_PMU_EVENT_ATTR(cpa_cycles, 0x00),
0218 HISI_PMU_EVENT_ATTR(cpa_p1_wr_dat, 0x61),
0219 HISI_PMU_EVENT_ATTR(cpa_p1_rd_dat, 0x62),
0220 HISI_PMU_EVENT_ATTR(cpa_p0_wr_dat, 0xE1),
0221 HISI_PMU_EVENT_ATTR(cpa_p0_rd_dat, 0xE2),
0222 NULL
0223 };
0224
0225 static const struct attribute_group hisi_cpa_pmu_events_group = {
0226 .name = "events",
0227 .attrs = hisi_cpa_pmu_events_attr,
0228 };
0229
0230 static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL);
0231
0232 static struct attribute *hisi_cpa_pmu_cpumask_attrs[] = {
0233 &dev_attr_cpumask.attr,
0234 NULL
0235 };
0236
0237 static const struct attribute_group hisi_cpa_pmu_cpumask_attr_group = {
0238 .attrs = hisi_cpa_pmu_cpumask_attrs,
0239 };
0240
0241 static struct device_attribute hisi_cpa_pmu_identifier_attr =
0242 __ATTR(identifier, 0444, hisi_uncore_pmu_identifier_attr_show, NULL);
0243
0244 static struct attribute *hisi_cpa_pmu_identifier_attrs[] = {
0245 &hisi_cpa_pmu_identifier_attr.attr,
0246 NULL
0247 };
0248
0249 static const struct attribute_group hisi_cpa_pmu_identifier_group = {
0250 .attrs = hisi_cpa_pmu_identifier_attrs,
0251 };
0252
0253 static const struct attribute_group *hisi_cpa_pmu_attr_groups[] = {
0254 &hisi_cpa_pmu_format_group,
0255 &hisi_cpa_pmu_events_group,
0256 &hisi_cpa_pmu_cpumask_attr_group,
0257 &hisi_cpa_pmu_identifier_group,
0258 NULL
0259 };
0260
0261 static const struct hisi_uncore_ops hisi_uncore_cpa_pmu_ops = {
0262 .write_evtype = hisi_cpa_pmu_write_evtype,
0263 .get_event_idx = hisi_uncore_pmu_get_event_idx,
0264 .start_counters = hisi_cpa_pmu_start_counters,
0265 .stop_counters = hisi_cpa_pmu_stop_counters,
0266 .enable_counter = hisi_cpa_pmu_enable_counter,
0267 .disable_counter = hisi_cpa_pmu_disable_counter,
0268 .enable_counter_int = hisi_cpa_pmu_enable_counter_int,
0269 .disable_counter_int = hisi_cpa_pmu_disable_counter_int,
0270 .write_counter = hisi_cpa_pmu_write_counter,
0271 .read_counter = hisi_cpa_pmu_read_counter,
0272 .get_int_status = hisi_cpa_pmu_get_int_status,
0273 .clear_int_status = hisi_cpa_pmu_clear_int_status,
0274 };
0275
0276 static int hisi_cpa_pmu_dev_probe(struct platform_device *pdev,
0277 struct hisi_pmu *cpa_pmu)
0278 {
0279 int ret;
0280
0281 ret = hisi_cpa_pmu_init_data(pdev, cpa_pmu);
0282 if (ret)
0283 return ret;
0284
0285 ret = hisi_uncore_pmu_init_irq(cpa_pmu, pdev);
0286 if (ret)
0287 return ret;
0288
0289 cpa_pmu->counter_bits = CPA_COUNTER_BITS;
0290 cpa_pmu->check_event = CPA_NR_EVENTS;
0291 cpa_pmu->pmu_events.attr_groups = hisi_cpa_pmu_attr_groups;
0292 cpa_pmu->ops = &hisi_uncore_cpa_pmu_ops;
0293 cpa_pmu->num_counters = CPA_NR_COUNTERS;
0294 cpa_pmu->dev = &pdev->dev;
0295 cpa_pmu->on_cpu = -1;
0296
0297 return 0;
0298 }
0299
0300 static int hisi_cpa_pmu_probe(struct platform_device *pdev)
0301 {
0302 struct hisi_pmu *cpa_pmu;
0303 char *name;
0304 int ret;
0305
0306 cpa_pmu = devm_kzalloc(&pdev->dev, sizeof(*cpa_pmu), GFP_KERNEL);
0307 if (!cpa_pmu)
0308 return -ENOMEM;
0309
0310 ret = hisi_cpa_pmu_dev_probe(pdev, cpa_pmu);
0311 if (ret)
0312 return ret;
0313
0314 name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sicl%d_cpa%u",
0315 cpa_pmu->sicl_id, cpa_pmu->index_id);
0316 if (!name)
0317 return -ENOMEM;
0318
0319 cpa_pmu->pmu = (struct pmu) {
0320 .name = name,
0321 .module = THIS_MODULE,
0322 .task_ctx_nr = perf_invalid_context,
0323 .event_init = hisi_uncore_pmu_event_init,
0324 .pmu_enable = hisi_uncore_pmu_enable,
0325 .pmu_disable = hisi_uncore_pmu_disable,
0326 .add = hisi_uncore_pmu_add,
0327 .del = hisi_uncore_pmu_del,
0328 .start = hisi_uncore_pmu_start,
0329 .stop = hisi_uncore_pmu_stop,
0330 .read = hisi_uncore_pmu_read,
0331 .attr_groups = cpa_pmu->pmu_events.attr_groups,
0332 .capabilities = PERF_PMU_CAP_NO_EXCLUDE,
0333 };
0334
0335
0336 hisi_cpa_pmu_disable_pm(cpa_pmu);
0337 ret = cpuhp_state_add_instance(CPUHP_AP_PERF_ARM_HISI_CPA_ONLINE,
0338 &cpa_pmu->node);
0339 if (ret) {
0340 dev_err(&pdev->dev, "Error %d registering hotplug\n", ret);
0341 hisi_cpa_pmu_enable_pm(cpa_pmu);
0342 return ret;
0343 }
0344
0345 ret = perf_pmu_register(&cpa_pmu->pmu, name, -1);
0346 if (ret) {
0347 dev_err(cpa_pmu->dev, "PMU register failed\n");
0348 cpuhp_state_remove_instance_nocalls(
0349 CPUHP_AP_PERF_ARM_HISI_CPA_ONLINE, &cpa_pmu->node);
0350 hisi_cpa_pmu_enable_pm(cpa_pmu);
0351 return ret;
0352 }
0353
0354 platform_set_drvdata(pdev, cpa_pmu);
0355 return ret;
0356 }
0357
0358 static int hisi_cpa_pmu_remove(struct platform_device *pdev)
0359 {
0360 struct hisi_pmu *cpa_pmu = platform_get_drvdata(pdev);
0361
0362 perf_pmu_unregister(&cpa_pmu->pmu);
0363 cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_HISI_CPA_ONLINE,
0364 &cpa_pmu->node);
0365 hisi_cpa_pmu_enable_pm(cpa_pmu);
0366 return 0;
0367 }
0368
0369 static struct platform_driver hisi_cpa_pmu_driver = {
0370 .driver = {
0371 .name = "hisi_cpa_pmu",
0372 .acpi_match_table = ACPI_PTR(hisi_cpa_pmu_acpi_match),
0373 .suppress_bind_attrs = true,
0374 },
0375 .probe = hisi_cpa_pmu_probe,
0376 .remove = hisi_cpa_pmu_remove,
0377 };
0378
0379 static int __init hisi_cpa_pmu_module_init(void)
0380 {
0381 int ret;
0382
0383 ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_HISI_CPA_ONLINE,
0384 "AP_PERF_ARM_HISI_CPA_ONLINE",
0385 hisi_uncore_pmu_online_cpu,
0386 hisi_uncore_pmu_offline_cpu);
0387 if (ret) {
0388 pr_err("setup hotplug failed: %d\n", ret);
0389 return ret;
0390 }
0391
0392 ret = platform_driver_register(&hisi_cpa_pmu_driver);
0393 if (ret)
0394 cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_CPA_ONLINE);
0395
0396 return ret;
0397 }
0398 module_init(hisi_cpa_pmu_module_init);
0399
0400 static void __exit hisi_cpa_pmu_module_exit(void)
0401 {
0402 platform_driver_unregister(&hisi_cpa_pmu_driver);
0403 cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_CPA_ONLINE);
0404 }
0405 module_exit(hisi_cpa_pmu_module_exit);
0406
0407 MODULE_DESCRIPTION("HiSilicon SoC CPA PMU driver");
0408 MODULE_LICENSE("GPL v2");
0409 MODULE_AUTHOR("Qi Liu <liuqi115@huawei.com>");