blob: 16de3d9f6befff827ebb7769df4403aa6f087403 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/ipa_wdi3.h>
#include <linux/msm_ipa.h>
#include <linux/string.h>
#include "../ipa_common_i.h"
#include "../ipa_v3/ipa_pm.h"
#include "../ipa_v3/ipa_i.h"
#define OFFLOAD_DRV_NAME "ipa_wdi"
#define IPA_WDI_DBG(fmt, args...) \
do { \
pr_debug(OFFLOAD_DRV_NAME " %s:%d " fmt, \
__func__, __LINE__, ## args); \
IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
OFFLOAD_DRV_NAME " %s:%d " fmt, ## args); \
IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
OFFLOAD_DRV_NAME " %s:%d " fmt, ## args); \
} while (0)
#define IPA_WDI_DBG_LOW(fmt, args...) \
do { \
pr_debug(OFFLOAD_DRV_NAME " %s:%d " fmt, \
__func__, __LINE__, ## args); \
IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
OFFLOAD_DRV_NAME " %s:%d " fmt, ## args); \
} while (0)
#define IPA_WDI_ERR(fmt, args...) \
do { \
pr_err(OFFLOAD_DRV_NAME " %s:%d " fmt, \
__func__, __LINE__, ## args); \
IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
OFFLOAD_DRV_NAME " %s:%d " fmt, ## args); \
IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
OFFLOAD_DRV_NAME " %s:%d " fmt, ## args); \
} while (0)
#define IPA_TX_MAX_INTF_PROP 2
#define IPA_RX_MAX_INTF_PROP 2
struct ipa_wdi_intf_info {
char netdev_name[IPA_RESOURCE_NAME_MAX];
u8 hdr_len;
u32 partial_hdr_hdl[IPA_IP_MAX];
struct list_head link;
};
struct ipa_wdi_context {
struct list_head head_intf_list;
struct completion wdi_completion;
struct mutex lock;
enum ipa_wdi_version wdi_version;
u8 is_smmu_enabled;
u32 tx_pipe_hdl;
u32 rx_pipe_hdl;
u8 num_sys_pipe_needed;
u32 sys_pipe_hdl[IPA_WDI_MAX_SUPPORTED_SYS_PIPE];
u32 ipa_pm_hdl;
#ifdef IPA_WAN_MSG_IPv6_ADDR_GW_LEN
ipa_wdi_meter_notifier_cb wdi_notify;
#endif
};
static struct ipa_wdi_context *ipa_wdi_ctx;
int ipa_wdi_init(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;
if (ipa_wdi_ctx) {
IPA_WDI_ERR("ipa_wdi_ctx was initialized before\n");
return -EFAULT;
}
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 (ipa_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 (ipa_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 (ipa3_ctx->ipa_wdi3_over_gsi)
out->is_over_gsi = true;
else
out->is_over_gsi = false;
return 0;
}
EXPORT_SYMBOL(ipa_wdi_init);
int ipa_wdi_cleanup(void)
{
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;
}
EXPORT_SYMBOL(ipa_wdi_cleanup);
static int ipa_wdi_commit_partial_hdr(
struct ipa_ioc_add_hdr *hdr,
const char *netdev_name,
struct ipa_wdi_hdr_info *hdr_info)
{
int i;
if (!hdr || !hdr_info || !netdev_name) {
IPA_WDI_ERR("Invalid input\n");
return -EINVAL;
}
hdr->commit = 1;
hdr->num_hdrs = 2;
snprintf(hdr->hdr[0].name, sizeof(hdr->hdr[0].name),
"%s_ipv4", netdev_name);
snprintf(hdr->hdr[1].name, sizeof(hdr->hdr[1].name),
"%s_ipv6", netdev_name);
for (i = IPA_IP_v4; i < IPA_IP_MAX; i++) {
hdr->hdr[i].hdr_len = hdr_info[i].hdr_len;
memcpy(hdr->hdr[i].hdr, hdr_info[i].hdr, hdr->hdr[i].hdr_len);
hdr->hdr[i].type = hdr_info[i].hdr_type;
hdr->hdr[i].is_partial = 1;
hdr->hdr[i].is_eth2_ofst_valid = 1;
hdr->hdr[i].eth2_ofst = hdr_info[i].dst_mac_addr_offset;
}
if (ipa_add_hdr(hdr)) {
IPA_WDI_ERR("fail to add partial headers\n");
return -EFAULT;
}
return 0;
}
int ipa_wdi_reg_intf(struct ipa_wdi_reg_intf_in_params *in)
{
struct ipa_ioc_add_hdr *hdr;
struct ipa_wdi_intf_info *new_intf;
struct ipa_wdi_intf_info *entry;
struct ipa_tx_intf tx;
struct ipa_rx_intf rx;
struct ipa_ioc_tx_intf_prop *tx_prop = NULL;
struct ipa_ioc_rx_intf_prop *rx_prop = NULL;
u32 len;
int ret = 0;
if (in == NULL) {
IPA_WDI_ERR("invalid params in=%pK\n", in);
return -EINVAL;
}
if (!ipa_wdi_ctx) {
IPA_WDI_ERR("wdi ctx is not initialized\n");
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)
if (strcmp(entry->netdev_name, in->netdev_name) == 0) {
IPA_WDI_DBG("intf was added before.\n");
mutex_unlock(&ipa_wdi_ctx->lock);
return 0;
}
IPA_WDI_DBG("intf was not added before, proceed.\n");
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);
return -ENOMEM;
}
INIT_LIST_HEAD(&new_intf->link);
strlcpy(new_intf->netdev_name, in->netdev_name,
sizeof(new_intf->netdev_name));
new_intf->hdr_len = in->hdr_info[0].hdr_len;
/* add partial header */
len = sizeof(struct ipa_ioc_add_hdr) + 2 * sizeof(struct ipa_hdr_add);
hdr = kzalloc(len, GFP_KERNEL);
if (hdr == NULL) {
IPA_WDI_ERR("fail to alloc %d bytes\n", len);
ret = -EFAULT;
goto fail_alloc_hdr;
}
if (ipa_wdi_commit_partial_hdr(hdr, in->netdev_name, in->hdr_info)) {
IPA_WDI_ERR("fail to commit partial headers\n");
ret = -EFAULT;
goto fail_commit_hdr;
}
new_intf->partial_hdr_hdl[IPA_IP_v4] = hdr->hdr[IPA_IP_v4].hdr_hdl;
new_intf->partial_hdr_hdl[IPA_IP_v6] = hdr->hdr[IPA_IP_v6].hdr_hdl;
IPA_WDI_DBG("IPv4 hdr hdl: %d IPv6 hdr hdl: %d\n",
hdr->hdr[IPA_IP_v4].hdr_hdl, hdr->hdr[IPA_IP_v6].hdr_hdl);
/* populate tx prop */
tx_prop = kmalloc(
sizeof(*tx_prop) * IPA_TX_MAX_INTF_PROP, GFP_KERNEL);
if (!tx_prop) {
IPAERR("failed to allocate memory\n");
ret = -ENOMEM;
goto fail_commit_hdr;
}
tx.num_props = 2;
memset(tx_prop, 0, sizeof(*tx_prop));
tx.prop = tx_prop;
tx_prop[0].ip = IPA_IP_v4;
if (!ipa3_ctx->ipa_wdi3_over_gsi)
tx_prop[0].dst_pipe = IPA_CLIENT_WLAN1_CONS;
else
tx_prop[0].dst_pipe = IPA_CLIENT_WLAN2_CONS;
tx_prop[0].alt_dst_pipe = in->alt_dst_pipe;
tx_prop[0].hdr_l2_type = in->hdr_info[0].hdr_type;
strlcpy(tx_prop[0].hdr_name, hdr->hdr[IPA_IP_v4].name,
sizeof(tx_prop[0].hdr_name));
tx_prop[1].ip = IPA_IP_v6;
if (!ipa3_ctx->ipa_wdi3_over_gsi)
tx_prop[1].dst_pipe = IPA_CLIENT_WLAN1_CONS;
else
tx_prop[1].dst_pipe = IPA_CLIENT_WLAN2_CONS;
tx_prop[1].alt_dst_pipe = in->alt_dst_pipe;
tx_prop[1].hdr_l2_type = in->hdr_info[1].hdr_type;
strlcpy(tx_prop[1].hdr_name, hdr->hdr[IPA_IP_v6].name,
sizeof(tx_prop[1].hdr_name));
/* populate rx prop */
rx_prop = kmalloc(
sizeof(*rx_prop) * IPA_RX_MAX_INTF_PROP, GFP_KERNEL);
if (!rx_prop) {
IPAERR("failed to allocate memory\n");
ret = -ENOMEM;
goto fail_commit_hdr;
}
rx.num_props = 2;
memset(rx_prop, 0, sizeof(*rx_prop));
rx.prop = rx_prop;
rx_prop[0].ip = IPA_IP_v4;
if (!ipa3_ctx->ipa_wdi3_over_gsi)
rx_prop[0].src_pipe = IPA_CLIENT_WLAN1_PROD;
else
rx_prop[0].src_pipe = IPA_CLIENT_WLAN2_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;
rx_prop[0].attrib.meta_data = in->meta_data;
rx_prop[0].attrib.meta_data_mask = in->meta_data_mask;
}
rx_prop[1].ip = IPA_IP_v6;
if (!ipa3_ctx->ipa_wdi3_over_gsi)
rx_prop[1].src_pipe = IPA_CLIENT_WLAN1_PROD;
else
rx_prop[1].src_pipe = IPA_CLIENT_WLAN2_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 (ipa_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);
kfree(hdr);
kfree(tx_prop);
kfree(rx_prop);
mutex_unlock(&ipa_wdi_ctx->lock);
return 0;
fail_commit_hdr:
kfree(hdr);
kfree(tx_prop);
kfree(rx_prop);
fail_alloc_hdr:
kfree(new_intf);
mutex_unlock(&ipa_wdi_ctx->lock);
return ret;
}
EXPORT_SYMBOL(ipa_wdi_reg_intf);
int ipa_wdi_dereg_intf(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 (ipa_del_hdr(hdr)) {
IPA_WDI_ERR("fail to delete partial header\n");
ret = -EFAULT;
goto fail;
}
if (ipa_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;
}
EXPORT_SYMBOL(ipa_wdi_dereg_intf);
static void ipa_wdi_pm_cb(void *p, enum ipa_pm_cb_event event)
{
IPA_WDI_DBG("received pm event %d\n", event);
}
int ipa_wdi_conn_pipes(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;
struct ipa_wdi_in_params in_tx;
struct ipa_wdi_in_params in_rx;
struct ipa_wdi_out_params out_tx;
struct ipa_wdi_out_params out_rx;
if (!(in && out)) {
IPA_WDI_ERR("empty parameters. in=%pK out=%pK\n", in, out);
return -EINVAL;
}
if (!ipa_wdi_ctx) {
IPA_WDI_ERR("wdi ctx is not initialized\n");
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_DBG("number of sys pipe %d\n", in->num_sys_pipe_needed);
/* setup sys pipe when needed */
for (i = 0; i < ipa_wdi_ctx->num_sys_pipe_needed; i++) {
ret = ipa_setup_sys_pipe(&in->sys_in[i],
&ipa_wdi_ctx->sys_pipe_hdl[i]);
if (ret) {
IPA_WDI_ERR("fail to setup sys pipe %d\n", i);
ret = -EFAULT;
goto fail_setup_sys_pipe;
}
}
memset(&pm_params, 0, sizeof(pm_params));
pm_params.name = "wdi";
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)) {
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 (ipa_conn_wdi_pipes(in, out, ipa_wdi_ctx->wdi_notify)) {
IPA_WDI_ERR("fail to setup wdi pipes\n");
ret = -EFAULT;
goto fail_connect_pipe;
}
} else {
memset(&in_tx, 0, sizeof(in_tx));
memset(&in_rx, 0, sizeof(in_rx));
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;
#endif
if (in->is_smmu_enabled == false) {
/* firsr setup rx pipe */
in_rx.sys.ipa_ep_cfg = in->u_rx.rx.ipa_ep_cfg;
in_rx.sys.client = in->u_rx.rx.client;
in_rx.sys.notify = in->notify;
in_rx.sys.priv = in->priv;
in_rx.smmu_enabled = in->is_smmu_enabled;
in_rx.u.ul.rdy_ring_base_pa =
in->u_rx.rx.transfer_ring_base_pa;
in_rx.u.ul.rdy_ring_size =
in->u_rx.rx.transfer_ring_size;
in_rx.u.ul.rdy_ring_rp_pa =
in->u_rx.rx.transfer_ring_doorbell_pa;
in_rx.u.ul.rdy_comp_ring_base_pa =
in->u_rx.rx.event_ring_base_pa;
in_rx.u.ul.rdy_comp_ring_wp_pa =
in->u_rx.rx.event_ring_doorbell_pa;
in_rx.u.ul.rdy_comp_ring_size =
in->u_rx.rx.event_ring_size;
if (ipa_connect_wdi_pipe(&in_rx, &out_rx)) {
IPA_WDI_ERR("fail to setup rx pipe\n");
ret = -EFAULT;
goto fail_connect_pipe;
}
ipa_wdi_ctx->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);
/* then setup tx pipe */
in_tx.sys.ipa_ep_cfg = in->u_tx.tx.ipa_ep_cfg;
in_tx.sys.client = in->u_tx.tx.client;
in_tx.smmu_enabled = in->is_smmu_enabled;
in_tx.u.dl.comp_ring_base_pa =
in->u_tx.tx.transfer_ring_base_pa;
in_tx.u.dl.comp_ring_size =
in->u_tx.tx.transfer_ring_size;
in_tx.u.dl.ce_ring_base_pa =
in->u_tx.tx.event_ring_base_pa;
in_tx.u.dl.ce_door_bell_pa =
in->u_tx.tx.event_ring_doorbell_pa;
in_tx.u.dl.ce_ring_size =
in->u_tx.tx.event_ring_size;
in_tx.u.dl.num_tx_buffers =
in->u_tx.tx.num_pkt_buffers;
if (ipa_connect_wdi_pipe(&in_tx, &out_tx)) {
IPA_WDI_ERR("fail to setup tx pipe\n");
ret = -EFAULT;
goto fail;
}
ipa_wdi_ctx->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 */
/* firsr setup rx pipe */
in_rx.sys.ipa_ep_cfg = in->u_rx.rx_smmu.ipa_ep_cfg;
in_rx.sys.client = in->u_rx.rx_smmu.client;
in_rx.sys.notify = in->notify;
in_rx.sys.priv = in->priv;
in_rx.smmu_enabled = in->is_smmu_enabled;
in_rx.u.ul_smmu.rdy_ring =
in->u_rx.rx_smmu.transfer_ring_base;
in_rx.u.ul_smmu.rdy_ring_size =
in->u_rx.rx_smmu.transfer_ring_size;
in_rx.u.ul_smmu.rdy_ring_rp_pa =
in->u_rx.rx_smmu.transfer_ring_doorbell_pa;
in_rx.u.ul_smmu.rdy_comp_ring =
in->u_rx.rx_smmu.event_ring_base;
in_rx.u.ul_smmu.rdy_comp_ring_wp_pa =
in->u_rx.rx_smmu.event_ring_doorbell_pa;
in_rx.u.ul_smmu.rdy_comp_ring_size =
in->u_rx.rx_smmu.event_ring_size;
if (ipa_connect_wdi_pipe(&in_rx, &out_rx)) {
IPA_WDI_ERR("fail to setup rx pipe\n");
ret = -EFAULT;
goto fail_connect_pipe;
}
ipa_wdi_ctx->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);
/* then setup tx pipe */
in_tx.sys.ipa_ep_cfg = in->u_tx.tx_smmu.ipa_ep_cfg;
in_tx.sys.client = in->u_tx.tx_smmu.client;
in_tx.smmu_enabled = in->is_smmu_enabled;
in_tx.u.dl_smmu.comp_ring =
in->u_tx.tx_smmu.transfer_ring_base;
in_tx.u.dl_smmu.comp_ring_size =
in->u_tx.tx_smmu.transfer_ring_size;
in_tx.u.dl_smmu.ce_ring =
in->u_tx.tx_smmu.event_ring_base;
in_tx.u.dl_smmu.ce_door_bell_pa =
in->u_tx.tx_smmu.event_ring_doorbell_pa;
in_tx.u.dl_smmu.ce_ring_size =
in->u_tx.tx_smmu.event_ring_size;
in_tx.u.dl_smmu.num_tx_buffers =
in->u_tx.tx_smmu.num_pkt_buffers;
if (ipa_connect_wdi_pipe(&in_tx, &out_tx)) {
IPA_WDI_ERR("fail to setup tx pipe\n");
ret = -EFAULT;
goto fail;
}
ipa_wdi_ctx->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);
}
}
return 0;
fail:
ipa_disconnect_wdi_pipe(ipa_wdi_ctx->rx_pipe_hdl);
fail_connect_pipe:
ipa_pm_deregister(ipa_wdi_ctx->ipa_pm_hdl);
fail_setup_sys_pipe:
for (j = 0; j < i; j++)
ipa_teardown_sys_pipe(ipa_wdi_ctx->sys_pipe_hdl[j]);
return ret;
}
EXPORT_SYMBOL(ipa_wdi_conn_pipes);
int ipa_wdi_disconn_pipes(void)
{
int i, ipa_ep_idx_rx, ipa_ep_idx_tx;
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 (!ipa3_ctx->ipa_wdi3_over_gsi) {
ipa_ep_idx_rx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_PROD);
ipa_ep_idx_tx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_CONS);
} else {
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->wdi_version == IPA_WDI_3) {
if (ipa_disconn_wdi_pipes(ipa_ep_idx_rx, ipa_ep_idx_tx)) {
IPA_WDI_ERR("fail to tear down wdi pipes\n");
return -EFAULT;
}
} else {
if (ipa_disconnect_wdi_pipe(ipa_wdi_ctx->tx_pipe_hdl)) {
IPA_WDI_ERR("fail to tear down wdi tx pipes\n");
return -EFAULT;
}
if (ipa_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;
}
EXPORT_SYMBOL(ipa_wdi_disconn_pipes);
int ipa_wdi_enable_pipes(void)
{
int ret;
int ipa_ep_idx_tx, ipa_ep_idx_rx;
if (!ipa_wdi_ctx) {
IPA_WDI_ERR("wdi ctx is not initialized.\n");
return -EPERM;
}
if (!ipa3_ctx->ipa_wdi3_over_gsi) {
ipa_ep_idx_rx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_PROD);
ipa_ep_idx_tx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_CONS);
} else {
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->wdi_version == IPA_WDI_3) {
if (ipa_enable_wdi_pipes(ipa_ep_idx_tx, ipa_ep_idx_rx)) {
IPA_WDI_ERR("fail to enable wdi pipes\n");
return -EFAULT;
}
} else {
if (ipa_enable_wdi_pipe(ipa_wdi_ctx->tx_pipe_hdl)) {
IPA_WDI_ERR("fail to enable wdi tx pipe\n");
return -EFAULT;
}
if (ipa_resume_wdi_pipe(ipa_wdi_ctx->tx_pipe_hdl)) {
IPA_WDI_ERR("fail to resume wdi tx pipe\n");
return -EFAULT;
}
if (ipa_enable_wdi_pipe(ipa_wdi_ctx->rx_pipe_hdl)) {
IPA_WDI_ERR("fail to enable wdi rx pipe\n");
return -EFAULT;
}
if (ipa_resume_wdi_pipe(ipa_wdi_ctx->rx_pipe_hdl)) {
IPA_WDI_ERR("fail to resume wdi rx pipe\n");
return -EFAULT;
}
}
ret = ipa_pm_activate_sync(ipa_wdi_ctx->ipa_pm_hdl);
if (ret) {
IPA_WDI_ERR("fail to activate ipa pm\n");
return -EFAULT;
}
return 0;
}
EXPORT_SYMBOL(ipa_wdi_enable_pipes);
int ipa_wdi_disable_pipes(void)
{
int ret;
int ipa_ep_idx_tx, ipa_ep_idx_rx;
if (!ipa_wdi_ctx) {
IPA_WDI_ERR("wdi ctx is not initialized.\n");
return -EPERM;
}
if (!ipa3_ctx->ipa_wdi3_over_gsi) {
ipa_ep_idx_rx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_PROD);
ipa_ep_idx_tx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_CONS);
} else {
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->wdi_version == IPA_WDI_3) {
if (ipa_disable_wdi_pipes(ipa_ep_idx_tx, ipa_ep_idx_rx)) {
IPA_WDI_ERR("fail to disable wdi pipes\n");
return -EFAULT;
}
} else {
if (ipa_suspend_wdi_pipe(ipa_wdi_ctx->tx_pipe_hdl)) {
IPA_WDI_ERR("fail to suspend wdi tx pipe\n");
return -EFAULT;
}
if (ipa_disable_wdi_pipe(ipa_wdi_ctx->tx_pipe_hdl)) {
IPA_WDI_ERR("fail to disable wdi tx pipe\n");
return -EFAULT;
}
if (ipa_suspend_wdi_pipe(ipa_wdi_ctx->rx_pipe_hdl)) {
IPA_WDI_ERR("fail to suspend wdi rx pipe\n");
return -EFAULT;
}
if (ipa_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;
}
EXPORT_SYMBOL(ipa_wdi_disable_pipes);
int ipa_wdi_set_perf_profile(struct ipa_wdi_perf_profile *profile)
{
if (profile == NULL) {
IPA_WDI_ERR("Invalid input\n");
return -EINVAL;
}
if (ipa_pm_set_throughput(ipa_wdi_ctx->ipa_pm_hdl,
profile->max_supported_bw_mbps)) {
IPA_WDI_ERR("fail to set pm throughput\n");
return -EFAULT;
}
return 0;
}
EXPORT_SYMBOL(ipa_wdi_set_perf_profile);
int ipa_wdi_create_smmu_mapping(u32 num_buffers,
struct ipa_wdi_buffer_info *info)
{
return ipa_create_wdi_mapping(num_buffers, info);
}
EXPORT_SYMBOL(ipa_wdi_create_smmu_mapping);
int ipa_wdi_release_smmu_mapping(u32 num_buffers,
struct ipa_wdi_buffer_info *info)
{
return ipa_release_wdi_mapping(num_buffers, info);
}
EXPORT_SYMBOL(ipa_wdi_release_smmu_mapping);
int ipa_wdi_get_stats(struct IpaHwStatsWDIInfoData_t *stats)
{
return ipa_get_wdi_stats(stats);
}
EXPORT_SYMBOL(ipa_wdi_get_stats);
int ipa_wdi_bw_monitor(struct ipa_wdi_bw_info *info)
{
return ipa_uc_bw_monitor(info);
}
EXPORT_SYMBOL(ipa_wdi_bw_monitor);
int ipa_wdi_sw_stats(struct ipa_wdi_tx_info *info)
{
return ipa_set_wlan_tx_info(info);
}
EXPORT_SYMBOL(ipa_wdi_sw_stats);