diff --git a/drivers/net/wireless/cnss2/main.c b/drivers/net/wireless/cnss2/main.c index 44dad7fbcd55..f06d2fd29c7a 100644 --- a/drivers/net/wireless/cnss2/main.c +++ b/drivers/net/wireless/cnss2/main.c @@ -2230,75 +2230,130 @@ int cnss_minidump_remove_region(struct cnss_plat_data *plat_priv, return ret; } +/** + * cnss_register_bus_scale() - Setup interconnect voting data + * @plat_priv: Platform data structure + * + * For different interconnect path configured in device tree setup voting data + * for list of bandwidth requirements. + * + * Result: 0 for success. -EINVAL if not configured + */ static int cnss_register_bus_scale(struct cnss_plat_data *plat_priv) { - int ret = 0, len = 0, i, j; - struct cnss_bus_bw_info *bus_bw_info; - const u32 *cfg_arr; + int ret = -EINVAL; + u32 idx, i, j, cfg_arr_size, *cfg_arr; + struct cnss_bus_bw_info *bus_bw_info, *tmp; + struct device *dev = &plat_priv->plat_dev->dev; - bus_bw_info = &plat_priv->bus_bw_info; - - bus_bw_info->cnss_path = - of_icc_get(&plat_priv->plat_dev->dev, NULL); - - if (!bus_bw_info->cnss_path) { - cnss_pr_err("Skip Bus BW setup. Interconnect not configured\n"); - return 0; - } - - if (IS_ERR(bus_bw_info->cnss_path)) { - ret = PTR_ERR(bus_bw_info->cnss_path); - if (ret != -EPROBE_DEFER) { - cnss_pr_err("Failed to get Interconnect path: %d!\n", - ret); - goto out; - } + INIT_LIST_HEAD(&plat_priv->icc.list_head); + ret = of_property_read_u32(dev->of_node, + "qcom,icc-path-count", + &plat_priv->icc.path_count); + if (ret) { + cnss_pr_err("Platform Bus Interconnect path not configured\n"); + return -EINVAL; } ret = of_property_read_u32(plat_priv->plat_dev->dev.of_node, - "qcom,bus-bw-cfg-num", - &bus_bw_info->num_cfg); + "qcom,bus-bw-cfg-count", + &plat_priv->icc.bus_bw_cfg_count); if (ret) { cnss_pr_err("Failed to get Bus BW Config table size\n"); - goto out; + goto cleanup; } - - cfg_arr = of_get_property(plat_priv->plat_dev->dev.of_node, - "qcom,bus-bw-cfg", &len); + cfg_arr_size = plat_priv->icc.path_count * + plat_priv->icc.bus_bw_cfg_count * CNSS_ICC_VOTE_MAX; + cfg_arr = kzalloc(cfg_arr_size, GFP_KERNEL); if (!cfg_arr) { - cnss_pr_err("Bus BW Config Table not setup!\n"); - ret = -EINVAL; - goto out; + cnss_pr_err("Failed to alloc cfg table mem\n"); + ret = -ENOMEM; + goto cleanup; } - bus_bw_info->cfg_table = kcalloc(bus_bw_info->num_cfg, - sizeof(*bus_bw_info->cfg_table), - GFP_KERNEL); - if (!bus_bw_info->cfg_table) { - cnss_pr_err("No mem for Bus BW config table\n"); - return -ENOMEM; + ret = of_property_read_u32_array(plat_priv->plat_dev->dev.of_node, + "qcom,bus-bw-cfg", cfg_arr, + cfg_arr_size); + if (ret) { + cnss_pr_err("Invalid Bus BW Config Table\n"); + goto cleanup; } - for (i = 0, j = 0; i < bus_bw_info->num_cfg; i++, j += 2) { - bus_bw_info->cfg_table[i].ab = be32_to_cpu(cfg_arr[j]); - bus_bw_info->cfg_table[i].ib = be32_to_cpu(cfg_arr[j + 1]); - cnss_pr_dbg("Bandwidth Type: %d bw_cfg.ab: %d bw_cfg.ib: %d\n", - i, bus_bw_info->cfg_table[i].ab, - bus_bw_info->cfg_table[i].ib); + + cnss_pr_dbg("ICC Path_Count: %d BW_CFG_Count: %d\n", + plat_priv->icc.path_count, plat_priv->icc.bus_bw_cfg_count); + + for (idx = 0; idx < plat_priv->icc.path_count; idx++) { + bus_bw_info = devm_kzalloc(dev, sizeof(*bus_bw_info), + GFP_KERNEL); + if (!bus_bw_info) { + ret = -ENOMEM; + goto out; + } + ret = of_property_read_string_index(dev->of_node, + "interconnect-names", idx, + &bus_bw_info->icc_name); + if (ret) + goto out; + + bus_bw_info->icc_path = + of_icc_get(&plat_priv->plat_dev->dev, + bus_bw_info->icc_name); + + if (IS_ERR(bus_bw_info->icc_path)) { + ret = PTR_ERR(bus_bw_info->icc_path); + if (ret != -EPROBE_DEFER) { + cnss_pr_err("Failed to get Interconnect path for %s. Err: %d\n", + bus_bw_info->icc_name, ret); + goto out; + } + } + + bus_bw_info->cfg_table = + devm_kcalloc(dev, plat_priv->icc.bus_bw_cfg_count, + sizeof(*bus_bw_info->cfg_table), + GFP_KERNEL); + if (!bus_bw_info->cfg_table) { + ret = -ENOMEM; + goto out; + } + cnss_pr_dbg("ICC Vote CFG for path: %s\n", + bus_bw_info->icc_name); + for (i = 0, j = (idx * plat_priv->icc.bus_bw_cfg_count * + CNSS_ICC_VOTE_MAX); + i < plat_priv->icc.bus_bw_cfg_count; + i++, j += 2) { + bus_bw_info->cfg_table[i].avg_bw = cfg_arr[j]; + bus_bw_info->cfg_table[i].peak_bw = cfg_arr[j + 1]; + cnss_pr_dbg("ICC Vote BW: %d avg: %d peak: %d\n", + i, bus_bw_info->cfg_table[i].avg_bw, + bus_bw_info->cfg_table[i].peak_bw); + } + list_add_tail(&bus_bw_info->list, + &plat_priv->icc.list_head); } + kfree(cfg_arr); return 0; out: + list_for_each_entry_safe(bus_bw_info, tmp, + &plat_priv->icc.list_head, list) { + list_del(&bus_bw_info->list); + } +cleanup: + kfree(cfg_arr); + memset(&plat_priv->icc, 0, sizeof(plat_priv->icc)); return ret; } static void cnss_unregister_bus_scale(struct cnss_plat_data *plat_priv) { - struct cnss_bus_bw_info *bus_bw_info; + struct cnss_bus_bw_info *bus_bw_info, *tmp; - bus_bw_info = &plat_priv->bus_bw_info; - - if (bus_bw_info->cnss_path) - icc_put(bus_bw_info->cnss_path); - - kfree(bus_bw_info->cfg_table); + list_for_each_entry_safe(bus_bw_info, tmp, + &plat_priv->icc.list_head, list) { + list_del(&bus_bw_info->list); + if (bus_bw_info->icc_path) + icc_put(bus_bw_info->icc_path); + } + memset(&plat_priv->icc, 0, sizeof(plat_priv->icc)); } static ssize_t recovery_store(struct device *dev, diff --git a/drivers/net/wireless/cnss2/main.h b/drivers/net/wireless/cnss2/main.h index 9e79766e71f1..a01bfdc421ca 100644 --- a/drivers/net/wireless/cnss2/main.h +++ b/drivers/net/wireless/cnss2/main.h @@ -140,18 +140,48 @@ struct cnss_esoc_info { }; #endif +/** + * struct cnss_bus_bw_cfg - Interconnect vote data + * @avg_bw: Vote for average bandwidth + * @peak_bw: Vote for peak bandwidth + */ struct cnss_bus_bw_cfg { - u32 ab; - u32 ib; + u32 avg_bw; + u32 peak_bw; }; +/* Number of bw votes (avg, peak) entries that ICC requires */ +#define CNSS_ICC_VOTE_MAX 2 + +/** + * struct cnss_bus_bw_info - Bus bandwidth config for interconnect path + * @list: Kernel linked list + * @icc_name: Name of interconnect path as defined in Device tree + * @icc_path: Interconnect path data structure + * @cfg_table: Interconnect vote data for average and peak bandwidth + */ struct cnss_bus_bw_info { - struct icc_path *cnss_path; - int current_bw_vote; - u32 num_cfg; + struct list_head list; + const char *icc_name; + struct icc_path *icc_path; struct cnss_bus_bw_cfg *cfg_table; }; +/** + * struct cnss_interconnect_cfg - CNSS platform interconnect config + * @list_head: List of interconnect path bandwidth configs + * @path_count: Count of interconnect path configured in device tree + * @current_bw_vote: WLAN driver provided bandwidth vote + * @bus_bw_cfg_count: Number of bandwidth configs for voting. It is the array + * size of struct cnss_bus_bw_info.cfg_table + */ +struct cnss_interconnect_cfg { + struct list_head list_head; + u32 path_count; + int current_bw_vote; + u32 bus_bw_cfg_count; +}; + struct cnss_fw_mem { size_t size; void *va; @@ -360,7 +390,7 @@ struct cnss_plat_data { #if IS_ENABLED(CONFIG_ESOC) struct cnss_esoc_info esoc_info; #endif - struct cnss_bus_bw_info bus_bw_info; + struct cnss_interconnect_cfg icc; struct notifier_block modem_nb; struct notifier_block reboot_nb; struct notifier_block panic_nb; diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c index 551536ed80d4..c5a3dbdce390 100644 --- a/drivers/net/wireless/cnss2/pci.c +++ b/drivers/net/wireless/cnss2/pci.c @@ -509,42 +509,58 @@ static int cnss_pci_force_wake_put(struct cnss_pci_data *pci_priv) return ret; } -int cnss_request_bus_bandwidth(struct device *dev, int bandwidth) +/** + * cnss_setup_bus_bandwidth() - Setup interconnect vote for given bandwidth + * @plat_priv: Platform private data struct + * @bw: bandwidth + * @save: toggle flag to save bandwidth to current_bw_vote + * + * Setup bandwidth votes for configured interconnect paths + * + * Return: 0 for success + */ +static int cnss_setup_bus_bandwidth(struct cnss_plat_data *plat_priv, + u32 bw, bool save) { int ret = 0; - struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); struct cnss_bus_bw_info *bus_bw_info; + if (!plat_priv->icc.path_count) + return -ENOTSUPP; + + if (bw >= plat_priv->icc.bus_bw_cfg_count) { + cnss_pr_err("Invalid bus bandwidth Type: %d", bw); + return -EINVAL; + } + + list_for_each_entry(bus_bw_info, &plat_priv->icc.list_head, list) { + ret = icc_set_bw(bus_bw_info->icc_path, + bus_bw_info->cfg_table[bw].avg_bw, + bus_bw_info->cfg_table[bw].peak_bw); + if (ret) { + cnss_pr_err("Could not set BW Cfg: %d, err = %d ICC Path: %s Val: %d %d\n", + bw, ret, bus_bw_info->icc_name, + bus_bw_info->cfg_table[bw].avg_bw, + bus_bw_info->cfg_table[bw].peak_bw); + break; + } + } + if (ret == 0 && save) + plat_priv->icc.current_bw_vote = bw; + return ret; +} + +int cnss_request_bus_bandwidth(struct device *dev, int bandwidth) +{ + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); + if (!plat_priv) return -ENODEV; - bus_bw_info = &plat_priv->bus_bw_info; - if (!bus_bw_info->cnss_path || bandwidth > bus_bw_info->num_cfg) + if (bandwidth < 0) return -EINVAL; - switch (bandwidth) { - case CNSS_BUS_WIDTH_NONE: - case CNSS_BUS_WIDTH_IDLE: - case CNSS_BUS_WIDTH_LOW: - case CNSS_BUS_WIDTH_MEDIUM: - case CNSS_BUS_WIDTH_HIGH: - case CNSS_BUS_WIDTH_VERY_HIGH: - case CNSS_BUS_WIDTH_LOW_LATENCY: - ret = icc_set_bw(bus_bw_info->cnss_path, - bus_bw_info->cfg_table[bandwidth].ab, - bus_bw_info->cfg_table[bandwidth].ib); - if (!ret) - bus_bw_info->current_bw_vote = bandwidth; - else - cnss_pr_err("Could not set bus bandwidth: %d, err = %d\n", - bandwidth, ret); - break; - default: - cnss_pr_err("Invalid bus bandwidth: %d", bandwidth); - ret = -EINVAL; - } - - return ret; + return cnss_setup_bus_bandwidth(plat_priv, (u32)bandwidth, true); } EXPORT_SYMBOL(cnss_request_bus_bandwidth); @@ -2987,7 +3003,6 @@ int cnss_auto_suspend(struct device *dev) struct pci_dev *pci_dev = to_pci_dev(dev); struct cnss_pci_data *pci_priv = cnss_get_pci_priv(pci_dev); struct cnss_plat_data *plat_priv; - struct cnss_bus_bw_info *bus_bw_info; if (!pci_priv) return -ENODEV; @@ -3008,18 +3023,11 @@ int cnss_auto_suspend(struct device *dev) cnss_pci_set_monitor_wake_intr(pci_priv, true); - bus_bw_info = &plat_priv->bus_bw_info; - if (!bus_bw_info->cnss_path) - goto out; - /* For suspend temporarily set bandwidth vote to NONE and dont save in * current_bw_vote as in resume path we should vote for last used * bandwidth vote. Also ignore error if bw voting is not setup. */ - icc_set_bw(bus_bw_info->cnss_path, - bus_bw_info->cfg_table[CNSS_BUS_WIDTH_NONE].ab, - bus_bw_info->cfg_table[CNSS_BUS_WIDTH_NONE].ib); -out: + cnss_setup_bus_bandwidth(plat_priv, CNSS_BUS_WIDTH_NONE, false); return 0; } EXPORT_SYMBOL(cnss_auto_suspend); @@ -3048,7 +3056,7 @@ int cnss_auto_resume(struct device *dev) cnss_pci_set_auto_suspended(pci_priv, 0); mutex_unlock(&pci_priv->bus_lock); - cnss_request_bus_bandwidth(dev, plat_priv->bus_bw_info.current_bw_vote); + cnss_request_bus_bandwidth(dev, plat_priv->icc.current_bw_vote); return 0; }