diff --git a/drivers/soc/qcom/llcc_perfmon.c b/drivers/soc/qcom/llcc_perfmon.c index 1e5a3f67278b..c2055e23f858 100644 --- a/drivers/soc/qcom/llcc_perfmon.c +++ b/drivers/soc/qcom/llcc_perfmon.c @@ -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;