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