icnss2: Add changes to vote for bus bandwidth

Add changes to support voting for bus bandwidth through driver
for different interconnect paths based on throughput threshols
and DDR frequency.

CRs-Fixed: 3887876
Change-Id: I4cf759d46a9ef8f3f5688d22e7619d5a010293a3
This commit is contained in:
Kartikey Arora 2024-08-04 22:38:01 +05:30 committed by Ravindra Konda
parent a9b214d91e
commit 681c14539a
3 changed files with 269 additions and 1 deletions

View File

@ -413,6 +413,217 @@ bool icnss_is_fw_ready(void)
} }
EXPORT_SYMBOL(icnss_is_fw_ready); EXPORT_SYMBOL(icnss_is_fw_ready);
#if IS_ENABLED(CONFIG_INTERCONNECT)
/**
* icnss_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 icnss_register_bus_scale(struct icnss_priv *plat_priv)
{
int ret = -EINVAL;
u32 idx, i, j, cfg_arr_size, *cfg_arr = NULL;
struct icnss_bus_bw_info *bus_bw_info, *tmp;
struct device *dev = &plat_priv->pdev->dev;
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) {
icnss_pr_dbg("Platform Bus Interconnect path not configured\n");
return 0;
}
ret = of_property_read_u32(plat_priv->pdev->dev.of_node,
"qcom,bus-bw-cfg-count",
&plat_priv->icc.bus_bw_cfg_count);
if (ret) {
icnss_pr_err("Failed to get Bus BW Config table size\n");
goto cleanup;
}
cfg_arr_size = plat_priv->icc.path_count *
plat_priv->icc.bus_bw_cfg_count * ICNSS_ICC_VOTE_MAX;
cfg_arr = kcalloc(cfg_arr_size, sizeof(*cfg_arr), GFP_KERNEL);
if (!cfg_arr) {
icnss_pr_err("Failed to alloc cfg table mem\n");
ret = -ENOMEM;
goto cleanup;
}
ret = of_property_read_u32_array(plat_priv->pdev->dev.of_node,
"qcom,bus-bw-cfg", cfg_arr,
cfg_arr_size);
if (ret) {
icnss_pr_err("Invalid Bus BW Config Table\n");
goto cleanup;
}
icnss_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->pdev->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) {
icnss_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;
}
icnss_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 *
ICNSS_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];
icnss_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 icnss_unregister_bus_scale(struct icnss_priv *plat_priv)
{
struct icnss_bus_bw_info *bus_bw_info, *tmp;
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));
}
/**
* icnss_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 icnss_setup_bus_bandwidth(struct icnss_priv *plat_priv,
u32 bw, bool save)
{
int ret = 0;
struct icnss_bus_bw_info *bus_bw_info;
if (!plat_priv->icc.path_count)
return -EOPNOTSUPP;
if (bw >= plat_priv->icc.bus_bw_cfg_count) {
icnss_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) {
icnss_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 icnss_request_bus_bandwidth(struct device *dev, int bandwidth)
{
struct icnss_priv *plat_priv = dev_get_drvdata(dev);
if (!plat_priv)
return -ENODEV;
if (bandwidth < 0)
return -EINVAL;
return icnss_setup_bus_bandwidth(plat_priv, (u32)bandwidth, true);
}
#else
static int icnss_register_bus_scale(struct icnss_priv *plat_priv)
{
return 0;
}
static void icnss_unregister_bus_scale(struct icnss_priv *plat_priv) {}
static int icnss_setup_bus_bandwidth(struct icnss_priv *plat_priv,
u32 bw, bool save)
{
return 0;
}
int icnss_request_bus_bandwidth(struct device *dev, int bandwidth)
{
return 0;
}
#endif /* CONFIG_INTERCONNECT */
EXPORT_SYMBOL(icnss_request_bus_bandwidth);
void icnss_block_shutdown(bool status) void icnss_block_shutdown(bool status)
{ {
if (!penv) if (!penv)
@ -4817,10 +5028,14 @@ static int icnss_probe(struct platform_device *pdev)
if (ret) if (ret)
goto out_free_resources; goto out_free_resources;
ret = icnss_smmu_dt_parse(priv); ret = icnss_register_bus_scale(priv);
if (ret) if (ret)
goto out_free_resources; goto out_free_resources;
ret = icnss_smmu_dt_parse(priv);
if (ret)
goto unreg_bus_scale;
spin_lock_init(&priv->event_lock); spin_lock_init(&priv->event_lock);
spin_lock_init(&priv->on_off_lock); spin_lock_init(&priv->on_off_lock);
spin_lock_init(&priv->soc_wake_msg_lock); spin_lock_init(&priv->soc_wake_msg_lock);
@ -4921,6 +5136,8 @@ out_destroy_wq:
destroy_workqueue(priv->event_wq); destroy_workqueue(priv->event_wq);
smmu_cleanup: smmu_cleanup:
priv->iommu_domain = NULL; priv->iommu_domain = NULL;
unreg_bus_scale:
icnss_unregister_bus_scale(priv);
out_free_resources: out_free_resources:
icnss_put_resources(priv); icnss_put_resources(priv);
out_reset_drvdata: out_reset_drvdata:

View File

@ -20,6 +20,9 @@
#else #else
#include <soc/qcom/icnss2.h> #include <soc/qcom/icnss2.h>
#endif #endif
#if IS_ENABLED(CONFIG_INTERCONNECT)
#include <linux/interconnect.h>
#endif
#include "wlan_firmware_service_v01.h" #include "wlan_firmware_service_v01.h"
#include "cnss_prealloc.h" #include "cnss_prealloc.h"
#include "cnss_common.h" #include "cnss_common.h"
@ -53,6 +56,50 @@ struct icnss_control_params {
unsigned int bdf_type; unsigned int bdf_type;
}; };
#if IS_ENABLED(CONFIG_INTERCONNECT)
/**
* struct icnss_bus_bw_cfg - Interconnect vote data
* @avg_bw: Vote for average bandwidth
* @peak_bw: Vote for peak bandwidth
*/
struct icnss_bus_bw_cfg {
u32 avg_bw;
u32 peak_bw;
};
/* Number of bw votes (avg, peak) entries that ICC requires */
#define ICNSS_ICC_VOTE_MAX 2
/**
* struct icnss_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 icnss_bus_bw_info {
struct list_head list;
const char *icc_name;
struct icc_path *icc_path;
struct icnss_bus_bw_cfg *cfg_table;
};
/**
* struct icnss_interconnect_cfg - ICNSS 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 icnss_bus_bw_info.cfg_table
*/
struct icnss_interconnect_cfg {
struct list_head list_head;
u32 path_count;
int current_bw_vote;
u32 bus_bw_cfg_count;
};
#endif
enum icnss_driver_event_type { enum icnss_driver_event_type {
ICNSS_DRIVER_EVENT_SERVER_ARRIVE, ICNSS_DRIVER_EVENT_SERVER_ARRIVE,
ICNSS_DRIVER_EVENT_SERVER_EXIT, ICNSS_DRIVER_EVENT_SERVER_EXIT,
@ -411,6 +458,9 @@ struct icnss_priv {
struct list_head soc_wake_msg_list; struct list_head soc_wake_msg_list;
spinlock_t event_lock; spinlock_t event_lock;
spinlock_t soc_wake_msg_lock; spinlock_t soc_wake_msg_lock;
#if IS_ENABLED(CONFIG_INTERCONNECT)
struct icnss_interconnect_cfg icc;
#endif
struct work_struct event_work; struct work_struct event_work;
struct work_struct fw_recv_msg_work; struct work_struct fw_recv_msg_work;
struct work_struct soc_wake_msg_work; struct work_struct soc_wake_msg_work;

View File

@ -189,6 +189,7 @@ extern int icnss_wlan_disable(struct device *dev, enum icnss_driver_mode mode);
extern void icnss_enable_irq(struct device *dev, unsigned int ce_id); extern void icnss_enable_irq(struct device *dev, unsigned int ce_id);
extern void icnss_disable_irq(struct device *dev, unsigned int ce_id); extern void icnss_disable_irq(struct device *dev, unsigned int ce_id);
extern int icnss_get_soc_info(struct device *dev, struct icnss_soc_info *info); extern int icnss_get_soc_info(struct device *dev, struct icnss_soc_info *info);
extern int icnss_request_bus_bandwidth(struct device *dev, int bandwidth);
extern int icnss_ce_free_irq(struct device *dev, unsigned int ce_id, void *ctx); extern int icnss_ce_free_irq(struct device *dev, unsigned int ce_id, void *ctx);
extern int icnss_ce_request_irq(struct device *dev, unsigned int ce_id, extern int icnss_ce_request_irq(struct device *dev, unsigned int ce_id,
irqreturn_t (*handler)(int, void *), irqreturn_t (*handler)(int, void *),