Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * HiSilicon SLLC uncore Hardware event counters support
0004  *
0005  * Copyright (C) 2020 HiSilicon Limited
0006  * Author: Shaokun Zhang <zhangshaokun@hisilicon.com>
0007  *
0008  * This code is based on the uncore PMUs like arm-cci and arm-ccn.
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 /* SLLC register definition */
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         /* Enable the tgtid */
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         /* Disable the tgtid */
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         /* Enable the srcid */
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         /* Disable the srcid */
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      * Select the appropriate event select register(SLLC_EVENT_TYPE0/1).
0196      * There are 2 event select registers for the 8 hardware counters.
0197      * Event code is 8-bits and for the former 4 hardware counters,
0198      * SLLC_EVENT_TYPE0 is chosen. For the latter 4 hardware counters,
0199      * SLLC_EVENT_TYPE1 is chosen.
0200      */
0201     reg = SLLC_EVENT_TYPE0 + (idx / 4) * 4;
0202     reg_idx = idx % 4;
0203     shift = 8 * reg_idx;
0204 
0205     /* Write event code to SLLC_EVENT_TYPEx Register */
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     /* Write 0 to enable interrupt */
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     /* Write 1 to mask interrupt */
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      * Use the SCCL_ID and the index ID to identify the SLLC PMU,
0293      * while SCCL_ID is from MPIDR_EL1 by CPU.
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     /* SLLC PMUs only share the same SCCL */
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>");