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:
parent
a9b214d91e
commit
681c14539a
219
icnss2/main.c
219
icnss2/main.c
@ -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:
|
||||||
|
@ -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;
|
||||||
|
@ -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 *),
|
||||||
|
Loading…
Reference in New Issue
Block a user