From 4c2cedeb0423a417c2846558345773fb625afdd1 Mon Sep 17 00:00:00 2001 From: Sankaran Nampoothiri Date: Tue, 27 Jun 2017 17:41:29 +0530 Subject: [PATCH] drivers: llcc_perfmon: Add llcc_perfmon support This is snapshot of the llcc_perfmon driver from msm-4.14 'commit c9961d61d6d8 ("drivers: soc: llcc_perfmon: Add llcc_perfmon support")' This LLCC perfmon driver provides Sysfs interfaces for the clients to monitor the events. Driver support for monitor events from LLCC sub blocks. Also updated driver to handle opcode & cache alloc filters. Change-Id: Ic711fdbc774d2da1938feff5c0d7888e5cf8495a Signed-off-by: Sankaran Nampoothiri Signed-off-by: Avinash Philip --- drivers/soc/qcom/Kconfig | 10 + drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/llcc_events.h | 296 ++++++++ drivers/soc/qcom/llcc_perfmon.c | 1251 +++++++++++++++++++++++++++++++ drivers/soc/qcom/llcc_perfmon.h | 270 +++++++ 5 files changed, 1828 insertions(+) create mode 100644 drivers/soc/qcom/llcc_events.h create mode 100644 drivers/soc/qcom/llcc_perfmon.c create mode 100644 drivers/soc/qcom/llcc_perfmon.h diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index cc497ebd393e..7a3a645412a1 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -90,6 +90,16 @@ config QCOM_SDM845_LLCC data required to configure LLCC so that clients can start using the LLCC slices. +config QCOM_LLCC_PERFMON + tristate "Qualcomm Technologies, Inc. LLCC Perfmon driver" + depends on QCOM_LLCC + help + This option enables driver for LLCC Performance monitor block. Using + this various events in different LLCC sub ports can be monitored. + This is used for performance and debug activity and exports sysfs + interface. sysfs interface is used to configure and dump the LLCC + performance events. + config QCOM_MDT_LOADER tristate select QCOM_SCM diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 7142bff8dc36..0e034f1129ea 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_QCOM_APR) += apr.o obj-$(CONFIG_QCOM_LLCC) += llcc-slice.o obj-$(CONFIG_QCOM_LAHAINA_LLCC) += llcc-lahaina.o obj-$(CONFIG_QCOM_SDM845_LLCC) += llcc-sdm845.o +obj-$(CONFIG_QCOM_LLCC_PERFMON) += llcc_perfmon.o obj-$(CONFIG_QCOM_RPMHPD) += rpmhpd.o obj-$(CONFIG_QCOM_RPMPD) += rpmpd.o obj-$(CONFIG_QMP_DEBUGFS_CLIENT) += qmp-debugfs-client.o diff --git a/drivers/soc/qcom/llcc_events.h b/drivers/soc/qcom/llcc_events.h new file mode 100644 index 000000000000..1c37cd04e42a --- /dev/null +++ b/drivers/soc/qcom/llcc_events.h @@ -0,0 +1,296 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2019, The Linux Foundation. All rights reserved. + */ + +#ifndef _SOC_QCOM_LLCC_EVENTS_H_ +#define _SOC_QCOM_LLCC_EVENTS_H_ + +enum event_port_select { + EVENT_PORT_FEAC, + EVENT_PORT_FERC, + EVENT_PORT_FEWC, + EVENT_PORT_BEAC, + EVENT_PORT_BERC, + EVENT_PORT_TRP, + EVENT_PORT_DRP, + EVENT_PORT_PMGR, + EVENT_PORT_TENURE, + EVENT_PORT_TLAT, +}; + +enum feac_events { + FEAC_ANY_ACCESS, + FEAC_READ_INCR, + FEAC_WRITE_INCR, + FEAC_WRITE_ORDERED, + FEAC_READE_EXCL, + FEAC_WRITE_EXCL, + FEAC_CMO, + FEAC_CMO_CLEAN, + FEAC_CMO_INVAL, + FEAC_CMO_CLEANINVAL, + FEAC_CMO_DCPLD, + FEAC_READ_NOALLOC, + FEAC_WRITE_NOALLOC, + FEAC_PREFETCH, + FEAC_RD_BYTES, + FEAC_RD_BEATS, + FEAC_WR_BYTES, + FEAC_WR_BEATS, + FEAC_FC_READ, + FEAC_EWD_ACCESS, + FEAC_TCM_ACCESS, + FEAC_GM_HIT, + FEAC_GM_MISS, + FEAC_GM_UNAVAILABLE, + FEAC_XPU_ERROR, + FEAC_READ_HAZARD, + FEAC_WRITE_HAZARD, + FEAC_GRANULE_READ, + FEAC_GRANULE_WRITE, + FEAC_RIFB_ALLOC, + FEAC_WIFB_ALLOC, + FEAC_RIFB_DEALLOC, + FEAC_WIFB_DEALLOC, + FEAC_RESERVED, + FEAC_RESERVED1, + FEAC_FEAC2TRP_LP_TX, + FEAC_TRP_LP_BUSY, + FEAC_FEAC2TRP_HP_TX, + FEAC_TRP_HP_BUSY, + FEAC_FEAC2FEWC_TX, + FEAC_BEAC_LP_BUSY, + FEAC_BEAC_HP_BUSY, + FEAC_RIFB_FULL, + FEAC_WIFB_FULL, + FEAC_RD_CRDT_TX, + FEAC_WR_CRDT_TX, + FEAC_PROMOTION, + FEAC_FEAC2TRP_LP_PRESSURE, + FEAC_FEAC2TRP_HP_PRESSURE, + FEAC_FEAC2FEWC_PRESSURE, + FEAC_FEAC2BEAC_LP_PRESSURE, + FEAC_FEAC2BEAC_HP_PRESSURE, + FEAC_WR_THROUGH, +}; + +enum ferc_events { + FERC_BERC_CMD, + FERC_BERC_BEAT, + FERC_DRP_CMD, + FERC_DRP_BEAT, + FERC_RD_CTRL_RSP_TX, + FERC_WR_CTRL_RSP_TX, + FERC_RD_DATA_TX, + FERC_MISS_TRUMPS_HIT, + FERC_HIT_TRUMPS_WRSP, + FERC_RD_INTRA_RSP_IDLE, +}; + +enum fewc_events { + FEWC_WR_CMD, + FEWC_WR_DATA_BEAT, + FEWC_WR_LAST, + FEWC_WBUF_DEALLOC, + FEWC_WR_HIT, + FEWC_WR_MISS, + FEWC_NC_RMW, + FEWC_WR_DOWNGRADE, + FEWC_BEAC_WR_CMD, + FEWC_BEAC_WR_BEAT, + FEWC_BEAC_RD_CMD, + FEWC_BERC_FILL_BEAT, + FEWC_DRP_WR_CMD, + FEWC_DRP_WR_BEAT, + FEWC_DRP_RD_BEAT, + FEWC_TRP_TAG_LOOKUP, + FEWC_TRP_TAG_UPDATE, + FEWC_TRP_UNSTALL, + FEWC_WBUFFS_FULL, + FEWC_DRP_BUSY, + FEWC_BEAC_WR_BUSY, + FEWC_BEAC_RD_BUSY, + FEWC_TRP_TAG_LOOKUP_BUSY, + FEWC_TRP_TAG_UPDATE_BUSY, + FEWC_C_RMW, + FEWC_NC_ALLOC_RMW, + FEWC_NC_NO_ALLOC_RMW, + FEWC_NC_RMW_DEALLOC, + FEWC_C_RMW_DEALLOC, + FEWC_STALLED_BY_EVICT, +}; + +enum beac_events { + BEAC_RD_TX, + BEAC_WR_TX, + BEAC_RD_GRANULE, + BEAC_WR_GRANULE, + BEAC_WR_BEAT_TX, + BEAC_RD_CRDT_ZERO, + BEAC_WR_CRDT_ZERO, + BEAC_WDATA_CRDT_ZERO, + BEAC_IFCMD_CRDT_ZERO, + BEAC_IFWDATA_CRDT_ZERO, + BEAC_PCT_ENTRY_ALLOC, + BEAC_PCT_ENTRY_FREE, + BEAC_PCT_FULL, + BEAC_RD_PROMOTION_TX, + BEAC_WR_PROMOTION_TX, + BEAC_RD_PRESSURE_TX, + BEAC_WR_PRESSURE_TX, +}; + +enum berc_events { + BERC_RD_CMD, + BERC_ERROR_CMD, + BERC_PCT_ENTRY_DEALLOC, + BERC_RD_RSP_RX, + BERC_RD_RSP_BEAT_RX, + BERC_RD_LA_RX, + BERC_UNSTALL_RX, + BERC_TX_RD_CMD, + BERC_TX_ERR_CMD, + BERC_TX_RD_BEAT, + BERC_TX_ERR_BEAT, + BERC_RESERVED, + BERC_RESERVED1, + BERC_CMO_RX, + BERC_CMO_TX, + BERC_DRP_WR_TX, + BERC_DRP_WR_BEAT_TX, + BERC_FEWC_WR_TX, + BERC_FEWC_WR_BEAT_TX, + BERC_LBUFFS_FULL, + BERC_DRP_BUSY, + BERC_FEWC_BUSY, + BERC_LBUFF_STALLED, +}; + +enum trp_events { + TRP_ANY_ACCESS, + TRP_INCR_RD, + TRP_INCR_WR, + TRP_ANY_HIT, + TRP_RD_HIT, + TRP_WR_HIT, + TRP_RD_MISS, + TRP_WR_MISS, + TRP_RD_HIT_MISS, + TRP_WR_HIT_MISS, + TRP_EVICT, + TRP_GRANULE_EVICT, + TRP_RD_EVICT, + TRP_WR_EVICT, + TRP_LINE_FILL, + TRP_GRANULE_FILL, + TRP_WSC_WRITE, + TRP_WSC_EVICT, + TRP_SUBCACHE_ACT, + TRP_SUBCACHE_DEACT, + TRP_RD_DEACTIVE_SUBCACHE, + TRP_WR_DEACTIVE_SUBCACHE, + TRP_INVALID_LINE_ALLOC, + TRP_DEACTIVE_LINE_ALLOC, + TRP_SELF_EVICTION_ALLOC, + TRP_UC_SUBCACHE_ALLOC, + TRP_FC_SELF_EVICTION_ALLOC, + TRP_LP_SUBCACHE_VICTIM, + TRP_OC_SUBCACHE_VICTIM, + TRP_MRU_ROLLOVER, + TRP_NC_DOWNGRADE, + TRP_TAGRAM_CORR_ERR, + TRP_TAGRAM_UNCORR_ERR, + TRP_RD_MISS_FC, + TRP_CPU_WRITE_EWD_LINE, + TRP_CLIENT_WRITE_EWD_LINE, + TRP_CLIENT_READ_EWD_LINE, + TRP_CMO_I_EWD_LINE, + TRP_CMO_I_DIRTY_LINE, + TRP_DRP_RD_NOTIFICATION, + TRP_DRP_WR_NOTIFICATION, + TRP_LINEFILL_TAG_UPDATE, + TRP_FEWC_TAG_UPDATE, + TRP_ET_FULL, + TRP_NAWT_FULL, + TRP_HITQ_FULL, + TRP_ET_ALLOC, + TRP_ET_DEALLOC, + TRP_NAWT_ALLOC, + TRP_NAWT_DEALLOC, + TRP_RD_REPLAY, + TRP_WR_ECC_RD, + TRP_ET_LP_FULL, + TRP_ET_HP_FULL, + TRP_SOEH, +}; + +enum drp_events { + DRP_TRP_RD_NOTIFICATION, + DRP_TRP_WR_NOTIFICATION, + DRP_BIST_WR_NOTIFICATION, + DRP_DRIE_WR_NOTIFICATION, + DRP_ECC_CORR_ERR, + DRP_ECC_UNCORR_ERR, + DRP_FERC_RD_TX, + DRP_FEWC_RD_TX, + DRP_EVICT_LINE_TX, + DRP_EVICT_GRANULE_TX, + DRP_BIST_TX, + DRP_FERC_RD_BEAT, + DRP_FEWC_RD_BEAT, + DRP_BIST_RD_BEAT, + DRP_EVICT_RD_BEAT, + DRP_BERC_WR_BEAT, + DRP_FEWC_WR_BEAT, + DRP_BIST_WR_BEAT, + DRP_DRIE_WR_BEAT, + DRP_BERC_UNSTALL, + DRP_FEWC_UNSTALL, + DRP_LB_RD, + DRP_LB_WR, + DRP_BANK_CONFLICT, + DRP_FILL_TRUMPS_RD, + DRP_RD_TRUMPS_WR, + DRP_LB_SLP_RET, + DRP_LB_SLP_NRET, + DRP_LB_WAKEUP, + DRP_TRP_EARLY_WAKEUP, + DRP_PCB_IDLE, + DRP_EVICT_RDFIFO_FULL, + DRP_FEWC_RDFIFO_FULL, + DRP_FERC_RDFIFO_FULL, + DRP_FERC_RD, + DRP_FEWC_RD, + DRP_LINE_EVICT, + DRP_GRANULE_EVICT, + DRP_BIST_RD, + DRP_FEWC_WR, + DRP_LINE_FILL, + DRP_GRANULE_FILL, + DRP_BIST_WR, + DRP_DRIE_WR, +}; + +enum pmgr_events { + PMGR_Q_RUN_STATE, + PMGR_Q_DENIED_STATE, + PMGR_Q_STOPEED_TO_Q_RUN, + PMGR_Q_RUN_TO_Q_FENCED, + PMGR_Q_RUN_TO_Q_DENIED, + PMGR_Q_DENIED_TO_Q_RUN, + PMGR_Q_FENCED_TO_Q_STOPPED, + PMGR_Q_FENCED_TO_Q_DENIED, +}; + +enum filter_type { + SCID, + MID, + PROFILING_TAG, + WAY_ID, + OPCODE, + CACHEALLOC, + UNKNOWN, +}; + +#endif /* _SOC_QCOM_LLCC_EVENTS_H_ */ diff --git a/drivers/soc/qcom/llcc_perfmon.c b/drivers/soc/qcom/llcc_perfmon.c new file mode 100644 index 000000000000..2e68bba5e94a --- /dev/null +++ b/drivers/soc/qcom/llcc_perfmon.c @@ -0,0 +1,1251 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2019, The Linux Foundation. All rights reserved. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "llcc_events.h" +#include "llcc_perfmon.h" + +#define LLCC_PERFMON_NAME "qcom_llcc_perfmon" +#define LLCC_PERFMON_COUNTER_MAX 16 +#define MAX_NUMBER_OF_PORTS 8 +#define NUM_CHANNELS 16 +#define DELIM_CHAR " " + +/** + * struct llcc_perfmon_counter_map - llcc perfmon counter map info + * @port_sel: Port selected for configured counter + * @event_sel: Event selected for configured counter + * @counter_dump: Cumulative counter dump + */ +struct llcc_perfmon_counter_map { + unsigned int port_sel; + unsigned int event_sel; + unsigned long long counter_dump[NUM_CHANNELS]; +}; + +struct llcc_perfmon_private; +/** + * struct event_port_ops - event port operation + * @event_config: Counter config support for port & event + * @event_enable: Counter enable support for port + * @event_filter_config: Port filter config support + */ +struct event_port_ops { + void (*event_config)(struct llcc_perfmon_private *priv, + unsigned int type, unsigned int num, bool enable); + void (*event_enable)(struct llcc_perfmon_private *priv, bool enable); + void (*event_filter_config)(struct llcc_perfmon_private *priv, + enum filter_type filter, unsigned long match, + unsigned long mask, bool enable); +}; + +/** + * struct llcc_perfmon_private - llcc perfmon private + * @llcc_map: llcc register address space map + * @bank_off: Offset of llcc banks + * @num_banks: Number of banks supported + * @port_ops: struct event_port_ops + * @configured: Mapping of configured event counters + * @configured_counters: + * Count of configured counters. + * @enables_port: Port enabled for perfmon configuration + * @filtered_ports: Port filter enabled + * @port_configd: Number of perfmon port configuration supported + * @mutex: mutex to protect this structure + * @hrtimer: hrtimer instance for timer functionality + * @expires: timer expire time in nano seconds + * @num_mc: number of MCS + * @version: Version information of llcc block + */ +struct llcc_perfmon_private { + struct regmap *llcc_map; + struct regmap *llcc_bcast_map; + unsigned int bank_off[NUM_CHANNELS]; + unsigned int num_banks; + struct event_port_ops *port_ops[MAX_NUMBER_OF_PORTS]; + struct llcc_perfmon_counter_map configured[LLCC_PERFMON_COUNTER_MAX]; + unsigned int configured_counters; + unsigned int enables_port; + unsigned int filtered_ports; + unsigned int port_configd; + struct mutex mutex; + struct hrtimer hrtimer; + ktime_t expires; + unsigned int num_mc; + unsigned int version; +}; + +static inline void llcc_bcast_write(struct llcc_perfmon_private *llcc_priv, + unsigned int offset, uint32_t val) +{ + regmap_write(llcc_priv->llcc_bcast_map, offset, val); +} + +static inline void llcc_bcast_read(struct llcc_perfmon_private *llcc_priv, + unsigned int offset, uint32_t *val) +{ + regmap_read(llcc_priv->llcc_bcast_map, offset, val); +} + +static void llcc_bcast_modify(struct llcc_perfmon_private *llcc_priv, + unsigned int offset, uint32_t val, uint32_t mask) +{ + uint32_t readval; + + llcc_bcast_read(llcc_priv, offset, &readval); + readval &= ~mask; + readval |= val & mask; + llcc_bcast_write(llcc_priv, offset, readval); +} + +static void perfmon_counter_dump(struct llcc_perfmon_private *llcc_priv) +{ + uint32_t val; + unsigned int i, j; + + if (!llcc_priv->configured_counters) + return; + + llcc_bcast_write(llcc_priv, PERFMON_DUMP, MONITOR_DUMP); + for (i = 0; i < llcc_priv->configured_counters; i++) { + for (j = 0; j < llcc_priv->num_banks; j++) { + regmap_read(llcc_priv->llcc_map, llcc_priv->bank_off[j] + + LLCC_COUNTER_n_VALUE(i), &val); + llcc_priv->configured[i].counter_dump[j] += val; + } + } +} + +static ssize_t perfmon_counter_dump_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct llcc_perfmon_private *llcc_priv = dev_get_drvdata(dev); + unsigned int i, j; + unsigned long long total; + ssize_t cnt = 0; + + if (llcc_priv->configured_counters == 0) { + pr_err("counters not configured\n"); + return cnt; + } + + perfmon_counter_dump(llcc_priv); + for (i = 0; i < llcc_priv->configured_counters - 1; i++) { + cnt += scnprintf(buf + cnt, PAGE_SIZE - cnt, "Port %02d,", + llcc_priv->configured[i].port_sel); + cnt += scnprintf(buf + cnt, PAGE_SIZE - cnt, "Event %02d,", + llcc_priv->configured[i].event_sel); + + total = 0; + for (j = 0; j < llcc_priv->num_banks; j++) { + total += llcc_priv->configured[i].counter_dump[j]; + llcc_priv->configured[i].counter_dump[j] = 0; + } + + cnt += scnprintf(buf + cnt, PAGE_SIZE - cnt, "0x%016llx\n", + total); + } + + cnt += scnprintf(buf + cnt, PAGE_SIZE - cnt, "CYCLE COUNT, ,"); + total = 0; + for (j = 0; j < llcc_priv->num_banks; j++) { + total += llcc_priv->configured[i].counter_dump[j]; + llcc_priv->configured[i].counter_dump[j] = 0; + } + + cnt += scnprintf(buf + cnt, PAGE_SIZE - cnt, "0x%016llx\n", total); + + if (llcc_priv->expires) + hrtimer_forward_now(&llcc_priv->hrtimer, llcc_priv->expires); + + return cnt; +} + +static ssize_t perfmon_configure_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct llcc_perfmon_private *llcc_priv = dev_get_drvdata(dev); + struct event_port_ops *port_ops; + unsigned int j = 0, k; + unsigned long port_sel, event_sel; + uint32_t val; + char *token, *delim = DELIM_CHAR; + + mutex_lock(&llcc_priv->mutex); + if (llcc_priv->configured_counters) { + pr_err("Counters configured already, remove & try again\n"); + mutex_unlock(&llcc_priv->mutex); + return -EINVAL; + } + + llcc_priv->configured_counters = 0; + token = strsep((char **)&buf, delim); + + while (token != NULL) { + if (kstrtoul(token, 0, &port_sel)) + break; + + if (port_sel >= llcc_priv->port_configd) + break; + + token = strsep((char **)&buf, delim); + if (token == NULL) + break; + + if (kstrtoul(token, 0, &event_sel)) + break; + + token = strsep((char **)&buf, delim); + if (event_sel >= EVENT_NUM_MAX) { + pr_err("unsupported event num %ld\n", event_sel); + continue; + } + + llcc_priv->configured[j].port_sel = port_sel; + llcc_priv->configured[j].event_sel = event_sel; + for (k = 0; k < llcc_priv->num_banks; k++) + llcc_priv->configured[j].counter_dump[k] = 0; + + port_ops = llcc_priv->port_ops[port_sel]; + pr_info("counter %d configured for event %ld from port %ld\n", + j, event_sel, port_sel); + port_ops->event_config(llcc_priv, event_sel, j++, true); + if (!(llcc_priv->enables_port & (1 << port_sel))) + if (port_ops->event_enable) + port_ops->event_enable(llcc_priv, true); + + llcc_priv->enables_port |= (1 << port_sel); + + /* Last perfmon counter for cycle counter */ + if (llcc_priv->configured_counters++ == + (LLCC_PERFMON_COUNTER_MAX - 2)) + break; + } + + /* configure clock event */ + val = COUNT_CLOCK_EVENT | CLEAR_ON_ENABLE | CLEAR_ON_DUMP; + llcc_bcast_write(llcc_priv, PERFMON_COUNTER_n_CONFIG(j), val); + + llcc_priv->configured_counters++; + mutex_unlock(&llcc_priv->mutex); + return count; +} + +static ssize_t perfmon_remove_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct llcc_perfmon_private *llcc_priv = dev_get_drvdata(dev); + struct event_port_ops *port_ops; + unsigned int j = 0, counter_remove = 0; + unsigned long port_sel, event_sel; + char *token, *delim = DELIM_CHAR; + + mutex_lock(&llcc_priv->mutex); + if (!llcc_priv->configured_counters) { + pr_err("Counters not configured\n"); + mutex_unlock(&llcc_priv->mutex); + return -EINVAL; + } + + token = strsep((char **)&buf, delim); + + while (token != NULL) { + if (kstrtoul(token, 0, &port_sel)) + break; + + if (port_sel >= llcc_priv->port_configd) + break; + + token = strsep((char **)&buf, delim); + if (token == NULL) + break; + + if (kstrtoul(token, 0, &event_sel)) + break; + + token = strsep((char **)&buf, delim); + if (event_sel >= EVENT_NUM_MAX) { + pr_err("unsupported event num %ld\n", event_sel); + continue; + } + + /* put dummy values */ + llcc_priv->configured[j].port_sel = MAX_NUMBER_OF_PORTS; + llcc_priv->configured[j].event_sel = 100; + port_ops = llcc_priv->port_ops[port_sel]; + pr_info("removed counter %d for event %ld from port %ld\n", + j, event_sel, port_sel); + + port_ops->event_config(llcc_priv, event_sel, j++, false); + if (llcc_priv->enables_port & (1 << port_sel)) + if (port_ops->event_enable) + port_ops->event_enable(llcc_priv, false); + + llcc_priv->enables_port &= ~(1 << port_sel); + + /* Last perfmon counter for cycle counter */ + if (counter_remove++ == (LLCC_PERFMON_COUNTER_MAX - 2)) + break; + } + + /* remove clock event */ + llcc_bcast_write(llcc_priv, PERFMON_COUNTER_n_CONFIG(j), 0); + + llcc_priv->configured_counters = 0; + mutex_unlock(&llcc_priv->mutex); + return count; +} + +static enum filter_type find_filter_type(char *filter) +{ + enum filter_type ret = UNKNOWN; + + if (!strcmp(filter, "SCID")) + ret = SCID; + else if (!strcmp(filter, "MID")) + ret = MID; + else if (!strcmp(filter, "PROFILING_TAG")) + ret = PROFILING_TAG; + else if (!strcmp(filter, "WAY_ID")) + ret = WAY_ID; + else if (!strcmp(filter, "OPCODE")) + ret = OPCODE; + else if (!strcmp(filter, "CACHEALLOC")) + ret = CACHEALLOC; + + return ret; +} + +static ssize_t perfmon_filter_config_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct llcc_perfmon_private *llcc_priv = dev_get_drvdata(dev); + unsigned long port, mask, match; + struct event_port_ops *port_ops; + char *token, *delim = DELIM_CHAR; + enum filter_type filter = UNKNOWN; + + if (llcc_priv->configured_counters) { + pr_err("remove configured events and try\n"); + return count; + } + + mutex_lock(&llcc_priv->mutex); + + token = strsep((char **)&buf, delim); + if (token != NULL) + filter = find_filter_type(token); + + if (filter == UNKNOWN) { + pr_err("filter configuration failed, Unsupported filter\n"); + goto filter_config_free; + } + + token = strsep((char **)&buf, delim); + if (token == NULL) { + pr_err("filter configuration failed, Wrong input\n"); + goto filter_config_free; + } + + if (kstrtoul(token, 0, &match)) { + pr_err("filter configuration failed, Wrong format\n"); + goto filter_config_free; + } + + if ((filter == SCID) && (match >= SCID_MAX)) { + pr_err("filter configuration failed, SCID above MAX value\n"); + goto filter_config_free; + } + + token = strsep((char **)&buf, delim); + if (token == NULL) { + pr_err("filter configuration failed, Wrong input\n"); + goto filter_config_free; + } + + if (kstrtoul(token, 0, &mask)) { + pr_err("filter configuration failed, Wrong format\n"); + goto filter_config_free; + } + + while (token != NULL) { + token = strsep((char **)&buf, delim); + if (token == NULL) + break; + + if (kstrtoul(token, 0, &port)) + break; + + llcc_priv->filtered_ports |= 1 << port; + port_ops = llcc_priv->port_ops[port]; + if (port_ops->event_filter_config) + port_ops->event_filter_config(llcc_priv, filter, match, + mask, true); + } + + mutex_unlock(&llcc_priv->mutex); + return count; + +filter_config_free: + mutex_unlock(&llcc_priv->mutex); + return -EINVAL; +} + +static ssize_t perfmon_filter_remove_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct llcc_perfmon_private *llcc_priv = dev_get_drvdata(dev); + struct event_port_ops *port_ops; + unsigned long port, mask, match; + char *token, *delim = DELIM_CHAR; + enum filter_type filter = UNKNOWN; + + mutex_lock(&llcc_priv->mutex); + token = strsep((char **)&buf, delim); + if (token != NULL) + filter = find_filter_type(token); + + if (filter == UNKNOWN) { + pr_err("filter configuration failed, Unsupported filter\n"); + goto filter_remove_free; + } + + token = strsep((char **)&buf, delim); + if (token == NULL) { + pr_err("filter configuration failed, Wrong input\n"); + goto filter_remove_free; + } + + if (kstrtoul(token, 0, &match)) { + pr_err("filter configuration failed, Wrong format\n"); + goto filter_remove_free; + } + + if ((filter == SCID) && (match >= SCID_MAX)) { + pr_err("filter configuration failed, SCID above MAX value\n"); + goto filter_remove_free; + } + + token = strsep((char **)&buf, delim); + if (token == NULL) { + pr_err("filter configuration failed, Wrong input\n"); + goto filter_remove_free; + } + + if (kstrtoul(token, 0, &mask)) { + pr_err("filter configuration failed, Wrong format\n"); + goto filter_remove_free; + } + + while (token != NULL) { + token = strsep((char **)&buf, delim); + if (token == NULL) + break; + + if (kstrtoul(token, 0, &port)) + break; + + llcc_priv->filtered_ports &= ~(1 << port); + port_ops = llcc_priv->port_ops[port]; + if (port_ops->event_filter_config) + port_ops->event_filter_config(llcc_priv, filter, match, + mask, false); + } + +filter_remove_free: + mutex_unlock(&llcc_priv->mutex); + return count; +} + +static ssize_t perfmon_start_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct llcc_perfmon_private *llcc_priv = dev_get_drvdata(dev); + uint32_t val = 0, mask_val; + unsigned long start; + + if (kstrtoul(buf, 0, &start)) + return -EINVAL; + + mutex_lock(&llcc_priv->mutex); + if (start) { + if (!llcc_priv->configured_counters) { + pr_err("start failed. perfmon not configured\n"); + mutex_unlock(&llcc_priv->mutex); + return -EINVAL; + } + + val = MANUAL_MODE | MONITOR_EN; + if (llcc_priv->expires) { + if (hrtimer_is_queued(&llcc_priv->hrtimer)) + hrtimer_forward_now(&llcc_priv->hrtimer, + llcc_priv->expires); + else + hrtimer_start(&llcc_priv->hrtimer, + llcc_priv->expires, + HRTIMER_MODE_REL_PINNED); + } + + } else { + if (llcc_priv->expires) + hrtimer_cancel(&llcc_priv->hrtimer); + + if (!llcc_priv->configured_counters) + pr_err("stop failed. perfmon not configured\n"); + } + + mask_val = PERFMON_MODE_MONITOR_MODE_MASK | + PERFMON_MODE_MONITOR_EN_MASK; + llcc_bcast_modify(llcc_priv, PERFMON_MODE, val, mask_val); + + mutex_unlock(&llcc_priv->mutex); + return count; +} + +static ssize_t perfmon_ns_periodic_dump_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct llcc_perfmon_private *llcc_priv = dev_get_drvdata(dev); + + if (kstrtos64(buf, 0, &llcc_priv->expires)) + return -EINVAL; + + mutex_lock(&llcc_priv->mutex); + if (!llcc_priv->expires) { + hrtimer_cancel(&llcc_priv->hrtimer); + mutex_unlock(&llcc_priv->mutex); + return count; + } + + if (hrtimer_is_queued(&llcc_priv->hrtimer)) + hrtimer_forward_now(&llcc_priv->hrtimer, llcc_priv->expires); + else + hrtimer_start(&llcc_priv->hrtimer, llcc_priv->expires, + HRTIMER_MODE_REL_PINNED); + + mutex_unlock(&llcc_priv->mutex); + return count; +} + +static ssize_t perfmon_scid_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct llcc_perfmon_private *llcc_priv = dev_get_drvdata(dev); + uint32_t val; + unsigned int i, j, offset; + ssize_t cnt = 0; + unsigned long total; + + for (i = 0; i < SCID_MAX; i++) { + total = 0; + offset = TRP_SCID_n_STATUS(i); + + for (j = 0; j < llcc_priv->num_banks; j++) { + regmap_read(llcc_priv->llcc_map, + llcc_priv->bank_off[j] + offset, &val); + val = (val & TRP_SCID_STATUS_CURRENT_CAP_MASK) >> + TRP_SCID_STATUS_CURRENT_CAP_SHIFT; + total += val; + } + + llcc_bcast_read(llcc_priv, offset, &val); + if (val & TRP_SCID_STATUS_ACTIVE_MASK) + cnt += scnprintf(buf + cnt, PAGE_SIZE - cnt, + "SCID %02d %10s", i, "ACTIVE"); + else + cnt += scnprintf(buf + cnt, PAGE_SIZE - cnt, + "SCID %02d %10s", i, "DEACTIVE"); + + cnt += scnprintf(buf + cnt, PAGE_SIZE - cnt, ",0x%08lx\n", + total); + } + + return cnt; +} + +static DEVICE_ATTR_RO(perfmon_counter_dump); +static DEVICE_ATTR_WO(perfmon_configure); +static DEVICE_ATTR_WO(perfmon_remove); +static DEVICE_ATTR_WO(perfmon_filter_config); +static DEVICE_ATTR_WO(perfmon_filter_remove); +static DEVICE_ATTR_WO(perfmon_start); +static DEVICE_ATTR_RO(perfmon_scid_status); +static DEVICE_ATTR_WO(perfmon_ns_periodic_dump); + +static struct attribute *llcc_perfmon_attrs[] = { + &dev_attr_perfmon_counter_dump.attr, + &dev_attr_perfmon_configure.attr, + &dev_attr_perfmon_remove.attr, + &dev_attr_perfmon_filter_config.attr, + &dev_attr_perfmon_filter_remove.attr, + &dev_attr_perfmon_start.attr, + &dev_attr_perfmon_scid_status.attr, + &dev_attr_perfmon_ns_periodic_dump.attr, + NULL, +}; + +static struct attribute_group llcc_perfmon_group = { + .attrs = llcc_perfmon_attrs, +}; + +static void perfmon_counter_config(struct llcc_perfmon_private *llcc_priv, + unsigned int port, unsigned int event_counter_num) +{ + uint32_t val; + + val = (port & PERFMON_PORT_SELECT_MASK) | + ((event_counter_num << EVENT_SELECT_SHIFT) & + PERFMON_EVENT_SELECT_MASK) | CLEAR_ON_ENABLE | CLEAR_ON_DUMP; + llcc_bcast_write(llcc_priv, PERFMON_COUNTER_n_CONFIG(event_counter_num), + val); +} + +static void feac_event_config(struct llcc_perfmon_private *llcc_priv, + unsigned int event_type, unsigned int event_counter_num, + bool enable) +{ + uint32_t val = 0, mask_val, counter_num = 0; + + mask_val = EVENT_SEL_MASK; + if (llcc_priv->filtered_ports & (1 << EVENT_PORT_FEAC)) + mask_val |= FILTER_SEL_MASK | FILTER_EN_MASK; + + if (enable) { + val = (event_type << EVENT_SEL_SHIFT) & EVENT_SEL_MASK; + if (llcc_priv->filtered_ports & (1 << EVENT_PORT_FEAC)) + val |= (FILTER_0 << FILTER_SEL_SHIFT) | FILTER_EN; + + counter_num = event_counter_num; + } + + llcc_bcast_modify(llcc_priv, FEAC_PROF_EVENT_n_CFG(event_counter_num), + val, mask_val); + perfmon_counter_config(llcc_priv, EVENT_PORT_FEAC, counter_num); +} + +static void feac_event_enable(struct llcc_perfmon_private *llcc_priv, + bool enable) +{ + uint32_t val = 0, mask_val; + + if (enable) { + val = (BYTE_SCALING << BYTE_SCALING_SHIFT) | + (BEAT_SCALING << BEAT_SCALING_SHIFT) | PROF_EN; + + if (llcc_priv->filtered_ports & (1 << EVENT_PORT_FEAC)) { + if (llcc_priv->version == REV_0) + val |= (FILTER_0 << + FEAC_SCALING_FILTER_SEL_SHIFT) | + FEAC_SCALING_FILTER_EN; + else + val |= (FILTER_0 << + FEAC_WR_BEAT_FILTER_SEL_SHIFT) | + FEAC_WR_BEAT_FILTER_EN | + (FILTER_0 << + FEAC_WR_BYTE_FILTER_SEL_SHIFT) | + FEAC_WR_BYTE_FILTER_EN | + (FILTER_0 << + FEAC_RD_BEAT_FILTER_SEL_SHIFT) | + FEAC_RD_BEAT_FILTER_EN | + (FILTER_0 << + FEAC_RD_BYTE_FILTER_SEL_SHIFT) | + FEAC_RD_BYTE_FILTER_EN; + } + } + + mask_val = PROF_CFG_BEAT_SCALING_MASK | PROF_CFG_BYTE_SCALING_MASK + | PROF_CFG_EN_MASK; + + if (llcc_priv->filtered_ports & (1 << EVENT_PORT_FEAC)) { + if (llcc_priv->version == REV_0) + mask_val |= FEAC_SCALING_FILTER_SEL_MASK | + FEAC_SCALING_FILTER_EN_MASK; + else + mask_val |= FEAC_WR_BEAT_FILTER_SEL_MASK | + FEAC_WR_BEAT_FILTER_EN_MASK | + FEAC_WR_BYTE_FILTER_SEL_MASK | + FEAC_WR_BYTE_FILTER_EN_MASK | + FEAC_RD_BEAT_FILTER_SEL_MASK | + FEAC_RD_BEAT_FILTER_EN_MASK | + FEAC_RD_BYTE_FILTER_SEL_MASK | + FEAC_RD_BYTE_FILTER_EN_MASK; + } + + llcc_bcast_modify(llcc_priv, FEAC_PROF_CFG, val, mask_val); +} + +static void feac_event_filter_config(struct llcc_perfmon_private *llcc_priv, + enum filter_type filter, unsigned long match, + unsigned long mask, bool enable) +{ + uint32_t val = 0, mask_val; + + if (filter == SCID) { + if (llcc_priv->version == REV_0) { + if (enable) + val = (match << SCID_MATCH_SHIFT) | + (mask << SCID_MASK_SHIFT); + + mask_val = SCID_MATCH_MASK | SCID_MASK_MASK; + } else { + if (enable) + val = (1 << match); + + mask_val = SCID_MULTI_MATCH_MASK; + } + + llcc_bcast_modify(llcc_priv, FEAC_PROF_FILTER_0_CFG6, val, + mask_val); + } else if (filter == MID) { + if (enable) + val = (match << MID_MATCH_SHIFT) | + (mask << MID_MASK_SHIFT); + + mask_val = MID_MATCH_MASK | MID_MASK_MASK; + llcc_bcast_modify(llcc_priv, FEAC_PROF_FILTER_0_CFG5, val, + mask_val); + } else if (filter == OPCODE) { + if (enable) + val = (match << OPCODE_MATCH_SHIFT) | + (mask << OPCODE_MASK_SHIFT); + + mask_val = OPCODE_MATCH_MASK | OPCODE_MASK_MASK; + llcc_bcast_modify(llcc_priv, FEAC_PROF_FILTER_0_CFG3, val, + mask_val); + } else if (filter == CACHEALLOC) { + if (enable) + val = (match << CACHEALLOC_MATCH_SHIFT) | + (mask << CACHEALLOC_MASK_SHIFT); + + mask_val = CACHEALLOC_MATCH_MASK | CACHEALLOC_MASK_MASK; + llcc_bcast_modify(llcc_priv, FEAC_PROF_FILTER_0_CFG3, val, + mask_val); + } else { + pr_err("unknown filter/not supported\n"); + } +} + +static struct event_port_ops feac_port_ops = { + .event_config = feac_event_config, + .event_enable = feac_event_enable, + .event_filter_config = feac_event_filter_config, +}; + +static void ferc_event_config(struct llcc_perfmon_private *llcc_priv, + unsigned int event_type, unsigned int event_counter_num, + bool enable) +{ + uint32_t val = 0, mask_val, counter_num = 0; + + mask_val = EVENT_SEL_MASK; + if (llcc_priv->filtered_ports & (1 << EVENT_PORT_FERC)) + mask_val |= FILTER_SEL_MASK | FILTER_EN_MASK; + + if (enable) { + val = event_type << EVENT_SEL_SHIFT; + if (llcc_priv->filtered_ports & (1 << EVENT_PORT_FERC)) + val |= (FILTER_0 << FILTER_SEL_SHIFT) | FILTER_EN; + + counter_num = event_counter_num; + } + + llcc_bcast_modify(llcc_priv, FERC_PROF_EVENT_n_CFG(event_counter_num), + val, mask_val); + perfmon_counter_config(llcc_priv, EVENT_PORT_FERC, counter_num); +} + +static void ferc_event_enable(struct llcc_perfmon_private *llcc_priv, + bool enable) +{ + uint32_t val = 0, mask_val; + + if (enable) + val = (BYTE_SCALING << BYTE_SCALING_SHIFT) | + (BEAT_SCALING << BEAT_SCALING_SHIFT) | PROF_EN; + + mask_val = PROF_CFG_BEAT_SCALING_MASK | PROF_CFG_BYTE_SCALING_MASK | + PROF_CFG_EN_MASK; + llcc_bcast_modify(llcc_priv, FERC_PROF_CFG, val, mask_val); +} + +static void ferc_event_filter_config(struct llcc_perfmon_private *llcc_priv, + enum filter_type filter, unsigned long match, + unsigned long mask, bool enable) +{ + uint32_t val = 0, mask_val; + + if (filter != PROFILING_TAG) { + pr_err("unknown filter/not supported\n"); + return; + } + + if (enable) + val = (match << PROFTAG_MATCH_SHIFT) | + (mask << PROFTAG_MASK_SHIFT); + + mask_val = PROFTAG_MATCH_MASK | PROFTAG_MASK_MASK; + llcc_bcast_modify(llcc_priv, FERC_PROF_FILTER_0_CFG0, val, mask_val); +} + +static struct event_port_ops ferc_port_ops = { + .event_config = ferc_event_config, + .event_enable = ferc_event_enable, + .event_filter_config = ferc_event_filter_config, +}; + +static void fewc_event_config(struct llcc_perfmon_private *llcc_priv, + unsigned int event_type, unsigned int event_counter_num, + bool enable) +{ + uint32_t val = 0, mask_val, counter_num = 0; + + mask_val = EVENT_SEL_MASK; + if (llcc_priv->filtered_ports & (1 << EVENT_PORT_FEWC)) + mask_val |= FILTER_SEL_MASK | FILTER_EN_MASK; + + if (enable) { + val = (event_type << EVENT_SEL_SHIFT) & EVENT_SEL_MASK; + if (llcc_priv->filtered_ports & (1 << EVENT_PORT_FEWC)) + val |= (FILTER_0 << FILTER_SEL_SHIFT) | FILTER_EN; + + counter_num = event_counter_num; + } + + llcc_bcast_modify(llcc_priv, FEWC_PROF_EVENT_n_CFG(event_counter_num), + val, mask_val); + perfmon_counter_config(llcc_priv, EVENT_PORT_FEWC, counter_num); +} + +static void fewc_event_filter_config(struct llcc_perfmon_private *llcc_priv, + enum filter_type filter, unsigned long match, + unsigned long mask, bool enable) +{ + uint32_t val = 0, mask_val; + + if (filter != PROFILING_TAG) { + pr_err("unknown filter/not supported\n"); + return; + } + + if (enable) + val = (match << PROFTAG_MATCH_SHIFT) | + (mask << PROFTAG_MASK_SHIFT); + + mask_val = PROFTAG_MATCH_MASK | PROFTAG_MASK_MASK; + llcc_bcast_modify(llcc_priv, FEWC_PROF_FILTER_0_CFG0, val, mask_val); +} + +static struct event_port_ops fewc_port_ops = { + .event_config = fewc_event_config, + .event_filter_config = fewc_event_filter_config, +}; + +static void beac_event_config(struct llcc_perfmon_private *llcc_priv, + unsigned int event_type, unsigned int event_counter_num, + bool enable) +{ + uint32_t val = 0, mask_val, counter_num = 0; + uint32_t valcfg = 0, mask_valcfg; + unsigned int mc_cnt, offset; + + mask_val = EVENT_SEL_MASK; + if (llcc_priv->filtered_ports & (1 << EVENT_PORT_BEAC)) { + mask_val |= FILTER_SEL_MASK | FILTER_EN_MASK; + if (llcc_priv->version == REV_0) + mask_valcfg = BEAC_WR_BEAT_FILTER_SEL_MASK | + BEAC_WR_BEAT_FILTER_EN_MASK | + BEAC_RD_BEAT_FILTER_SEL_MASK | + BEAC_RD_BEAT_FILTER_EN_MASK; + } + + if (enable) { + val = (event_type << EVENT_SEL_SHIFT) & EVENT_SEL_MASK; + if (llcc_priv->filtered_ports & (1 << EVENT_PORT_BEAC)) { + val |= (FILTER_0 << FILTER_SEL_SHIFT) | + FILTER_EN; + if (llcc_priv->version == REV_0) + valcfg = (FILTER_0 << + BEAC_WR_BEAT_FILTER_SEL_SHIFT) | + BEAC_WR_BEAT_FILTER_EN | + (FILTER_0 << + BEAC_RD_BEAT_FILTER_SEL_SHIFT) | + BEAC_RD_BEAT_FILTER_EN; + } + + counter_num = event_counter_num; + } + + for (mc_cnt = 0; mc_cnt < llcc_priv->num_mc; mc_cnt++) { + offset = BEAC_PROF_EVENT_n_CFG(event_counter_num) + + mc_cnt * BEAC_INST_OFF; + llcc_bcast_modify(llcc_priv, offset, val, mask_val); + + offset = BEAC_PROF_CFG + mc_cnt * BEAC_INST_OFF; + llcc_bcast_modify(llcc_priv, offset, valcfg, mask_valcfg); + } + + perfmon_counter_config(llcc_priv, EVENT_PORT_BEAC, counter_num); +} + +static void beac_event_enable(struct llcc_perfmon_private *llcc_priv, + bool enable) +{ + uint32_t val = 0, mask_val; + unsigned int mc_cnt, offset; + + if (enable) + val = (BYTE_SCALING << BYTE_SCALING_SHIFT) | + (BEAT_SCALING << BEAT_SCALING_SHIFT) | PROF_EN; + + mask_val = PROF_CFG_BEAT_SCALING_MASK | PROF_CFG_BYTE_SCALING_MASK + | PROF_CFG_EN_MASK; + + for (mc_cnt = 0; mc_cnt < llcc_priv->num_mc; mc_cnt++) { + offset = BEAC_PROF_CFG + mc_cnt * BEAC_INST_OFF; + llcc_bcast_modify(llcc_priv, offset, val, mask_val); + } +} + +static void beac_event_filter_config(struct llcc_perfmon_private *llcc_priv, + enum filter_type filter, unsigned long match, + unsigned long mask, bool enable) +{ + uint32_t val = 0, mask_val; + unsigned int mc_cnt, offset; + + if (filter != PROFILING_TAG) { + pr_err("unknown filter/not supported\n"); + return; + } + + if (enable) + val = (match << BEAC_PROFTAG_MATCH_SHIFT) | + (mask << BEAC_PROFTAG_MASK_SHIFT); + + mask_val = BEAC_PROFTAG_MASK_MASK | BEAC_PROFTAG_MATCH_MASK; + for (mc_cnt = 0; mc_cnt < llcc_priv->num_mc; mc_cnt++) { + offset = BEAC_PROF_FILTER_0_CFG5 + mc_cnt * BEAC_INST_OFF; + llcc_bcast_modify(llcc_priv, offset, val, mask_val); + } + + if (enable) + val = match << BEAC_MC_PROFTAG_SHIFT; + + mask_val = BEAC_MC_PROFTAG_MASK; + for (mc_cnt = 0; mc_cnt < llcc_priv->num_mc; mc_cnt++) { + offset = BEAC_PROF_CFG + mc_cnt * BEAC_INST_OFF; + llcc_bcast_modify(llcc_priv, offset, val, mask_val); + } +} + +static struct event_port_ops beac_port_ops = { + .event_config = beac_event_config, + .event_enable = beac_event_enable, + .event_filter_config = beac_event_filter_config, +}; + +static void berc_event_config(struct llcc_perfmon_private *llcc_priv, + unsigned int event_type, unsigned int event_counter_num, + bool enable) +{ + uint32_t val = 0, mask_val, counter_num = 0; + + mask_val = EVENT_SEL_MASK; + if (llcc_priv->filtered_ports & (1 << EVENT_PORT_BERC)) + mask_val |= FILTER_SEL_MASK | FILTER_EN_MASK; + + if (enable) { + val = (event_type << EVENT_SEL_SHIFT) & EVENT_SEL_MASK; + if (llcc_priv->filtered_ports & (1 << EVENT_PORT_BERC)) + val |= (FILTER_0 << FILTER_SEL_SHIFT) | FILTER_EN; + + counter_num = event_counter_num; + } + + llcc_bcast_modify(llcc_priv, BERC_PROF_EVENT_n_CFG(event_counter_num), + val, mask_val); + perfmon_counter_config(llcc_priv, EVENT_PORT_BERC, counter_num); +} + +static void berc_event_enable(struct llcc_perfmon_private *llcc_priv, + bool enable) +{ + uint32_t val = 0, mask_val; + + if (enable) + val = (BYTE_SCALING << BYTE_SCALING_SHIFT) | + (BEAT_SCALING << BEAT_SCALING_SHIFT) | PROF_EN; + + mask_val = PROF_CFG_BEAT_SCALING_MASK | PROF_CFG_BYTE_SCALING_MASK + | PROF_CFG_EN_MASK; + llcc_bcast_modify(llcc_priv, BERC_PROF_CFG, val, mask_val); +} + +static void berc_event_filter_config(struct llcc_perfmon_private *llcc_priv, + enum filter_type filter, unsigned long match, + unsigned long mask, bool enable) +{ + uint32_t val = 0, mask_val; + + if (filter != PROFILING_TAG) { + pr_err("unknown filter/not supported\n"); + return; + } + + if (enable) + val = (match << PROFTAG_MATCH_SHIFT) | + (mask << PROFTAG_MASK_SHIFT); + + mask_val = PROFTAG_MATCH_MASK | PROFTAG_MASK_MASK; + llcc_bcast_modify(llcc_priv, BERC_PROF_FILTER_0_CFG0, val, mask_val); +} + +static struct event_port_ops berc_port_ops = { + .event_config = berc_event_config, + .event_enable = berc_event_enable, + .event_filter_config = berc_event_filter_config, +}; + +static void trp_event_config(struct llcc_perfmon_private *llcc_priv, + unsigned int event_type, unsigned int event_counter_num, + bool enable) +{ + uint32_t val = 0, mask_val, counter_num = 0; + + mask_val = EVENT_SEL_MASK; + if (llcc_priv->filtered_ports & (1 << EVENT_PORT_TRP)) + mask_val |= FILTER_SEL_MASK | FILTER_EN_MASK; + + if (enable) { + val = (event_type << EVENT_SEL_SHIFT) & EVENT_SEL_MASK; + if (llcc_priv->filtered_ports & (1 << EVENT_PORT_TRP)) + val |= (FILTER_0 << FILTER_SEL_SHIFT) | FILTER_EN; + + counter_num = event_counter_num; + } + + llcc_bcast_modify(llcc_priv, TRP_PROF_EVENT_n_CFG(event_counter_num), + val, mask_val); + perfmon_counter_config(llcc_priv, EVENT_PORT_TRP, counter_num); +} + +static void trp_event_filter_config(struct llcc_perfmon_private *llcc_priv, + enum filter_type filter, unsigned long match, + unsigned long mask, bool enable) +{ + uint32_t val = 0, mask_val; + + if (filter == SCID) { + if (enable) + val = (match << TRP_SCID_MATCH_SHIFT) | + (mask << TRP_SCID_MASK_SHIFT); + + mask_val = TRP_SCID_MATCH_MASK | TRP_SCID_MASK_MASK; + } else if (filter == WAY_ID) { + if (enable) + val = (match << TRP_WAY_ID_MATCH_SHIFT) | + (mask << TRP_WAY_ID_MASK_SHIFT); + + mask_val = TRP_WAY_ID_MATCH_MASK | TRP_WAY_ID_MASK_MASK; + } else if (filter == PROFILING_TAG) { + if (enable) + val = (match << TRP_PROFTAG_MATCH_SHIFT) | + (mask << TRP_PROFTAG_MASK_SHIFT); + + mask_val = TRP_PROFTAG_MATCH_MASK | TRP_PROFTAG_MASK_MASK; + } else { + pr_err("unknown filter/not supported\n"); + return; + } + + llcc_bcast_modify(llcc_priv, TRP_PROF_FILTER_0_CFG1, val, mask_val); +} + +static struct event_port_ops trp_port_ops = { + .event_config = trp_event_config, + .event_filter_config = trp_event_filter_config, +}; + +static void drp_event_config(struct llcc_perfmon_private *llcc_priv, + unsigned int event_type, unsigned int event_counter_num, + bool enable) +{ + uint32_t val = 0, mask_val, counter_num = 0; + + mask_val = EVENT_SEL_MASK; + if (llcc_priv->filtered_ports & (1 << EVENT_PORT_DRP)) + mask_val |= FILTER_SEL_MASK | FILTER_EN_MASK; + + if (enable) { + val = (event_type << EVENT_SEL_SHIFT) & EVENT_SEL_MASK; + if (llcc_priv->filtered_ports & (1 << EVENT_PORT_DRP)) + val |= (FILTER_0 << FILTER_SEL_SHIFT) | FILTER_EN; + + counter_num = event_counter_num; + } + + llcc_bcast_modify(llcc_priv, DRP_PROF_EVENT_n_CFG(event_counter_num), + val, mask_val); + perfmon_counter_config(llcc_priv, EVENT_PORT_DRP, counter_num); +} + +static void drp_event_enable(struct llcc_perfmon_private *llcc_priv, + bool enable) +{ + uint32_t val = 0, mask_val; + + if (enable) + val = (BEAT_SCALING << BEAT_SCALING_SHIFT) | PROF_EN; + + mask_val = PROF_CFG_BEAT_SCALING_MASK | PROF_CFG_EN_MASK; + llcc_bcast_modify(llcc_priv, DRP_PROF_CFG, val, mask_val); +} + +static struct event_port_ops drp_port_ops = { + .event_config = drp_event_config, + .event_enable = drp_event_enable, +}; + +static void pmgr_event_config(struct llcc_perfmon_private *llcc_priv, + unsigned int event_type, unsigned int event_counter_num, + bool enable) +{ + uint32_t val = 0, mask_val, counter_num = 0; + + mask_val = EVENT_SEL_MASK; + if (llcc_priv->filtered_ports & (1 << EVENT_PORT_PMGR)) + mask_val |= FILTER_SEL_MASK | FILTER_EN_MASK; + + if (enable) { + val = (event_type << EVENT_SEL_SHIFT) & EVENT_SEL_MASK; + if (llcc_priv->filtered_ports & (1 << EVENT_PORT_PMGR)) + val |= (FILTER_0 << FILTER_SEL_SHIFT) | FILTER_EN; + + counter_num = event_counter_num; + } + + llcc_bcast_modify(llcc_priv, PMGR_PROF_EVENT_n_CFG(event_counter_num), + val, mask_val); + perfmon_counter_config(llcc_priv, EVENT_PORT_PMGR, counter_num); +} + +static struct event_port_ops pmgr_port_ops = { + .event_config = pmgr_event_config, +}; + +static void llcc_register_event_port(struct llcc_perfmon_private *llcc_priv, + struct event_port_ops *ops, unsigned int event_port_num) +{ + if (llcc_priv->port_configd >= MAX_NUMBER_OF_PORTS) { + pr_err("Register port Failure!\n"); + return; + } + + llcc_priv->port_configd = llcc_priv->port_configd + 1; + llcc_priv->port_ops[event_port_num] = ops; +} + +static enum hrtimer_restart llcc_perfmon_timer_handler(struct hrtimer *hrtimer) +{ + struct llcc_perfmon_private *llcc_priv = container_of(hrtimer, + struct llcc_perfmon_private, hrtimer); + + perfmon_counter_dump(llcc_priv); + hrtimer_forward_now(&llcc_priv->hrtimer, llcc_priv->expires); + return HRTIMER_RESTART; +} + +static int llcc_perfmon_probe(struct platform_device *pdev) +{ + int result = 0; + struct llcc_perfmon_private *llcc_priv; + struct llcc_drv_data *llcc_driv_data = pdev->dev.platform_data; + uint32_t val; + + llcc_priv = devm_kzalloc(&pdev->dev, sizeof(*llcc_priv), GFP_KERNEL); + if (llcc_priv == NULL) + return -ENOMEM; + + if (!llcc_driv_data) + return -ENOMEM; + + if ((llcc_driv_data->regmap == NULL) || + (llcc_driv_data->bcast_regmap == NULL)) + return -ENODEV; + + llcc_priv->llcc_map = llcc_driv_data->regmap; + llcc_priv->llcc_bcast_map = llcc_driv_data->bcast_regmap; + + llcc_bcast_read(llcc_priv, LLCC_COMMON_STATUS0, &val); + llcc_priv->num_mc = (val & NUM_MC_MASK) >> NUM_MC_SHIFT; + llcc_priv->num_banks = (val & LB_CNT_MASK) >> LB_CNT_SHIFT; + for (val = 0; val < llcc_priv->num_banks; val++) + llcc_priv->bank_off[val] = BANK_OFFSET * val; + + llcc_priv->version = REV_0; + llcc_bcast_read(llcc_priv, LLCC_COMMON_HW_INFO, &val); + if (val >= LLCC_VERSION) + llcc_priv->version = REV_1; + + result = sysfs_create_group(&pdev->dev.kobj, &llcc_perfmon_group); + if (result) { + pr_err("Unable to create sysfs group\n"); + return result; + } + + mutex_init(&llcc_priv->mutex); + platform_set_drvdata(pdev, llcc_priv); + llcc_register_event_port(llcc_priv, &feac_port_ops, EVENT_PORT_FEAC); + llcc_register_event_port(llcc_priv, &ferc_port_ops, EVENT_PORT_FERC); + llcc_register_event_port(llcc_priv, &fewc_port_ops, EVENT_PORT_FEWC); + llcc_register_event_port(llcc_priv, &beac_port_ops, EVENT_PORT_BEAC); + llcc_register_event_port(llcc_priv, &berc_port_ops, EVENT_PORT_BERC); + llcc_register_event_port(llcc_priv, &trp_port_ops, EVENT_PORT_TRP); + llcc_register_event_port(llcc_priv, &drp_port_ops, EVENT_PORT_DRP); + llcc_register_event_port(llcc_priv, &pmgr_port_ops, EVENT_PORT_PMGR); + hrtimer_init(&llcc_priv->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + llcc_priv->hrtimer.function = llcc_perfmon_timer_handler; + llcc_priv->expires = 0; + return 0; +} + +static int llcc_perfmon_remove(struct platform_device *pdev) +{ + struct llcc_perfmon_private *llcc_priv = platform_get_drvdata(pdev); + + while (hrtimer_active(&llcc_priv->hrtimer)) + hrtimer_cancel(&llcc_priv->hrtimer); + + mutex_destroy(&llcc_priv->mutex); + sysfs_remove_group(&pdev->dev.kobj, &llcc_perfmon_group); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static struct platform_driver llcc_perfmon_driver = { + .probe = llcc_perfmon_probe, + .remove = llcc_perfmon_remove, + .driver = { + .name = LLCC_PERFMON_NAME, + } +}; +module_platform_driver(llcc_perfmon_driver); + +MODULE_DESCRIPTION("QCOM LLCC PMU MONITOR"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/llcc_perfmon.h b/drivers/soc/qcom/llcc_perfmon.h new file mode 100644 index 000000000000..816e4c7126dd --- /dev/null +++ b/drivers/soc/qcom/llcc_perfmon.h @@ -0,0 +1,270 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2019, The Linux Foundation. All rights reserved. + */ + +#ifndef _SOC_QCOM_LLCC_PERFMON_H_ +#define _SOC_QCOM_LLCC_PERFMON_H_ + +#define LLCC_COMMON_HW_INFO (0x30000) +#define LLCC_COMMON_STATUS0 (0x3000C) +/* FEAC */ +#define FEAC_PROF_FILTER_0_CFG3 (0x03700C) +#define FEAC_PROF_FILTER_0_CFG5 (0x037014) +#define FEAC_PROF_FILTER_0_CFG6 (0x037018) +#define FEAC_PROF_EVENT_n_CFG(n) (0x037060 + 4 * n) +#define FEAC_PROF_CFG (0x0370A0) + +/* FERC */ +#define FERC_PROF_FILTER_0_CFG0 (0x03B000) +#define FERC_PROF_EVENT_n_CFG(n) (0x03B020 + 4 * n) +#define FERC_PROF_CFG (0x03B060) + +/* FEWC */ +#define FEWC_PROF_FILTER_0_CFG0 (0x033000) +#define FEWC_PROF_EVENT_n_CFG(n) (0x033020 + 4 * n) + +/* BEAC */ +#define BEAC_PROF_FILTER_0_CFG5 (0x049014) +#define BEAC_PROF_EVENT_n_CFG(n) (0x049040 + 4 * n) +#define BEAC_PROF_CFG (0x049080) +#define BEAC_INST_OFF (0x4000) + +/* BERC */ +#define BERC_PROF_FILTER_0_CFG0 (0x039000) +#define BERC_PROF_EVENT_n_CFG(n) (0x039020 + 4 * n) +#define BERC_PROF_CFG (0x039060) + +/* TRP */ +#define TRP_PROF_FILTER_0_CFG1 (0x024004) +#define TRP_PROF_EVENT_n_CFG(n) (0x024020 + 4 * n) +#define TRP_SCID_n_STATUS(n) (0x000004 + 0x1000 * n) + +/* DRP */ +#define DRP_PROF_EVENT_n_CFG(n) (0x044010 + 4 * n) +#define DRP_PROF_CFG (0x044050) + +/* PMGR */ +#define PMGR_PROF_EVENT_n_CFG(n) (0x03F000 + 4 * n) + +#define PERFMON_COUNTER_n_CONFIG(n) (0x031020 + 4 * n) +#define PERFMON_MODE (0x03100C) +#define PERFMON_DUMP (0x031010) +#define BROADCAST_COUNTER_n_VALUE(n) (0x031060 + 4 * n) + +#define LLCC_COUNTER_n_VALUE(n) (0x031060 + 4 * n) + +#define EVENT_NUM_MAX (64) +#define SCID_MAX (32) + +/* Perfmon */ +#define CLEAR_ON_ENABLE BIT(31) +#define CLEAR_ON_DUMP BIT(30) +#define FREEZE_ON_SATURATE BIT(29) +#define CHAINING_EN BIT(28) +#define COUNT_CLOCK_EVENT BIT(24) + +#define EVENT_SELECT_SHIFT (16) +#define PERFMON_EVENT_SELECT_MASK GENMASK(EVENT_SELECT_SHIFT + 4,\ + EVENT_SELECT_SHIFT) +#define PORT_SELECT_SHIFT (0) +#define PERFMON_PORT_SELECT_MASK GENMASK(PORT_SELECT_SHIFT + 3,\ + PORT_SELECT_SHIFT) + +#define MANUAL_MODE (0) +#define TIMED_MODE (1) +#define TRIGGER_MODE (2) +#define MONITOR_EN_SHIFT (15) +#define MONITOR_EN BIT(MONITOR_EN_SHIFT) +#define PERFMON_MODE_MONITOR_EN_MASK GENMASK(MONITOR_EN_SHIFT + 0,\ + MONITOR_EN_SHIFT) +#define MONITOR_MODE_SHIFT (0) +#define PERFMON_MODE_MONITOR_MODE_MASK GENMASK(MONITOR_MODE_SHIFT + 0,\ + MONITOR_MODE_SHIFT) + +#define MONITOR_DUMP BIT(0) + +/* COMMON */ +#define BYTE_SCALING (1024) +#define BEAT_SCALING (32) +#define LB_CNT_SHIFT (28) +#define LB_CNT_MASK GENMASK(LB_CNT_SHIFT + 3, \ + LB_CNT_SHIFT) +#define NUM_MC_SHIFT (10) +#define NUM_MC_MASK GENMASK(NUM_MC_SHIFT + 1, \ + NUM_MC_SHIFT) + +#define BYTE_SCALING_SHIFT (16) +#define PROF_CFG_BYTE_SCALING_MASK GENMASK(BYTE_SCALING_SHIFT + 11,\ + BYTE_SCALING_SHIFT) +#define BEAT_SCALING_SHIFT (8) +#define PROF_CFG_BEAT_SCALING_MASK GENMASK(BEAT_SCALING_SHIFT + 7,\ + BEAT_SCALING_SHIFT) +#define PROF_EN_SHIFT (0) +#define PROF_EN BIT(PROF_EN_SHIFT) +#define PROF_CFG_EN_MASK GENMASK(PROF_EN_SHIFT + 0,\ + PROF_EN_SHIFT) + +#define FILTER_EN_SHIFT (31) +#define FILTER_EN BIT(FILTER_EN_SHIFT) +#define FILTER_EN_MASK GENMASK(FILTER_EN_SHIFT + 0,\ + FILTER_EN_SHIFT) +#define FILTER_0 (0) +#define FILTER_0_MASK GENMASK(FILTER_0 + 0, \ + FILTER_0) +#define FILTER_1 (1) +#define FILTER_1_MASK GENMASK(FILTER_1 + 0, \ + FILTER_1) + +#define FILTER_SEL_SHIFT (16) +#define FILTER_SEL_MASK GENMASK(FILTER_SEL_SHIFT + 0,\ + FILTER_SEL_SHIFT) +#define EVENT_SEL_SHIFT (0) +#define EVENT_SEL_MASK GENMASK(EVENT_SEL_SHIFT + 5,\ + EVENT_SEL_SHIFT) + +#define CACHEALLOC_MASK_SHIFT (16) +#define CACHEALLOC_MASK_MASK GENMASK(CACHEALLOC_MASK_SHIFT + 3, \ + CACHEALLOC_MASK_SHIFT) +#define CACHEALLOC_MATCH_SHIFT (12) +#define CACHEALLOC_MATCH_MASK GENMASK(CACHEALLOC_MATCH_SHIFT + 3, \ + CACHEALLOC_MATCH_SHIFT) +#define OPCODE_MASK_SHIFT (28) +#define OPCODE_MASK_MASK GENMASK(OPCODE_MASK_SHIFT + 3, \ + OPCODE_MASK_SHIFT) +#define OPCODE_MATCH_SHIFT (24) +#define OPCODE_MATCH_MASK GENMASK(OPCODE_MATCH_SHIFT + 3, \ + OPCODE_MATCH_SHIFT) +#define MID_MASK_SHIFT (16) +#define MID_MASK_MASK GENMASK(MID_MASK_SHIFT + 15, \ + MID_MASK_SHIFT) +#define MID_MATCH_SHIFT (0) +#define MID_MATCH_MASK GENMASK(MID_MATCH_SHIFT + 15, \ + MID_MATCH_SHIFT) +#define SCID_MASK_SHIFT (16) +#define SCID_MASK_MASK GENMASK(SCID_MASK_SHIFT + 15, \ + SCID_MASK_SHIFT) +#define SCID_MATCH_SHIFT (0) +#define SCID_MATCH_MASK GENMASK(SCID_MATCH_SHIFT + 15, \ + SCID_MATCH_SHIFT) +#define SCID_MULTI_MATCH_SHIFT (0) +#define SCID_MULTI_MATCH_MASK GENMASK(SCID_MULTI_MATCH_SHIFT + 31, \ + SCID_MULTI_MATCH_SHIFT) +#define PROFTAG_MASK_SHIFT (2) +#define PROFTAG_MASK_MASK GENMASK(PROFTAG_MASK_SHIFT + 1,\ + PROFTAG_MASK_SHIFT) +#define PROFTAG_MATCH_SHIFT (0) +#define PROFTAG_MATCH_MASK GENMASK(PROFTAG_MATCH_SHIFT + 1,\ + PROFTAG_MATCH_SHIFT) +/* FEAC */ +#define FEAC_SCALING_FILTER_SEL_SHIFT (2) +#define FEAC_SCALING_FILTER_SEL_MASK GENMASK(FEAC_SCALING_FILTER_SEL_SHIFT \ + + 0, \ + FEAC_SCALING_FILTER_SEL_SHIFT) +#define FEAC_SCALING_FILTER_EN_SHIFT (1) +#define FEAC_SCALING_FILTER_EN BIT(FEAC_SCALING_FILTER_EN_SHIFT) +#define FEAC_SCALING_FILTER_EN_MASK GENMASK(FEAC_SCALING_FILTER_EN_SHIFT \ + + 0, \ + FEAC_SCALING_FILTER_EN_SHIFT) + +#define FEAC_WR_BEAT_FILTER_SEL_SHIFT (29) +#define FEAC_WR_BEAT_FILTER_SEL_MASK GENMASK(FEAC_WR_BEAT_FILTER_SEL_SHIFT \ + + 0, \ + FEAC_WR_BEAT_FILTER_SEL_SHIFT) +#define FEAC_WR_BEAT_FILTER_EN_SHIFT (28) +#define FEAC_WR_BEAT_FILTER_EN_MASK GENMASK(FEAC_WR_BEAT_FILTER_EN_SHIFT \ + + 0, \ + FEAC_WR_BEAT_FILTER_EN_SHIFT) +#define FEAC_WR_BEAT_FILTER_EN BIT(FEAC_WR_BEAT_FILTER_EN_SHIFT) +#define FEAC_WR_BYTE_FILTER_SEL_SHIFT (6) +#define FEAC_WR_BYTE_FILTER_SEL_MASK GENMASK(FEAC_WR_BYTE_FILTER_SEL_SHIFT \ + + 0, \ + FEAC_WR_BYTE_FILTER_SEL_SHIFT) +#define FEAC_WR_BYTE_FILTER_EN_SHIFT (5) +#define FEAC_WR_BYTE_FILTER_EN_MASK GENMASK(FEAC_WR_BYTE_FILTER_EN_SHIFT \ + + 0, \ + FEAC_WR_BYTE_FILTER_EN_SHIFT) +#define FEAC_WR_BYTE_FILTER_EN BIT(FEAC_WR_BYTE_FILTER_EN_SHIFT) +#define FEAC_RD_BEAT_FILTER_SEL_SHIFT (4) +#define FEAC_RD_BEAT_FILTER_SEL_MASK GENMASK(FEAC_RD_BEAT_FILTER_SEL_SHIFT \ + + 0, \ + FEAC_RD_BEAT_FILTER_SEL_SHIFT) +#define FEAC_RD_BEAT_FILTER_EN_SHIFT (3) +#define FEAC_RD_BEAT_FILTER_EN_MASK GENMASK(FEAC_RD_BEAT_FILTER_EN_SHIFT \ + + 0, \ + FEAC_RD_BEAT_FILTER_EN_SHIFT) +#define FEAC_RD_BEAT_FILTER_EN BIT(FEAC_RD_BEAT_FILTER_EN_SHIFT) +#define FEAC_RD_BYTE_FILTER_SEL_SHIFT (2) +#define FEAC_RD_BYTE_FILTER_SEL_MASK GENMASK(FEAC_RD_BYTE_FILTER_SEL_SHIFT \ + + 0, \ + FEAC_RD_BYTE_FILTER_SEL_SHIFT) +#define FEAC_RD_BYTE_FILTER_EN_SHIFT (1) +#define FEAC_RD_BYTE_FILTER_EN_MASK GENMASK(FEAC_RD_BYTE_FILTER_EN_SHIFT \ + + 0, \ + FEAC_RD_BYTE_FILTER_EN_SHIFT) +#define FEAC_RD_BYTE_FILTER_EN BIT(FEAC_RD_BYTE_FILTER_EN_SHIFT) +/* BEAC */ +#define BEAC_PROFTAG_MASK_SHIFT (14) +#define BEAC_PROFTAG_MASK_MASK GENMASK(BEAC_PROFTAG_MASK_SHIFT + 1,\ + BEAC_PROFTAG_MASK_SHIFT) +#define BEAC_PROFTAG_MATCH_SHIFT (12) +#define BEAC_PROFTAG_MATCH_MASK GENMASK(BEAC_PROFTAG_MATCH_SHIFT + 1,\ + BEAC_PROFTAG_MATCH_SHIFT) +#define BEAC_MC_PROFTAG_SHIFT (1) +#define BEAC_MC_PROFTAG_MASK GENMASK(BEAC_MC_PROFTAG_SHIFT + 1,\ + BEAC_MC_PROFTAG_SHIFT) +#define BEAC_WR_BEAT_FILTER_SEL_SHIFT (6) +#define BEAC_WR_BEAT_FILTER_SEL_MASK GENMASK(BEAC_WR_BEAT_FILTER_SEL_SHIFT \ + + 0, \ + BEAC_WR_BEAT_FILTER_SEL_SHIFT) +#define BEAC_WR_BEAT_FILTER_EN_SHIFT (5) +#define BEAC_WR_BEAT_FILTER_EN_MASK GENMASK(BEAC_WR_BEAT_FILTER_EN_SHIFT \ + + 0, \ + BEAC_WR_BEAT_FILTER_EN_SHIFT) +#define BEAC_WR_BEAT_FILTER_EN BIT(BEAC_WR_BEAT_FILTER_EN_SHIFT) +#define BEAC_RD_BEAT_FILTER_SEL_SHIFT (4) +#define BEAC_RD_BEAT_FILTER_SEL_MASK GENMASK(BEAC_RD_BEAT_FILTER_SEL_SHIFT \ + + 0, \ + BEAC_RD_BEAT_FILTER_SEL_SHIFT) +#define BEAC_RD_BEAT_FILTER_EN_SHIFT (3) +#define BEAC_RD_BEAT_FILTER_EN_MASK GENMASK(BEAC_RD_BEAT_FILTER_EN_SHIFT \ + + 0, \ + BEAC_RD_BEAT_FILTER_EN_SHIFT) +#define BEAC_RD_BEAT_FILTER_EN BIT(BEAC_RD_BEAT_FILTER_EN_SHIFT) +/* TRP */ +#define TRP_SCID_MATCH_SHIFT (0) +#define TRP_SCID_MATCH_MASK GENMASK(TRP_SCID_MATCH_SHIFT + 4,\ + TRP_SCID_MATCH_SHIFT) +#define TRP_SCID_MASK_SHIFT (8) +#define TRP_SCID_MASK_MASK GENMASK(TRP_SCID_MASK_SHIFT + 4,\ + TRP_SCID_MASK_SHIFT) +#define TRP_WAY_ID_MATCH_SHIFT (16) +#define TRP_WAY_ID_MATCH_MASK GENMASK(TRP_WAY_ID_MATCH_SHIFT + 3,\ + TRP_WAY_ID_MATCH_SHIFT) +#define TRP_WAY_ID_MASK_SHIFT (20) +#define TRP_WAY_ID_MASK_MASK GENMASK(TRP_WAY_ID_MASK_SHIFT + 3,\ + TRP_WAY_ID_MASK_SHIFT) +#define TRP_PROFTAG_MATCH_SHIFT (24) +#define TRP_PROFTAG_MATCH_MASK GENMASK(TRP_PROFTAG_MATCH_SHIFT + 1,\ + TRP_PROFTAG_MATCH_SHIFT) +#define TRP_PROFTAG_MASK_SHIFT (28) +#define TRP_PROFTAG_MASK_MASK GENMASK(TRP_PROFTAG_MASK_SHIFT + 1,\ + TRP_PROFTAG_MASK_SHIFT) + +#define TRP_SCID_STATUS_ACTIVE_SHIFT (0) +#define TRP_SCID_STATUS_ACTIVE_MASK GENMASK( \ + TRP_SCID_STATUS_ACTIVE_SHIFT \ + + 0, \ + TRP_SCID_STATUS_ACTIVE_SHIFT) +#define TRP_SCID_STATUS_DEACTIVE_SHIFT (1) +#define TRP_SCID_STATUS_CURRENT_CAP_SHIFT (16) +#define TRP_SCID_STATUS_CURRENT_CAP_MASK GENMASK( \ + TRP_SCID_STATUS_CURRENT_CAP_SHIFT \ + + 13, \ + TRP_SCID_STATUS_CURRENT_CAP_SHIFT) + +#define LLCC_VERSION (0x01010100) +#define REV_0 (0x0) +#define REV_1 (0x1) +#define BANK_OFFSET (0x80000) +#endif /* _SOC_QCOM_LLCC_PERFMON_H_ */