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 SLLC_INT_MASK 0x0814
0021 #define SLLC_INT_STATUS 0x0818
0022 #define SLLC_INT_CLEAR 0x081c
0023 #define SLLC_PERF_CTRL 0x1c00
0024 #define SLLC_SRCID_CTRL 0x1c04
0025 #define SLLC_TGTID_CTRL 0x1c08
0026 #define SLLC_EVENT_CTRL 0x1c14
0027 #define SLLC_EVENT_TYPE0 0x1c18
0028 #define SLLC_VERSION 0x1cf0
0029 #define SLLC_EVENT_CNT0_L 0x1d00
0030
0031 #define SLLC_EVTYPE_MASK 0xff
0032 #define SLLC_PERF_CTRL_EN BIT(0)
0033 #define SLLC_FILT_EN BIT(1)
0034 #define SLLC_TRACETAG_EN BIT(2)
0035 #define SLLC_SRCID_EN BIT(4)
0036 #define SLLC_SRCID_NONE 0x0
0037 #define SLLC_TGTID_EN BIT(5)
0038 #define SLLC_TGTID_NONE 0x0
0039 #define SLLC_TGTID_MIN_SHIFT 1
0040 #define SLLC_TGTID_MAX_SHIFT 12
0041 #define SLLC_SRCID_CMD_SHIFT 1
0042 #define SLLC_SRCID_MSK_SHIFT 12
0043 #define SLLC_NR_EVENTS 0x80
0044
0045 HISI_PMU_EVENT_ATTR_EXTRACTOR(tgtid_min, config1, 10, 0);
0046 HISI_PMU_EVENT_ATTR_EXTRACTOR(tgtid_max, config1, 21, 11);
0047 HISI_PMU_EVENT_ATTR_EXTRACTOR(srcid_cmd, config1, 32, 22);
0048 HISI_PMU_EVENT_ATTR_EXTRACTOR(srcid_msk, config1, 43, 33);
0049 HISI_PMU_EVENT_ATTR_EXTRACTOR(tracetag_en, config1, 44, 44);
0050
0051 static bool tgtid_is_valid(u32 max, u32 min)
0052 {
0053 return max > 0 && max >= min;
0054 }
0055
0056 static void hisi_sllc_pmu_enable_tracetag(struct perf_event *event)
0057 {
0058 struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu);
0059 u32 tt_en = hisi_get_tracetag_en(event);
0060
0061 if (tt_en) {
0062 u32 val;
0063
0064 val = readl(sllc_pmu->base + SLLC_PERF_CTRL);
0065 val |= SLLC_TRACETAG_EN | SLLC_FILT_EN;
0066 writel(val, sllc_pmu->base + SLLC_PERF_CTRL);
0067 }
0068 }
0069
0070 static void hisi_sllc_pmu_disable_tracetag(struct perf_event *event)
0071 {
0072 struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu);
0073 u32 tt_en = hisi_get_tracetag_en(event);
0074
0075 if (tt_en) {
0076 u32 val;
0077
0078 val = readl(sllc_pmu->base + SLLC_PERF_CTRL);
0079 val &= ~(SLLC_TRACETAG_EN | SLLC_FILT_EN);
0080 writel(val, sllc_pmu->base + SLLC_PERF_CTRL);
0081 }
0082 }
0083
0084 static void hisi_sllc_pmu_config_tgtid(struct perf_event *event)
0085 {
0086 struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu);
0087 u32 min = hisi_get_tgtid_min(event);
0088 u32 max = hisi_get_tgtid_max(event);
0089
0090 if (tgtid_is_valid(max, min)) {
0091 u32 val = (max << SLLC_TGTID_MAX_SHIFT) | (min << SLLC_TGTID_MIN_SHIFT);
0092
0093 writel(val, sllc_pmu->base + SLLC_TGTID_CTRL);
0094
0095 val = readl(sllc_pmu->base + SLLC_PERF_CTRL);
0096 val |= SLLC_TGTID_EN | SLLC_FILT_EN;
0097 writel(val, sllc_pmu->base + SLLC_PERF_CTRL);
0098 }
0099 }
0100
0101 static void hisi_sllc_pmu_clear_tgtid(struct perf_event *event)
0102 {
0103 struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu);
0104 u32 min = hisi_get_tgtid_min(event);
0105 u32 max = hisi_get_tgtid_max(event);
0106
0107 if (tgtid_is_valid(max, min)) {
0108 u32 val;
0109
0110 writel(SLLC_TGTID_NONE, sllc_pmu->base + SLLC_TGTID_CTRL);
0111
0112 val = readl(sllc_pmu->base + SLLC_PERF_CTRL);
0113 val &= ~(SLLC_TGTID_EN | SLLC_FILT_EN);
0114 writel(val, sllc_pmu->base + SLLC_PERF_CTRL);
0115 }
0116 }
0117
0118 static void hisi_sllc_pmu_config_srcid(struct perf_event *event)
0119 {
0120 struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu);
0121 u32 cmd = hisi_get_srcid_cmd(event);
0122
0123 if (cmd) {
0124 u32 val, msk;
0125
0126 msk = hisi_get_srcid_msk(event);
0127 val = (cmd << SLLC_SRCID_CMD_SHIFT) | (msk << SLLC_SRCID_MSK_SHIFT);
0128 writel(val, sllc_pmu->base + SLLC_SRCID_CTRL);
0129
0130 val = readl(sllc_pmu->base + SLLC_PERF_CTRL);
0131 val |= SLLC_SRCID_EN | SLLC_FILT_EN;
0132 writel(val, sllc_pmu->base + SLLC_PERF_CTRL);
0133 }
0134 }
0135
0136 static void hisi_sllc_pmu_clear_srcid(struct perf_event *event)
0137 {
0138 struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu);
0139 u32 cmd = hisi_get_srcid_cmd(event);
0140
0141 if (cmd) {
0142 u32 val;
0143
0144 writel(SLLC_SRCID_NONE, sllc_pmu->base + SLLC_SRCID_CTRL);
0145
0146 val = readl(sllc_pmu->base + SLLC_PERF_CTRL);
0147 val &= ~(SLLC_SRCID_EN | SLLC_FILT_EN);
0148 writel(val, sllc_pmu->base + SLLC_PERF_CTRL);
0149 }
0150 }
0151
0152 static void hisi_sllc_pmu_enable_filter(struct perf_event *event)
0153 {
0154 if (event->attr.config1 != 0x0) {
0155 hisi_sllc_pmu_enable_tracetag(event);
0156 hisi_sllc_pmu_config_srcid(event);
0157 hisi_sllc_pmu_config_tgtid(event);
0158 }
0159 }
0160
0161 static void hisi_sllc_pmu_clear_filter(struct perf_event *event)
0162 {
0163 if (event->attr.config1 != 0x0) {
0164 hisi_sllc_pmu_disable_tracetag(event);
0165 hisi_sllc_pmu_clear_srcid(event);
0166 hisi_sllc_pmu_clear_tgtid(event);
0167 }
0168 }
0169
0170 static u32 hisi_sllc_pmu_get_counter_offset(int idx)
0171 {
0172 return (SLLC_EVENT_CNT0_L + idx * 8);
0173 }
0174
0175 static u64 hisi_sllc_pmu_read_counter(struct hisi_pmu *sllc_pmu,
0176 struct hw_perf_event *hwc)
0177 {
0178 return readq(sllc_pmu->base +
0179 hisi_sllc_pmu_get_counter_offset(hwc->idx));
0180 }
0181
0182 static void hisi_sllc_pmu_write_counter(struct hisi_pmu *sllc_pmu,
0183 struct hw_perf_event *hwc, u64 val)
0184 {
0185 writeq(val, sllc_pmu->base +
0186 hisi_sllc_pmu_get_counter_offset(hwc->idx));
0187 }
0188
0189 static void hisi_sllc_pmu_write_evtype(struct hisi_pmu *sllc_pmu, int idx,
0190 u32 type)
0191 {
0192 u32 reg, reg_idx, shift, val;
0193
0194
0195
0196
0197
0198
0199
0200
0201 reg = SLLC_EVENT_TYPE0 + (idx / 4) * 4;
0202 reg_idx = idx % 4;
0203 shift = 8 * reg_idx;
0204
0205
0206 val = readl(sllc_pmu->base + reg);
0207 val &= ~(SLLC_EVTYPE_MASK << shift);
0208 val |= (type << shift);
0209 writel(val, sllc_pmu->base + reg);
0210 }
0211
0212 static void hisi_sllc_pmu_start_counters(struct hisi_pmu *sllc_pmu)
0213 {
0214 u32 val;
0215
0216 val = readl(sllc_pmu->base + SLLC_PERF_CTRL);
0217 val |= SLLC_PERF_CTRL_EN;
0218 writel(val, sllc_pmu->base + SLLC_PERF_CTRL);
0219 }
0220
0221 static void hisi_sllc_pmu_stop_counters(struct hisi_pmu *sllc_pmu)
0222 {
0223 u32 val;
0224
0225 val = readl(sllc_pmu->base + SLLC_PERF_CTRL);
0226 val &= ~(SLLC_PERF_CTRL_EN);
0227 writel(val, sllc_pmu->base + SLLC_PERF_CTRL);
0228 }
0229
0230 static void hisi_sllc_pmu_enable_counter(struct hisi_pmu *sllc_pmu,
0231 struct hw_perf_event *hwc)
0232 {
0233 u32 val;
0234
0235 val = readl(sllc_pmu->base + SLLC_EVENT_CTRL);
0236 val |= 1 << hwc->idx;
0237 writel(val, sllc_pmu->base + SLLC_EVENT_CTRL);
0238 }
0239
0240 static void hisi_sllc_pmu_disable_counter(struct hisi_pmu *sllc_pmu,
0241 struct hw_perf_event *hwc)
0242 {
0243 u32 val;
0244
0245 val = readl(sllc_pmu->base + SLLC_EVENT_CTRL);
0246 val &= ~(1 << hwc->idx);
0247 writel(val, sllc_pmu->base + SLLC_EVENT_CTRL);
0248 }
0249
0250 static void hisi_sllc_pmu_enable_counter_int(struct hisi_pmu *sllc_pmu,
0251 struct hw_perf_event *hwc)
0252 {
0253 u32 val;
0254
0255 val = readl(sllc_pmu->base + SLLC_INT_MASK);
0256
0257 val &= ~(1 << hwc->idx);
0258 writel(val, sllc_pmu->base + SLLC_INT_MASK);
0259 }
0260
0261 static void hisi_sllc_pmu_disable_counter_int(struct hisi_pmu *sllc_pmu,
0262 struct hw_perf_event *hwc)
0263 {
0264 u32 val;
0265
0266 val = readl(sllc_pmu->base + SLLC_INT_MASK);
0267
0268 val |= 1 << hwc->idx;
0269 writel(val, sllc_pmu->base + SLLC_INT_MASK);
0270 }
0271
0272 static u32 hisi_sllc_pmu_get_int_status(struct hisi_pmu *sllc_pmu)
0273 {
0274 return readl(sllc_pmu->base + SLLC_INT_STATUS);
0275 }
0276
0277 static void hisi_sllc_pmu_clear_int_status(struct hisi_pmu *sllc_pmu, int idx)
0278 {
0279 writel(1 << idx, sllc_pmu->base + SLLC_INT_CLEAR);
0280 }
0281
0282 static const struct acpi_device_id hisi_sllc_pmu_acpi_match[] = {
0283 { "HISI0263", },
0284 {}
0285 };
0286 MODULE_DEVICE_TABLE(acpi, hisi_sllc_pmu_acpi_match);
0287
0288 static int hisi_sllc_pmu_init_data(struct platform_device *pdev,
0289 struct hisi_pmu *sllc_pmu)
0290 {
0291
0292
0293
0294
0295 if (device_property_read_u32(&pdev->dev, "hisilicon,scl-id",
0296 &sllc_pmu->sccl_id)) {
0297 dev_err(&pdev->dev, "Cannot read sccl-id!\n");
0298 return -EINVAL;
0299 }
0300
0301 if (device_property_read_u32(&pdev->dev, "hisilicon,idx-id",
0302 &sllc_pmu->index_id)) {
0303 dev_err(&pdev->dev, "Cannot read idx-id!\n");
0304 return -EINVAL;
0305 }
0306
0307
0308 sllc_pmu->ccl_id = -1;
0309
0310 sllc_pmu->base = devm_platform_ioremap_resource(pdev, 0);
0311 if (IS_ERR(sllc_pmu->base)) {
0312 dev_err(&pdev->dev, "ioremap failed for sllc_pmu resource.\n");
0313 return PTR_ERR(sllc_pmu->base);
0314 }
0315
0316 sllc_pmu->identifier = readl(sllc_pmu->base + SLLC_VERSION);
0317
0318 return 0;
0319 }
0320
0321 static struct attribute *hisi_sllc_pmu_v2_format_attr[] = {
0322 HISI_PMU_FORMAT_ATTR(event, "config:0-7"),
0323 HISI_PMU_FORMAT_ATTR(tgtid_min, "config1:0-10"),
0324 HISI_PMU_FORMAT_ATTR(tgtid_max, "config1:11-21"),
0325 HISI_PMU_FORMAT_ATTR(srcid_cmd, "config1:22-32"),
0326 HISI_PMU_FORMAT_ATTR(srcid_msk, "config1:33-43"),
0327 HISI_PMU_FORMAT_ATTR(tracetag_en, "config1:44"),
0328 NULL
0329 };
0330
0331 static const struct attribute_group hisi_sllc_pmu_v2_format_group = {
0332 .name = "format",
0333 .attrs = hisi_sllc_pmu_v2_format_attr,
0334 };
0335
0336 static struct attribute *hisi_sllc_pmu_v2_events_attr[] = {
0337 HISI_PMU_EVENT_ATTR(rx_req, 0x30),
0338 HISI_PMU_EVENT_ATTR(rx_data, 0x31),
0339 HISI_PMU_EVENT_ATTR(tx_req, 0x34),
0340 HISI_PMU_EVENT_ATTR(tx_data, 0x35),
0341 HISI_PMU_EVENT_ATTR(cycles, 0x09),
0342 NULL
0343 };
0344
0345 static const struct attribute_group hisi_sllc_pmu_v2_events_group = {
0346 .name = "events",
0347 .attrs = hisi_sllc_pmu_v2_events_attr,
0348 };
0349
0350 static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL);
0351
0352 static struct attribute *hisi_sllc_pmu_cpumask_attrs[] = {
0353 &dev_attr_cpumask.attr,
0354 NULL
0355 };
0356
0357 static const struct attribute_group hisi_sllc_pmu_cpumask_attr_group = {
0358 .attrs = hisi_sllc_pmu_cpumask_attrs,
0359 };
0360
0361 static struct device_attribute hisi_sllc_pmu_identifier_attr =
0362 __ATTR(identifier, 0444, hisi_uncore_pmu_identifier_attr_show, NULL);
0363
0364 static struct attribute *hisi_sllc_pmu_identifier_attrs[] = {
0365 &hisi_sllc_pmu_identifier_attr.attr,
0366 NULL
0367 };
0368
0369 static const struct attribute_group hisi_sllc_pmu_identifier_group = {
0370 .attrs = hisi_sllc_pmu_identifier_attrs,
0371 };
0372
0373 static const struct attribute_group *hisi_sllc_pmu_v2_attr_groups[] = {
0374 &hisi_sllc_pmu_v2_format_group,
0375 &hisi_sllc_pmu_v2_events_group,
0376 &hisi_sllc_pmu_cpumask_attr_group,
0377 &hisi_sllc_pmu_identifier_group,
0378 NULL
0379 };
0380
0381 static const struct hisi_uncore_ops hisi_uncore_sllc_ops = {
0382 .write_evtype = hisi_sllc_pmu_write_evtype,
0383 .get_event_idx = hisi_uncore_pmu_get_event_idx,
0384 .start_counters = hisi_sllc_pmu_start_counters,
0385 .stop_counters = hisi_sllc_pmu_stop_counters,
0386 .enable_counter = hisi_sllc_pmu_enable_counter,
0387 .disable_counter = hisi_sllc_pmu_disable_counter,
0388 .enable_counter_int = hisi_sllc_pmu_enable_counter_int,
0389 .disable_counter_int = hisi_sllc_pmu_disable_counter_int,
0390 .write_counter = hisi_sllc_pmu_write_counter,
0391 .read_counter = hisi_sllc_pmu_read_counter,
0392 .get_int_status = hisi_sllc_pmu_get_int_status,
0393 .clear_int_status = hisi_sllc_pmu_clear_int_status,
0394 .enable_filter = hisi_sllc_pmu_enable_filter,
0395 .disable_filter = hisi_sllc_pmu_clear_filter,
0396 };
0397
0398 static int hisi_sllc_pmu_dev_probe(struct platform_device *pdev,
0399 struct hisi_pmu *sllc_pmu)
0400 {
0401 int ret;
0402
0403 ret = hisi_sllc_pmu_init_data(pdev, sllc_pmu);
0404 if (ret)
0405 return ret;
0406
0407 ret = hisi_uncore_pmu_init_irq(sllc_pmu, pdev);
0408 if (ret)
0409 return ret;
0410
0411 sllc_pmu->pmu_events.attr_groups = hisi_sllc_pmu_v2_attr_groups;
0412 sllc_pmu->ops = &hisi_uncore_sllc_ops;
0413 sllc_pmu->check_event = SLLC_NR_EVENTS;
0414 sllc_pmu->counter_bits = 64;
0415 sllc_pmu->num_counters = 8;
0416 sllc_pmu->dev = &pdev->dev;
0417 sllc_pmu->on_cpu = -1;
0418
0419 return 0;
0420 }
0421
0422 static int hisi_sllc_pmu_probe(struct platform_device *pdev)
0423 {
0424 struct hisi_pmu *sllc_pmu;
0425 char *name;
0426 int ret;
0427
0428 sllc_pmu = devm_kzalloc(&pdev->dev, sizeof(*sllc_pmu), GFP_KERNEL);
0429 if (!sllc_pmu)
0430 return -ENOMEM;
0431
0432 ret = hisi_sllc_pmu_dev_probe(pdev, sllc_pmu);
0433 if (ret)
0434 return ret;
0435
0436 name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sccl%u_sllc%u",
0437 sllc_pmu->sccl_id, sllc_pmu->index_id);
0438 if (!name)
0439 return -ENOMEM;
0440
0441 ret = cpuhp_state_add_instance(CPUHP_AP_PERF_ARM_HISI_SLLC_ONLINE,
0442 &sllc_pmu->node);
0443 if (ret) {
0444 dev_err(&pdev->dev, "Error %d registering hotplug\n", ret);
0445 return ret;
0446 }
0447
0448 hisi_pmu_init(&sllc_pmu->pmu, name, sllc_pmu->pmu_events.attr_groups, THIS_MODULE);
0449
0450 ret = perf_pmu_register(&sllc_pmu->pmu, name, -1);
0451 if (ret) {
0452 dev_err(sllc_pmu->dev, "PMU register failed, ret = %d\n", ret);
0453 cpuhp_state_remove_instance(CPUHP_AP_PERF_ARM_HISI_SLLC_ONLINE,
0454 &sllc_pmu->node);
0455 return ret;
0456 }
0457
0458 platform_set_drvdata(pdev, sllc_pmu);
0459
0460 return ret;
0461 }
0462
0463 static int hisi_sllc_pmu_remove(struct platform_device *pdev)
0464 {
0465 struct hisi_pmu *sllc_pmu = platform_get_drvdata(pdev);
0466
0467 perf_pmu_unregister(&sllc_pmu->pmu);
0468 cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_HISI_SLLC_ONLINE,
0469 &sllc_pmu->node);
0470 return 0;
0471 }
0472
0473 static struct platform_driver hisi_sllc_pmu_driver = {
0474 .driver = {
0475 .name = "hisi_sllc_pmu",
0476 .acpi_match_table = hisi_sllc_pmu_acpi_match,
0477 .suppress_bind_attrs = true,
0478 },
0479 .probe = hisi_sllc_pmu_probe,
0480 .remove = hisi_sllc_pmu_remove,
0481 };
0482
0483 static int __init hisi_sllc_pmu_module_init(void)
0484 {
0485 int ret;
0486
0487 ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_HISI_SLLC_ONLINE,
0488 "AP_PERF_ARM_HISI_SLLC_ONLINE",
0489 hisi_uncore_pmu_online_cpu,
0490 hisi_uncore_pmu_offline_cpu);
0491 if (ret) {
0492 pr_err("SLLC PMU: cpuhp state setup failed, ret = %d\n", ret);
0493 return ret;
0494 }
0495
0496 ret = platform_driver_register(&hisi_sllc_pmu_driver);
0497 if (ret)
0498 cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_SLLC_ONLINE);
0499
0500 return ret;
0501 }
0502 module_init(hisi_sllc_pmu_module_init);
0503
0504 static void __exit hisi_sllc_pmu_module_exit(void)
0505 {
0506 platform_driver_unregister(&hisi_sllc_pmu_driver);
0507 cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_SLLC_ONLINE);
0508 }
0509 module_exit(hisi_sllc_pmu_module_exit);
0510
0511 MODULE_DESCRIPTION("HiSilicon SLLC uncore PMU driver");
0512 MODULE_LICENSE("GPL v2");
0513 MODULE_AUTHOR("Shaokun Zhang <zhangshaokun@hisilicon.com>");
0514 MODULE_AUTHOR("Qi Liu <liuqi115@huawei.com>");