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_ */