0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/acpi.h>
0011 #include <linux/cpuhotplug.h>
0012 #include <linux/interrupt.h>
0013 #include <linux/irq.h>
0014 #include <linux/list.h>
0015 #include <linux/smp.h>
0016
0017 #include "hisi_uncore_pmu.h"
0018
0019
0020 #define PA_PERF_CTRL 0x1c00
0021 #define PA_EVENT_CTRL 0x1c04
0022 #define PA_TT_CTRL 0x1c08
0023 #define PA_TGTID_CTRL 0x1c14
0024 #define PA_SRCID_CTRL 0x1c18
0025 #define PA_INT_MASK 0x1c70
0026 #define PA_INT_STATUS 0x1c78
0027 #define PA_INT_CLEAR 0x1c7c
0028 #define PA_EVENT_TYPE0 0x1c80
0029 #define PA_PMU_VERSION 0x1cf0
0030 #define PA_EVENT_CNT0_L 0x1d00
0031
0032 #define PA_EVTYPE_MASK 0xff
0033 #define PA_NR_COUNTERS 0x8
0034 #define PA_PERF_CTRL_EN BIT(0)
0035 #define PA_TRACETAG_EN BIT(4)
0036 #define PA_TGTID_EN BIT(11)
0037 #define PA_SRCID_EN BIT(11)
0038 #define PA_TGTID_NONE 0
0039 #define PA_SRCID_NONE 0
0040 #define PA_TGTID_MSK_SHIFT 12
0041 #define PA_SRCID_MSK_SHIFT 12
0042
0043 HISI_PMU_EVENT_ATTR_EXTRACTOR(tgtid_cmd, config1, 10, 0);
0044 HISI_PMU_EVENT_ATTR_EXTRACTOR(tgtid_msk, config1, 21, 11);
0045 HISI_PMU_EVENT_ATTR_EXTRACTOR(srcid_cmd, config1, 32, 22);
0046 HISI_PMU_EVENT_ATTR_EXTRACTOR(srcid_msk, config1, 43, 33);
0047 HISI_PMU_EVENT_ATTR_EXTRACTOR(tracetag_en, config1, 44, 44);
0048
0049 static void hisi_pa_pmu_enable_tracetag(struct perf_event *event)
0050 {
0051 struct hisi_pmu *pa_pmu = to_hisi_pmu(event->pmu);
0052 u32 tt_en = hisi_get_tracetag_en(event);
0053
0054 if (tt_en) {
0055 u32 val;
0056
0057 val = readl(pa_pmu->base + PA_TT_CTRL);
0058 val |= PA_TRACETAG_EN;
0059 writel(val, pa_pmu->base + PA_TT_CTRL);
0060 }
0061 }
0062
0063 static void hisi_pa_pmu_clear_tracetag(struct perf_event *event)
0064 {
0065 struct hisi_pmu *pa_pmu = to_hisi_pmu(event->pmu);
0066 u32 tt_en = hisi_get_tracetag_en(event);
0067
0068 if (tt_en) {
0069 u32 val;
0070
0071 val = readl(pa_pmu->base + PA_TT_CTRL);
0072 val &= ~PA_TRACETAG_EN;
0073 writel(val, pa_pmu->base + PA_TT_CTRL);
0074 }
0075 }
0076
0077 static void hisi_pa_pmu_config_tgtid(struct perf_event *event)
0078 {
0079 struct hisi_pmu *pa_pmu = to_hisi_pmu(event->pmu);
0080 u32 cmd = hisi_get_tgtid_cmd(event);
0081
0082 if (cmd) {
0083 u32 msk = hisi_get_tgtid_msk(event);
0084 u32 val = cmd | PA_TGTID_EN | (msk << PA_TGTID_MSK_SHIFT);
0085
0086 writel(val, pa_pmu->base + PA_TGTID_CTRL);
0087 }
0088 }
0089
0090 static void hisi_pa_pmu_clear_tgtid(struct perf_event *event)
0091 {
0092 struct hisi_pmu *pa_pmu = to_hisi_pmu(event->pmu);
0093 u32 cmd = hisi_get_tgtid_cmd(event);
0094
0095 if (cmd)
0096 writel(PA_TGTID_NONE, pa_pmu->base + PA_TGTID_CTRL);
0097 }
0098
0099 static void hisi_pa_pmu_config_srcid(struct perf_event *event)
0100 {
0101 struct hisi_pmu *pa_pmu = to_hisi_pmu(event->pmu);
0102 u32 cmd = hisi_get_srcid_cmd(event);
0103
0104 if (cmd) {
0105 u32 msk = hisi_get_srcid_msk(event);
0106 u32 val = cmd | PA_SRCID_EN | (msk << PA_SRCID_MSK_SHIFT);
0107
0108 writel(val, pa_pmu->base + PA_SRCID_CTRL);
0109 }
0110 }
0111
0112 static void hisi_pa_pmu_clear_srcid(struct perf_event *event)
0113 {
0114 struct hisi_pmu *pa_pmu = to_hisi_pmu(event->pmu);
0115 u32 cmd = hisi_get_srcid_cmd(event);
0116
0117 if (cmd)
0118 writel(PA_SRCID_NONE, pa_pmu->base + PA_SRCID_CTRL);
0119 }
0120
0121 static void hisi_pa_pmu_enable_filter(struct perf_event *event)
0122 {
0123 if (event->attr.config1 != 0x0) {
0124 hisi_pa_pmu_enable_tracetag(event);
0125 hisi_pa_pmu_config_srcid(event);
0126 hisi_pa_pmu_config_tgtid(event);
0127 }
0128 }
0129
0130 static void hisi_pa_pmu_disable_filter(struct perf_event *event)
0131 {
0132 if (event->attr.config1 != 0x0) {
0133 hisi_pa_pmu_clear_tgtid(event);
0134 hisi_pa_pmu_clear_srcid(event);
0135 hisi_pa_pmu_clear_tracetag(event);
0136 }
0137 }
0138
0139 static u32 hisi_pa_pmu_get_counter_offset(int idx)
0140 {
0141 return (PA_EVENT_CNT0_L + idx * 8);
0142 }
0143
0144 static u64 hisi_pa_pmu_read_counter(struct hisi_pmu *pa_pmu,
0145 struct hw_perf_event *hwc)
0146 {
0147 return readq(pa_pmu->base + hisi_pa_pmu_get_counter_offset(hwc->idx));
0148 }
0149
0150 static void hisi_pa_pmu_write_counter(struct hisi_pmu *pa_pmu,
0151 struct hw_perf_event *hwc, u64 val)
0152 {
0153 writeq(val, pa_pmu->base + hisi_pa_pmu_get_counter_offset(hwc->idx));
0154 }
0155
0156 static void hisi_pa_pmu_write_evtype(struct hisi_pmu *pa_pmu, int idx,
0157 u32 type)
0158 {
0159 u32 reg, reg_idx, shift, val;
0160
0161
0162
0163
0164
0165
0166
0167
0168 reg = PA_EVENT_TYPE0 + (idx / 4) * 4;
0169 reg_idx = idx % 4;
0170 shift = 8 * reg_idx;
0171
0172
0173 val = readl(pa_pmu->base + reg);
0174 val &= ~(PA_EVTYPE_MASK << shift);
0175 val |= (type << shift);
0176 writel(val, pa_pmu->base + reg);
0177 }
0178
0179 static void hisi_pa_pmu_start_counters(struct hisi_pmu *pa_pmu)
0180 {
0181 u32 val;
0182
0183 val = readl(pa_pmu->base + PA_PERF_CTRL);
0184 val |= PA_PERF_CTRL_EN;
0185 writel(val, pa_pmu->base + PA_PERF_CTRL);
0186 }
0187
0188 static void hisi_pa_pmu_stop_counters(struct hisi_pmu *pa_pmu)
0189 {
0190 u32 val;
0191
0192 val = readl(pa_pmu->base + PA_PERF_CTRL);
0193 val &= ~(PA_PERF_CTRL_EN);
0194 writel(val, pa_pmu->base + PA_PERF_CTRL);
0195 }
0196
0197 static void hisi_pa_pmu_enable_counter(struct hisi_pmu *pa_pmu,
0198 struct hw_perf_event *hwc)
0199 {
0200 u32 val;
0201
0202
0203 val = readl(pa_pmu->base + PA_EVENT_CTRL);
0204 val |= 1 << hwc->idx;
0205 writel(val, pa_pmu->base + PA_EVENT_CTRL);
0206 }
0207
0208 static void hisi_pa_pmu_disable_counter(struct hisi_pmu *pa_pmu,
0209 struct hw_perf_event *hwc)
0210 {
0211 u32 val;
0212
0213
0214 val = readl(pa_pmu->base + PA_EVENT_CTRL);
0215 val &= ~(1 << hwc->idx);
0216 writel(val, pa_pmu->base + PA_EVENT_CTRL);
0217 }
0218
0219 static void hisi_pa_pmu_enable_counter_int(struct hisi_pmu *pa_pmu,
0220 struct hw_perf_event *hwc)
0221 {
0222 u32 val;
0223
0224
0225 val = readl(pa_pmu->base + PA_INT_MASK);
0226 val &= ~(1 << hwc->idx);
0227 writel(val, pa_pmu->base + PA_INT_MASK);
0228 }
0229
0230 static void hisi_pa_pmu_disable_counter_int(struct hisi_pmu *pa_pmu,
0231 struct hw_perf_event *hwc)
0232 {
0233 u32 val;
0234
0235
0236 val = readl(pa_pmu->base + PA_INT_MASK);
0237 val |= 1 << hwc->idx;
0238 writel(val, pa_pmu->base + PA_INT_MASK);
0239 }
0240
0241 static u32 hisi_pa_pmu_get_int_status(struct hisi_pmu *pa_pmu)
0242 {
0243 return readl(pa_pmu->base + PA_INT_STATUS);
0244 }
0245
0246 static void hisi_pa_pmu_clear_int_status(struct hisi_pmu *pa_pmu, int idx)
0247 {
0248 writel(1 << idx, pa_pmu->base + PA_INT_CLEAR);
0249 }
0250
0251 static const struct acpi_device_id hisi_pa_pmu_acpi_match[] = {
0252 { "HISI0273", },
0253 {}
0254 };
0255 MODULE_DEVICE_TABLE(acpi, hisi_pa_pmu_acpi_match);
0256
0257 static int hisi_pa_pmu_init_data(struct platform_device *pdev,
0258 struct hisi_pmu *pa_pmu)
0259 {
0260
0261
0262
0263
0264 if (device_property_read_u32(&pdev->dev, "hisilicon,scl-id",
0265 &pa_pmu->sicl_id)) {
0266 dev_err(&pdev->dev, "Cannot read sicl-id!\n");
0267 return -EINVAL;
0268 }
0269
0270 if (device_property_read_u32(&pdev->dev, "hisilicon,idx-id",
0271 &pa_pmu->index_id)) {
0272 dev_err(&pdev->dev, "Cannot read idx-id!\n");
0273 return -EINVAL;
0274 }
0275
0276 pa_pmu->ccl_id = -1;
0277 pa_pmu->sccl_id = -1;
0278
0279 pa_pmu->base = devm_platform_ioremap_resource(pdev, 0);
0280 if (IS_ERR(pa_pmu->base)) {
0281 dev_err(&pdev->dev, "ioremap failed for pa_pmu resource.\n");
0282 return PTR_ERR(pa_pmu->base);
0283 }
0284
0285 pa_pmu->identifier = readl(pa_pmu->base + PA_PMU_VERSION);
0286
0287 return 0;
0288 }
0289
0290 static struct attribute *hisi_pa_pmu_v2_format_attr[] = {
0291 HISI_PMU_FORMAT_ATTR(event, "config:0-7"),
0292 HISI_PMU_FORMAT_ATTR(tgtid_cmd, "config1:0-10"),
0293 HISI_PMU_FORMAT_ATTR(tgtid_msk, "config1:11-21"),
0294 HISI_PMU_FORMAT_ATTR(srcid_cmd, "config1:22-32"),
0295 HISI_PMU_FORMAT_ATTR(srcid_msk, "config1:33-43"),
0296 HISI_PMU_FORMAT_ATTR(tracetag_en, "config1:44"),
0297 NULL,
0298 };
0299
0300 static const struct attribute_group hisi_pa_pmu_v2_format_group = {
0301 .name = "format",
0302 .attrs = hisi_pa_pmu_v2_format_attr,
0303 };
0304
0305 static struct attribute *hisi_pa_pmu_v2_events_attr[] = {
0306 HISI_PMU_EVENT_ATTR(rx_req, 0x40),
0307 HISI_PMU_EVENT_ATTR(tx_req, 0x5c),
0308 HISI_PMU_EVENT_ATTR(cycle, 0x78),
0309 NULL
0310 };
0311
0312 static const struct attribute_group hisi_pa_pmu_v2_events_group = {
0313 .name = "events",
0314 .attrs = hisi_pa_pmu_v2_events_attr,
0315 };
0316
0317 static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL);
0318
0319 static struct attribute *hisi_pa_pmu_cpumask_attrs[] = {
0320 &dev_attr_cpumask.attr,
0321 NULL
0322 };
0323
0324 static const struct attribute_group hisi_pa_pmu_cpumask_attr_group = {
0325 .attrs = hisi_pa_pmu_cpumask_attrs,
0326 };
0327
0328 static struct device_attribute hisi_pa_pmu_identifier_attr =
0329 __ATTR(identifier, 0444, hisi_uncore_pmu_identifier_attr_show, NULL);
0330
0331 static struct attribute *hisi_pa_pmu_identifier_attrs[] = {
0332 &hisi_pa_pmu_identifier_attr.attr,
0333 NULL
0334 };
0335
0336 static const struct attribute_group hisi_pa_pmu_identifier_group = {
0337 .attrs = hisi_pa_pmu_identifier_attrs,
0338 };
0339
0340 static const struct attribute_group *hisi_pa_pmu_v2_attr_groups[] = {
0341 &hisi_pa_pmu_v2_format_group,
0342 &hisi_pa_pmu_v2_events_group,
0343 &hisi_pa_pmu_cpumask_attr_group,
0344 &hisi_pa_pmu_identifier_group,
0345 NULL
0346 };
0347
0348 static const struct hisi_uncore_ops hisi_uncore_pa_ops = {
0349 .write_evtype = hisi_pa_pmu_write_evtype,
0350 .get_event_idx = hisi_uncore_pmu_get_event_idx,
0351 .start_counters = hisi_pa_pmu_start_counters,
0352 .stop_counters = hisi_pa_pmu_stop_counters,
0353 .enable_counter = hisi_pa_pmu_enable_counter,
0354 .disable_counter = hisi_pa_pmu_disable_counter,
0355 .enable_counter_int = hisi_pa_pmu_enable_counter_int,
0356 .disable_counter_int = hisi_pa_pmu_disable_counter_int,
0357 .write_counter = hisi_pa_pmu_write_counter,
0358 .read_counter = hisi_pa_pmu_read_counter,
0359 .get_int_status = hisi_pa_pmu_get_int_status,
0360 .clear_int_status = hisi_pa_pmu_clear_int_status,
0361 .enable_filter = hisi_pa_pmu_enable_filter,
0362 .disable_filter = hisi_pa_pmu_disable_filter,
0363 };
0364
0365 static int hisi_pa_pmu_dev_probe(struct platform_device *pdev,
0366 struct hisi_pmu *pa_pmu)
0367 {
0368 int ret;
0369
0370 ret = hisi_pa_pmu_init_data(pdev, pa_pmu);
0371 if (ret)
0372 return ret;
0373
0374 ret = hisi_uncore_pmu_init_irq(pa_pmu, pdev);
0375 if (ret)
0376 return ret;
0377
0378 pa_pmu->pmu_events.attr_groups = hisi_pa_pmu_v2_attr_groups;
0379 pa_pmu->num_counters = PA_NR_COUNTERS;
0380 pa_pmu->ops = &hisi_uncore_pa_ops;
0381 pa_pmu->check_event = 0xB0;
0382 pa_pmu->counter_bits = 64;
0383 pa_pmu->dev = &pdev->dev;
0384 pa_pmu->on_cpu = -1;
0385
0386 return 0;
0387 }
0388
0389 static int hisi_pa_pmu_probe(struct platform_device *pdev)
0390 {
0391 struct hisi_pmu *pa_pmu;
0392 char *name;
0393 int ret;
0394
0395 pa_pmu = devm_kzalloc(&pdev->dev, sizeof(*pa_pmu), GFP_KERNEL);
0396 if (!pa_pmu)
0397 return -ENOMEM;
0398
0399 ret = hisi_pa_pmu_dev_probe(pdev, pa_pmu);
0400 if (ret)
0401 return ret;
0402
0403 name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sicl%u_pa%u",
0404 pa_pmu->sicl_id, pa_pmu->index_id);
0405 if (!name)
0406 return -ENOMEM;
0407
0408 ret = cpuhp_state_add_instance(CPUHP_AP_PERF_ARM_HISI_PA_ONLINE,
0409 &pa_pmu->node);
0410 if (ret) {
0411 dev_err(&pdev->dev, "Error %d registering hotplug\n", ret);
0412 return ret;
0413 }
0414
0415 hisi_pmu_init(&pa_pmu->pmu, name, pa_pmu->pmu_events.attr_groups, THIS_MODULE);
0416 ret = perf_pmu_register(&pa_pmu->pmu, name, -1);
0417 if (ret) {
0418 dev_err(pa_pmu->dev, "PMU register failed, ret = %d\n", ret);
0419 cpuhp_state_remove_instance(CPUHP_AP_PERF_ARM_HISI_PA_ONLINE,
0420 &pa_pmu->node);
0421 return ret;
0422 }
0423
0424 platform_set_drvdata(pdev, pa_pmu);
0425 return ret;
0426 }
0427
0428 static int hisi_pa_pmu_remove(struct platform_device *pdev)
0429 {
0430 struct hisi_pmu *pa_pmu = platform_get_drvdata(pdev);
0431
0432 perf_pmu_unregister(&pa_pmu->pmu);
0433 cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_HISI_PA_ONLINE,
0434 &pa_pmu->node);
0435 return 0;
0436 }
0437
0438 static struct platform_driver hisi_pa_pmu_driver = {
0439 .driver = {
0440 .name = "hisi_pa_pmu",
0441 .acpi_match_table = hisi_pa_pmu_acpi_match,
0442 .suppress_bind_attrs = true,
0443 },
0444 .probe = hisi_pa_pmu_probe,
0445 .remove = hisi_pa_pmu_remove,
0446 };
0447
0448 static int __init hisi_pa_pmu_module_init(void)
0449 {
0450 int ret;
0451
0452 ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_HISI_PA_ONLINE,
0453 "AP_PERF_ARM_HISI_PA_ONLINE",
0454 hisi_uncore_pmu_online_cpu,
0455 hisi_uncore_pmu_offline_cpu);
0456 if (ret) {
0457 pr_err("PA PMU: cpuhp state setup failed, ret = %d\n", ret);
0458 return ret;
0459 }
0460
0461 ret = platform_driver_register(&hisi_pa_pmu_driver);
0462 if (ret)
0463 cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_PA_ONLINE);
0464
0465 return ret;
0466 }
0467 module_init(hisi_pa_pmu_module_init);
0468
0469 static void __exit hisi_pa_pmu_module_exit(void)
0470 {
0471 platform_driver_unregister(&hisi_pa_pmu_driver);
0472 cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_PA_ONLINE);
0473 }
0474 module_exit(hisi_pa_pmu_module_exit);
0475
0476 MODULE_DESCRIPTION("HiSilicon Protocol Adapter uncore PMU driver");
0477 MODULE_LICENSE("GPL v2");
0478 MODULE_AUTHOR("Shaokun Zhang <zhangshaokun@hisilicon.com>");
0479 MODULE_AUTHOR("Qi Liu <liuqi115@huawei.com>");