From 88c20c8e4dc8c25ba83b0dd91d39e068b9b56a26 Mon Sep 17 00:00:00 2001 From: Piyush Dhyani Date: Thu, 27 Jan 2022 10:59:00 -0800 Subject: [PATCH] Revert "Revert "msm: ipa3: Changes to support 2X PINE."" This reverts commit 3518342e1b9d2ed5a90a85d3f968ad44534bf93e Change-Id: I7e12f2ee01101f42e665fbc6f080eee60e9a72ca --- .../platform/msm/ipa/ipa_clients/ipa_wdi3.c | 1099 ++++++++++++----- drivers/platform/msm/ipa/ipa_v3/ipa.c | 2 + drivers/platform/msm/ipa/ipa_v3/ipa_utils.c | 12 +- 3 files changed, 794 insertions(+), 319 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_wdi3.c b/drivers/platform/msm/ipa/ipa_clients/ipa_wdi3.c index f911cc8a4d..d12bbd0215 100644 --- a/drivers/platform/msm/ipa/ipa_clients/ipa_wdi3.c +++ b/drivers/platform/msm/ipa/ipa_clients/ipa_wdi3.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -39,6 +40,13 @@ OFFLOAD_DRV_NAME " %s:%d " fmt, ## args); \ } while (0) +#define IPA_CLIENT_IS_WLAN0_INSTANCE(inst_id) \ + (inst_id == 0 || inst_id == -1) +#define IPA_CLIENT_IS_WLAN1_INSTANCE(inst_id) \ + (inst_id == 1) +#define DEFAULT_INSTANCE_ID (-1) +#define INVALID_INSTANCE_ID (-2) + struct ipa_wdi_intf_info { char netdev_name[IPA_RESOURCE_NAME_MAX]; u8 hdr_len; @@ -58,100 +66,68 @@ struct ipa_wdi_context { bool is_tx1_used; u32 sys_pipe_hdl[IPA_WDI_MAX_SUPPORTED_SYS_PIPE]; u32 ipa_pm_hdl; + int inst_id; #ifdef IPA_WAN_MSG_IPv6_ADDR_GW_LEN ipa_wdi_meter_notifier_cb wdi_notify; #endif }; -static struct ipa_wdi_context *ipa_wdi_ctx; +static struct ipa_wdi_context *ipa_wdi_ctx_list[IPA_WDI_INST_MAX]; -static int ipa_wdi_init_internal(struct ipa_wdi_init_in_params *in, - struct ipa_wdi_init_out_params *out) +/** + * function to Assign Handle for instance + * + * Note: If it is called for Old API then + * max one handle is allowed. + * + * @Return handle on success, negative on failure + */ +static int assign_hdl_for_inst(int inst_id) { - struct ipa_wdi_uc_ready_params uc_ready_params; - struct ipa_smmu_in_params smmu_in; - struct ipa_smmu_out_params smmu_out; + int hdl; - if (ipa_wdi_ctx) { - IPA_WDI_ERR("ipa_wdi_ctx was initialized before\n"); - return -EFAULT; + IPA_WDI_DBG("Assigning handle for instance id %d\n", inst_id); + if (inst_id <= INVALID_INSTANCE_ID) { + IPA_WDI_ERR("Invalid instance id %d\n", inst_id); + return -EINVAL; + } + else if (ipa_wdi_ctx_list[0] && (inst_id == DEFAULT_INSTANCE_ID || + ipa_wdi_ctx_list[0]->inst_id == DEFAULT_INSTANCE_ID)) { + IPA_WDI_ERR("Invalid instance id %d\n", inst_id); + return -EINVAL; + } + else { + for (hdl = 0; hdl < IPA_WDI_INST_MAX; hdl++) { + if (!ipa_wdi_ctx_list[hdl]) + break; + } + } + if (hdl == IPA_WDI_INST_MAX) { + IPA_WDI_ERR("Already Maximum Instance Registered\n"); + return -EINVAL; } - if (in->wdi_version > IPA_WDI_3 || in->wdi_version < IPA_WDI_1) { - IPA_WDI_ERR("wrong wdi version: %d\n", in->wdi_version); - return -EFAULT; - } - - ipa_wdi_ctx = kzalloc(sizeof(*ipa_wdi_ctx), GFP_KERNEL); - if (ipa_wdi_ctx == NULL) { - IPA_WDI_ERR("fail to alloc wdi ctx\n"); - return -ENOMEM; - } - mutex_init(&ipa_wdi_ctx->lock); - init_completion(&ipa_wdi_ctx->wdi_completion); - INIT_LIST_HEAD(&ipa_wdi_ctx->head_intf_list); - - ipa_wdi_ctx->wdi_version = in->wdi_version; - uc_ready_params.notify = in->notify; - uc_ready_params.priv = in->priv; -#ifdef IPA_WAN_MSG_IPv6_ADDR_GW_LEN - ipa_wdi_ctx->wdi_notify = in->wdi_notify; -#endif - - if (ipa3_uc_reg_rdyCB(&uc_ready_params) != 0) { - mutex_destroy(&ipa_wdi_ctx->lock); - kfree(ipa_wdi_ctx); - ipa_wdi_ctx = NULL; - return -EFAULT; - } - - out->is_uC_ready = uc_ready_params.is_uC_ready; - - smmu_in.smmu_client = IPA_SMMU_WLAN_CLIENT; - if (ipa3_get_smmu_params(&smmu_in, &smmu_out)) - out->is_smmu_enabled = false; - else - out->is_smmu_enabled = smmu_out.smmu_enable; - - ipa_wdi_ctx->is_smmu_enabled = out->is_smmu_enabled; - - if (IPA_WDI2_OVER_GSI() || (in->wdi_version == IPA_WDI_3)) - out->is_over_gsi = true; - else - out->is_over_gsi = false; - return 0; + return hdl; } static int ipa_get_wdi_version_internal(void) { - if (ipa_wdi_ctx) - return ipa_wdi_ctx->wdi_version; + if (ipa_wdi_ctx_list[0]) + return ipa_wdi_ctx_list[0]->wdi_version; /* default version is IPA_WDI_3 */ return IPA_WDI_3; } static bool ipa_wdi_is_tx1_used_internal(void) { - if (ipa_wdi_ctx) - return ipa_wdi_ctx->is_tx1_used; + if (ipa_wdi_ctx_list[0]) + return ipa_wdi_ctx_list[0]->is_tx1_used; return 0; } -static int ipa_wdi_cleanup_internal(void) +static void ipa_wdi_pm_cb(void *p, enum ipa_pm_cb_event event) { - struct ipa_wdi_intf_info *entry; - struct ipa_wdi_intf_info *next; - - /* clear interface list */ - list_for_each_entry_safe(entry, next, - &ipa_wdi_ctx->head_intf_list, link) { - list_del(&entry->link); - kfree(entry); - } - mutex_destroy(&ipa_wdi_ctx->lock); - kfree(ipa_wdi_ctx); - ipa_wdi_ctx = NULL; - return 0; + IPA_WDI_DBG("received pm event %d\n", event); } static int ipa_wdi_commit_partial_hdr( @@ -190,7 +166,114 @@ static int ipa_wdi_commit_partial_hdr( return 0; } -static int ipa_wdi_reg_intf_internal(struct ipa_wdi_reg_intf_in_params *in) +/** + * function to know the WDI capabilities + * + * Note: Should not be called from atomic context and only + * after checking IPA readiness using ipa_register_ipa_ready_cb() + * + * @Return 0 on success, negative on failure + */ +static int ipa_wdi_get_capabilities_internal( + struct ipa_wdi_capabilities_out_params *out) +{ + if (out == NULL) { + IPA_WDI_ERR("invalid params out=%pK\n", out); + return -EINVAL; + } + + out->num_of_instances = IPA_WDI_INST_MAX; + IPA_WDI_DBG("Wdi Capability: %d\n", out->num_of_instances); + return 0; +} + +/** + * function to init WDI IPA offload data path + * + * Note: Should not be called from atomic context and only + * after checking IPA readiness using ipa_register_ipa_ready_cb() + * + * @Return 0 on success, negative on failure + */ +static int ipa_wdi_init_per_inst_internal(struct ipa_wdi_init_in_params *in, + struct ipa_wdi_init_out_params *out) +{ + struct ipa_wdi_uc_ready_params uc_ready_params; + struct ipa_smmu_in_params smmu_in; + struct ipa_smmu_out_params smmu_out; + int hdl; + + if (!(in && out)) { + IPA_WDI_ERR("empty parameters. in=%pK out=%pK\n", in, out); + return -EINVAL; + } + + if (in->wdi_version > IPA_WDI_3 || in->wdi_version < IPA_WDI_1) { + IPA_WDI_ERR("wrong wdi version: %d\n", in->wdi_version); + return -EFAULT; + } + + hdl = assign_hdl_for_inst(in->inst_id); + if (hdl < 0) { + IPA_WDI_ERR("Error assigning hdl\n"); + return hdl; + } + + IPA_WDI_DBG("Assigned Handle %d\n",hdl); + ipa_wdi_ctx_list[hdl] = kzalloc(sizeof(struct ipa_wdi_context), GFP_KERNEL); + if (ipa_wdi_ctx_list[hdl] == NULL) { + IPA_WDI_ERR("fail to alloc wdi ctx\n"); + return -ENOMEM; + } + mutex_init(&ipa_wdi_ctx_list[hdl]->lock); + init_completion(&ipa_wdi_ctx_list[hdl]->wdi_completion); + INIT_LIST_HEAD(&ipa_wdi_ctx_list[hdl]->head_intf_list); + + ipa_wdi_ctx_list[hdl]->inst_id = in->inst_id; + ipa_wdi_ctx_list[hdl]->wdi_version = in->wdi_version; + uc_ready_params.notify = in->notify; + uc_ready_params.priv = in->priv; + + if (ipa3_uc_reg_rdyCB(&uc_ready_params) != 0) { + mutex_destroy(&ipa_wdi_ctx_list[hdl]->lock); + kfree(ipa_wdi_ctx_list[hdl]); + ipa_wdi_ctx_list[hdl] = NULL; + return -EFAULT; + } + + out->is_uC_ready = uc_ready_params.is_uC_ready; + + if (IPA_CLIENT_IS_WLAN0_INSTANCE(ipa_wdi_ctx_list[hdl]->inst_id)) + smmu_in.smmu_client = IPA_SMMU_WLAN_CLIENT; + else + smmu_in.smmu_client = IPA_SMMU_WLAN1_CLIENT; + + if (ipa3_get_smmu_params(&smmu_in, &smmu_out)) + out->is_smmu_enabled = false; + else + out->is_smmu_enabled = smmu_out.smmu_enable; + + ipa_wdi_ctx_list[hdl]->is_smmu_enabled = out->is_smmu_enabled; + + if (IPA_WDI2_OVER_GSI() || (in->wdi_version == IPA_WDI_3)) + out->is_over_gsi = true; + else + out->is_over_gsi = false; + + out->hdl = hdl; + + return 0; +} + +/** + * function to register interface + * + * Note: Should not be called from atomic context + * + * @Return 0 on success, negative on failure + */ +static int ipa_wdi_reg_intf_per_inst_internal( + struct ipa_wdi_reg_intf_in_params *in) { struct ipa_ioc_add_hdr *hdr; struct ipa_wdi_intf_info *new_intf; @@ -207,19 +290,32 @@ static int ipa_wdi_reg_intf_internal(struct ipa_wdi_reg_intf_in_params *in) return -EINVAL; } - if (!ipa_wdi_ctx) { + if (in->hdl < 0 || in->hdl >=IPA_WDI_INST_MAX) { + IPA_WDI_ERR("Invalid handle =%d\n", in->hdl); + return -EINVAL; + } + + if (!ipa_wdi_ctx_list[in->hdl]) { IPA_WDI_ERR("wdi ctx is not initialized\n"); return -EPERM; } + if (ipa_wdi_ctx_list[in->hdl]->wdi_version >= IPA_WDI_1 && + ipa_wdi_ctx_list[in->hdl]->wdi_version < IPA_WDI_3 && + in->hdl > 0) { + IPA_WDI_ERR("More than one instance not supported for WDI ver = %d\n", + ipa_wdi_ctx_list[in->hdl]->wdi_version); + return -EPERM; + } + IPA_WDI_DBG("register interface for netdev %s\n", in->netdev_name); - mutex_lock(&ipa_wdi_ctx->lock); - list_for_each_entry(entry, &ipa_wdi_ctx->head_intf_list, link) + mutex_lock(&ipa_wdi_ctx_list[in->hdl]->lock); + list_for_each_entry(entry, &ipa_wdi_ctx_list[in->hdl]->head_intf_list, link) if (strcmp(entry->netdev_name, in->netdev_name) == 0) { IPA_WDI_DBG("intf was added before.\n"); - mutex_unlock(&ipa_wdi_ctx->lock); + mutex_unlock(&ipa_wdi_ctx_list[in->hdl]->lock); return 0; } @@ -233,7 +329,7 @@ static int ipa_wdi_reg_intf_internal(struct ipa_wdi_reg_intf_in_params *in) new_intf = kzalloc(sizeof(*new_intf), GFP_KERNEL); if (new_intf == NULL) { IPA_WDI_ERR("fail to alloc new intf\n"); - mutex_unlock(&ipa_wdi_ctx->lock); + mutex_unlock(&ipa_wdi_ctx_list[in->hdl]->lock); return -ENOMEM; } @@ -265,14 +361,16 @@ static int ipa_wdi_reg_intf_internal(struct ipa_wdi_reg_intf_in_params *in) /* populate tx prop */ tx.num_props = 2; tx.prop = tx_prop; - + IPA_WDI_DBG("Setting tx/rx props\n"); memset(tx_prop, 0, sizeof(tx_prop)); tx_prop[0].ip = IPA_IP_v4; if (ipa3_get_ctx()->ipa_wdi3_over_gsi) { if (in->is_tx1_used && ipa3_ctx->is_wdi3_tx1_needed) - tx_prop[0].dst_pipe = IPA_CLIENT_WLAN2_CONS1; - else + tx_prop[0].dst_pipe = IPA_CLIENT_WLAN2_CONS1; + else if (IPA_CLIENT_IS_WLAN0_INSTANCE(ipa_wdi_ctx_list[in->hdl]->inst_id)) tx_prop[0].dst_pipe = IPA_CLIENT_WLAN2_CONS; + else + tx_prop[0].dst_pipe = IPA_CLIENT_WLAN4_CONS; } else tx_prop[0].dst_pipe = IPA_CLIENT_WLAN1_CONS; @@ -285,8 +383,10 @@ static int ipa_wdi_reg_intf_internal(struct ipa_wdi_reg_intf_in_params *in) if (ipa3_get_ctx()->ipa_wdi3_over_gsi) { if (in->is_tx1_used && ipa3_ctx->is_wdi3_tx1_needed) tx_prop[1].dst_pipe = IPA_CLIENT_WLAN2_CONS1; - else + else if (IPA_CLIENT_IS_WLAN0_INSTANCE(ipa_wdi_ctx_list[in->hdl]->inst_id)) tx_prop[1].dst_pipe = IPA_CLIENT_WLAN2_CONS; + else + tx_prop[1].dst_pipe = IPA_CLIENT_WLAN4_CONS; } else tx_prop[1].dst_pipe = IPA_CLIENT_WLAN1_CONS; @@ -300,10 +400,14 @@ static int ipa_wdi_reg_intf_internal(struct ipa_wdi_reg_intf_in_params *in) rx.prop = rx_prop; memset(rx_prop, 0, sizeof(rx_prop)); rx_prop[0].ip = IPA_IP_v4; - if (ipa_wdi_ctx->wdi_version == IPA_WDI_3) - rx_prop[0].src_pipe = IPA_CLIENT_WLAN2_PROD; - else + if (ipa_wdi_ctx_list[in->hdl]->wdi_version == IPA_WDI_3) { + if (IPA_CLIENT_IS_WLAN0_INSTANCE(ipa_wdi_ctx_list[in->hdl]->inst_id)) + rx_prop[0].src_pipe = IPA_CLIENT_WLAN2_PROD; + else + rx_prop[0].src_pipe = IPA_CLIENT_WLAN3_PROD; + } else { rx_prop[0].src_pipe = IPA_CLIENT_WLAN1_PROD; + } rx_prop[0].hdr_l2_type = in->hdr_info[0].hdr_type; if (in->is_meta_data_valid) { rx_prop[0].attrib.attrib_mask |= IPA_FLT_META_DATA; @@ -312,107 +416,52 @@ static int ipa_wdi_reg_intf_internal(struct ipa_wdi_reg_intf_in_params *in) } rx_prop[1].ip = IPA_IP_v6; - if (ipa_wdi_ctx->wdi_version == IPA_WDI_3) - rx_prop[1].src_pipe = IPA_CLIENT_WLAN2_PROD; - else + if (ipa_wdi_ctx_list[in->hdl]->wdi_version == IPA_WDI_3) { + if (IPA_CLIENT_IS_WLAN0_INSTANCE(ipa_wdi_ctx_list[in->hdl]->inst_id)) + rx_prop[1].src_pipe = IPA_CLIENT_WLAN2_PROD; + else + rx_prop[1].src_pipe = IPA_CLIENT_WLAN3_PROD; + } else { rx_prop[1].src_pipe = IPA_CLIENT_WLAN1_PROD; + } rx_prop[1].hdr_l2_type = in->hdr_info[1].hdr_type; if (in->is_meta_data_valid) { rx_prop[1].attrib.attrib_mask |= IPA_FLT_META_DATA; rx_prop[1].attrib.meta_data = in->meta_data; rx_prop[1].attrib.meta_data_mask = in->meta_data_mask; } - if (ipa3_register_intf(in->netdev_name, &tx, &rx)) { IPA_WDI_ERR("fail to add interface prop\n"); ret = -EFAULT; - goto fail_commit_hdr; } - - list_add(&new_intf->link, &ipa_wdi_ctx->head_intf_list); - init_completion(&ipa_wdi_ctx->wdi_completion); + IPA_WDI_DBG("Done Register Interface\n"); + list_add(&new_intf->link, &ipa_wdi_ctx_list[in->hdl]->head_intf_list); + init_completion(&ipa_wdi_ctx_list[in->hdl]->wdi_completion); kfree(hdr); - mutex_unlock(&ipa_wdi_ctx->lock); + mutex_unlock(&ipa_wdi_ctx_list[in->hdl]->lock); return 0; fail_commit_hdr: kfree(hdr); fail_alloc_hdr: kfree(new_intf); - mutex_unlock(&ipa_wdi_ctx->lock); + mutex_unlock(&ipa_wdi_ctx_list[in->hdl]->lock); return ret; } -static int ipa_wdi_dereg_intf_internal(const char *netdev_name) -{ - int len, ret = 0; - struct ipa_ioc_del_hdr *hdr = NULL; - struct ipa_wdi_intf_info *entry; - struct ipa_wdi_intf_info *next; - - if (!netdev_name) { - IPA_WDI_ERR("no netdev name.\n"); - return -EINVAL; - } - - if (!ipa_wdi_ctx) { - IPA_WDI_ERR("wdi ctx is not initialized.\n"); - return -EPERM; - } - - mutex_lock(&ipa_wdi_ctx->lock); - list_for_each_entry_safe(entry, next, &ipa_wdi_ctx->head_intf_list, - link) - if (strcmp(entry->netdev_name, netdev_name) == 0) { - len = sizeof(struct ipa_ioc_del_hdr) + - 2 * sizeof(struct ipa_hdr_del); - hdr = kzalloc(len, GFP_KERNEL); - if (hdr == NULL) { - IPA_WDI_ERR("fail to alloc %d bytes\n", len); - mutex_unlock(&ipa_wdi_ctx->lock); - return -ENOMEM; - } - - hdr->commit = 1; - hdr->num_hdls = 2; - hdr->hdl[0].hdl = entry->partial_hdr_hdl[0]; - hdr->hdl[1].hdl = entry->partial_hdr_hdl[1]; - IPA_WDI_DBG("IPv4 hdr hdl: %d IPv6 hdr hdl: %d\n", - hdr->hdl[0].hdl, hdr->hdl[1].hdl); - - if (ipa3_del_hdr(hdr)) { - IPA_WDI_ERR("fail to delete partial header\n"); - ret = -EFAULT; - goto fail; - } - - if (ipa3_deregister_intf(entry->netdev_name)) { - IPA_WDI_ERR("fail to del interface props\n"); - ret = -EFAULT; - goto fail; - } - - list_del(&entry->link); - kfree(entry); - - break; - } - -fail: - kfree(hdr); - mutex_unlock(&ipa_wdi_ctx->lock); - return ret; -} - - -static void ipa_wdi_pm_cb(void *p, enum ipa_pm_cb_event event) -{ - IPA_WDI_DBG("received pm event %d\n", event); -} - -static int ipa_wdi_conn_pipes_internal(struct ipa_wdi_conn_in_params *in, - struct ipa_wdi_conn_out_params *out) +/** + * function to connect pipes + * + * @in: [in] input parameters from client + * @out: [out] output params to client + * + * Note: Should not be called from atomic context + * + * @Return 0 on success, negative on failure + */ +static int ipa_wdi_conn_pipes_per_inst_internal(struct ipa_wdi_conn_in_params *in, + struct ipa_wdi_conn_out_params *out) { int i, j, ret = 0; struct ipa_pm_register_params pm_params; @@ -427,33 +476,46 @@ static int ipa_wdi_conn_pipes_internal(struct ipa_wdi_conn_in_params *in, return -EINVAL; } - if (!ipa_wdi_ctx) { + if (in->hdl < 0 || in->hdl >= IPA_WDI_INST_MAX) { + IPA_WDI_ERR("Invalid handle %d \n", in->hdl); + return -EINVAL; + } + + if (!ipa_wdi_ctx_list[in->hdl]) { IPA_WDI_ERR("wdi ctx is not initialized\n"); return -EPERM; } + if (ipa_wdi_ctx_list[in->hdl]->wdi_version >= IPA_WDI_1 && + ipa_wdi_ctx_list[in->hdl]->wdi_version < IPA_WDI_3 && + in->hdl > 0) { + IPA_WDI_ERR("More than one instance not supported for WDI ver = %d\n", + ipa_wdi_ctx_list[in->hdl]->wdi_version); + return -EPERM; + } + if (in->num_sys_pipe_needed > IPA_WDI_MAX_SUPPORTED_SYS_PIPE) { IPA_WDI_ERR("ipa can only support up to %d sys pipe\n", IPA_WDI_MAX_SUPPORTED_SYS_PIPE); return -EINVAL; } - ipa_wdi_ctx->num_sys_pipe_needed = in->num_sys_pipe_needed; + ipa_wdi_ctx_list[in->hdl]->num_sys_pipe_needed = in->num_sys_pipe_needed; IPA_WDI_DBG("number of sys pipe %d\n", in->num_sys_pipe_needed); ipa_ep_idx_tx1 = ipa_get_ep_mapping(IPA_CLIENT_WLAN2_CONS1); if ((ipa_ep_idx_tx1 != IPA_EP_NOT_ALLOCATED) && (ipa_ep_idx_tx1 < IPA3_MAX_NUM_PIPES) && ipa3_ctx->is_wdi3_tx1_needed) { - ipa_wdi_ctx->is_tx1_used = in->is_tx1_used; + ipa_wdi_ctx_list[in->hdl]->is_tx1_used = in->is_tx1_used; } else - ipa_wdi_ctx->is_tx1_used = false; + ipa_wdi_ctx_list[in->hdl]->is_tx1_used = false; IPA_WDI_DBG("number of sys pipe %d,Tx1 asked=%d,Tx1 supported=%d\n", in->num_sys_pipe_needed, in->is_tx1_used, ipa3_ctx->is_wdi3_tx1_needed); /* setup sys pipe when needed */ - for (i = 0; i < ipa_wdi_ctx->num_sys_pipe_needed; i++) { + for (i = 0; i < in->num_sys_pipe_needed; i++) { ret = ipa_setup_sys_pipe(&in->sys_in[i], - &ipa_wdi_ctx->sys_pipe_hdl[i]); + &ipa_wdi_ctx_list[in->hdl]->sys_pipe_hdl[i]); if (ret) { IPA_WDI_ERR("fail to setup sys pipe %d\n", i); ret = -EFAULT; @@ -462,18 +524,21 @@ static int ipa_wdi_conn_pipes_internal(struct ipa_wdi_conn_in_params *in, } memset(&pm_params, 0, sizeof(pm_params)); - pm_params.name = "wdi"; + if (IPA_CLIENT_IS_WLAN0_INSTANCE(ipa_wdi_ctx_list[in->hdl]->inst_id)) + pm_params.name = "wdi"; + else + pm_params.name = "wdi1"; pm_params.callback = ipa_wdi_pm_cb; pm_params.user_data = NULL; pm_params.group = IPA_PM_GROUP_DEFAULT; - if (ipa_pm_register(&pm_params, &ipa_wdi_ctx->ipa_pm_hdl)) { + if (ipa_pm_register(&pm_params, &ipa_wdi_ctx_list[in->hdl]->ipa_pm_hdl)) { IPA_WDI_ERR("fail to register ipa pm\n"); ret = -EFAULT; goto fail_setup_sys_pipe; } - - if (ipa_wdi_ctx->wdi_version == IPA_WDI_3) { - if (ipa3_conn_wdi3_pipes(in, out, ipa_wdi_ctx->wdi_notify)) { + IPA_WDI_DBG("PM handle Registered\n"); + if (ipa_wdi_ctx_list[in->hdl]->wdi_version == IPA_WDI_3) { + if (ipa3_conn_wdi3_pipes(in, out, ipa_wdi_ctx_list[in->hdl]->wdi_notify)) { IPA_WDI_ERR("fail to setup wdi pipes\n"); ret = -EFAULT; goto fail_connect_pipe; @@ -484,7 +549,7 @@ static int ipa_wdi_conn_pipes_internal(struct ipa_wdi_conn_in_params *in, memset(&out_tx, 0, sizeof(out_tx)); memset(&out_rx, 0, sizeof(out_rx)); #ifdef IPA_WAN_MSG_IPv6_ADDR_GW_LEN - in_rx.wdi_notify = ipa_wdi_ctx->wdi_notify; + in_rx.wdi_notify = ipa_wdi_ctx_list[in->hdl]->wdi_notify; #endif if (in->is_smmu_enabled == false) { /* firsr setup rx pipe */ @@ -514,7 +579,7 @@ static int ipa_wdi_conn_pipes_internal(struct ipa_wdi_conn_in_params *in, ret = -EFAULT; goto fail_connect_pipe; } - ipa_wdi_ctx->rx_pipe_hdl = out_rx.clnt_hdl; + ipa_wdi_ctx_list[in->hdl]->rx_pipe_hdl = out_rx.clnt_hdl; out->rx_uc_db_pa = out_rx.uc_door_bell_pa; IPA_WDI_DBG("rx uc db pa: 0x%pad\n", &out->rx_uc_db_pa); @@ -543,7 +608,7 @@ static int ipa_wdi_conn_pipes_internal(struct ipa_wdi_conn_in_params *in, ret = -EFAULT; goto fail; } - ipa_wdi_ctx->tx_pipe_hdl = out_tx.clnt_hdl; + ipa_wdi_ctx_list[in->hdl]->tx_pipe_hdl = out_tx.clnt_hdl; out->tx_uc_db_pa = out_tx.uc_door_bell_pa; IPA_WDI_DBG("tx uc db pa: 0x%pad\n", &out->tx_uc_db_pa); } else { /* smmu is enabled */ @@ -574,7 +639,7 @@ static int ipa_wdi_conn_pipes_internal(struct ipa_wdi_conn_in_params *in, ret = -EFAULT; goto fail_connect_pipe; } - ipa_wdi_ctx->rx_pipe_hdl = out_rx.clnt_hdl; + ipa_wdi_ctx_list[in->hdl]->rx_pipe_hdl = out_rx.clnt_hdl; out->rx_uc_db_pa = out_rx.uc_door_bell_pa; IPA_WDI_DBG("rx uc db pa: 0x%pad\n", &out->rx_uc_db_pa); @@ -603,100 +668,72 @@ static int ipa_wdi_conn_pipes_internal(struct ipa_wdi_conn_in_params *in, ret = -EFAULT; goto fail; } - ipa_wdi_ctx->tx_pipe_hdl = out_tx.clnt_hdl; + ipa_wdi_ctx_list[in->hdl]->tx_pipe_hdl = out_tx.clnt_hdl; out->tx_uc_db_pa = out_tx.uc_door_bell_pa; - ret = ipa_pm_associate_ipa_cons_to_client(ipa_wdi_ctx->ipa_pm_hdl, - in_tx.sys.client); + ret = ipa_pm_associate_ipa_cons_to_client(ipa_wdi_ctx_list[in->hdl]->ipa_pm_hdl, + in_tx.sys.client); if (ret) { IPA_WDI_ERR("fail to associate cons with PM %d\n", ret); goto fail; } IPA_WDI_DBG("tx uc db pa: 0x%pad\n", &out->tx_uc_db_pa); } + IPA_WDI_DBG("conn pipes done\n"); } return 0; fail: - ipa3_disconnect_wdi_pipe(ipa_wdi_ctx->rx_pipe_hdl); + ipa3_disconnect_wdi_pipe(ipa_wdi_ctx_list[in->hdl]->rx_pipe_hdl); fail_connect_pipe: - ipa_pm_deregister(ipa_wdi_ctx->ipa_pm_hdl); + ipa_pm_deregister(ipa_wdi_ctx_list[in->hdl]->ipa_pm_hdl); fail_setup_sys_pipe: for (j = 0; j < i; j++) - ipa_teardown_sys_pipe(ipa_wdi_ctx->sys_pipe_hdl[j]); + ipa_teardown_sys_pipe(ipa_wdi_ctx_list[in->hdl]->sys_pipe_hdl[j]); return ret; } -static int ipa_wdi_disconn_pipes_internal(void) -{ - int i, ipa_ep_idx_rx, ipa_ep_idx_tx; - int ipa_ep_idx_tx1 = IPA_EP_NOT_ALLOCATED; - - if (!ipa_wdi_ctx) { - IPA_WDI_ERR("wdi ctx is not initialized\n"); - return -EPERM; - } - - /* tear down sys pipe if needed */ - for (i = 0; i < ipa_wdi_ctx->num_sys_pipe_needed; i++) { - if (ipa_teardown_sys_pipe(ipa_wdi_ctx->sys_pipe_hdl[i])) { - IPA_WDI_ERR("fail to tear down sys pipe %d\n", i); - return -EFAULT; - } - } - - if (ipa_wdi_ctx->wdi_version == IPA_WDI_3) { - ipa_ep_idx_rx = ipa_get_ep_mapping(IPA_CLIENT_WLAN2_PROD); - ipa_ep_idx_tx = ipa_get_ep_mapping(IPA_CLIENT_WLAN2_CONS); - if (ipa_wdi_ctx->is_tx1_used) - ipa_ep_idx_tx1 = - ipa_get_ep_mapping(IPA_CLIENT_WLAN2_CONS1); - } else { - ipa_ep_idx_rx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_PROD); - ipa_ep_idx_tx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_CONS); - } - - if (ipa_wdi_ctx->wdi_version == IPA_WDI_3) { - if (ipa3_disconn_wdi3_pipes( - ipa_ep_idx_tx, ipa_ep_idx_rx, ipa_ep_idx_tx1)) { - IPA_WDI_ERR("fail to tear down wdi pipes\n"); - return -EFAULT; - } - } else { - if (ipa3_disconnect_wdi_pipe(ipa_wdi_ctx->tx_pipe_hdl)) { - IPA_WDI_ERR("fail to tear down wdi tx pipes\n"); - return -EFAULT; - } - if (ipa3_disconnect_wdi_pipe(ipa_wdi_ctx->rx_pipe_hdl)) { - IPA_WDI_ERR("fail to tear down wdi rx pipes\n"); - return -EFAULT; - } - } - - if (ipa_pm_deregister(ipa_wdi_ctx->ipa_pm_hdl)) { - IPA_WDI_ERR("fail to deregister ipa pm\n"); - return -EFAULT; - } - - return 0; -} - -static int ipa_wdi_enable_pipes_internal(void) +/** + * function to enable IPA offload data path + * + * @hdl: hdl to wdi client + * Note: Should not be called from atomic context + * + * Returns: 0 on success, negative on failure + */ +static int ipa_wdi_enable_pipes_per_inst_internal(ipa_wdi_hdl_t hdl) { int ret; int ipa_ep_idx_tx, ipa_ep_idx_rx; int ipa_ep_idx_tx1 = IPA_EP_NOT_ALLOCATED; - if (!ipa_wdi_ctx) { + if (hdl < 0 || hdl >= IPA_WDI_INST_MAX) { + IPA_WDI_ERR("Invalid handle %d\n", hdl); + } + + if (!ipa_wdi_ctx_list[hdl]) { IPA_WDI_ERR("wdi ctx is not initialized.\n"); return -EPERM; } - if (ipa_wdi_ctx->wdi_version == IPA_WDI_3) { - ipa_ep_idx_rx = ipa_get_ep_mapping(IPA_CLIENT_WLAN2_PROD); - ipa_ep_idx_tx = ipa_get_ep_mapping(IPA_CLIENT_WLAN2_CONS); - if (ipa_wdi_ctx->is_tx1_used) + if (ipa_wdi_ctx_list[hdl]->wdi_version >= IPA_WDI_1 && + ipa_wdi_ctx_list[hdl]->wdi_version < IPA_WDI_3 && + hdl > 0) { + IPA_WDI_ERR("More than one instance not supported for WDI ver = %d\n", + ipa_wdi_ctx_list[hdl]->wdi_version); + return -EPERM; + } + + if (ipa_wdi_ctx_list[hdl]->wdi_version == IPA_WDI_3) { + if (IPA_CLIENT_IS_WLAN0_INSTANCE(ipa_wdi_ctx_list[hdl]->inst_id)) { + ipa_ep_idx_rx = ipa_get_ep_mapping(IPA_CLIENT_WLAN2_PROD); + ipa_ep_idx_tx = ipa_get_ep_mapping(IPA_CLIENT_WLAN2_CONS); + } else { + ipa_ep_idx_rx = ipa_get_ep_mapping(IPA_CLIENT_WLAN3_PROD); + ipa_ep_idx_tx = ipa_get_ep_mapping(IPA_CLIENT_WLAN4_CONS); + } + if (ipa_wdi_ctx_list[hdl]->is_tx1_used) ipa_ep_idx_tx1 = ipa_get_ep_mapping(IPA_CLIENT_WLAN2_CONS1); } else { @@ -706,40 +743,39 @@ static int ipa_wdi_enable_pipes_internal(void) if (ipa_ep_idx_tx <= 0 || ipa_ep_idx_rx <= 0) return -EFAULT; - - ret = ipa_pm_activate_sync(ipa_wdi_ctx->ipa_pm_hdl); + ret = ipa_pm_activate_sync(ipa_wdi_ctx_list[hdl]->ipa_pm_hdl); if (ret) { IPA_WDI_ERR("fail to activate ipa pm\n"); return -EFAULT; } - - if (ipa_wdi_ctx->wdi_version == IPA_WDI_3) { + IPA_WDI_DBG("Enable WDI pipes\n"); + if (ipa_wdi_ctx_list[hdl]->wdi_version == IPA_WDI_3) { if (ipa3_enable_wdi3_pipes( ipa_ep_idx_tx, ipa_ep_idx_rx, ipa_ep_idx_tx1)) { IPA_WDI_ERR("fail to enable wdi pipes\n"); return -EFAULT; } } else { - if ((ipa_wdi_ctx->tx_pipe_hdl >= IPA3_MAX_NUM_PIPES) || - (ipa_wdi_ctx->tx_pipe_hdl < 0) || - (ipa_wdi_ctx->rx_pipe_hdl >= IPA3_MAX_NUM_PIPES) || - (ipa_wdi_ctx->rx_pipe_hdl < 0)) { + if ((ipa_wdi_ctx_list[hdl]->tx_pipe_hdl >= IPA3_MAX_NUM_PIPES) || + (ipa_wdi_ctx_list[hdl]->tx_pipe_hdl < 0) || + (ipa_wdi_ctx_list[hdl]->rx_pipe_hdl >= IPA3_MAX_NUM_PIPES) || + (ipa_wdi_ctx_list[hdl]->rx_pipe_hdl < 0)) { IPA_WDI_ERR("pipe handle not valid\n"); return -EFAULT; } - if (ipa3_enable_wdi_pipe(ipa_wdi_ctx->tx_pipe_hdl)) { + if (ipa3_enable_wdi_pipe(ipa_wdi_ctx_list[hdl]->tx_pipe_hdl)) { IPA_WDI_ERR("fail to enable wdi tx pipe\n"); return -EFAULT; } - if (ipa3_resume_wdi_pipe(ipa_wdi_ctx->tx_pipe_hdl)) { + if (ipa3_resume_wdi_pipe(ipa_wdi_ctx_list[hdl]->tx_pipe_hdl)) { IPA_WDI_ERR("fail to resume wdi tx pipe\n"); return -EFAULT; } - if (ipa3_enable_wdi_pipe(ipa_wdi_ctx->rx_pipe_hdl)) { + if (ipa3_enable_wdi_pipe(ipa_wdi_ctx_list[hdl]->rx_pipe_hdl)) { IPA_WDI_ERR("fail to enable wdi rx pipe\n"); return -EFAULT; } - if (ipa3_resume_wdi_pipe(ipa_wdi_ctx->rx_pipe_hdl)) { + if (ipa3_resume_wdi_pipe(ipa_wdi_ctx_list[hdl]->rx_pipe_hdl)) { IPA_WDI_ERR("fail to resume wdi rx pipe\n"); return -EFAULT; } @@ -748,75 +784,40 @@ static int ipa_wdi_enable_pipes_internal(void) return 0; } -static int ipa_wdi_disable_pipes_internal(void) -{ - int ret; - int ipa_ep_idx_tx, ipa_ep_idx_rx; - int ipa_ep_idx_tx1 = IPA_EP_NOT_ALLOCATED; - - if (!ipa_wdi_ctx) { - IPA_WDI_ERR("wdi ctx is not initialized.\n"); - return -EPERM; - } - - if (ipa_wdi_ctx->wdi_version == IPA_WDI_3) { - ipa_ep_idx_rx = ipa_get_ep_mapping(IPA_CLIENT_WLAN2_PROD); - ipa_ep_idx_tx = ipa_get_ep_mapping(IPA_CLIENT_WLAN2_CONS); - if (ipa_wdi_ctx->is_tx1_used) - ipa_ep_idx_tx1 = - ipa_get_ep_mapping(IPA_CLIENT_WLAN2_CONS1); - } else { - ipa_ep_idx_rx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_PROD); - ipa_ep_idx_tx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_CONS); - } - - if (ipa_wdi_ctx->wdi_version == IPA_WDI_3) { - if (ipa3_disable_wdi3_pipes( - ipa_ep_idx_tx, ipa_ep_idx_rx, ipa_ep_idx_tx1)) { - IPA_WDI_ERR("fail to disable wdi pipes\n"); - return -EFAULT; - } - } else { - if (ipa3_suspend_wdi_pipe(ipa_wdi_ctx->tx_pipe_hdl)) { - IPA_WDI_ERR("fail to suspend wdi tx pipe\n"); - return -EFAULT; - } - if (ipa3_disable_wdi_pipe(ipa_wdi_ctx->tx_pipe_hdl)) { - IPA_WDI_ERR("fail to disable wdi tx pipe\n"); - return -EFAULT; - } - if (ipa3_suspend_wdi_pipe(ipa_wdi_ctx->rx_pipe_hdl)) { - IPA_WDI_ERR("fail to suspend wdi rx pipe\n"); - return -EFAULT; - } - if (ipa3_disable_wdi_pipe(ipa_wdi_ctx->rx_pipe_hdl)) { - IPA_WDI_ERR("fail to disable wdi rx pipe\n"); - return -EFAULT; - } - } - - ret = ipa_pm_deactivate_sync(ipa_wdi_ctx->ipa_pm_hdl); - if (ret) { - IPA_WDI_ERR("fail to deactivate ipa pm\n"); - return -EFAULT; - } - - return 0; -} - -static int ipa_wdi_set_perf_profile_internal(struct ipa_wdi_perf_profile *profile) +/** + * set IPA clock bandwidth based on data rates + * + * @hdl: hdl to wdi client + * @profile: [in] BandWidth profile to use + * + * Returns: 0 on success, negative on failure + */ +static int ipa_wdi_set_perf_profile_per_inst_internal(ipa_wdi_hdl_t hdl, + struct ipa_wdi_perf_profile *profile) { int res = 0; - if (profile == NULL) { IPA_WDI_ERR("Invalid input\n"); return -EINVAL; } + if (hdl < 0 || hdl >= IPA_WDI_INST_MAX) { + IPA_WDI_ERR("Invalid Handle %d\n",hdl); + return -EFAULT; + } + + if (ipa_wdi_ctx_list[hdl]->wdi_version >= IPA_WDI_1 && + ipa_wdi_ctx_list[hdl]->wdi_version < IPA_WDI_3 && + hdl > 0) { + IPA_WDI_ERR("More than one instance not supported for WDI ver = %d\n", + ipa_wdi_ctx_list[hdl]->wdi_version); + return -EPERM; + } + if (ipa3_ctx->use_pm_wrapper) { res = ipa_pm_wrapper_wdi_set_perf_profile_internal(profile); } else { - res = ipa_pm_set_throughput(ipa_wdi_ctx->ipa_pm_hdl, + res = ipa_pm_set_throughput(ipa_wdi_ctx_list[hdl]->ipa_pm_hdl, profile->max_supported_bw_mbps); } @@ -828,6 +829,464 @@ static int ipa_wdi_set_perf_profile_internal(struct ipa_wdi_perf_profile *profil return res; } +/** + * function to create smmu mapping + * + * @hdl: hdl to wdi client + * @num_buffers: number of buffers + * @info: wdi buffer info + */ +static int ipa_wdi_create_smmu_mapping_per_inst_internal(ipa_wdi_hdl_t hdl, + u32 num_buffers, + struct ipa_wdi_buffer_info *info) +{ + struct ipa_smmu_cb_ctx *cb; + int i; + int ret = 0; + int prot = IOMMU_READ | IOMMU_WRITE; + + if (!info) { + IPAERR_RL("info = %pK\n", info); + return -EINVAL; + } + + if (hdl < 0 || hdl >= IPA_WDI_INST_MAX) { + IPA_WDI_ERR("Invalid Handle %d\n",hdl); + return -EFAULT; + } + + if (IPA_CLIENT_IS_WLAN0_INSTANCE(ipa_wdi_ctx_list[hdl]->inst_id)) + cb = ipa3_get_smmu_ctx(IPA_SMMU_CB_WLAN); + else + cb = ipa3_get_smmu_ctx(IPA_SMMU_CB_WLAN1); + + if (!cb->valid) { + IPA_WDI_ERR("No SMMU CB setup\n"); + return -EINVAL; + } + + if ((IPA_CLIENT_IS_WLAN0_INSTANCE(ipa_wdi_ctx_list[hdl]->inst_id) && + ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_WLAN]) || + (IPA_CLIENT_IS_WLAN1_INSTANCE(ipa_wdi_ctx_list[hdl]->inst_id) && + ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_WLAN1])) { + IPA_WDI_ERR("IPA SMMU not enabled\n"); + return -EINVAL; + } + + for (i = 0; i < num_buffers; i++) { + IPA_WDI_DBG_LOW("i=%d pa=0x%pa iova=0x%lx sz=0x%zx\n", i, + &info[i].pa, info[i].iova, info[i].size); + info[i].result = ipa3_iommu_map(cb->iommu_domain, + rounddown(info[i].iova, PAGE_SIZE), + rounddown(info[i].pa, PAGE_SIZE), + roundup(info[i].size + info[i].pa - + rounddown(info[i].pa, PAGE_SIZE), PAGE_SIZE), + prot); + } + + return ret; +} + + +/** + * function to release smmu mapping + * + * @hdl: hdl to wdi client + * @num_buffers: number of buffers + * + * @info: wdi buffer info + */ +static int ipa_wdi_release_smmu_mapping_per_inst_internal(ipa_wdi_hdl_t hdl, + u32 num_buffers, + struct ipa_wdi_buffer_info *info) +{ + struct ipa_smmu_cb_ctx *cb; + int i; + int ret = 0; + + if (!info) { + IPAERR_RL("info = %pK\n", info); + return -EINVAL; + } + + if (hdl < 0 || hdl >= IPA_WDI_INST_MAX) { + IPA_WDI_ERR("Invalid Handle %d\n",hdl); + return -EFAULT; + } + + if (IPA_CLIENT_IS_WLAN0_INSTANCE(ipa_wdi_ctx_list[hdl]->inst_id)) + cb = ipa3_get_smmu_ctx(IPA_SMMU_CB_WLAN); + else + cb = ipa3_get_smmu_ctx(IPA_SMMU_CB_WLAN1); + + if (!cb->valid) { + IPA_WDI_ERR("No SMMU CB setup\n"); + return -EINVAL; + } + + for (i = 0; i < num_buffers; i++) { + IPA_WDI_DBG_LOW("i=%d pa=0x%pa iova=0x%lx sz=0x%zx\n", i, + &info[i].pa, info[i].iova, info[i].size); + info[i].result = iommu_unmap(cb->iommu_domain, + rounddown(info[i].iova, PAGE_SIZE), + roundup(info[i].size + info[i].pa - + rounddown(info[i].pa, PAGE_SIZE), PAGE_SIZE)); + } + + return ret; +} + +/** + * clean up WDI IPA offload data path + * + * @hdl: hdl to wdi client + * + * @Return 0 on success, negative on failure + */ +static int ipa_wdi_cleanup_per_inst_internal(ipa_wdi_hdl_t hdl) +{ + struct ipa_wdi_intf_info *entry; + struct ipa_wdi_intf_info *next; + + IPA_WDI_DBG("client hdl = %d, Instance = %d\n", hdl,ipa_wdi_ctx_list[hdl]->inst_id); + if (hdl < 0 || hdl >= IPA_WDI_INST_MAX) { + IPA_WDI_ERR("Invalid Handle %d\n",hdl); + return -EFAULT; + } + + if (ipa_wdi_ctx_list[hdl]->wdi_version >= IPA_WDI_1 && + ipa_wdi_ctx_list[hdl]->wdi_version < IPA_WDI_3 && + hdl > 0) { + IPA_WDI_ERR("More than one instance not supported for WDI ver = %d\n", + ipa_wdi_ctx_list[hdl]->wdi_version); + return -EPERM; + } + + /* clear interface list */ + list_for_each_entry_safe(entry, next, + &ipa_wdi_ctx_list[hdl]->head_intf_list, link) { + list_del(&entry->link); + kfree(entry); + } + mutex_destroy(&ipa_wdi_ctx_list[hdl]->lock); + kfree(ipa_wdi_ctx_list[hdl]); + ipa_wdi_ctx_list[hdl] = NULL; + return 0; +} + +/** + * function to deregister before unload and after disconnect + * + * @Return 0 on success, negative on failure + */ +static int ipa_wdi_dereg_intf_per_inst_internal(const char *netdev_name,ipa_wdi_hdl_t hdl) +{ + int len, ret = 0; + struct ipa_ioc_del_hdr *hdr = NULL; + struct ipa_wdi_intf_info *entry; + struct ipa_wdi_intf_info *next; + + if (!netdev_name) { + IPA_WDI_ERR("no netdev name.\n"); + return -EINVAL; + } + + if (hdl < 0 || hdl >= IPA_WDI_INST_MAX) { + IPA_WDI_ERR("Invalid Handle %d\n",hdl); + return -EFAULT; + } + + if (ipa_wdi_ctx_list[hdl]->wdi_version >= IPA_WDI_1 && + ipa_wdi_ctx_list[hdl]->wdi_version < IPA_WDI_3 && + hdl > 0) { + IPA_WDI_ERR("More than one instance not supported for WDI ver = %d\n", + ipa_wdi_ctx_list[hdl]->wdi_version); + return -EPERM; + } + + if (!ipa_wdi_ctx_list[hdl]) { + IPA_WDI_ERR("wdi ctx is not initialized.\n"); + return -EPERM; + } + IPA_WDI_DBG("Deregister Instance hdl %d\n",hdl); + mutex_lock(&ipa_wdi_ctx_list[hdl]->lock); + list_for_each_entry_safe(entry, next, &ipa_wdi_ctx_list[hdl]->head_intf_list, + link) + if (strcmp(entry->netdev_name, netdev_name) == 0) { + len = sizeof(struct ipa_ioc_del_hdr) + + 2 * sizeof(struct ipa_hdr_del); + hdr = kzalloc(len, GFP_KERNEL); + if (hdr == NULL) { + IPA_WDI_ERR("fail to alloc %d bytes\n", len); + mutex_unlock(&ipa_wdi_ctx_list[hdl]->lock); + return -ENOMEM; + } + + hdr->commit = 1; + hdr->num_hdls = 2; + hdr->hdl[0].hdl = entry->partial_hdr_hdl[0]; + hdr->hdl[1].hdl = entry->partial_hdr_hdl[1]; + IPA_WDI_DBG("IPv4 hdr hdl: %d IPv6 hdr hdl: %d\n", + hdr->hdl[0].hdl, hdr->hdl[1].hdl); + + if (ipa3_del_hdr(hdr)) { + IPA_WDI_ERR("fail to delete partial header\n"); + ret = -EFAULT; + goto fail; + } + + if (ipa3_deregister_intf(entry->netdev_name)) { + IPA_WDI_ERR("fail to del interface props\n"); + ret = -EFAULT; + goto fail; + } + + list_del(&entry->link); + kfree(entry); + + break; + } + +fail: + kfree(hdr); + mutex_unlock(&ipa_wdi_ctx_list[hdl]->lock); + return ret; +} + +/** + * function to disconnect pipes + * + * @hdl: hdl to wdi client + * Note: Should not be called from atomic context + * + * Returns: 0 on success, negative on failure + */ +static int ipa_wdi_disconn_pipes_per_inst_internal(ipa_wdi_hdl_t hdl) +{ + int i, ipa_ep_idx_rx, ipa_ep_idx_tx; + int ipa_ep_idx_tx1 = IPA_EP_NOT_ALLOCATED; + + if (hdl < 0 || hdl >= IPA_WDI_INST_MAX) { + IPA_WDI_ERR("Invalid Handle %d\n",hdl); + return -EFAULT; + } + + if (ipa_wdi_ctx_list[hdl]->wdi_version >= IPA_WDI_1 && + ipa_wdi_ctx_list[hdl]->wdi_version < IPA_WDI_3 && + hdl > 0) { + IPA_WDI_ERR("More than one instance not supported for WDI ver = %d\n", + ipa_wdi_ctx_list[hdl]->wdi_version); + return -EPERM; + } + + if (!ipa_wdi_ctx_list[hdl]) { + IPA_WDI_ERR("wdi ctx is not initialized\n"); + return -EPERM; + } + IPA_WDI_DBG("Disconnect pipes for hdl %d\n",hdl); + /* tear down sys pipe if needed */ + for (i = 0; i < ipa_wdi_ctx_list[hdl]->num_sys_pipe_needed; i++) { + if (ipa_teardown_sys_pipe(ipa_wdi_ctx_list[hdl]->sys_pipe_hdl[i])) { + IPA_WDI_ERR("fail to tear down sys pipe %d\n", i); + return -EFAULT; + } + } + + if (ipa_wdi_ctx_list[hdl]->wdi_version == IPA_WDI_3) { + if (IPA_CLIENT_IS_WLAN0_INSTANCE(ipa_wdi_ctx_list[hdl]->inst_id)) { + ipa_ep_idx_rx = ipa_get_ep_mapping(IPA_CLIENT_WLAN2_PROD); + ipa_ep_idx_tx = ipa_get_ep_mapping(IPA_CLIENT_WLAN2_CONS); + } else { + ipa_ep_idx_rx = ipa_get_ep_mapping(IPA_CLIENT_WLAN3_PROD); + ipa_ep_idx_tx = ipa_get_ep_mapping(IPA_CLIENT_WLAN4_CONS); + } + + if (ipa_wdi_ctx_list[hdl]->is_tx1_used) + ipa_ep_idx_tx1 = + ipa_get_ep_mapping(IPA_CLIENT_WLAN2_CONS1); + } else { + ipa_ep_idx_rx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_PROD); + ipa_ep_idx_tx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_CONS); + } + + if (ipa_wdi_ctx_list[hdl]->wdi_version == IPA_WDI_3) { + if (ipa3_disconn_wdi3_pipes( + ipa_ep_idx_tx, ipa_ep_idx_rx, ipa_ep_idx_tx1)) { + IPA_WDI_ERR("fail to tear down wdi pipes\n"); + return -EFAULT; + } + } else { + if (ipa3_disconnect_wdi_pipe(ipa_wdi_ctx_list[hdl]->tx_pipe_hdl)) { + IPA_WDI_ERR("fail to tear down wdi tx pipes\n"); + return -EFAULT; + } + if (ipa3_disconnect_wdi_pipe(ipa_wdi_ctx_list[hdl]->rx_pipe_hdl)) { + IPA_WDI_ERR("fail to tear down wdi rx pipes\n"); + return -EFAULT; + } + } + + if (ipa_pm_deregister(ipa_wdi_ctx_list[hdl]->ipa_pm_hdl)) { + IPA_WDI_ERR("fail to deregister ipa pm\n"); + return -EFAULT; + } + + return 0; +} + +/** + * function to disable IPA offload data path + * + * @hdl: hdl to wdi client + * Note: Should not be called from atomic context + * + * Returns: 0 on success, negative on failure + */ +static int ipa_wdi_disable_pipes_per_inst_internal(ipa_wdi_hdl_t hdl) +{ + int ret; + int ipa_ep_idx_tx, ipa_ep_idx_rx; + int ipa_ep_idx_tx1 = IPA_EP_NOT_ALLOCATED; + + + if (hdl < 0 || hdl >= IPA_WDI_INST_MAX) { + IPA_WDI_ERR("Invalid Handle %d\n",hdl); + return -EFAULT; + } + + if (ipa_wdi_ctx_list[hdl]->wdi_version >= IPA_WDI_1 && + ipa_wdi_ctx_list[hdl]->wdi_version < IPA_WDI_3 && + hdl > 0) { + IPA_WDI_ERR("More than one instance not supported for WDI ver = %d\n", + ipa_wdi_ctx_list[hdl]->wdi_version); + return -EPERM; + } + + if (!ipa_wdi_ctx_list[hdl]) { + IPA_WDI_ERR("wdi ctx is not initialized.\n"); + return -EPERM; + } + + if (ipa_wdi_ctx_list[hdl]->wdi_version == IPA_WDI_3) { + if (IPA_CLIENT_IS_WLAN0_INSTANCE(ipa_wdi_ctx_list[hdl]->inst_id)) { + ipa_ep_idx_rx = ipa_get_ep_mapping(IPA_CLIENT_WLAN2_PROD); + ipa_ep_idx_tx = ipa_get_ep_mapping(IPA_CLIENT_WLAN2_CONS); + } else { + ipa_ep_idx_rx = ipa_get_ep_mapping(IPA_CLIENT_WLAN3_PROD); + ipa_ep_idx_tx = ipa_get_ep_mapping(IPA_CLIENT_WLAN4_CONS); + } + + if (ipa_wdi_ctx_list[hdl]->is_tx1_used) + ipa_ep_idx_tx1 = + ipa_get_ep_mapping(IPA_CLIENT_WLAN2_CONS1); + } else { + ipa_ep_idx_rx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_PROD); + ipa_ep_idx_tx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_CONS); + } + + if (ipa_wdi_ctx_list[hdl]->wdi_version == IPA_WDI_3) { + if (ipa3_disable_wdi3_pipes( + ipa_ep_idx_tx, ipa_ep_idx_rx, ipa_ep_idx_tx1)) { + IPA_WDI_ERR("fail to disable wdi pipes\n"); + return -EFAULT; + } + } else { + if (ipa3_suspend_wdi_pipe(ipa_wdi_ctx_list[hdl]->tx_pipe_hdl)) { + IPA_WDI_ERR("fail to suspend wdi tx pipe\n"); + return -EFAULT; + } + if (ipa3_disable_wdi_pipe(ipa_wdi_ctx_list[hdl]->tx_pipe_hdl)) { + IPA_WDI_ERR("fail to disable wdi tx pipe\n"); + return -EFAULT; + } + if (ipa3_suspend_wdi_pipe(ipa_wdi_ctx_list[hdl]->rx_pipe_hdl)) { + IPA_WDI_ERR("fail to suspend wdi rx pipe\n"); + return -EFAULT; + } + if (ipa3_disable_wdi_pipe(ipa_wdi_ctx_list[hdl]->rx_pipe_hdl)) { + IPA_WDI_ERR("fail to disable wdi rx pipe\n"); + return -EFAULT; + } + } + + ret = ipa_pm_deactivate_sync(ipa_wdi_ctx_list[hdl]->ipa_pm_hdl); + if (ret) { + IPA_WDI_ERR("fail to deactivate ipa pm\n"); + return -EFAULT; + } + + return 0; +} + +static int ipa_wdi_init_internal(struct ipa_wdi_init_in_params *in, + struct ipa_wdi_init_out_params *out) +{ + if (in == NULL) { + IPA_WDI_ERR("invalid params in=%pK\n", in); + return -EINVAL; + } + + in->inst_id = DEFAULT_INSTANCE_ID; + return ipa_wdi_init_per_inst_internal(in, out); +} + +static int ipa_wdi_cleanup_internal(void) +{ + return ipa_wdi_cleanup_per_inst_internal(0); +} + +static int ipa_wdi_reg_intf_internal(struct ipa_wdi_reg_intf_in_params *in) +{ + if (in == NULL) { + IPA_WDI_ERR("invalid params in=%pK\n", in); + return -EINVAL; + } + in->hdl = 0; + return ipa_wdi_reg_intf_per_inst_internal(in); +} + +static int ipa_wdi_dereg_intf_internal(const char *netdev_name) +{ + return ipa_wdi_dereg_intf_per_inst_internal(netdev_name, 0); +} + +static int ipa_wdi_conn_pipes_internal(struct ipa_wdi_conn_in_params *in, + struct ipa_wdi_conn_out_params *out) +{ + if (!(in && out)) { + IPA_WDI_ERR("empty parameters. in=%pK out=%pK\n", in, out); + return -EINVAL; + } + + in->hdl = 0; + return ipa_wdi_conn_pipes_per_inst_internal(in, out); +} + +static int ipa_wdi_disconn_pipes_internal(void) +{ + return ipa_wdi_disconn_pipes_per_inst_internal(0); +} + +static int ipa_wdi_enable_pipes_internal(void) +{ + return ipa_wdi_enable_pipes_per_inst_internal(0); +} + +static int ipa_wdi_disable_pipes_internal(void) +{ + return ipa_wdi_disable_pipes_per_inst_internal(0); +} + +static int ipa_wdi_set_perf_profile_internal(struct ipa_wdi_perf_profile *profile) +{ + if (profile == NULL) { + IPA_WDI_ERR("Invalid input\n"); + return -EINVAL; + } + + return ipa_wdi_set_perf_profile_per_inst_internal(0, profile); +} + void ipa_wdi3_register(void) { struct ipa_wdi3_data funcs; @@ -848,6 +1307,18 @@ void ipa_wdi3_register(void) funcs.ipa_wdi_sw_stats = ipa3_set_wlan_tx_info; funcs.ipa_get_wdi_version = ipa_get_wdi_version_internal; funcs.ipa_wdi_is_tx1_used = ipa_wdi_is_tx1_used_internal; + funcs.ipa_wdi_get_capabilities = ipa_wdi_get_capabilities_internal; + funcs.ipa_wdi_init_per_inst = ipa_wdi_init_per_inst_internal; + funcs.ipa_wdi_cleanup_per_inst = ipa_wdi_cleanup_per_inst_internal; + funcs.ipa_wdi_reg_intf_per_inst = ipa_wdi_reg_intf_per_inst_internal; + funcs.ipa_wdi_dereg_intf_per_inst = ipa_wdi_dereg_intf_per_inst_internal; + funcs.ipa_wdi_conn_pipes_per_inst = ipa_wdi_conn_pipes_per_inst_internal; + funcs.ipa_wdi_disconn_pipes_per_inst = ipa_wdi_disconn_pipes_per_inst_internal; + funcs.ipa_wdi_enable_pipes_per_inst = ipa_wdi_enable_pipes_per_inst_internal; + funcs.ipa_wdi_disable_pipes_per_inst = ipa_wdi_disable_pipes_per_inst_internal; + funcs.ipa_wdi_set_perf_profile_per_inst = ipa_wdi_set_perf_profile_per_inst_internal; + funcs.ipa_wdi_create_smmu_mapping_per_inst = ipa_wdi_create_smmu_mapping_per_inst_internal; + funcs.ipa_wdi_release_smmu_mapping_per_inst = ipa_wdi_release_smmu_mapping_per_inst_internal; if (ipa_fmwk_register_ipa_wdi3(&funcs)) pr_err("failed to register ipa_wdi3 APIs\n"); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c index 7295f6e8ff..12ca2ac721 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c @@ -979,6 +979,7 @@ struct ipa_smmu_cb_ctx *ipa3_get_smmu_ctx(enum ipa_smmu_cb_type cb_type) { return &smmu_cb[cb_type]; } +EXPORT_SYMBOL(ipa3_get_smmu_ctx); static int ipa3_open(struct inode *inode, struct file *filp) { @@ -11932,6 +11933,7 @@ int ipa3_iommu_map(struct iommu_domain *domain, return iommu_map(domain, iova, paddr, size, prot); } +EXPORT_SYMBOL(ipa3_iommu_map); /** * ipa3_get_smmu_params()- Return the ipa3 smmu related params. diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c index 5a93158eb0..fd44782938 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c @@ -4186,7 +4186,7 @@ static const struct ipa_ep_configuration ipa3_ep_mapping IPA_TX_INSTANCE_NA }, [IPA_5_0][IPA_CLIENT_WLAN3_PROD] = { true, IPA_v5_0_GROUP_UL, - false, + true, IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP, QMB_MASTER_SELECT_DDR, { 1 , 0, 8, 16, IPA_EE_AP, GSI_SMART_PRE_FETCH, 2}, @@ -9362,11 +9362,13 @@ int ipa3_write_qmap_id(struct ipa_ioc_write_qmapid *param_in) param_in->client == IPA_CLIENT_RTK_ETHERNET_PROD) { result = ipa3_cfg_ep_metadata(ipa_ep_idx, &meta); } else if (param_in->client == IPA_CLIENT_WLAN1_PROD || - param_in->client == IPA_CLIENT_WLAN2_PROD) { + param_in->client == IPA_CLIENT_WLAN2_PROD || + param_in->client == IPA_CLIENT_WLAN3_PROD) { ipa3_ctx->ep[ipa_ep_idx].cfg.meta = meta; - if (param_in->client == IPA_CLIENT_WLAN2_PROD) - result = ipa3_write_qmapid_wdi3_gsi_pipe( - ipa_ep_idx, meta.qmap_id); + if (param_in->client == IPA_CLIENT_WLAN2_PROD || + param_in->client == IPA_CLIENT_WLAN3_PROD) + result = ipa3_write_qmapid_wdi3_gsi_pipe( + ipa_ep_idx, meta.qmap_id); else result = ipa3_write_qmapid_wdi_pipe( ipa_ep_idx, meta.qmap_id);