Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /* Marvell CN10K LLC-TAD perf driver
0003  *
0004  * Copyright (C) 2021 Marvell
0005  */
0006 
0007 #define pr_fmt(fmt) "tad_pmu: " fmt
0008 
0009 #include <linux/module.h>
0010 #include <linux/of.h>
0011 #include <linux/of_address.h>
0012 #include <linux/of_device.h>
0013 #include <linux/cpuhotplug.h>
0014 #include <linux/perf_event.h>
0015 #include <linux/platform_device.h>
0016 
0017 #define TAD_PFC_OFFSET      0x800
0018 #define TAD_PFC(counter)    (TAD_PFC_OFFSET | (counter << 3))
0019 #define TAD_PRF_OFFSET      0x900
0020 #define TAD_PRF(counter)    (TAD_PRF_OFFSET | (counter << 3))
0021 #define TAD_PRF_CNTSEL_MASK 0xFF
0022 #define TAD_MAX_COUNTERS    8
0023 
0024 #define to_tad_pmu(p) (container_of(p, struct tad_pmu, pmu))
0025 
0026 struct tad_region {
0027     void __iomem    *base;
0028 };
0029 
0030 struct tad_pmu {
0031     struct pmu pmu;
0032     struct tad_region *regions;
0033     u32 region_cnt;
0034     unsigned int cpu;
0035     struct hlist_node node;
0036     struct perf_event *events[TAD_MAX_COUNTERS];
0037     DECLARE_BITMAP(counters_map, TAD_MAX_COUNTERS);
0038 };
0039 
0040 static int tad_pmu_cpuhp_state;
0041 
0042 static void tad_pmu_event_counter_read(struct perf_event *event)
0043 {
0044     struct tad_pmu *tad_pmu = to_tad_pmu(event->pmu);
0045     struct hw_perf_event *hwc = &event->hw;
0046     u32 counter_idx = hwc->idx;
0047     u64 prev, new;
0048     int i;
0049 
0050     do {
0051         prev = local64_read(&hwc->prev_count);
0052         for (i = 0, new = 0; i < tad_pmu->region_cnt; i++)
0053             new += readq(tad_pmu->regions[i].base +
0054                      TAD_PFC(counter_idx));
0055     } while (local64_cmpxchg(&hwc->prev_count, prev, new) != prev);
0056 
0057     local64_add(new - prev, &event->count);
0058 }
0059 
0060 static void tad_pmu_event_counter_stop(struct perf_event *event, int flags)
0061 {
0062     struct tad_pmu *tad_pmu = to_tad_pmu(event->pmu);
0063     struct hw_perf_event *hwc = &event->hw;
0064     u32 counter_idx = hwc->idx;
0065     int i;
0066 
0067     /* TAD()_PFC() stop counting on the write
0068      * which sets TAD()_PRF()[CNTSEL] == 0
0069      */
0070     for (i = 0; i < tad_pmu->region_cnt; i++) {
0071         writeq_relaxed(0, tad_pmu->regions[i].base +
0072                    TAD_PRF(counter_idx));
0073     }
0074 
0075     tad_pmu_event_counter_read(event);
0076     hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE;
0077 }
0078 
0079 static void tad_pmu_event_counter_start(struct perf_event *event, int flags)
0080 {
0081     struct tad_pmu *tad_pmu = to_tad_pmu(event->pmu);
0082     struct hw_perf_event *hwc = &event->hw;
0083     u32 event_idx = event->attr.config;
0084     u32 counter_idx = hwc->idx;
0085     u64 reg_val;
0086     int i;
0087 
0088     hwc->state = 0;
0089 
0090     /* Typically TAD_PFC() are zeroed to start counting */
0091     for (i = 0; i < tad_pmu->region_cnt; i++)
0092         writeq_relaxed(0, tad_pmu->regions[i].base +
0093                    TAD_PFC(counter_idx));
0094 
0095     /* TAD()_PFC() start counting on the write
0096      * which sets TAD()_PRF()[CNTSEL] != 0
0097      */
0098     for (i = 0; i < tad_pmu->region_cnt; i++) {
0099         reg_val = event_idx & 0xFF;
0100         writeq_relaxed(reg_val, tad_pmu->regions[i].base +
0101                    TAD_PRF(counter_idx));
0102     }
0103 }
0104 
0105 static void tad_pmu_event_counter_del(struct perf_event *event, int flags)
0106 {
0107     struct tad_pmu *tad_pmu = to_tad_pmu(event->pmu);
0108     struct hw_perf_event *hwc = &event->hw;
0109     int idx = hwc->idx;
0110 
0111     tad_pmu_event_counter_stop(event, flags | PERF_EF_UPDATE);
0112     tad_pmu->events[idx] = NULL;
0113     clear_bit(idx, tad_pmu->counters_map);
0114 }
0115 
0116 static int tad_pmu_event_counter_add(struct perf_event *event, int flags)
0117 {
0118     struct tad_pmu *tad_pmu = to_tad_pmu(event->pmu);
0119     struct hw_perf_event *hwc = &event->hw;
0120     int idx;
0121 
0122     /* Get a free counter for this event */
0123     idx = find_first_zero_bit(tad_pmu->counters_map, TAD_MAX_COUNTERS);
0124     if (idx == TAD_MAX_COUNTERS)
0125         return -EAGAIN;
0126 
0127     set_bit(idx, tad_pmu->counters_map);
0128 
0129     hwc->idx = idx;
0130     hwc->state = PERF_HES_STOPPED;
0131     tad_pmu->events[idx] = event;
0132 
0133     if (flags & PERF_EF_START)
0134         tad_pmu_event_counter_start(event, flags);
0135 
0136     return 0;
0137 }
0138 
0139 static int tad_pmu_event_init(struct perf_event *event)
0140 {
0141     struct tad_pmu *tad_pmu = to_tad_pmu(event->pmu);
0142 
0143     if (event->attr.type != event->pmu->type)
0144         return -ENOENT;
0145 
0146     if (!event->attr.disabled)
0147         return -EINVAL;
0148 
0149     if (event->state != PERF_EVENT_STATE_OFF)
0150         return -EINVAL;
0151 
0152     event->cpu = tad_pmu->cpu;
0153     event->hw.idx = -1;
0154     event->hw.config_base = event->attr.config;
0155 
0156     return 0;
0157 }
0158 
0159 static ssize_t tad_pmu_event_show(struct device *dev,
0160                 struct device_attribute *attr, char *page)
0161 {
0162     struct perf_pmu_events_attr *pmu_attr;
0163 
0164     pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr);
0165     return sysfs_emit(page, "event=0x%02llx\n", pmu_attr->id);
0166 }
0167 
0168 #define TAD_PMU_EVENT_ATTR(name, config)            \
0169     PMU_EVENT_ATTR_ID(name, tad_pmu_event_show, config)
0170 
0171 static struct attribute *tad_pmu_event_attrs[] = {
0172     TAD_PMU_EVENT_ATTR(tad_none, 0x0),
0173     TAD_PMU_EVENT_ATTR(tad_req_msh_in_any, 0x1),
0174     TAD_PMU_EVENT_ATTR(tad_req_msh_in_mn, 0x2),
0175     TAD_PMU_EVENT_ATTR(tad_req_msh_in_exlmn, 0x3),
0176     TAD_PMU_EVENT_ATTR(tad_rsp_msh_in_any, 0x4),
0177     TAD_PMU_EVENT_ATTR(tad_rsp_msh_in_mn, 0x5),
0178     TAD_PMU_EVENT_ATTR(tad_rsp_msh_in_exlmn, 0x6),
0179     TAD_PMU_EVENT_ATTR(tad_rsp_msh_in_dss, 0x7),
0180     TAD_PMU_EVENT_ATTR(tad_rsp_msh_in_retry_dss, 0x8),
0181     TAD_PMU_EVENT_ATTR(tad_dat_msh_in_any, 0x9),
0182     TAD_PMU_EVENT_ATTR(tad_dat_msh_in_dss, 0xa),
0183     TAD_PMU_EVENT_ATTR(tad_req_msh_out_any, 0xb),
0184     TAD_PMU_EVENT_ATTR(tad_req_msh_out_dss_rd, 0xc),
0185     TAD_PMU_EVENT_ATTR(tad_req_msh_out_dss_wr, 0xd),
0186     TAD_PMU_EVENT_ATTR(tad_req_msh_out_evict, 0xe),
0187     TAD_PMU_EVENT_ATTR(tad_rsp_msh_out_any, 0xf),
0188     TAD_PMU_EVENT_ATTR(tad_rsp_msh_out_retry_exlmn, 0x10),
0189     TAD_PMU_EVENT_ATTR(tad_rsp_msh_out_retry_mn, 0x11),
0190     TAD_PMU_EVENT_ATTR(tad_rsp_msh_out_exlmn, 0x12),
0191     TAD_PMU_EVENT_ATTR(tad_rsp_msh_out_mn, 0x13),
0192     TAD_PMU_EVENT_ATTR(tad_snp_msh_out_any, 0x14),
0193     TAD_PMU_EVENT_ATTR(tad_snp_msh_out_mn, 0x15),
0194     TAD_PMU_EVENT_ATTR(tad_snp_msh_out_exlmn, 0x16),
0195     TAD_PMU_EVENT_ATTR(tad_dat_msh_out_any, 0x17),
0196     TAD_PMU_EVENT_ATTR(tad_dat_msh_out_fill, 0x18),
0197     TAD_PMU_EVENT_ATTR(tad_dat_msh_out_dss, 0x19),
0198     TAD_PMU_EVENT_ATTR(tad_alloc_dtg, 0x1a),
0199     TAD_PMU_EVENT_ATTR(tad_alloc_ltg, 0x1b),
0200     TAD_PMU_EVENT_ATTR(tad_alloc_any, 0x1c),
0201     TAD_PMU_EVENT_ATTR(tad_hit_dtg, 0x1d),
0202     TAD_PMU_EVENT_ATTR(tad_hit_ltg, 0x1e),
0203     TAD_PMU_EVENT_ATTR(tad_hit_any, 0x1f),
0204     TAD_PMU_EVENT_ATTR(tad_tag_rd, 0x20),
0205     TAD_PMU_EVENT_ATTR(tad_dat_rd, 0x21),
0206     TAD_PMU_EVENT_ATTR(tad_dat_rd_byp, 0x22),
0207     TAD_PMU_EVENT_ATTR(tad_ifb_occ, 0x23),
0208     TAD_PMU_EVENT_ATTR(tad_req_occ, 0x24),
0209     NULL
0210 };
0211 
0212 static const struct attribute_group tad_pmu_events_attr_group = {
0213     .name = "events",
0214     .attrs = tad_pmu_event_attrs,
0215 };
0216 
0217 PMU_FORMAT_ATTR(event, "config:0-7");
0218 
0219 static struct attribute *tad_pmu_format_attrs[] = {
0220     &format_attr_event.attr,
0221     NULL
0222 };
0223 
0224 static struct attribute_group tad_pmu_format_attr_group = {
0225     .name = "format",
0226     .attrs = tad_pmu_format_attrs,
0227 };
0228 
0229 static ssize_t tad_pmu_cpumask_show(struct device *dev,
0230                 struct device_attribute *attr, char *buf)
0231 {
0232     struct tad_pmu *tad_pmu = to_tad_pmu(dev_get_drvdata(dev));
0233 
0234     return cpumap_print_to_pagebuf(true, buf, cpumask_of(tad_pmu->cpu));
0235 }
0236 
0237 static DEVICE_ATTR(cpumask, 0444, tad_pmu_cpumask_show, NULL);
0238 
0239 static struct attribute *tad_pmu_cpumask_attrs[] = {
0240     &dev_attr_cpumask.attr,
0241     NULL
0242 };
0243 
0244 static struct attribute_group tad_pmu_cpumask_attr_group = {
0245     .attrs = tad_pmu_cpumask_attrs,
0246 };
0247 
0248 static const struct attribute_group *tad_pmu_attr_groups[] = {
0249     &tad_pmu_events_attr_group,
0250     &tad_pmu_format_attr_group,
0251     &tad_pmu_cpumask_attr_group,
0252     NULL
0253 };
0254 
0255 static int tad_pmu_probe(struct platform_device *pdev)
0256 {
0257     struct device_node *node = pdev->dev.of_node;
0258     struct tad_region *regions;
0259     struct tad_pmu *tad_pmu;
0260     struct resource *res;
0261     u32 tad_pmu_page_size;
0262     u32 tad_page_size;
0263     u32 tad_cnt;
0264     int i, ret;
0265     char *name;
0266 
0267     tad_pmu = devm_kzalloc(&pdev->dev, sizeof(*tad_pmu), GFP_KERNEL);
0268     if (!tad_pmu)
0269         return -ENOMEM;
0270 
0271     platform_set_drvdata(pdev, tad_pmu);
0272 
0273     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0274     if (!res) {
0275         dev_err(&pdev->dev, "Mem resource not found\n");
0276         return -ENODEV;
0277     }
0278 
0279     ret = of_property_read_u32(node, "marvell,tad-page-size",
0280                    &tad_page_size);
0281     if (ret) {
0282         dev_err(&pdev->dev, "Can't find tad-page-size property\n");
0283         return ret;
0284     }
0285 
0286     ret = of_property_read_u32(node, "marvell,tad-pmu-page-size",
0287                    &tad_pmu_page_size);
0288     if (ret) {
0289         dev_err(&pdev->dev, "Can't find tad-pmu-page-size property\n");
0290         return ret;
0291     }
0292 
0293     ret = of_property_read_u32(node, "marvell,tad-cnt", &tad_cnt);
0294     if (ret) {
0295         dev_err(&pdev->dev, "Can't find tad-cnt property\n");
0296         return ret;
0297     }
0298 
0299     regions = devm_kcalloc(&pdev->dev, tad_cnt,
0300                    sizeof(*regions), GFP_KERNEL);
0301     if (!regions)
0302         return -ENOMEM;
0303 
0304     /* ioremap the distributed TAD pmu regions */
0305     for (i = 0; i < tad_cnt && res->start < res->end; i++) {
0306         regions[i].base = devm_ioremap(&pdev->dev,
0307                            res->start,
0308                            tad_pmu_page_size);
0309         if (!regions[i].base) {
0310             dev_err(&pdev->dev, "TAD%d ioremap fail\n", i);
0311             return -ENOMEM;
0312         }
0313         res->start += tad_page_size;
0314     }
0315 
0316     tad_pmu->regions = regions;
0317     tad_pmu->region_cnt = tad_cnt;
0318 
0319     tad_pmu->pmu = (struct pmu) {
0320 
0321         .module     = THIS_MODULE,
0322         .attr_groups    = tad_pmu_attr_groups,
0323         .capabilities   = PERF_PMU_CAP_NO_EXCLUDE |
0324                   PERF_PMU_CAP_NO_INTERRUPT,
0325         .task_ctx_nr    = perf_invalid_context,
0326 
0327         .event_init = tad_pmu_event_init,
0328         .add        = tad_pmu_event_counter_add,
0329         .del        = tad_pmu_event_counter_del,
0330         .start      = tad_pmu_event_counter_start,
0331         .stop       = tad_pmu_event_counter_stop,
0332         .read       = tad_pmu_event_counter_read,
0333     };
0334 
0335     tad_pmu->cpu = raw_smp_processor_id();
0336 
0337     /* Register pmu instance for cpu hotplug */
0338     ret = cpuhp_state_add_instance_nocalls(tad_pmu_cpuhp_state,
0339                            &tad_pmu->node);
0340     if (ret) {
0341         dev_err(&pdev->dev, "Error %d registering hotplug\n", ret);
0342         return ret;
0343     }
0344 
0345     name = "tad";
0346     ret = perf_pmu_register(&tad_pmu->pmu, name, -1);
0347     if (ret)
0348         cpuhp_state_remove_instance_nocalls(tad_pmu_cpuhp_state,
0349                             &tad_pmu->node);
0350 
0351     return ret;
0352 }
0353 
0354 static int tad_pmu_remove(struct platform_device *pdev)
0355 {
0356     struct tad_pmu *pmu = platform_get_drvdata(pdev);
0357 
0358     cpuhp_state_remove_instance_nocalls(tad_pmu_cpuhp_state,
0359                         &pmu->node);
0360     perf_pmu_unregister(&pmu->pmu);
0361 
0362     return 0;
0363 }
0364 
0365 #ifdef CONFIG_OF
0366 static const struct of_device_id tad_pmu_of_match[] = {
0367     { .compatible = "marvell,cn10k-tad-pmu", },
0368     {},
0369 };
0370 #endif
0371 
0372 static struct platform_driver tad_pmu_driver = {
0373     .driver         = {
0374         .name   = "cn10k_tad_pmu",
0375         .of_match_table = of_match_ptr(tad_pmu_of_match),
0376         .suppress_bind_attrs = true,
0377     },
0378     .probe          = tad_pmu_probe,
0379     .remove         = tad_pmu_remove,
0380 };
0381 
0382 static int tad_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node)
0383 {
0384     struct tad_pmu *pmu = hlist_entry_safe(node, struct tad_pmu, node);
0385     unsigned int target;
0386 
0387     if (cpu != pmu->cpu)
0388         return 0;
0389 
0390     target = cpumask_any_but(cpu_online_mask, cpu);
0391     if (target >= nr_cpu_ids)
0392         return 0;
0393 
0394     perf_pmu_migrate_context(&pmu->pmu, cpu, target);
0395     pmu->cpu = target;
0396 
0397     return 0;
0398 }
0399 
0400 static int __init tad_pmu_init(void)
0401 {
0402     int ret;
0403 
0404     ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN,
0405                       "perf/cn10k/tadpmu:online",
0406                       NULL,
0407                       tad_pmu_offline_cpu);
0408     if (ret < 0)
0409         return ret;
0410     tad_pmu_cpuhp_state = ret;
0411     return platform_driver_register(&tad_pmu_driver);
0412 }
0413 
0414 static void __exit tad_pmu_exit(void)
0415 {
0416     platform_driver_unregister(&tad_pmu_driver);
0417     cpuhp_remove_multi_state(tad_pmu_cpuhp_state);
0418 }
0419 
0420 module_init(tad_pmu_init);
0421 module_exit(tad_pmu_exit);
0422 
0423 MODULE_DESCRIPTION("Marvell CN10K LLC-TAD Perf driver");
0424 MODULE_AUTHOR("Bhaskara Budiredla <bbudiredla@marvell.com>");
0425 MODULE_LICENSE("GPL v2");