0001
0002
0003
0004
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
0068
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
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
0096
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
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
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
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");