162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Marvell CN10K DRAM Subsystem (DSS) Performance Monitor Driver 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2021 Marvell. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/init.h> 862306a36Sopenharmony_ci#include <linux/io.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/of.h> 1162306a36Sopenharmony_ci#include <linux/perf_event.h> 1262306a36Sopenharmony_ci#include <linux/hrtimer.h> 1362306a36Sopenharmony_ci#include <linux/acpi.h> 1462306a36Sopenharmony_ci#include <linux/platform_device.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* Performance Counters Operating Mode Control Registers */ 1762306a36Sopenharmony_ci#define DDRC_PERF_CNT_OP_MODE_CTRL 0x8020 1862306a36Sopenharmony_ci#define OP_MODE_CTRL_VAL_MANNUAL 0x1 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* Performance Counters Start Operation Control Registers */ 2162306a36Sopenharmony_ci#define DDRC_PERF_CNT_START_OP_CTRL 0x8028 2262306a36Sopenharmony_ci#define START_OP_CTRL_VAL_START 0x1ULL 2362306a36Sopenharmony_ci#define START_OP_CTRL_VAL_ACTIVE 0x2 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* Performance Counters End Operation Control Registers */ 2662306a36Sopenharmony_ci#define DDRC_PERF_CNT_END_OP_CTRL 0x8030 2762306a36Sopenharmony_ci#define END_OP_CTRL_VAL_END 0x1ULL 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* Performance Counters End Status Registers */ 3062306a36Sopenharmony_ci#define DDRC_PERF_CNT_END_STATUS 0x8038 3162306a36Sopenharmony_ci#define END_STATUS_VAL_END_TIMER_MODE_END 0x1 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* Performance Counters Configuration Registers */ 3462306a36Sopenharmony_ci#define DDRC_PERF_CFG_BASE 0x8040 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* 8 Generic event counter + 2 fixed event counters */ 3762306a36Sopenharmony_ci#define DDRC_PERF_NUM_GEN_COUNTERS 8 3862306a36Sopenharmony_ci#define DDRC_PERF_NUM_FIX_COUNTERS 2 3962306a36Sopenharmony_ci#define DDRC_PERF_READ_COUNTER_IDX DDRC_PERF_NUM_GEN_COUNTERS 4062306a36Sopenharmony_ci#define DDRC_PERF_WRITE_COUNTER_IDX (DDRC_PERF_NUM_GEN_COUNTERS + 1) 4162306a36Sopenharmony_ci#define DDRC_PERF_NUM_COUNTERS (DDRC_PERF_NUM_GEN_COUNTERS + \ 4262306a36Sopenharmony_ci DDRC_PERF_NUM_FIX_COUNTERS) 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* Generic event counter registers */ 4562306a36Sopenharmony_ci#define DDRC_PERF_CFG(n) (DDRC_PERF_CFG_BASE + 8 * (n)) 4662306a36Sopenharmony_ci#define EVENT_ENABLE BIT_ULL(63) 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* Two dedicated event counters for DDR reads and writes */ 4962306a36Sopenharmony_ci#define EVENT_DDR_READS 101 5062306a36Sopenharmony_ci#define EVENT_DDR_WRITES 100 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* 5362306a36Sopenharmony_ci * programmable events IDs in programmable event counters. 5462306a36Sopenharmony_ci * DO NOT change these event-id numbers, they are used to 5562306a36Sopenharmony_ci * program event bitmap in h/w. 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_ci#define EVENT_OP_IS_ZQLATCH 55 5862306a36Sopenharmony_ci#define EVENT_OP_IS_ZQSTART 54 5962306a36Sopenharmony_ci#define EVENT_OP_IS_TCR_MRR 53 6062306a36Sopenharmony_ci#define EVENT_OP_IS_DQSOSC_MRR 52 6162306a36Sopenharmony_ci#define EVENT_OP_IS_DQSOSC_MPC 51 6262306a36Sopenharmony_ci#define EVENT_VISIBLE_WIN_LIMIT_REACHED_WR 50 6362306a36Sopenharmony_ci#define EVENT_VISIBLE_WIN_LIMIT_REACHED_RD 49 6462306a36Sopenharmony_ci#define EVENT_BSM_STARVATION 48 6562306a36Sopenharmony_ci#define EVENT_BSM_ALLOC 47 6662306a36Sopenharmony_ci#define EVENT_LPR_REQ_WITH_NOCREDIT 46 6762306a36Sopenharmony_ci#define EVENT_HPR_REQ_WITH_NOCREDIT 45 6862306a36Sopenharmony_ci#define EVENT_OP_IS_ZQCS 44 6962306a36Sopenharmony_ci#define EVENT_OP_IS_ZQCL 43 7062306a36Sopenharmony_ci#define EVENT_OP_IS_LOAD_MODE 42 7162306a36Sopenharmony_ci#define EVENT_OP_IS_SPEC_REF 41 7262306a36Sopenharmony_ci#define EVENT_OP_IS_CRIT_REF 40 7362306a36Sopenharmony_ci#define EVENT_OP_IS_REFRESH 39 7462306a36Sopenharmony_ci#define EVENT_OP_IS_ENTER_MPSM 35 7562306a36Sopenharmony_ci#define EVENT_OP_IS_ENTER_POWERDOWN 31 7662306a36Sopenharmony_ci#define EVENT_OP_IS_ENTER_SELFREF 27 7762306a36Sopenharmony_ci#define EVENT_WAW_HAZARD 26 7862306a36Sopenharmony_ci#define EVENT_RAW_HAZARD 25 7962306a36Sopenharmony_ci#define EVENT_WAR_HAZARD 24 8062306a36Sopenharmony_ci#define EVENT_WRITE_COMBINE 23 8162306a36Sopenharmony_ci#define EVENT_RDWR_TRANSITIONS 22 8262306a36Sopenharmony_ci#define EVENT_PRECHARGE_FOR_OTHER 21 8362306a36Sopenharmony_ci#define EVENT_PRECHARGE_FOR_RDWR 20 8462306a36Sopenharmony_ci#define EVENT_OP_IS_PRECHARGE 19 8562306a36Sopenharmony_ci#define EVENT_OP_IS_MWR 18 8662306a36Sopenharmony_ci#define EVENT_OP_IS_WR 17 8762306a36Sopenharmony_ci#define EVENT_OP_IS_RD 16 8862306a36Sopenharmony_ci#define EVENT_OP_IS_RD_ACTIVATE 15 8962306a36Sopenharmony_ci#define EVENT_OP_IS_RD_OR_WR 14 9062306a36Sopenharmony_ci#define EVENT_OP_IS_ACTIVATE 13 9162306a36Sopenharmony_ci#define EVENT_WR_XACT_WHEN_CRITICAL 12 9262306a36Sopenharmony_ci#define EVENT_LPR_XACT_WHEN_CRITICAL 11 9362306a36Sopenharmony_ci#define EVENT_HPR_XACT_WHEN_CRITICAL 10 9462306a36Sopenharmony_ci#define EVENT_DFI_RD_DATA_CYCLES 9 9562306a36Sopenharmony_ci#define EVENT_DFI_WR_DATA_CYCLES 8 9662306a36Sopenharmony_ci#define EVENT_ACT_BYPASS 7 9762306a36Sopenharmony_ci#define EVENT_READ_BYPASS 6 9862306a36Sopenharmony_ci#define EVENT_HIF_HI_PRI_RD 5 9962306a36Sopenharmony_ci#define EVENT_HIF_RMW 4 10062306a36Sopenharmony_ci#define EVENT_HIF_RD 3 10162306a36Sopenharmony_ci#define EVENT_HIF_WR 2 10262306a36Sopenharmony_ci#define EVENT_HIF_RD_OR_WR 1 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/* Event counter value registers */ 10562306a36Sopenharmony_ci#define DDRC_PERF_CNT_VALUE_BASE 0x8080 10662306a36Sopenharmony_ci#define DDRC_PERF_CNT_VALUE(n) (DDRC_PERF_CNT_VALUE_BASE + 8 * (n)) 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci/* Fixed event counter enable/disable register */ 10962306a36Sopenharmony_ci#define DDRC_PERF_CNT_FREERUN_EN 0x80C0 11062306a36Sopenharmony_ci#define DDRC_PERF_FREERUN_WRITE_EN 0x1 11162306a36Sopenharmony_ci#define DDRC_PERF_FREERUN_READ_EN 0x2 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* Fixed event counter control register */ 11462306a36Sopenharmony_ci#define DDRC_PERF_CNT_FREERUN_CTRL 0x80C8 11562306a36Sopenharmony_ci#define DDRC_FREERUN_WRITE_CNT_CLR 0x1 11662306a36Sopenharmony_ci#define DDRC_FREERUN_READ_CNT_CLR 0x2 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* Fixed event counter value register */ 11962306a36Sopenharmony_ci#define DDRC_PERF_CNT_VALUE_WR_OP 0x80D0 12062306a36Sopenharmony_ci#define DDRC_PERF_CNT_VALUE_RD_OP 0x80D8 12162306a36Sopenharmony_ci#define DDRC_PERF_CNT_VALUE_OVERFLOW BIT_ULL(48) 12262306a36Sopenharmony_ci#define DDRC_PERF_CNT_MAX_VALUE GENMASK_ULL(48, 0) 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistruct cn10k_ddr_pmu { 12562306a36Sopenharmony_ci struct pmu pmu; 12662306a36Sopenharmony_ci void __iomem *base; 12762306a36Sopenharmony_ci unsigned int cpu; 12862306a36Sopenharmony_ci struct device *dev; 12962306a36Sopenharmony_ci int active_events; 13062306a36Sopenharmony_ci struct perf_event *events[DDRC_PERF_NUM_COUNTERS]; 13162306a36Sopenharmony_ci struct hrtimer hrtimer; 13262306a36Sopenharmony_ci struct hlist_node node; 13362306a36Sopenharmony_ci}; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci#define to_cn10k_ddr_pmu(p) container_of(p, struct cn10k_ddr_pmu, pmu) 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic ssize_t cn10k_ddr_pmu_event_show(struct device *dev, 13862306a36Sopenharmony_ci struct device_attribute *attr, 13962306a36Sopenharmony_ci char *page) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci struct perf_pmu_events_attr *pmu_attr; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr); 14462306a36Sopenharmony_ci return sysfs_emit(page, "event=0x%02llx\n", pmu_attr->id); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci#define CN10K_DDR_PMU_EVENT_ATTR(_name, _id) \ 14962306a36Sopenharmony_ci PMU_EVENT_ATTR_ID(_name, cn10k_ddr_pmu_event_show, _id) 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic struct attribute *cn10k_ddr_perf_events_attrs[] = { 15262306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_hif_rd_or_wr_access, EVENT_HIF_RD_OR_WR), 15362306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_hif_wr_access, EVENT_HIF_WR), 15462306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_hif_rd_access, EVENT_HIF_RD), 15562306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_hif_rmw_access, EVENT_HIF_RMW), 15662306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_hif_pri_rdaccess, EVENT_HIF_HI_PRI_RD), 15762306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_rd_bypass_access, EVENT_READ_BYPASS), 15862306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_act_bypass_access, EVENT_ACT_BYPASS), 15962306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_dif_wr_data_access, EVENT_DFI_WR_DATA_CYCLES), 16062306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_dif_rd_data_access, EVENT_DFI_RD_DATA_CYCLES), 16162306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_hpri_sched_rd_crit_access, 16262306a36Sopenharmony_ci EVENT_HPR_XACT_WHEN_CRITICAL), 16362306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_lpri_sched_rd_crit_access, 16462306a36Sopenharmony_ci EVENT_LPR_XACT_WHEN_CRITICAL), 16562306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_wr_trxn_crit_access, 16662306a36Sopenharmony_ci EVENT_WR_XACT_WHEN_CRITICAL), 16762306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_cam_active_access, EVENT_OP_IS_ACTIVATE), 16862306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_cam_rd_or_wr_access, EVENT_OP_IS_RD_OR_WR), 16962306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_cam_rd_active_access, EVENT_OP_IS_RD_ACTIVATE), 17062306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_cam_read, EVENT_OP_IS_RD), 17162306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_cam_write, EVENT_OP_IS_WR), 17262306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_cam_mwr, EVENT_OP_IS_MWR), 17362306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_precharge, EVENT_OP_IS_PRECHARGE), 17462306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_precharge_for_rdwr, EVENT_PRECHARGE_FOR_RDWR), 17562306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_precharge_for_other, 17662306a36Sopenharmony_ci EVENT_PRECHARGE_FOR_OTHER), 17762306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_rdwr_transitions, EVENT_RDWR_TRANSITIONS), 17862306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_write_combine, EVENT_WRITE_COMBINE), 17962306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_war_hazard, EVENT_WAR_HAZARD), 18062306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_raw_hazard, EVENT_RAW_HAZARD), 18162306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_waw_hazard, EVENT_WAW_HAZARD), 18262306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_enter_selfref, EVENT_OP_IS_ENTER_SELFREF), 18362306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_enter_powerdown, EVENT_OP_IS_ENTER_POWERDOWN), 18462306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_enter_mpsm, EVENT_OP_IS_ENTER_MPSM), 18562306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_refresh, EVENT_OP_IS_REFRESH), 18662306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_crit_ref, EVENT_OP_IS_CRIT_REF), 18762306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_spec_ref, EVENT_OP_IS_SPEC_REF), 18862306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_load_mode, EVENT_OP_IS_LOAD_MODE), 18962306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_zqcl, EVENT_OP_IS_ZQCL), 19062306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_cam_wr_access, EVENT_OP_IS_ZQCS), 19162306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_hpr_req_with_nocredit, 19262306a36Sopenharmony_ci EVENT_HPR_REQ_WITH_NOCREDIT), 19362306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_lpr_req_with_nocredit, 19462306a36Sopenharmony_ci EVENT_LPR_REQ_WITH_NOCREDIT), 19562306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_bsm_alloc, EVENT_BSM_ALLOC), 19662306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_bsm_starvation, EVENT_BSM_STARVATION), 19762306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_win_limit_reached_rd, 19862306a36Sopenharmony_ci EVENT_VISIBLE_WIN_LIMIT_REACHED_RD), 19962306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_win_limit_reached_wr, 20062306a36Sopenharmony_ci EVENT_VISIBLE_WIN_LIMIT_REACHED_WR), 20162306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_dqsosc_mpc, EVENT_OP_IS_DQSOSC_MPC), 20262306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_dqsosc_mrr, EVENT_OP_IS_DQSOSC_MRR), 20362306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_tcr_mrr, EVENT_OP_IS_TCR_MRR), 20462306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_zqstart, EVENT_OP_IS_ZQSTART), 20562306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_zqlatch, EVENT_OP_IS_ZQLATCH), 20662306a36Sopenharmony_ci /* Free run event counters */ 20762306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_ddr_reads, EVENT_DDR_READS), 20862306a36Sopenharmony_ci CN10K_DDR_PMU_EVENT_ATTR(ddr_ddr_writes, EVENT_DDR_WRITES), 20962306a36Sopenharmony_ci NULL 21062306a36Sopenharmony_ci}; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic struct attribute_group cn10k_ddr_perf_events_attr_group = { 21362306a36Sopenharmony_ci .name = "events", 21462306a36Sopenharmony_ci .attrs = cn10k_ddr_perf_events_attrs, 21562306a36Sopenharmony_ci}; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ciPMU_FORMAT_ATTR(event, "config:0-8"); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic struct attribute *cn10k_ddr_perf_format_attrs[] = { 22062306a36Sopenharmony_ci &format_attr_event.attr, 22162306a36Sopenharmony_ci NULL, 22262306a36Sopenharmony_ci}; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic struct attribute_group cn10k_ddr_perf_format_attr_group = { 22562306a36Sopenharmony_ci .name = "format", 22662306a36Sopenharmony_ci .attrs = cn10k_ddr_perf_format_attrs, 22762306a36Sopenharmony_ci}; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic ssize_t cn10k_ddr_perf_cpumask_show(struct device *dev, 23062306a36Sopenharmony_ci struct device_attribute *attr, 23162306a36Sopenharmony_ci char *buf) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci struct cn10k_ddr_pmu *pmu = dev_get_drvdata(dev); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci return cpumap_print_to_pagebuf(true, buf, cpumask_of(pmu->cpu)); 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cistatic struct device_attribute cn10k_ddr_perf_cpumask_attr = 23962306a36Sopenharmony_ci __ATTR(cpumask, 0444, cn10k_ddr_perf_cpumask_show, NULL); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic struct attribute *cn10k_ddr_perf_cpumask_attrs[] = { 24262306a36Sopenharmony_ci &cn10k_ddr_perf_cpumask_attr.attr, 24362306a36Sopenharmony_ci NULL, 24462306a36Sopenharmony_ci}; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic struct attribute_group cn10k_ddr_perf_cpumask_attr_group = { 24762306a36Sopenharmony_ci .attrs = cn10k_ddr_perf_cpumask_attrs, 24862306a36Sopenharmony_ci}; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic const struct attribute_group *cn10k_attr_groups[] = { 25162306a36Sopenharmony_ci &cn10k_ddr_perf_events_attr_group, 25262306a36Sopenharmony_ci &cn10k_ddr_perf_format_attr_group, 25362306a36Sopenharmony_ci &cn10k_ddr_perf_cpumask_attr_group, 25462306a36Sopenharmony_ci NULL, 25562306a36Sopenharmony_ci}; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci/* Default poll timeout is 100 sec, which is very sufficient for 25862306a36Sopenharmony_ci * 48 bit counter incremented max at 5.6 GT/s, which may take many 25962306a36Sopenharmony_ci * hours to overflow. 26062306a36Sopenharmony_ci */ 26162306a36Sopenharmony_cistatic unsigned long cn10k_ddr_pmu_poll_period_sec = 100; 26262306a36Sopenharmony_cimodule_param_named(poll_period_sec, cn10k_ddr_pmu_poll_period_sec, ulong, 0644); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic ktime_t cn10k_ddr_pmu_timer_period(void) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci return ms_to_ktime((u64)cn10k_ddr_pmu_poll_period_sec * USEC_PER_SEC); 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic int ddr_perf_get_event_bitmap(int eventid, u64 *event_bitmap) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci switch (eventid) { 27262306a36Sopenharmony_ci case EVENT_HIF_RD_OR_WR ... EVENT_WAW_HAZARD: 27362306a36Sopenharmony_ci case EVENT_OP_IS_REFRESH ... EVENT_OP_IS_ZQLATCH: 27462306a36Sopenharmony_ci *event_bitmap = (1ULL << (eventid - 1)); 27562306a36Sopenharmony_ci break; 27662306a36Sopenharmony_ci case EVENT_OP_IS_ENTER_SELFREF: 27762306a36Sopenharmony_ci case EVENT_OP_IS_ENTER_POWERDOWN: 27862306a36Sopenharmony_ci case EVENT_OP_IS_ENTER_MPSM: 27962306a36Sopenharmony_ci *event_bitmap = (0xFULL << (eventid - 1)); 28062306a36Sopenharmony_ci break; 28162306a36Sopenharmony_ci default: 28262306a36Sopenharmony_ci pr_err("%s Invalid eventid %d\n", __func__, eventid); 28362306a36Sopenharmony_ci return -EINVAL; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci return 0; 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic int cn10k_ddr_perf_alloc_counter(struct cn10k_ddr_pmu *pmu, 29062306a36Sopenharmony_ci struct perf_event *event) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci u8 config = event->attr.config; 29362306a36Sopenharmony_ci int i; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci /* DDR read free-run counter index */ 29662306a36Sopenharmony_ci if (config == EVENT_DDR_READS) { 29762306a36Sopenharmony_ci pmu->events[DDRC_PERF_READ_COUNTER_IDX] = event; 29862306a36Sopenharmony_ci return DDRC_PERF_READ_COUNTER_IDX; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* DDR write free-run counter index */ 30262306a36Sopenharmony_ci if (config == EVENT_DDR_WRITES) { 30362306a36Sopenharmony_ci pmu->events[DDRC_PERF_WRITE_COUNTER_IDX] = event; 30462306a36Sopenharmony_ci return DDRC_PERF_WRITE_COUNTER_IDX; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci /* Allocate DDR generic counters */ 30862306a36Sopenharmony_ci for (i = 0; i < DDRC_PERF_NUM_GEN_COUNTERS; i++) { 30962306a36Sopenharmony_ci if (pmu->events[i] == NULL) { 31062306a36Sopenharmony_ci pmu->events[i] = event; 31162306a36Sopenharmony_ci return i; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci return -ENOENT; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic void cn10k_ddr_perf_free_counter(struct cn10k_ddr_pmu *pmu, int counter) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci pmu->events[counter] = NULL; 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic int cn10k_ddr_perf_event_init(struct perf_event *event) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci struct cn10k_ddr_pmu *pmu = to_cn10k_ddr_pmu(event->pmu); 32662306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (event->attr.type != event->pmu->type) 32962306a36Sopenharmony_ci return -ENOENT; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (is_sampling_event(event)) { 33262306a36Sopenharmony_ci dev_info(pmu->dev, "Sampling not supported!\n"); 33362306a36Sopenharmony_ci return -EOPNOTSUPP; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (event->cpu < 0) { 33762306a36Sopenharmony_ci dev_warn(pmu->dev, "Can't provide per-task data!\n"); 33862306a36Sopenharmony_ci return -EOPNOTSUPP; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* We must NOT create groups containing mixed PMUs */ 34262306a36Sopenharmony_ci if (event->group_leader->pmu != event->pmu && 34362306a36Sopenharmony_ci !is_software_event(event->group_leader)) 34462306a36Sopenharmony_ci return -EINVAL; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci /* Set ownership of event to one CPU, same event can not be observed 34762306a36Sopenharmony_ci * on multiple cpus at same time. 34862306a36Sopenharmony_ci */ 34962306a36Sopenharmony_ci event->cpu = pmu->cpu; 35062306a36Sopenharmony_ci hwc->idx = -1; 35162306a36Sopenharmony_ci return 0; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic void cn10k_ddr_perf_counter_enable(struct cn10k_ddr_pmu *pmu, 35562306a36Sopenharmony_ci int counter, bool enable) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci u32 reg; 35862306a36Sopenharmony_ci u64 val; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (counter > DDRC_PERF_NUM_COUNTERS) { 36162306a36Sopenharmony_ci pr_err("Error: unsupported counter %d\n", counter); 36262306a36Sopenharmony_ci return; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (counter < DDRC_PERF_NUM_GEN_COUNTERS) { 36662306a36Sopenharmony_ci reg = DDRC_PERF_CFG(counter); 36762306a36Sopenharmony_ci val = readq_relaxed(pmu->base + reg); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (enable) 37062306a36Sopenharmony_ci val |= EVENT_ENABLE; 37162306a36Sopenharmony_ci else 37262306a36Sopenharmony_ci val &= ~EVENT_ENABLE; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci writeq_relaxed(val, pmu->base + reg); 37562306a36Sopenharmony_ci } else { 37662306a36Sopenharmony_ci val = readq_relaxed(pmu->base + DDRC_PERF_CNT_FREERUN_EN); 37762306a36Sopenharmony_ci if (enable) { 37862306a36Sopenharmony_ci if (counter == DDRC_PERF_READ_COUNTER_IDX) 37962306a36Sopenharmony_ci val |= DDRC_PERF_FREERUN_READ_EN; 38062306a36Sopenharmony_ci else 38162306a36Sopenharmony_ci val |= DDRC_PERF_FREERUN_WRITE_EN; 38262306a36Sopenharmony_ci } else { 38362306a36Sopenharmony_ci if (counter == DDRC_PERF_READ_COUNTER_IDX) 38462306a36Sopenharmony_ci val &= ~DDRC_PERF_FREERUN_READ_EN; 38562306a36Sopenharmony_ci else 38662306a36Sopenharmony_ci val &= ~DDRC_PERF_FREERUN_WRITE_EN; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci writeq_relaxed(val, pmu->base + DDRC_PERF_CNT_FREERUN_EN); 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic u64 cn10k_ddr_perf_read_counter(struct cn10k_ddr_pmu *pmu, int counter) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci u64 val; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci if (counter == DDRC_PERF_READ_COUNTER_IDX) 39762306a36Sopenharmony_ci return readq_relaxed(pmu->base + DDRC_PERF_CNT_VALUE_RD_OP); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (counter == DDRC_PERF_WRITE_COUNTER_IDX) 40062306a36Sopenharmony_ci return readq_relaxed(pmu->base + DDRC_PERF_CNT_VALUE_WR_OP); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci val = readq_relaxed(pmu->base + DDRC_PERF_CNT_VALUE(counter)); 40362306a36Sopenharmony_ci return val; 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic void cn10k_ddr_perf_event_update(struct perf_event *event) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci struct cn10k_ddr_pmu *pmu = to_cn10k_ddr_pmu(event->pmu); 40962306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 41062306a36Sopenharmony_ci u64 prev_count, new_count, mask; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci do { 41362306a36Sopenharmony_ci prev_count = local64_read(&hwc->prev_count); 41462306a36Sopenharmony_ci new_count = cn10k_ddr_perf_read_counter(pmu, hwc->idx); 41562306a36Sopenharmony_ci } while (local64_xchg(&hwc->prev_count, new_count) != prev_count); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci mask = DDRC_PERF_CNT_MAX_VALUE; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci local64_add((new_count - prev_count) & mask, &event->count); 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cistatic void cn10k_ddr_perf_event_start(struct perf_event *event, int flags) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci struct cn10k_ddr_pmu *pmu = to_cn10k_ddr_pmu(event->pmu); 42562306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 42662306a36Sopenharmony_ci int counter = hwc->idx; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci local64_set(&hwc->prev_count, 0); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci cn10k_ddr_perf_counter_enable(pmu, counter, true); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci hwc->state = 0; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic int cn10k_ddr_perf_event_add(struct perf_event *event, int flags) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci struct cn10k_ddr_pmu *pmu = to_cn10k_ddr_pmu(event->pmu); 43862306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 43962306a36Sopenharmony_ci u8 config = event->attr.config; 44062306a36Sopenharmony_ci int counter, ret; 44162306a36Sopenharmony_ci u32 reg_offset; 44262306a36Sopenharmony_ci u64 val; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci counter = cn10k_ddr_perf_alloc_counter(pmu, event); 44562306a36Sopenharmony_ci if (counter < 0) 44662306a36Sopenharmony_ci return -EAGAIN; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci pmu->active_events++; 44962306a36Sopenharmony_ci hwc->idx = counter; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci if (pmu->active_events == 1) 45262306a36Sopenharmony_ci hrtimer_start(&pmu->hrtimer, cn10k_ddr_pmu_timer_period(), 45362306a36Sopenharmony_ci HRTIMER_MODE_REL_PINNED); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci if (counter < DDRC_PERF_NUM_GEN_COUNTERS) { 45662306a36Sopenharmony_ci /* Generic counters, configure event id */ 45762306a36Sopenharmony_ci reg_offset = DDRC_PERF_CFG(counter); 45862306a36Sopenharmony_ci ret = ddr_perf_get_event_bitmap(config, &val); 45962306a36Sopenharmony_ci if (ret) 46062306a36Sopenharmony_ci return ret; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci writeq_relaxed(val, pmu->base + reg_offset); 46362306a36Sopenharmony_ci } else { 46462306a36Sopenharmony_ci /* fixed event counter, clear counter value */ 46562306a36Sopenharmony_ci if (counter == DDRC_PERF_READ_COUNTER_IDX) 46662306a36Sopenharmony_ci val = DDRC_FREERUN_READ_CNT_CLR; 46762306a36Sopenharmony_ci else 46862306a36Sopenharmony_ci val = DDRC_FREERUN_WRITE_CNT_CLR; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci writeq_relaxed(val, pmu->base + DDRC_PERF_CNT_FREERUN_CTRL); 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci hwc->state |= PERF_HES_STOPPED; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci if (flags & PERF_EF_START) 47662306a36Sopenharmony_ci cn10k_ddr_perf_event_start(event, flags); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci return 0; 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic void cn10k_ddr_perf_event_stop(struct perf_event *event, int flags) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci struct cn10k_ddr_pmu *pmu = to_cn10k_ddr_pmu(event->pmu); 48462306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 48562306a36Sopenharmony_ci int counter = hwc->idx; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci cn10k_ddr_perf_counter_enable(pmu, counter, false); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci if (flags & PERF_EF_UPDATE) 49062306a36Sopenharmony_ci cn10k_ddr_perf_event_update(event); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci hwc->state |= PERF_HES_STOPPED; 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic void cn10k_ddr_perf_event_del(struct perf_event *event, int flags) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci struct cn10k_ddr_pmu *pmu = to_cn10k_ddr_pmu(event->pmu); 49862306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 49962306a36Sopenharmony_ci int counter = hwc->idx; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci cn10k_ddr_perf_event_stop(event, PERF_EF_UPDATE); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci cn10k_ddr_perf_free_counter(pmu, counter); 50462306a36Sopenharmony_ci pmu->active_events--; 50562306a36Sopenharmony_ci hwc->idx = -1; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci /* Cancel timer when no events to capture */ 50862306a36Sopenharmony_ci if (pmu->active_events == 0) 50962306a36Sopenharmony_ci hrtimer_cancel(&pmu->hrtimer); 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_cistatic void cn10k_ddr_perf_pmu_enable(struct pmu *pmu) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci struct cn10k_ddr_pmu *ddr_pmu = to_cn10k_ddr_pmu(pmu); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci writeq_relaxed(START_OP_CTRL_VAL_START, ddr_pmu->base + 51762306a36Sopenharmony_ci DDRC_PERF_CNT_START_OP_CTRL); 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_cistatic void cn10k_ddr_perf_pmu_disable(struct pmu *pmu) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci struct cn10k_ddr_pmu *ddr_pmu = to_cn10k_ddr_pmu(pmu); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci writeq_relaxed(END_OP_CTRL_VAL_END, ddr_pmu->base + 52562306a36Sopenharmony_ci DDRC_PERF_CNT_END_OP_CTRL); 52662306a36Sopenharmony_ci} 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_cistatic void cn10k_ddr_perf_event_update_all(struct cn10k_ddr_pmu *pmu) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci struct hw_perf_event *hwc; 53162306a36Sopenharmony_ci int i; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci for (i = 0; i < DDRC_PERF_NUM_GEN_COUNTERS; i++) { 53462306a36Sopenharmony_ci if (pmu->events[i] == NULL) 53562306a36Sopenharmony_ci continue; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci cn10k_ddr_perf_event_update(pmu->events[i]); 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci /* Reset previous count as h/w counter are reset */ 54162306a36Sopenharmony_ci for (i = 0; i < DDRC_PERF_NUM_GEN_COUNTERS; i++) { 54262306a36Sopenharmony_ci if (pmu->events[i] == NULL) 54362306a36Sopenharmony_ci continue; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci hwc = &pmu->events[i]->hw; 54662306a36Sopenharmony_ci local64_set(&hwc->prev_count, 0); 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_cistatic irqreturn_t cn10k_ddr_pmu_overflow_handler(struct cn10k_ddr_pmu *pmu) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci struct perf_event *event; 55362306a36Sopenharmony_ci struct hw_perf_event *hwc; 55462306a36Sopenharmony_ci u64 prev_count, new_count; 55562306a36Sopenharmony_ci u64 value; 55662306a36Sopenharmony_ci int i; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci event = pmu->events[DDRC_PERF_READ_COUNTER_IDX]; 55962306a36Sopenharmony_ci if (event) { 56062306a36Sopenharmony_ci hwc = &event->hw; 56162306a36Sopenharmony_ci prev_count = local64_read(&hwc->prev_count); 56262306a36Sopenharmony_ci new_count = cn10k_ddr_perf_read_counter(pmu, hwc->idx); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci /* Overflow condition is when new count less than 56562306a36Sopenharmony_ci * previous count 56662306a36Sopenharmony_ci */ 56762306a36Sopenharmony_ci if (new_count < prev_count) 56862306a36Sopenharmony_ci cn10k_ddr_perf_event_update(event); 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci event = pmu->events[DDRC_PERF_WRITE_COUNTER_IDX]; 57262306a36Sopenharmony_ci if (event) { 57362306a36Sopenharmony_ci hwc = &event->hw; 57462306a36Sopenharmony_ci prev_count = local64_read(&hwc->prev_count); 57562306a36Sopenharmony_ci new_count = cn10k_ddr_perf_read_counter(pmu, hwc->idx); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci /* Overflow condition is when new count less than 57862306a36Sopenharmony_ci * previous count 57962306a36Sopenharmony_ci */ 58062306a36Sopenharmony_ci if (new_count < prev_count) 58162306a36Sopenharmony_ci cn10k_ddr_perf_event_update(event); 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci for (i = 0; i < DDRC_PERF_NUM_GEN_COUNTERS; i++) { 58562306a36Sopenharmony_ci if (pmu->events[i] == NULL) 58662306a36Sopenharmony_ci continue; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci value = cn10k_ddr_perf_read_counter(pmu, i); 58962306a36Sopenharmony_ci if (value == DDRC_PERF_CNT_MAX_VALUE) { 59062306a36Sopenharmony_ci pr_info("Counter-(%d) reached max value\n", i); 59162306a36Sopenharmony_ci cn10k_ddr_perf_event_update_all(pmu); 59262306a36Sopenharmony_ci cn10k_ddr_perf_pmu_disable(&pmu->pmu); 59362306a36Sopenharmony_ci cn10k_ddr_perf_pmu_enable(&pmu->pmu); 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci return IRQ_HANDLED; 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_cistatic enum hrtimer_restart cn10k_ddr_pmu_timer_handler(struct hrtimer *hrtimer) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci struct cn10k_ddr_pmu *pmu = container_of(hrtimer, struct cn10k_ddr_pmu, 60362306a36Sopenharmony_ci hrtimer); 60462306a36Sopenharmony_ci unsigned long flags; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci local_irq_save(flags); 60762306a36Sopenharmony_ci cn10k_ddr_pmu_overflow_handler(pmu); 60862306a36Sopenharmony_ci local_irq_restore(flags); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci hrtimer_forward_now(hrtimer, cn10k_ddr_pmu_timer_period()); 61162306a36Sopenharmony_ci return HRTIMER_RESTART; 61262306a36Sopenharmony_ci} 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_cistatic int cn10k_ddr_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci struct cn10k_ddr_pmu *pmu = hlist_entry_safe(node, struct cn10k_ddr_pmu, 61762306a36Sopenharmony_ci node); 61862306a36Sopenharmony_ci unsigned int target; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci if (cpu != pmu->cpu) 62162306a36Sopenharmony_ci return 0; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci target = cpumask_any_but(cpu_online_mask, cpu); 62462306a36Sopenharmony_ci if (target >= nr_cpu_ids) 62562306a36Sopenharmony_ci return 0; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci perf_pmu_migrate_context(&pmu->pmu, cpu, target); 62862306a36Sopenharmony_ci pmu->cpu = target; 62962306a36Sopenharmony_ci return 0; 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_cistatic int cn10k_ddr_perf_probe(struct platform_device *pdev) 63362306a36Sopenharmony_ci{ 63462306a36Sopenharmony_ci struct cn10k_ddr_pmu *ddr_pmu; 63562306a36Sopenharmony_ci struct resource *res; 63662306a36Sopenharmony_ci void __iomem *base; 63762306a36Sopenharmony_ci char *name; 63862306a36Sopenharmony_ci int ret; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci ddr_pmu = devm_kzalloc(&pdev->dev, sizeof(*ddr_pmu), GFP_KERNEL); 64162306a36Sopenharmony_ci if (!ddr_pmu) 64262306a36Sopenharmony_ci return -ENOMEM; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci ddr_pmu->dev = &pdev->dev; 64562306a36Sopenharmony_ci platform_set_drvdata(pdev, ddr_pmu); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 64862306a36Sopenharmony_ci if (IS_ERR(base)) 64962306a36Sopenharmony_ci return PTR_ERR(base); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci ddr_pmu->base = base; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci /* Setup the PMU counter to work in manual mode */ 65462306a36Sopenharmony_ci writeq_relaxed(OP_MODE_CTRL_VAL_MANNUAL, ddr_pmu->base + 65562306a36Sopenharmony_ci DDRC_PERF_CNT_OP_MODE_CTRL); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci ddr_pmu->pmu = (struct pmu) { 65862306a36Sopenharmony_ci .module = THIS_MODULE, 65962306a36Sopenharmony_ci .capabilities = PERF_PMU_CAP_NO_EXCLUDE, 66062306a36Sopenharmony_ci .task_ctx_nr = perf_invalid_context, 66162306a36Sopenharmony_ci .attr_groups = cn10k_attr_groups, 66262306a36Sopenharmony_ci .event_init = cn10k_ddr_perf_event_init, 66362306a36Sopenharmony_ci .add = cn10k_ddr_perf_event_add, 66462306a36Sopenharmony_ci .del = cn10k_ddr_perf_event_del, 66562306a36Sopenharmony_ci .start = cn10k_ddr_perf_event_start, 66662306a36Sopenharmony_ci .stop = cn10k_ddr_perf_event_stop, 66762306a36Sopenharmony_ci .read = cn10k_ddr_perf_event_update, 66862306a36Sopenharmony_ci .pmu_enable = cn10k_ddr_perf_pmu_enable, 66962306a36Sopenharmony_ci .pmu_disable = cn10k_ddr_perf_pmu_disable, 67062306a36Sopenharmony_ci }; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci /* Choose this cpu to collect perf data */ 67362306a36Sopenharmony_ci ddr_pmu->cpu = raw_smp_processor_id(); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci name = devm_kasprintf(ddr_pmu->dev, GFP_KERNEL, "mrvl_ddr_pmu_%llx", 67662306a36Sopenharmony_ci res->start); 67762306a36Sopenharmony_ci if (!name) 67862306a36Sopenharmony_ci return -ENOMEM; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci hrtimer_init(&ddr_pmu->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); 68162306a36Sopenharmony_ci ddr_pmu->hrtimer.function = cn10k_ddr_pmu_timer_handler; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci cpuhp_state_add_instance_nocalls( 68462306a36Sopenharmony_ci CPUHP_AP_PERF_ARM_MARVELL_CN10K_DDR_ONLINE, 68562306a36Sopenharmony_ci &ddr_pmu->node); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci ret = perf_pmu_register(&ddr_pmu->pmu, name, -1); 68862306a36Sopenharmony_ci if (ret) 68962306a36Sopenharmony_ci goto error; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci pr_info("CN10K DDR PMU Driver for ddrc@%llx\n", res->start); 69262306a36Sopenharmony_ci return 0; 69362306a36Sopenharmony_cierror: 69462306a36Sopenharmony_ci cpuhp_state_remove_instance_nocalls( 69562306a36Sopenharmony_ci CPUHP_AP_PERF_ARM_MARVELL_CN10K_DDR_ONLINE, 69662306a36Sopenharmony_ci &ddr_pmu->node); 69762306a36Sopenharmony_ci return ret; 69862306a36Sopenharmony_ci} 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_cistatic int cn10k_ddr_perf_remove(struct platform_device *pdev) 70162306a36Sopenharmony_ci{ 70262306a36Sopenharmony_ci struct cn10k_ddr_pmu *ddr_pmu = platform_get_drvdata(pdev); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci cpuhp_state_remove_instance_nocalls( 70562306a36Sopenharmony_ci CPUHP_AP_PERF_ARM_MARVELL_CN10K_DDR_ONLINE, 70662306a36Sopenharmony_ci &ddr_pmu->node); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci perf_pmu_unregister(&ddr_pmu->pmu); 70962306a36Sopenharmony_ci return 0; 71062306a36Sopenharmony_ci} 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci#ifdef CONFIG_OF 71362306a36Sopenharmony_cistatic const struct of_device_id cn10k_ddr_pmu_of_match[] = { 71462306a36Sopenharmony_ci { .compatible = "marvell,cn10k-ddr-pmu", }, 71562306a36Sopenharmony_ci { }, 71662306a36Sopenharmony_ci}; 71762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, cn10k_ddr_pmu_of_match); 71862306a36Sopenharmony_ci#endif 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci#ifdef CONFIG_ACPI 72162306a36Sopenharmony_cistatic const struct acpi_device_id cn10k_ddr_pmu_acpi_match[] = { 72262306a36Sopenharmony_ci {"MRVL000A", 0}, 72362306a36Sopenharmony_ci {}, 72462306a36Sopenharmony_ci}; 72562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, cn10k_ddr_pmu_acpi_match); 72662306a36Sopenharmony_ci#endif 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_cistatic struct platform_driver cn10k_ddr_pmu_driver = { 72962306a36Sopenharmony_ci .driver = { 73062306a36Sopenharmony_ci .name = "cn10k-ddr-pmu", 73162306a36Sopenharmony_ci .of_match_table = of_match_ptr(cn10k_ddr_pmu_of_match), 73262306a36Sopenharmony_ci .acpi_match_table = ACPI_PTR(cn10k_ddr_pmu_acpi_match), 73362306a36Sopenharmony_ci .suppress_bind_attrs = true, 73462306a36Sopenharmony_ci }, 73562306a36Sopenharmony_ci .probe = cn10k_ddr_perf_probe, 73662306a36Sopenharmony_ci .remove = cn10k_ddr_perf_remove, 73762306a36Sopenharmony_ci}; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_cistatic int __init cn10k_ddr_pmu_init(void) 74062306a36Sopenharmony_ci{ 74162306a36Sopenharmony_ci int ret; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci ret = cpuhp_setup_state_multi( 74462306a36Sopenharmony_ci CPUHP_AP_PERF_ARM_MARVELL_CN10K_DDR_ONLINE, 74562306a36Sopenharmony_ci "perf/marvell/cn10k/ddr:online", NULL, 74662306a36Sopenharmony_ci cn10k_ddr_pmu_offline_cpu); 74762306a36Sopenharmony_ci if (ret) 74862306a36Sopenharmony_ci return ret; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci ret = platform_driver_register(&cn10k_ddr_pmu_driver); 75162306a36Sopenharmony_ci if (ret) 75262306a36Sopenharmony_ci cpuhp_remove_multi_state( 75362306a36Sopenharmony_ci CPUHP_AP_PERF_ARM_MARVELL_CN10K_DDR_ONLINE); 75462306a36Sopenharmony_ci return ret; 75562306a36Sopenharmony_ci} 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_cistatic void __exit cn10k_ddr_pmu_exit(void) 75862306a36Sopenharmony_ci{ 75962306a36Sopenharmony_ci platform_driver_unregister(&cn10k_ddr_pmu_driver); 76062306a36Sopenharmony_ci cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_MARVELL_CN10K_DDR_ONLINE); 76162306a36Sopenharmony_ci} 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_cimodule_init(cn10k_ddr_pmu_init); 76462306a36Sopenharmony_cimodule_exit(cn10k_ddr_pmu_exit); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ciMODULE_AUTHOR("Bharat Bhushan <bbhushan2@marvell.com>"); 76762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 768