Merge "qcom: llcc_perfmon: Add support for new configuration format"

This commit is contained in:
qctecmdr 2023-03-26 21:24:48 -07:00 committed by Gerrit - the friendly Code Review server
commit 047374a076

View File

@ -46,7 +46,6 @@
* @event_sel: Event selected for configured counter
* @filter_en: Filter activation flag for configured counter
* @filter_sel: Filter applied for configured counter
* @ports_supported: Ports reserved for configured counter
* @counter_dump: Cumulative counter dump
*/
struct llcc_perfmon_counter_map {
@ -54,7 +53,6 @@ struct llcc_perfmon_counter_map {
unsigned int event_sel;
bool filter_en;
u8 filter_sel;
u16 ports_supported;
unsigned long long counter_dump[NUM_CHANNELS];
};
@ -89,8 +87,8 @@ enum fltr_config {
* @port_ops: struct event_port_ops
* @configured: Mapping of configured event counters
* @configured_cntrs: Count of configured counters.
* @removed_cntrs: Count of removed counter configurations.
* @enables_port: Port enabled for perfmon configuration
* @filtered_ports: Port filter enabled
* @port_filter_sel: Port filter enabled for Filter0 and Filter1
* @filters_applied: List of all filters applied on ports
* @fltr_logic: Filter selection logic to support existing Filter 0 approach
@ -113,8 +111,8 @@ struct llcc_perfmon_private {
struct event_port_ops *port_ops[MAX_NUMBER_OF_PORTS];
struct llcc_perfmon_counter_map configured[MAX_CNTR];
unsigned int configured_cntrs;
unsigned int removed_cntrs;
unsigned int enables_port;
unsigned int filtered_ports;
unsigned int port_filter_sel[MAX_FILTERS_TYPE];
u8 filters_applied[MAX_NUMBER_OF_PORTS][MAX_FILTERS][MAX_FILTERS_TYPE];
enum fltr_config fltr_logic;
@ -299,6 +297,10 @@ static void remove_counters(struct llcc_perfmon_private *llcc_priv)
/* Remove the counters configured for ports */
for (i = 0; i < llcc_priv->configured_cntrs - 1; i++) {
counter_map = &llcc_priv->configured[i];
/* In case the port configuration is already removed, skip */
if (counter_map->port_sel == MAX_NUMBER_OF_PORTS)
continue;
port_ops = llcc_priv->port_ops[counter_map->port_sel];
port_ops->event_config(llcc_priv, 0, &i, false);
pr_info("removed counter %2d for event %2ld from port %2ld\n", i,
@ -325,6 +327,19 @@ static void remove_counters(struct llcc_perfmon_private *llcc_priv)
}
}
static bool find_filter_index(const char *token, u8 *filter_idx)
{
if (sysfs_streq(token, "FILTER0")) {
*filter_idx = FILTER_0;
return true;
} else if (sysfs_streq(token, "FILTER1")) {
*filter_idx = FILTER_1;
return true;
} else {
return false;
}
}
static ssize_t perfmon_configure_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
@ -335,15 +350,38 @@ static ssize_t perfmon_configure_store(struct device *dev,
unsigned long port_sel, event_sel;
uint32_t val, offset;
char *token, *delim = DELIM_CHAR;
u8 filter_idx = FILTER_0;
bool multi_fltr_flag = false;
mutex_lock(&llcc_priv->mutex);
if (llcc_priv->configured_cntrs) {
if (llcc_priv->configured_cntrs == MAX_CNTR - 1) {
pr_err("Counters configured already, remove & try again\n");
mutex_unlock(&llcc_priv->mutex);
return -EINVAL;
}
/* Cheking whether an existing counter configuration is present, if present initializing
* the count to number of configured counters - 1 to overwrite the last configured cyclic
* counter. Cyclic count will be configured to the last counter available.
*/
if (llcc_priv->configured_cntrs)
j = llcc_priv->configured_cntrs - 1;
token = strsep((char **)&buf, delim);
/* Getting filter information if provided */
if (strlen(token) == strlen("FILTERX")) {
if (llcc_priv->fltr_logic != multiple_filtr) {
pr_err("Error Multifilter configuration not present\n");
goto out_configure;
}
if (!find_filter_index(token, &filter_idx)) {
pr_err("Invalid Filter Index, supported are FILTER0/1\n");
goto out_configure;
}
multi_fltr_flag = true;
token = strsep((char **)&buf, delim);
}
while (token != NULL) {
if (kstrtoul(token, 0, &port_sel))
@ -352,6 +390,14 @@ static ssize_t perfmon_configure_store(struct device *dev,
if (port_sel >= llcc_priv->port_configd)
break;
/* Checking whether given filter is enabled for the port */
if (multi_fltr_flag &&
!(llcc_priv->port_filter_sel[filter_idx] & (1 << port_sel))) {
pr_err("Filter not configured for given port, removing configurations\n");
remove_counters(llcc_priv);
goto out_configure;
}
token = strsep((char **)&buf, delim);
if (token == NULL)
break;
@ -376,30 +422,16 @@ static ssize_t perfmon_configure_store(struct device *dev,
counter_map = &llcc_priv->configured[j];
counter_map->port_sel = port_sel;
counter_map->event_sel = event_sel;
/* Conflict in Filter selection logic */
if (counter_map->filter_en && !(counter_map->ports_supported & (1 << port_sel))) {
pr_err("Counter %hhu is not reserved for port %hhu. %s\n", j, port_sel,
"Reversing the configured counters");
for (k = 0; k < j; k++) {
counter_map = &llcc_priv->configured[k];
port_ops = llcc_priv->port_ops[counter_map->port_sel];
port_ops->event_config(llcc_priv, counter_map->event_sel,
&k, false);
pr_info("Removed counter %2d for event %2ld from port %ld\n",
k, counter_map->event_sel, counter_map->port_sel);
}
/* Removing configured filters as well */
remove_filters(llcc_priv);
goto out_configure;
if (multi_fltr_flag) {
counter_map->filter_en = true;
counter_map->filter_sel = filter_idx;
}
for (k = 0; k < llcc_priv->num_banks; k++)
counter_map->counter_dump[k] = 0;
port_ops = llcc_priv->port_ops[port_sel];
/* if any perfmon configuration fails, remove the existing configurations */
if (!port_ops->event_config(llcc_priv, event_sel, &j, true)) {
llcc_priv->configured_cntrs = ++j;
remove_counters(llcc_priv);
@ -441,6 +473,8 @@ static ssize_t perfmon_remove_store(struct device *dev,
unsigned long port_sel, event_sel;
char *token, *delim = DELIM_CHAR;
uint32_t offset;
u8 filter_idx = FILTER_0;
bool multi_fltr_flag = false, filter_en = false;
mutex_lock(&llcc_priv->mutex);
if (!llcc_priv->configured_cntrs) {
@ -449,14 +483,35 @@ static ssize_t perfmon_remove_store(struct device *dev,
return -EINVAL;
}
/* Checking if counters were removed earlier, setting the count value to next counter */
if (llcc_priv->removed_cntrs &&
llcc_priv->removed_cntrs < (llcc_priv->configured_cntrs - MAX_CLOCK_CNTR))
j = llcc_priv->removed_cntrs;
token = strsep((char **)&buf, delim);
/* Case for removing all the counters at once */
if (token && sysfs_streq(token, "REMOVE")) {
pr_info("Removing all configured counters and Filters\n");
remove_counters(llcc_priv);
goto out_remove_store;
}
/* Getting filter information if provided */
if (strlen(token) == strlen("FILTERX")) {
if (llcc_priv->fltr_logic != multiple_filtr) {
pr_err("Error! Multifilter configuration not present\n");
goto out_remove_store_err;
}
if (!find_filter_index(token, &filter_idx)) {
pr_err("Error! Invalid Filter Index, supported are FILTER0/1\n");
goto out_remove_store_err;
}
multi_fltr_flag = true;
token = strsep((char **)&buf, delim);
}
while (token != NULL) {
if (kstrtoul(token, 0, &port_sel))
break;
@ -464,6 +519,34 @@ static ssize_t perfmon_remove_store(struct device *dev,
if (port_sel >= llcc_priv->port_configd)
break;
/* Counter mapping for last removed counter */
counter_map = &llcc_priv->configured[j];
/* Getting filter activation status for given port and filter type 0/1 */
if (counter_map->filter_en)
filter_en = llcc_priv->port_filter_sel[counter_map->filter_sel] &
(1 << port_sel);
/* If multifilters format is used to remove perfmon configuration checking if given
* port is same as configured port on current counter checking if filter is enabled
* on current counter for given port with same filter type FILTER0/FILTER1
*/
if (counter_map->port_sel == port_sel) {
if (multi_fltr_flag && !filter_en) {
pr_err("Error! filter not present counter:%u for port:%u\n", j,
port_sel);
goto out_remove_store_err;
} else if (!multi_fltr_flag && filter_en) {
pr_err("Error! Filter is present on counter:%u for port:%u\n", j,
port_sel);
goto out_remove_store_err;
}
} else {
pr_err("Error! Given port %u is not configured on counter %u\n", port_sel,
j);
goto out_remove_store_err;
}
token = strsep((char **)&buf, delim);
if (token == NULL)
break;
@ -486,11 +569,11 @@ static ssize_t perfmon_remove_store(struct device *dev,
break;
/* put dummy values */
counter_map = &llcc_priv->configured[j];
counter_map->port_sel = MAX_NUMBER_OF_PORTS;
counter_map->event_sel = 0;
port_ops = llcc_priv->port_ops[port_sel];
port_ops->event_config(llcc_priv, event_sel, &j, false);
llcc_priv->removed_cntrs++;
pr_info("removed counter %2d for event %2ld from port %2ld\n", j++, event_sel,
port_sel);
if ((llcc_priv->enables_port & (1 << port_sel)) && port_ops->event_enable)
@ -499,66 +582,24 @@ static ssize_t perfmon_remove_store(struct device *dev,
llcc_priv->enables_port &= ~(1 << port_sel);
}
/* remove clock event */
offset = PERFMON_COUNTER_n_CONFIG(llcc_priv->drv_ver, j);
llcc_bcast_write(llcc_priv, offset, 0);
llcc_priv->configured_cntrs = 0;
/* If count reached to configured counters then removing clock event, else updating the
* removed counters list to support next removal configuraiton
*/
if (j == llcc_priv->configured_cntrs - 1) {
pr_info("All couters removed, removing last cyclic counter\n");
offset = PERFMON_COUNTER_n_CONFIG(llcc_priv->drv_ver, j);
llcc_bcast_write(llcc_priv, offset, 0);
llcc_priv->configured_cntrs = 0;
llcc_priv->removed_cntrs = 0;
}
out_remove_store:
mutex_unlock(&llcc_priv->mutex);
return count;
}
static int cntr_fltr_select(const char *buf, struct llcc_perfmon_private *llcc_priv,
u8 filter_idx, unsigned long ports, u16 *cntrs_res_temp, bool enable)
{
char *token, *delim = DELIM_CHAR;
int ret = -EINVAL;
unsigned long long cntr;
token = strsep((char **)&buf, delim);
/*
* For special cases where counter reservation is not needed and multiple filters are
* required on a given port
*/
if (token == NULL) {
pr_info("Selected filter configuration without counters reservation\n");
ret = 0;
}
while (token != NULL) {
if (kstrtoull(token, 0, &cntr)) {
pr_err("filter configuration failed, Wrong format\n");
return ret;
}
if (enable) {
if (!llcc_priv->configured[cntr].filter_en) {
llcc_priv->configured[cntr].filter_en = true;
llcc_priv->configured[cntr].filter_sel = filter_idx;
llcc_priv->configured[cntr].ports_supported = ports;
*cntrs_res_temp |= (1 << cntr);
ret = 0;
} else {
pr_err("Conflict, counter already configured for filter\n");
ret = -EINVAL;
}
} else {
if (llcc_priv->configured[cntr].filter_en &&
llcc_priv->configured[cntr].filter_sel == filter_idx) {
llcc_priv->configured[cntr].filter_en = false;
ret = 0;
} else {
pr_err("Conflict, Filter counter mismatch, removal failed\n");
ret = -EINVAL;
}
}
token = strsep((char **)&buf, delim);
}
return ret;
out_remove_store_err:
remove_counters(llcc_priv);
mutex_unlock(&llcc_priv->mutex);
return -EINVAL;
}
static enum filter_type find_filter_type(char *filter)
@ -589,18 +630,6 @@ static enum filter_type find_filter_type(char *filter)
return ret;
}
static bool find_filter_index(const char *token, u8 *filter_idx)
{
if (sysfs_streq(token, "FILTER0"))
return true;
else if (sysfs_streq(token, "FILTER1"))
*filter_idx = FILTER_1;
else
return false;
return true;
}
static ssize_t perfmon_filter_config_store(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
@ -612,7 +641,6 @@ static ssize_t perfmon_filter_config_store(struct device *dev,
char *token, *delim = DELIM_CHAR;
enum filter_type fil_applied = UNKNOWN_FILTER;
u8 filter_idx = 0, i;
u16 cntrs_res_temp = 0;
bool filter_status;
if (llcc_priv->configured_cntrs) {
@ -677,12 +705,6 @@ static ssize_t perfmon_filter_config_store(struct device *dev,
goto filter_config_free;
}
if (cntr_fltr_select(buf, llcc_priv, filter_idx, port_filter_en,
&cntrs_res_temp, true)) {
pr_err("Counter values not provided, try again\n");
goto filter_config_free;
}
llcc_priv->fltr_logic = multiple_filtr;
pr_info("Selective filter configuration selected\n");
break;
@ -836,9 +858,6 @@ static ssize_t perfmon_filter_remove_store(struct device *dev,
goto filter_remove_free;
}
if (cntr_fltr_select(buf, llcc_priv, filter_idx, port_filter_en, NULL,
false))
goto filter_remove_free;
break;
}
@ -1121,10 +1140,9 @@ static bool feac_event_config(struct llcc_perfmon_private *llcc_priv,
bool enable)
{
uint32_t val = 0, mask_val, offset;
int filter_en, filter_sel;
u8 filter_en, filter_sel = FILTER_0;
filter_en = llcc_priv->port_filter_sel[0] & (1 << EVENT_PORT_FEAC);
filter_sel = FILTER_0;
filter_en = llcc_priv->port_filter_sel[filter_sel] & (1 << EVENT_PORT_FEAC);
if (llcc_priv->fltr_logic == multiple_filtr) {
filter_en = llcc_priv->configured[*counter_num].filter_en;
filter_sel = llcc_priv->configured[*counter_num].filter_sel;
@ -1173,16 +1191,16 @@ static void feac_event_enable(struct llcc_perfmon_private *llcc_priv,
bool enable)
{
uint32_t val = 0, val_cfg1 = 0, mask_val = 0, offset;
bool filter_0 = false, filter_1 = false;
bool prof_cfg_filter = false, prof_cfg1_filter1 = false;
filter_0 = llcc_priv->port_filter_sel[0] & (1 << EVENT_PORT_FEAC);
filter_1 = llcc_priv->port_filter_sel[1] & (1 << EVENT_PORT_FEAC);
prof_cfg_filter = llcc_priv->port_filter_sel[FILTER_0] & (1 << EVENT_PORT_FEAC);
prof_cfg1_filter1 = llcc_priv->port_filter_sel[FILTER_1] & (1 << EVENT_PORT_FEAC);
val = (BYTE_SCALING << BYTE_SCALING_SHIFT) | (BEAT_SCALING << BEAT_SCALING_SHIFT);
val_cfg1 = (BYTE_SCALING << BYTE_SCALING_SHIFT) | (BEAT_SCALING << BEAT_SCALING_SHIFT);
mask_val = PROF_CFG_BEAT_SCALING_MASK | PROF_CFG_BYTE_SCALING_MASK | PROF_CFG_EN_MASK;
if (filter_0 || filter_1) {
if (prof_cfg_filter || prof_cfg1_filter1) {
if (llcc_priv->version == REV_0) {
mask_val |= FEAC_SCALING_FILTER_SEL_MASK | FEAC_SCALING_FILTER_EN_MASK;
val |= (FILTER_0 << FEAC_SCALING_FILTER_SEL_SHIFT) |
@ -1195,7 +1213,7 @@ static void feac_event_enable(struct llcc_perfmon_private *llcc_priv,
val |= FEAC_WR_BEAT_FILTER_EN | FEAC_WR_BYTE_FILTER_EN |
FEAC_RD_BEAT_FILTER_EN | FEAC_RD_BYTE_FILTER_EN;
if (filter_0 && filter_1) {
if (prof_cfg_filter && prof_cfg1_filter1) {
val_cfg1 = val;
val |= (FILTER_0 << FEAC_WR_BEAT_FILTER_SEL_SHIFT) |
(FILTER_0 << FEAC_WR_BYTE_FILTER_SEL_SHIFT) |
@ -1205,12 +1223,12 @@ static void feac_event_enable(struct llcc_perfmon_private *llcc_priv,
(FILTER_1 << FEAC_WR_BYTE_FILTER_SEL_SHIFT) |
(FILTER_1 << FEAC_RD_BEAT_FILTER_SEL_SHIFT) |
(FILTER_1 << FEAC_RD_BYTE_FILTER_SEL_SHIFT);
} else if (filter_1) {
} else if (prof_cfg1_filter1) {
val |= (FILTER_1 << FEAC_WR_BEAT_FILTER_SEL_SHIFT) |
(FILTER_1 << FEAC_WR_BYTE_FILTER_SEL_SHIFT) |
(FILTER_1 << FEAC_RD_BEAT_FILTER_SEL_SHIFT) |
(FILTER_1 << FEAC_RD_BYTE_FILTER_SEL_SHIFT);
} else if (filter_0) {
} else if (prof_cfg_filter) {
val |= (FILTER_0 << FEAC_WR_BEAT_FILTER_SEL_SHIFT) |
(FILTER_0 << FEAC_WR_BYTE_FILTER_SEL_SHIFT) |
(FILTER_0 << FEAC_RD_BEAT_FILTER_SEL_SHIFT) |
@ -1230,7 +1248,7 @@ static void feac_event_enable(struct llcc_perfmon_private *llcc_priv,
* filter0 & 1 can be applied on PROF_CFG and PROG_CFG1 respectively. Otherwise for a
* single applied filter only PROF_CFG will be used for either filter 0 or 1
*/
if ((llcc_priv->version >= REV_2) && (filter_0 && filter_1)) {
if (llcc_priv->version >= REV_2 && (prof_cfg_filter && prof_cfg1_filter1)) {
offset = FEAC_PROF_CFG(llcc_priv->drv_ver);
llcc_bcast_modify(llcc_priv, offset, val, mask_val);
mask_val &= ~PROF_CFG_EN_MASK;
@ -1395,10 +1413,9 @@ static bool ferc_event_config(struct llcc_perfmon_private *llcc_priv,
bool enable)
{
uint32_t val = 0, mask_val, offset;
int filter_en, filter_sel;
u8 filter_en, filter_sel = FILTER_0;
filter_en = llcc_priv->port_filter_sel[0] & (1 << EVENT_PORT_FERC);
filter_sel = FILTER_0;
filter_en = llcc_priv->port_filter_sel[filter_sel] & (1 << EVENT_PORT_FERC);
if (llcc_priv->fltr_logic == multiple_filtr) {
filter_en = llcc_priv->configured[*counter_num].filter_en;
filter_sel = llcc_priv->configured[*counter_num].filter_sel;
@ -1468,10 +1485,9 @@ static bool fewc_event_config(struct llcc_perfmon_private *llcc_priv,
bool enable)
{
uint32_t val = 0, mask_val, offset;
int filter_en, filter_sel;
u8 filter_en, filter_sel = FILTER_0;
filter_en = llcc_priv->port_filter_sel[0] & (1 << EVENT_PORT_FEWC);
filter_sel = FILTER_0;
filter_en = llcc_priv->port_filter_sel[filter_sel] & (1 << EVENT_PORT_FEWC);
if (llcc_priv->fltr_logic == multiple_filtr) {
filter_en = llcc_priv->configured[*counter_num].filter_en;
filter_sel = llcc_priv->configured[*counter_num].filter_sel;
@ -1530,11 +1546,10 @@ static bool beac_event_config(struct llcc_perfmon_private *llcc_priv,
uint32_t valcfg = 0, mask_valcfg = 0;
unsigned int mc_cnt, offset;
struct llcc_perfmon_counter_map *counter_map;
int filter_en, filter_sel;
u8 filter_en, filter_sel = FILTER_0;
uint32_t mask_val_cfg, val_cfg0 = 0, val_cfg1 = 0;
filter_en = llcc_priv->port_filter_sel[0] & (1 << EVENT_PORT_BEAC);
filter_sel = FILTER_0;
filter_en = llcc_priv->port_filter_sel[filter_sel] & (1 << EVENT_PORT_BEAC);
if (llcc_priv->fltr_logic == multiple_filtr) {
filter_en = llcc_priv->configured[*counter_num].filter_en;
filter_sel = llcc_priv->configured[*counter_num].filter_sel;
@ -1776,10 +1791,9 @@ static bool berc_event_config(struct llcc_perfmon_private *llcc_priv,
bool enable)
{
uint32_t val = 0, mask_val, offset;
int filter_en, filter_sel;
u8 filter_en, filter_sel = FILTER_0;
filter_en = llcc_priv->port_filter_sel[0] & (1 << EVENT_PORT_BERC);
filter_sel = FILTER_0;
filter_en = llcc_priv->port_filter_sel[filter_sel] & (1 << EVENT_PORT_BERC);
if (llcc_priv->fltr_logic == multiple_filtr) {
filter_en = llcc_priv->configured[*counter_num].filter_en;
filter_sel = llcc_priv->configured[*counter_num].filter_sel;
@ -1849,10 +1863,9 @@ static bool trp_event_config(struct llcc_perfmon_private *llcc_priv,
bool enable)
{
uint32_t val = 0, mask_val;
int filter_en, filter_sel;
u8 filter_en, filter_sel = FILTER_0;
filter_en = llcc_priv->port_filter_sel[0] & (1 << EVENT_PORT_TRP);
filter_sel = FILTER_0;
filter_en = llcc_priv->port_filter_sel[filter_sel] & (1 << EVENT_PORT_TRP);
if (llcc_priv->fltr_logic == multiple_filtr) {
filter_en = llcc_priv->configured[*counter_num].filter_en;
filter_sel = llcc_priv->configured[*counter_num].filter_sel;
@ -1952,10 +1965,9 @@ static bool drp_event_config(struct llcc_perfmon_private *llcc_priv,
bool enable)
{
uint32_t val = 0, mask_val, offset;
int filter_en, filter_sel;
u8 filter_en, filter_sel = FILTER_0;
filter_en = llcc_priv->port_filter_sel[0] & (1 << EVENT_PORT_DRP);
filter_sel = FILTER_0;
filter_en = llcc_priv->port_filter_sel[filter_sel] & (1 << EVENT_PORT_DRP);
if (llcc_priv->fltr_logic == multiple_filtr) {
filter_en = llcc_priv->configured[*counter_num].filter_en;
filter_sel = llcc_priv->configured[*counter_num].filter_sel;
@ -2007,10 +2019,9 @@ static bool pmgr_event_config(struct llcc_perfmon_private *llcc_priv,
bool enable)
{
uint32_t val = 0, mask_val, offset;
int filter_en, filter_sel;
u8 filter_en, filter_sel = FILTER_0;
filter_en = llcc_priv->port_filter_sel[0] & (1 << EVENT_PORT_PMGR);
filter_sel = FILTER_0;
filter_en = llcc_priv->port_filter_sel[filter_sel] & (1 << EVENT_PORT_PMGR);
if (llcc_priv->fltr_logic == multiple_filtr) {
filter_en = llcc_priv->configured[*counter_num].filter_en;
filter_sel = llcc_priv->configured[*counter_num].filter_sel;