diff --git a/drivers/platform/msm/ipa/ipa_api.c b/drivers/platform/msm/ipa/ipa_api.c index b1fa79dd870c..0cef7a32beb2 100644 --- a/drivers/platform/msm/ipa/ipa_api.c +++ b/drivers/platform/msm/ipa/ipa_api.c @@ -200,6 +200,14 @@ const char *ipa_clients_strings[IPA_CLIENT_MAX] = { __stringify(IPA_CLIENT_Q6_AUDIO_DMA_MHI_CONS), __stringify(RESERVED_PROD_86), __stringify(IPA_CLIENT_APPS_WAN_COAL_CONS), + __stringify(IPA_CLIENT_WIGIG_PROD), + __stringify(IPA_CLIENT_WIGIG_CONS1), + __stringify(RESERVERD_PROD_90), + __stringify(IPA_CLIENT_WIGIG_CONS2), + __stringify(RESERVERD_PROD_92), + __stringify(IPA_CLIENT_WIGIG_CONS3), + __stringify(RESERVERD_PROD_94), + __stringify(IPA_CLIENT_WIGIG_CONS4), }; /** @@ -1929,7 +1937,7 @@ EXPORT_SYMBOL(ipa_uc_wdi_get_dbpa); /** * ipa_uc_reg_rdyCB() - To register uC - * ready CB if uC not ready + * ready CB if uC not ready, wdi only. * @inout: [in/out] input/output parameters * from/to client * @@ -1947,6 +1955,31 @@ int ipa_uc_reg_rdyCB( } EXPORT_SYMBOL(ipa_uc_reg_rdyCB); +/** +* ipa_wigig_uc_init() - get uc db and register uC +* ready CB if uC not ready, wigig only. +* @inout: [in/out] uc ready input/output parameters +* from/to client +* @int_notify: [in] wigig misc interrupt handler function +* +* Returns: 0 on success, negative on failure +* +*/ + +int ipa_wigig_uc_init( + struct ipa_wdi_uc_ready_params *inout, + ipa_wigig_misc_int_cb int_notify, + phys_addr_t *uc_db_pa) +{ + int ret; + + IPA_API_DISPATCH_RETURN(ipa_wigig_uc_init, inout, + int_notify, uc_db_pa); + + return ret; +} +EXPORT_SYMBOL(ipa_wigig_uc_init); + /** * ipa_uc_dereg_rdyCB() - To de-register uC ready CB * @@ -3374,6 +3407,98 @@ int ipa_disable_wdi_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx) return ret; } +/** + * ipa_wigig_uc_msi_init() - smmu map\unmap msi related wigig HW registers + * and init\deinit uC msi config + */ +int ipa_wigig_uc_msi_init(bool init, + phys_addr_t periph_baddr_pa, + phys_addr_t pseudo_cause_pa, + phys_addr_t int_gen_tx_pa, + phys_addr_t int_gen_rx_pa, + phys_addr_t dma_ep_misc_pa) +{ + int ret; + + IPA_API_DISPATCH_RETURN(ipa_wigig_uc_msi_init, init, + periph_baddr_pa, + pseudo_cause_pa, + int_gen_tx_pa, + int_gen_rx_pa, + dma_ep_misc_pa); + + return ret; +} +EXPORT_SYMBOL(ipa_wigig_uc_msi_init); + +/** + * ipa_conn_wigig_rx_pipe_i() - connect wigig rx pipe + */ +int ipa_conn_wigig_rx_pipe_i(void *in, struct ipa_wigig_conn_out_params *out) +{ + int ret; + + IPA_API_DISPATCH_RETURN(ipa_conn_wigig_rx_pipe_i, in, out); + + return ret; +} +EXPORT_SYMBOL(ipa_conn_wigig_rx_pipe_i); + +/** + * ipa_conn_wigig_client_i() - connect a wigig client + */ +int ipa_conn_wigig_client_i(void *in, struct ipa_wigig_conn_out_params *out) +{ + int ret; + + IPA_API_DISPATCH_RETURN(ipa_conn_wigig_client_i, in, out); + + return ret; +} +EXPORT_SYMBOL(ipa_conn_wigig_client_i); + +/** + * ipa_disconn_wigig_pipe_i() - disconnect a wigig pipe + */ +int ipa_disconn_wigig_pipe_i(enum ipa_client_type client, + struct ipa_wigig_pipe_setup_info_smmu *pipe_smmu, + void *dbuff) +{ + int ret; + + IPA_API_DISPATCH_RETURN(ipa_disconn_wigig_pipe_i, client, + pipe_smmu, dbuff); + + return ret; +} +EXPORT_SYMBOL(ipa_disconn_wigig_pipe_i); + +/** + * ipa_enable_wigig_pipe() - enable a wigig pipe + */ +int ipa_enable_wigig_pipe_i(enum ipa_client_type client) +{ + int ret; + + IPA_API_DISPATCH_RETURN(ipa_enable_wigig_pipe_i, client); + + return ret; +} +EXPORT_SYMBOL(ipa_enable_wigig_pipe_i); + +/** + * ipa_disable_wigig_pipe_i() - disable a wigig pipe + */ +int ipa_disable_wigig_pipe_i(enum ipa_client_type client) +{ + int ret; + + IPA_API_DISPATCH_RETURN(ipa_disable_wigig_pipe_i, client); + + return ret; +} +EXPORT_SYMBOL(ipa_disable_wigig_pipe_i); + /** * ipa_tz_unlock_reg() - Allow AP access to memory regions controlled by TZ */ diff --git a/drivers/platform/msm/ipa/ipa_api.h b/drivers/platform/msm/ipa/ipa_api.h index 1c84f89a823f..d633eeb4c888 100644 --- a/drivers/platform/msm/ipa/ipa_api.h +++ b/drivers/platform/msm/ipa/ipa_api.h @@ -428,6 +428,32 @@ struct ipa_api_controller { int (*ipa_is_vlan_mode)(enum ipa_vlan_ifaces iface, bool *res); bool (*ipa_pm_is_used)(void); + + int (*ipa_wigig_uc_init)( + struct ipa_wdi_uc_ready_params *inout, + ipa_wigig_misc_int_cb int_notify, + phys_addr_t *uc_db_pa); + + int (*ipa_conn_wigig_rx_pipe_i)(void *in, + struct ipa_wigig_conn_out_params *out); + + int (*ipa_conn_wigig_client_i)(void *in, + struct ipa_wigig_conn_out_params *out); + + int (*ipa_disconn_wigig_pipe_i)(enum ipa_client_type client, + struct ipa_wigig_pipe_setup_info_smmu *pipe_smmu, + void *dbuff); + + int (*ipa_wigig_uc_msi_init)(bool init, + phys_addr_t periph_baddr_pa, + phys_addr_t pseudo_cause_pa, + phys_addr_t int_gen_tx_pa, + phys_addr_t int_gen_rx_pa, + phys_addr_t dma_ep_misc_pa); + + int (*ipa_enable_wigig_pipe_i)(enum ipa_client_type client); + + int (*ipa_disable_wigig_pipe_i)(enum ipa_client_type client); }; #ifdef CONFIG_IPA3 diff --git a/drivers/platform/msm/ipa/ipa_clients/Makefile b/drivers/platform/msm/ipa/ipa_clients/Makefile index a21313085c56..bdd86a9af797 100644 --- a/drivers/platform/msm/ipa/ipa_clients/Makefile +++ b/drivers/platform/msm/ipa/ipa_clients/Makefile @@ -1,4 +1,4 @@ -obj-$(CONFIG_IPA3) += ipa_usb.o odu_bridge.o ipa_mhi_client.o ipa_uc_offload.o ipa_wdi3.o ipa_gsb.o +obj-$(CONFIG_IPA3) += ipa_usb.o odu_bridge.o ipa_mhi_client.o ipa_uc_offload.o ipa_wdi3.o ipa_gsb.o ipa_wigig.o obj-$(CONFIG_IPA) += odu_bridge.o ipa_mhi_client.o ipa_uc_offload.o ipa_wdi3.o obj-$(CONFIG_ECM_IPA) += ecm_ipa.o obj-$(CONFIG_RNDIS_IPA) += rndis_ipa.o diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_wigig.c b/drivers/platform/msm/ipa/ipa_clients/ipa_wigig.c new file mode 100644 index 000000000000..7ea71c9c96a1 --- /dev/null +++ b/drivers/platform/msm/ipa/ipa_clients/ipa_wigig.c @@ -0,0 +1,1334 @@ +/* Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +#include +#include +#include "../ipa_common_i.h" +#include "../ipa_v3/ipa_pm.h" + +#define OFFLOAD_DRV_NAME "ipa_wigig" +#define IPA_WIGIG_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_WIGIG_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_WIGIG_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_WIGIG_RX_PIPE_IDX 0 +#define IPA_WIGIG_TX_PIPE_NUM 4 +#define IPA_WIGIG_MAX_PIPES 5 + +struct ipa_wigig_intf_info { + char netdev_name[IPA_RESOURCE_NAME_MAX]; + u8 netdev_mac[IPA_MAC_ADDR_SIZE]; + u8 hdr_len; + u32 partial_hdr_hdl[IPA_IP_MAX]; + struct list_head link; +}; + +struct ipa_wigig_context { + struct list_head head_intf_list; + struct mutex lock; + u32 ipa_pm_hdl; + phys_addr_t periph_baddr_pa; + phys_addr_t pseudo_cause_pa; + phys_addr_t int_gen_tx_pa; + phys_addr_t int_gen_rx_pa; + phys_addr_t dma_ep_misc_pa; + struct ipa_wigig_pipe_setup_info_smmu pipes_smmu[IPA_WIGIG_MAX_PIPES]; + struct ipa_wigig_rx_pipe_data_buffer_info_smmu rx_buff_smmu; + struct ipa_wigig_tx_pipe_data_buffer_info_smmu + tx_buff_smmu[IPA_WIGIG_TX_PIPE_NUM]; +}; + +static struct ipa_wigig_context *ipa_wigig_ctx; + +int ipa_wigig_init(struct ipa_wigig_init_in_params *in, + struct ipa_wigig_init_out_params *out) +{ + struct ipa_wdi_uc_ready_params inout; + + if (!in || !out) { + IPA_WIGIG_ERR("invalid params in=%pK, out %pK\n", in, out); + return -EINVAL; + } + + IPA_WIGIG_DBG("\n"); + if (ipa_wigig_ctx) { + IPA_WIGIG_ERR("ipa_wigig_ctx was initialized before\n"); + return -EINVAL; + } + + ipa_wigig_ctx = kzalloc(sizeof(*ipa_wigig_ctx), GFP_KERNEL); + if (ipa_wigig_ctx == NULL) + return -ENOMEM; + + mutex_init(&ipa_wigig_ctx->lock); + INIT_LIST_HEAD(&ipa_wigig_ctx->head_intf_list); + + ipa_wigig_ctx->pseudo_cause_pa = in->pseudo_cause_pa; + ipa_wigig_ctx->int_gen_tx_pa = in->int_gen_tx_pa; + ipa_wigig_ctx->int_gen_rx_pa = in->int_gen_rx_pa; + ipa_wigig_ctx->dma_ep_misc_pa = in->dma_ep_misc_pa; + ipa_wigig_ctx->periph_baddr_pa = in->periph_baddr_pa; + + inout.notify = in->notify; + inout.priv = in->priv; + if (ipa_wigig_uc_init(&inout, in->int_notify, &out->uc_db_pa)) { + kfree(ipa_wigig_ctx); + ipa_wigig_ctx = NULL; + return -EFAULT; + } + + out->is_uc_ready = inout.is_uC_ready; + + IPA_WIGIG_DBG("exit\n"); + + return 0; +} +EXPORT_SYMBOL(ipa_wigig_init); + +int ipa_wigig_cleanup(void) +{ + struct ipa_wigig_intf_info *entry; + struct ipa_wigig_intf_info *next; + + IPA_WIGIG_DBG("\n"); + + if (!ipa_wigig_ctx) + return -ENODEV; + + /* clear interface list */ + list_for_each_entry_safe(entry, next, + &ipa_wigig_ctx->head_intf_list, link) { + list_del(&entry->link); + kfree(entry); + } + + mutex_destroy(&ipa_wigig_ctx->lock); + kfree(ipa_wigig_ctx); + ipa_wigig_ctx = NULL; + + IPA_WIGIG_DBG("exit\n"); + return 0; +} +EXPORT_SYMBOL(ipa_wigig_cleanup); + +bool ipa_wigig_is_smmu_enabled(void) +{ + struct ipa_smmu_in_params in; + struct ipa_smmu_out_params out; + + IPA_WIGIG_DBG("\n"); + + in.smmu_client = IPA_SMMU_WLAN_CLIENT; + + ipa_get_smmu_params(&in, &out); + + IPA_WIGIG_DBG("exit\n"); + + return out.smmu_enable; +} +EXPORT_SYMBOL(ipa_wigig_is_smmu_enabled); + +static int ipa_wigig_commit_partial_hdr( + struct ipa_ioc_add_hdr *hdr, + const char *netdev_name, + struct ipa_wigig_hdr_info *hdr_info) +{ + int i; + + IPA_WIGIG_DBG("\n"); + + if (!netdev_name) { + IPA_WIGIG_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_WIGIG_ERR("fail to add partial headers\n"); + return -EFAULT; + } + + IPA_WIGIG_DBG("exit\n"); + + return 0; +} + +static void ipa_wigig_free_msg(void *msg, uint32_t len, uint32_t type) +{ + IPA_WIGIG_DBG("free msg type:%d, len:%d, buff %pK", type, len, msg); + kfree(msg); + IPA_WIGIG_DBG("exit\n"); +} + +static int ipa_wigig_send_msg(uint8_t msg_type, + const char *netdev_name, + u8 *netdev_mac) +{ + struct ipa_msg_meta msg_meta; + struct ipa_wlan_msg *msg; + int ret; + + IPA_WIGIG_DBG("\n"); + + msg_meta.msg_type = msg_type; + msg_meta.msg_len = sizeof(struct ipa_wlan_msg); + + msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (msg == NULL) + return -ENOMEM; + + strlcpy(msg->name, netdev_name, IPA_RESOURCE_NAME_MAX); + memcpy(msg->mac_addr, netdev_mac, IPA_MAC_ADDR_SIZE); + + IPA_WIGIG_DBG("send msg type:%d, len:%d, buff %pK", msg_meta.msg_type, + msg_meta.msg_len, msg); + ret = ipa_send_msg(&msg_meta, msg, ipa_wigig_free_msg); + + IPA_WIGIG_DBG("exit\n"); + + return ret; +} + +int ipa_wigig_reg_intf( + struct ipa_wigig_reg_intf_in_params *in) +{ + struct ipa_wigig_intf_info *new_intf; + struct ipa_wigig_intf_info *entry; + struct ipa_tx_intf tx; + struct ipa_rx_intf rx; + struct ipa_ioc_tx_intf_prop tx_prop[2]; + struct ipa_ioc_rx_intf_prop rx_prop[2]; + struct ipa_ioc_add_hdr *hdr; + struct ipa_ioc_del_hdr *del_hdr = NULL; + u32 len; + int ret = 0; + + IPA_WIGIG_DBG("\n"); + + if (in == NULL) { + IPA_WIGIG_ERR("invalid params in=%pK\n", in); + return -EINVAL; + } + + if (!ipa_wigig_ctx) { + IPA_WIGIG_ERR("wigig ctx is not initialized\n"); + return -EPERM; + } + + IPA_WIGIG_DBG("register interface for netdev %s\n", + in->netdev_name); + + mutex_lock(&ipa_wigig_ctx->lock); + list_for_each_entry(entry, &ipa_wigig_ctx->head_intf_list, link) + if (strcmp(entry->netdev_name, in->netdev_name) == 0) { + IPA_WIGIG_DBG("intf was added before.\n"); + mutex_unlock(&ipa_wigig_ctx->lock); + return 0; + } + + IPA_WIGIG_DBG("intf was not added before, proceed.\n"); + new_intf = kzalloc(sizeof(*new_intf), GFP_KERNEL); + if (new_intf == NULL) { + ret = -ENOMEM; + goto fail; + } + + 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; + memcpy(new_intf->netdev_mac, in->netdev_mac, IPA_MAC_ADDR_SIZE); + + /* add partial header */ + len = sizeof(struct ipa_ioc_add_hdr) + 2 * sizeof(struct ipa_hdr_add); + hdr = kzalloc(len, GFP_KERNEL); + if (hdr == NULL) { + ret = -EFAULT; + goto fail_alloc_hdr; + } + + if (ipa_wigig_commit_partial_hdr(hdr, + in->netdev_name, + in->hdr_info)) { + IPA_WIGIG_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_WIGIG_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.num_props = 2; + tx.prop = tx_prop; + + memset(tx_prop, 0, sizeof(tx_prop)); + tx_prop[0].ip = IPA_IP_v4; + /* + * for consumers, we register a default pipe, but IPACM will determine + * the actual pipe according to the relevant client MAC + */ + tx_prop[0].dst_pipe = IPA_CLIENT_WIGIG1_CONS; + 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; + tx_prop[1].dst_pipe = IPA_CLIENT_WIGIG1_CONS; + 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.num_props = 2; + rx.prop = rx_prop; + + memset(rx_prop, 0, sizeof(rx_prop)); + rx_prop[0].ip = IPA_IP_v4; + rx_prop[0].src_pipe = IPA_CLIENT_WIGIG_PROD; + rx_prop[0].hdr_l2_type = in->hdr_info[0].hdr_type; + + rx_prop[1].ip = IPA_IP_v6; + rx_prop[1].src_pipe = IPA_CLIENT_WIGIG_PROD; + rx_prop[1].hdr_l2_type = in->hdr_info[1].hdr_type; + + if (ipa_register_intf(in->netdev_name, &tx, &rx)) { + IPA_WIGIG_ERR("fail to add interface prop\n"); + ret = -EFAULT; + goto fail_register; + } + + if (ipa_wigig_send_msg(WLAN_AP_CONNECT, + in->netdev_name, + in->netdev_mac)) { + IPA_WIGIG_ERR("couldn't send msg to IPACM\n"); + ret = -EFAULT; + goto fail_sendmsg; + } + + list_add(&new_intf->link, &ipa_wigig_ctx->head_intf_list); + + kfree(hdr); + mutex_unlock(&ipa_wigig_ctx->lock); + + IPA_WIGIG_DBG("exit\n"); + return 0; +fail_sendmsg: + ipa_deregister_intf(in->netdev_name); +fail_register: + del_hdr = kzalloc(sizeof(struct ipa_ioc_del_hdr) + + 2 * sizeof(struct ipa_hdr_del), GFP_KERNEL); + if (del_hdr) { + del_hdr->commit = 1; + del_hdr->num_hdls = 2; + del_hdr->hdl[0].hdl = new_intf->partial_hdr_hdl[IPA_IP_v4]; + del_hdr->hdl[1].hdl = new_intf->partial_hdr_hdl[IPA_IP_v6]; + ipa_del_hdr(del_hdr); + kfree(del_hdr); + } + new_intf->partial_hdr_hdl[IPA_IP_v4] = 0; + new_intf->partial_hdr_hdl[IPA_IP_v6] = 0; +fail_commit_hdr: + kfree(hdr); +fail_alloc_hdr: + kfree(new_intf); +fail: + mutex_unlock(&ipa_wigig_ctx->lock); + return ret; +} +EXPORT_SYMBOL(ipa_wigig_reg_intf); + +int ipa_wigig_dereg_intf(const char *netdev_name) +{ + int len, ret; + struct ipa_ioc_del_hdr *hdr = NULL; + struct ipa_wigig_intf_info *entry; + struct ipa_wigig_intf_info *next; + + IPA_WIGIG_DBG("\n"); + + if (!netdev_name) { + IPA_WIGIG_ERR("no netdev name\n"); + return -EINVAL; + } + + if (!ipa_wigig_ctx) { + IPA_WIGIG_ERR("wigig ctx is not initialized\n"); + return -EPERM; + } + + mutex_lock(&ipa_wigig_ctx->lock); + + ret = -EFAULT; + + list_for_each_entry_safe(entry, next, &ipa_wigig_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) { + mutex_unlock(&ipa_wigig_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_WIGIG_DBG("IPv4 hdr hdl: %d IPv6 hdr hdl: %d\n", + hdr->hdl[0].hdl, hdr->hdl[1].hdl); + + if (ipa_del_hdr(hdr)) { + IPA_WIGIG_ERR( + "fail to delete partial header\n"); + ret = -EFAULT; + goto fail; + } + + if (ipa_deregister_intf(entry->netdev_name)) { + IPA_WIGIG_ERR("fail to del interface props\n"); + ret = -EFAULT; + goto fail; + } + + if (ipa_wigig_send_msg(WLAN_AP_DISCONNECT, + entry->netdev_name, + entry->netdev_mac)) { + IPA_WIGIG_ERR("couldn't send msg to IPACM\n"); + ret = -EFAULT; + goto fail; + } + + list_del(&entry->link); + kfree(entry); + + ret = 0; + break; + } + + IPA_WIGIG_DBG("exit\n"); +fail: + kfree(hdr); + mutex_unlock(&ipa_wigig_ctx->lock); + return ret; +} +EXPORT_SYMBOL(ipa_wigig_dereg_intf); + +static void ipa_wigig_pm_cb(void *p, enum ipa_pm_cb_event event) +{ + IPA_WIGIG_DBG("received pm event %d\n", event); +} + +int ipa_wigig_conn_rx_pipe(struct ipa_wigig_conn_rx_in_params *in, + struct ipa_wigig_conn_out_params *out) +{ + int ret; + struct ipa_pm_register_params pm_params; + + IPA_WIGIG_DBG("\n"); + + if (!in || !out) { + IPA_WIGIG_ERR("empty parameters. in=%pK out=%pK\n", in, out); + return -EINVAL; + } + + if (!ipa_wigig_ctx) { + IPA_WIGIG_ERR("wigig ctx is not initialized\n"); + return -EPERM; + } + + if (ipa_wigig_is_smmu_enabled()) { + IPA_WIGIG_ERR("IPA SMMU is enabled, wrong API used\n"); + return -EFAULT; + } + + ret = ipa_uc_state_check(); + if (ret) { + IPA_WIGIG_ERR("uC not ready\n"); + return ret; + } + + pm_params.name = "wigig"; + pm_params.callback = ipa_wigig_pm_cb; + pm_params.user_data = NULL; + pm_params.group = IPA_PM_GROUP_DEFAULT; + if (ipa_pm_register(&pm_params, &ipa_wigig_ctx->ipa_pm_hdl)) { + IPA_WIGIG_ERR("fail to register ipa pm\n"); + ret = -EFAULT; + goto fail_pm; + } + IPA_WIGIG_DBG("pm hdl %d\n", ipa_wigig_ctx->ipa_pm_hdl); + + ret = ipa_wigig_uc_msi_init(true, + ipa_wigig_ctx->periph_baddr_pa, + ipa_wigig_ctx->pseudo_cause_pa, + ipa_wigig_ctx->int_gen_tx_pa, + ipa_wigig_ctx->int_gen_rx_pa, + ipa_wigig_ctx->dma_ep_misc_pa); + if (ret) { + IPA_WIGIG_ERR("failed configuring msi regs at uC\n"); + ret = -EFAULT; + goto fail_msi; + } + + if (ipa_conn_wigig_rx_pipe_i(in, out)) { + IPA_WIGIG_ERR("fail to connect rx pipe\n"); + ret = -EFAULT; + goto fail_connect_pipe; + } + + IPA_WIGIG_DBG("exit\n"); + + return 0; + +fail_connect_pipe: + ipa_wigig_uc_msi_init(false, + ipa_wigig_ctx->periph_baddr_pa, + ipa_wigig_ctx->pseudo_cause_pa, + ipa_wigig_ctx->int_gen_tx_pa, + ipa_wigig_ctx->int_gen_rx_pa, + ipa_wigig_ctx->dma_ep_misc_pa); +fail_msi: + ipa_pm_deregister(ipa_wigig_ctx->ipa_pm_hdl); +fail_pm: + return ret; +} +EXPORT_SYMBOL(ipa_wigig_conn_rx_pipe); + +static int ipa_wigig_client_to_idx(enum ipa_client_type client, + unsigned int *idx) +{ + switch (client) { + case IPA_CLIENT_WIGIG1_CONS: + *idx = 1; + break; + case IPA_CLIENT_WIGIG2_CONS: + *idx = 2; + break; + case IPA_CLIENT_WIGIG3_CONS: + *idx = 3; + break; + case IPA_CLIENT_WIGIG4_CONS: + *idx = 4; + break; + default: + IPA_WIGIG_ERR("invalid client %d\n", client); + return -EINVAL; + } + + return 0; +} + +static int ipa_wigig_clean_pipe_smmu_info(unsigned int idx) +{ + IPA_WIGIG_DBG("cleaning pipe %d info\n", idx); + + if (idx >= IPA_WIGIG_MAX_PIPES) { + IPA_WIGIG_ERR("invalid index %d\n", idx); + return -EINVAL; + } + kfree(ipa_wigig_ctx->pipes_smmu[idx].desc_ring_base.sgl); + kfree(ipa_wigig_ctx->pipes_smmu[idx].status_ring_base.sgl); + + memset(ipa_wigig_ctx->pipes_smmu + idx, + 0, + sizeof(ipa_wigig_ctx->pipes_smmu[idx])); + + IPA_WIGIG_DBG("exit\n"); + + return 0; +} + +static int ipa_wigig_store_pipe_smmu_info + (struct ipa_wigig_pipe_setup_info_smmu *pipe_smmu, unsigned int idx) +{ + unsigned int nents; + struct scatterlist *sgl; + int ret; + + IPA_WIGIG_DBG("\n"); + + /* store regs */ + ipa_wigig_ctx->pipes_smmu[idx].desc_ring_HWHEAD_pa = + pipe_smmu->desc_ring_HWHEAD_pa; + ipa_wigig_ctx->pipes_smmu[idx].desc_ring_HWTAIL_pa = + pipe_smmu->desc_ring_HWTAIL_pa; + + ipa_wigig_ctx->pipes_smmu[idx].status_ring_HWHEAD_pa = + pipe_smmu->status_ring_HWHEAD_pa; + ipa_wigig_ctx->pipes_smmu[idx].status_ring_HWTAIL_pa = + pipe_smmu->status_ring_HWTAIL_pa; + + /* store rings IOVAs */ + ipa_wigig_ctx->pipes_smmu[idx].desc_ring_base_iova = + pipe_smmu->desc_ring_base_iova; + ipa_wigig_ctx->pipes_smmu[idx].status_ring_base_iova = + pipe_smmu->status_ring_base_iova; + + /* copy sgt */ + nents = pipe_smmu->desc_ring_base.nents; + sgl = kmemdup(pipe_smmu->desc_ring_base.sgl, + nents * sizeof(struct scatterlist), + GFP_KERNEL); + if (sgl == NULL) { + ret = -ENOMEM; + goto fail_desc; + } + ipa_wigig_ctx->pipes_smmu[idx].desc_ring_base.sgl = sgl; + ipa_wigig_ctx->pipes_smmu[idx].desc_ring_base.nents = nents; + ipa_wigig_ctx->pipes_smmu[idx].desc_ring_base.orig_nents = + pipe_smmu->desc_ring_base.orig_nents; + + nents = pipe_smmu->status_ring_base.nents; + sgl = kmemdup(pipe_smmu->status_ring_base.sgl, + nents * sizeof(struct scatterlist), + GFP_KERNEL); + if (sgl == NULL) { + ret = -ENOMEM; + goto fail_stat; + } + ipa_wigig_ctx->pipes_smmu[idx].status_ring_base.sgl = sgl; + ipa_wigig_ctx->pipes_smmu[idx].status_ring_base.nents = nents; + ipa_wigig_ctx->pipes_smmu[idx].status_ring_base.orig_nents = + pipe_smmu->status_ring_base.orig_nents; + + IPA_WIGIG_DBG("exit\n"); + + return 0; +fail_stat: + kfree(ipa_wigig_ctx->pipes_smmu[idx].desc_ring_base.sgl); + memset(&ipa_wigig_ctx->pipes_smmu[idx].desc_ring_base, + 0, sizeof(ipa_wigig_ctx->pipes_smmu[idx].desc_ring_base)); +fail_desc: + return ret; +} + +static int ipa_wigig_get_pipe_smmu_info( + struct ipa_wigig_pipe_setup_info_smmu **pipe_smmu, unsigned int idx) +{ + if (idx >= IPA_WIGIG_MAX_PIPES) { + IPA_WIGIG_ERR("exceeded pipe num %d > %d\n", + idx, IPA_WIGIG_MAX_PIPES); + return -EINVAL; + } + + *pipe_smmu = &ipa_wigig_ctx->pipes_smmu[idx]; + + return 0; +} +static void ipa_wigig_clean_rx_buff_smmu_info(void) +{ + IPA_WIGIG_DBG("clearing rx buff smmu info\n"); + + kfree(ipa_wigig_ctx->rx_buff_smmu.data_buffer_base.sgl); + memset(&ipa_wigig_ctx->rx_buff_smmu, + 0, + sizeof(ipa_wigig_ctx->rx_buff_smmu)); + + IPA_WIGIG_DBG("\n"); + + return; + +} + +static int ipa_wigig_store_rx_buff_smmu_info( + struct ipa_wigig_rx_pipe_data_buffer_info_smmu *dbuff_smmu) +{ + unsigned int nents; + struct scatterlist *sgl; + + IPA_WIGIG_DBG("\n"); + + nents = dbuff_smmu->data_buffer_base.nents; + sgl = kmemdup(dbuff_smmu->data_buffer_base.sgl, + nents * sizeof(struct scatterlist), + GFP_KERNEL); + if (sgl == NULL) + return -ENOMEM; + + ipa_wigig_ctx->rx_buff_smmu.data_buffer_base.sgl = sgl; + ipa_wigig_ctx->rx_buff_smmu.data_buffer_base.nents = nents; + ipa_wigig_ctx->rx_buff_smmu.data_buffer_base.orig_nents = + dbuff_smmu->data_buffer_base.orig_nents; + ipa_wigig_ctx->rx_buff_smmu.data_buffer_base_iova = + dbuff_smmu->data_buffer_base_iova; + ipa_wigig_ctx->rx_buff_smmu.data_buffer_size = + dbuff_smmu->data_buffer_size; + + IPA_WIGIG_DBG("exit\n"); + + return 0; +} + +static int ipa_wigig_get_rx_buff_smmu_info( + struct ipa_wigig_rx_pipe_data_buffer_info_smmu **dbuff_smmu) +{ + IPA_WIGIG_DBG("\n"); + + *dbuff_smmu = &ipa_wigig_ctx->rx_buff_smmu; + + IPA_WIGIG_DBG("exit\n"); + + return 0; +} + +static int ipa_wigig_store_tx_buff_smmu_info( + struct ipa_wigig_tx_pipe_data_buffer_info_smmu *dbuff_smmu, + unsigned int idx) +{ + unsigned int nents; + struct scatterlist *sgl; + int result, i; + struct ipa_wigig_tx_pipe_data_buffer_info_smmu *tx_buff_smmu; + + IPA_WIGIG_DBG("\n"); + + if (idx > (IPA_WIGIG_TX_PIPE_NUM - 1)) { + IPA_WIGIG_ERR("invalid tx index %d\n", idx); + return -EINVAL; + } + + tx_buff_smmu = ipa_wigig_ctx->tx_buff_smmu + idx; + + tx_buff_smmu->data_buffer_base = + kcalloc(dbuff_smmu->num_buffers, + sizeof(struct sg_table), + GFP_KERNEL); + if (!tx_buff_smmu->data_buffer_base) + return -ENOMEM; + + tx_buff_smmu->data_buffer_base_iova = + kcalloc(dbuff_smmu->num_buffers, sizeof(u64), GFP_KERNEL); + if (!tx_buff_smmu->data_buffer_base_iova) { + result = -ENOMEM; + goto fail_iova; + } + + for (i = 0; i < dbuff_smmu->num_buffers; i++) { + nents = dbuff_smmu->data_buffer_base[i].nents; + sgl = kmemdup(dbuff_smmu->data_buffer_base[i].sgl, + nents * sizeof(struct scatterlist), + GFP_KERNEL); + if (sgl == NULL) { + result = -ENOMEM; + goto fail_sgl; + } + + tx_buff_smmu->data_buffer_base[i].sgl = + sgl; + tx_buff_smmu->data_buffer_base[i].nents = + nents; + tx_buff_smmu->data_buffer_base[i].orig_nents = + dbuff_smmu->data_buffer_base[i].orig_nents; + tx_buff_smmu->data_buffer_base_iova[i] = + dbuff_smmu->data_buffer_base_iova[i]; + } + tx_buff_smmu->num_buffers = dbuff_smmu->num_buffers; + tx_buff_smmu->data_buffer_size = + dbuff_smmu->data_buffer_size; + + IPA_WIGIG_DBG("exit\n"); + + return 0; + +fail_sgl: + i--; + for (; i >= 0; i--) + kfree(tx_buff_smmu->data_buffer_base[i].sgl); + kfree(tx_buff_smmu->data_buffer_base_iova); + tx_buff_smmu->data_buffer_base_iova = NULL; +fail_iova: + kfree(tx_buff_smmu->data_buffer_base); + tx_buff_smmu->data_buffer_base = NULL; + return result; +} + +static int ipa_wigig_clean_tx_buff_smmu_info(unsigned int idx) +{ + unsigned int i; + struct ipa_wigig_tx_pipe_data_buffer_info_smmu *dbuff_smmu; + + IPA_WIGIG_DBG("\n"); + + if (idx > (IPA_WIGIG_TX_PIPE_NUM - 1)) { + IPA_WIGIG_ERR("invalid tx index %d\n", idx); + return -EINVAL; + } + + dbuff_smmu = &ipa_wigig_ctx->tx_buff_smmu[idx]; + + if (!dbuff_smmu->data_buffer_base) { + IPA_WIGIG_ERR("no pa has been allocated\n"); + return -EFAULT; + } + + for (i = 0; i < dbuff_smmu->num_buffers; i++) + kfree(dbuff_smmu->data_buffer_base[i].sgl); + + kfree(dbuff_smmu->data_buffer_base); + dbuff_smmu->data_buffer_base = NULL; + + kfree(dbuff_smmu->data_buffer_base_iova); + dbuff_smmu->data_buffer_base_iova = NULL; + + dbuff_smmu->data_buffer_size = 0; + dbuff_smmu->num_buffers = 0; + + IPA_WIGIG_DBG("exit\n"); + + return 0; +} + +static int ipa_wigig_get_tx_buff_smmu_info( +struct ipa_wigig_tx_pipe_data_buffer_info_smmu **dbuff_smmu, + unsigned int idx) +{ + if (idx > (IPA_WIGIG_TX_PIPE_NUM - 1)) { + IPA_WIGIG_ERR("invalid tx index %d\n", idx); + return -EINVAL; + } + + *dbuff_smmu = &ipa_wigig_ctx->tx_buff_smmu[idx]; + + return 0; +} + +static int ipa_wigig_store_rx_smmu_info + (struct ipa_wigig_conn_rx_in_params_smmu *in) +{ + int ret; + + IPA_WIGIG_DBG("\n"); + + ret = ipa_wigig_store_pipe_smmu_info(&in->pipe_smmu, + IPA_WIGIG_RX_PIPE_IDX); + if (ret) + return ret; + + ret = ipa_wigig_store_rx_buff_smmu_info(&in->dbuff_smmu); + if (ret) + goto fail_buff; + + IPA_WIGIG_DBG("exit\n"); + + return 0; + +fail_buff: + ipa_wigig_clean_pipe_smmu_info(IPA_WIGIG_RX_PIPE_IDX); + return ret; +} + +static int ipa_wigig_store_client_smmu_info +(struct ipa_wigig_conn_tx_in_params_smmu *in, enum ipa_client_type client) +{ + int ret; + unsigned int idx; + + IPA_WIGIG_DBG("\n"); + + ret = ipa_wigig_client_to_idx(client, &idx); + if (ret) + return ret; + + ret = ipa_wigig_store_pipe_smmu_info(&in->pipe_smmu, idx); + if (ret) + return ret; + + ret = ipa_wigig_store_tx_buff_smmu_info(&in->dbuff_smmu, idx - 1); + if (ret) + goto fail_buff; + + IPA_WIGIG_DBG("exit\n"); + + return 0; + +fail_buff: + ipa_wigig_clean_pipe_smmu_info(IPA_WIGIG_RX_PIPE_IDX); + return ret; +} + +static int ipa_wigig_get_rx_smmu_info( + struct ipa_wigig_pipe_setup_info_smmu **pipe_smmu, + struct ipa_wigig_rx_pipe_data_buffer_info_smmu **dbuff_smmu) +{ + int ret; + + ret = ipa_wigig_get_pipe_smmu_info(pipe_smmu, IPA_WIGIG_RX_PIPE_IDX); + if (ret) + return ret; + + ret = ipa_wigig_get_rx_buff_smmu_info(dbuff_smmu); + if (ret) + return ret; + + return 0; +} + +static int ipa_wigig_get_tx_smmu_info( + struct ipa_wigig_pipe_setup_info_smmu **pipe_smmu, + struct ipa_wigig_tx_pipe_data_buffer_info_smmu **dbuff_smmu, + enum ipa_client_type client) +{ + unsigned int idx; + int ret; + + ret = ipa_wigig_client_to_idx(client, &idx); + if (ret) + return ret; + + ret = ipa_wigig_get_pipe_smmu_info(pipe_smmu, idx); + if (ret) + return ret; + + ret = ipa_wigig_get_tx_buff_smmu_info(dbuff_smmu, idx - 1); + if (ret) + return ret; + + return 0; +} + +static int ipa_wigig_clean_smmu_info(enum ipa_client_type client) +{ + int ret; + + if (client == IPA_CLIENT_WIGIG_PROD) { + ret = ipa_wigig_clean_pipe_smmu_info(IPA_WIGIG_RX_PIPE_IDX); + if (ret) + return ret; + ipa_wigig_clean_rx_buff_smmu_info(); + } else { + unsigned int idx; + + ret = ipa_wigig_client_to_idx(client, &idx); + if (ret) + return ret; + + ret = ipa_wigig_clean_pipe_smmu_info(idx); + if (ret) + return ret; + + ret = ipa_wigig_clean_tx_buff_smmu_info(idx - 1); + if (ret) { + IPA_WIGIG_ERR( + "cleaned tx pipe info but wasn't able to clean buff info, client %d\n" + , client); + WARN_ON(1); + return ret; + } + } + + return 0; +} +int ipa_wigig_conn_rx_pipe_smmu( + struct ipa_wigig_conn_rx_in_params_smmu *in, + struct ipa_wigig_conn_out_params *out) +{ + int ret; + struct ipa_pm_register_params pm_params; + + IPA_WIGIG_DBG("\n"); + + if (!in || !out) { + IPA_WIGIG_ERR("empty parameters. in=%pK out=%pK\n", in, out); + return -EINVAL; + } + + if (!ipa_wigig_ctx) { + IPA_WIGIG_ERR("wigig ctx is not initialized\n"); + return -EPERM; + } + + if (!ipa_wigig_is_smmu_enabled()) { + IPA_WIGIG_ERR("IPA SMMU is disabled, wrong API used\n"); + return -EFAULT; + } + + ret = ipa_uc_state_check(); + if (ret) { + IPA_WIGIG_ERR("uC not ready\n"); + return ret; + } + + pm_params.name = "wigig"; + pm_params.callback = ipa_wigig_pm_cb; + pm_params.user_data = NULL; + pm_params.group = IPA_PM_GROUP_DEFAULT; + if (ipa_pm_register(&pm_params, &ipa_wigig_ctx->ipa_pm_hdl)) { + IPA_WIGIG_ERR("fail to register ipa pm\n"); + ret = -EFAULT; + goto fail_pm; + } + + ret = ipa_wigig_uc_msi_init(true, + ipa_wigig_ctx->periph_baddr_pa, + ipa_wigig_ctx->pseudo_cause_pa, + ipa_wigig_ctx->int_gen_tx_pa, + ipa_wigig_ctx->int_gen_rx_pa, + ipa_wigig_ctx->dma_ep_misc_pa); + if (ret) { + IPA_WIGIG_ERR("failed configuring msi regs at uC\n"); + ret = -EFAULT; + goto fail_msi; + } + + if (ipa_conn_wigig_rx_pipe_i(in, out)) { + IPA_WIGIG_ERR("fail to connect rx pipe\n"); + ret = -EFAULT; + goto fail_connect_pipe; + } + + if (ipa_wigig_store_rx_smmu_info(in)) { + IPA_WIGIG_ERR("fail to store smmu data for rx pipe\n"); + ret = -EFAULT; + goto fail_smmu_store; + } + + IPA_WIGIG_DBG("exit\n"); + + return 0; + +fail_smmu_store: + ipa_disconn_wigig_pipe_i(IPA_CLIENT_WIGIG_PROD, + &in->pipe_smmu, + &in->dbuff_smmu); +fail_connect_pipe: + ipa_wigig_uc_msi_init(false, + ipa_wigig_ctx->periph_baddr_pa, + ipa_wigig_ctx->pseudo_cause_pa, + ipa_wigig_ctx->int_gen_tx_pa, + ipa_wigig_ctx->int_gen_rx_pa, + ipa_wigig_ctx->dma_ep_misc_pa); +fail_msi: + ipa_pm_deregister(ipa_wigig_ctx->ipa_pm_hdl); +fail_pm: + return ret; +} +EXPORT_SYMBOL(ipa_wigig_conn_rx_pipe_smmu); + +int ipa_wigig_set_perf_profile(u32 max_supported_bw_mbps) +{ + IPA_WIGIG_DBG("setting throughput to %d\n", max_supported_bw_mbps); + + if (!ipa_wigig_ctx) { + IPA_WIGIG_ERR("wigig ctx is not initialized\n"); + return -EPERM; + } + + IPA_WIGIG_DBG("ipa_pm handle %d\n", ipa_wigig_ctx->ipa_pm_hdl); + if (ipa_pm_set_throughput(ipa_wigig_ctx->ipa_pm_hdl, + max_supported_bw_mbps)) { + IPA_WIGIG_ERR("fail to setup pm perf profile\n"); + return -EFAULT; + } + IPA_WIGIG_DBG("exit\n"); + + return 0; +} +EXPORT_SYMBOL(ipa_wigig_set_perf_profile); + +int ipa_wigig_conn_client(struct ipa_wigig_conn_tx_in_params *in, + struct ipa_wigig_conn_out_params *out) +{ + IPA_WIGIG_DBG("\n"); + + if (!in || !out) { + IPA_WIGIG_ERR("empty parameters. in=%pK out=%pK\n", in, out); + return -EINVAL; + } + + if (!ipa_wigig_ctx) { + IPA_WIGIG_ERR("wigig ctx is not initialized\n"); + return -EPERM; + } + + if (ipa_wigig_is_smmu_enabled()) { + IPA_WIGIG_ERR("IPA SMMU is enabled, wrong API used\n"); + return -EFAULT; + } + + if (ipa_uc_state_check()) { + IPA_WIGIG_ERR("uC not ready\n"); + return -EFAULT; + } + + if (ipa_conn_wigig_client_i(in, out)) { + IPA_WIGIG_ERR( + "fail to connect client. MAC [%X][%X][%X][%X][%X][%X]\n" + , in->client_mac[0], in->client_mac[1], in->client_mac[2] + , in->client_mac[3], in->client_mac[4], in->client_mac[5]); + return -EFAULT; + } + + IPA_WIGIG_DBG("exit\n"); + return 0; +} +EXPORT_SYMBOL(ipa_wigig_conn_client); + +int ipa_wigig_conn_client_smmu( + struct ipa_wigig_conn_tx_in_params_smmu *in, + struct ipa_wigig_conn_out_params *out) +{ + int ret; + + IPA_WIGIG_DBG("\n"); + + if (!in || !out) { + IPA_WIGIG_ERR("empty parameters. in=%pK out=%pK\n", in, out); + return -EINVAL; + } + + if (!ipa_wigig_ctx) { + IPA_WIGIG_ERR("wigig ctx is not initialized\n"); + return -EPERM; + } + + if (!ipa_wigig_is_smmu_enabled()) { + IPA_WIGIG_ERR("IPA SMMU is disabled, wrong API used\n"); + return -EFAULT; + } + + ret = ipa_uc_state_check(); + if (ret) { + IPA_WIGIG_ERR("uC not ready\n"); + return ret; + } + + if (ipa_conn_wigig_client_i(in, out)) { + IPA_WIGIG_ERR( + "fail to connect client. MAC [%X][%X][%X][%X][%X][%X]\n" + , in->client_mac[0], in->client_mac[1] + , in->client_mac[2], in->client_mac[3] + , in->client_mac[4], in->client_mac[5]); + return -EFAULT; + } + + ret = ipa_wigig_store_client_smmu_info(in, out->client); + if (ret) + goto fail_smmu; + + IPA_WIGIG_DBG("exit\n"); + return 0; + +fail_smmu: + ipa_disconn_wigig_pipe_i(out->client, &in->pipe_smmu, &in->dbuff_smmu); + return ret; +} +EXPORT_SYMBOL(ipa_wigig_conn_client_smmu); + +static inline int ipa_wigig_validate_client_type(enum ipa_client_type client) +{ + switch (client) { + case IPA_CLIENT_WIGIG_PROD: + case IPA_CLIENT_WIGIG1_CONS: + case IPA_CLIENT_WIGIG2_CONS: + case IPA_CLIENT_WIGIG3_CONS: + case IPA_CLIENT_WIGIG4_CONS: + break; + default: + IPA_WIGIG_ERR("invalid client type %d\n", client); + return -EINVAL; + } + + return 0; +} + +int ipa_wigig_disconn_pipe(enum ipa_client_type client) +{ + int ret; + + IPA_WIGIG_DBG("\n"); + + ret = ipa_wigig_validate_client_type(client); + if (ret) + return ret; + + IPA_WIGIG_DBG("disconnecting ipa_client_type %d\n", client); + + if (ipa_wigig_is_smmu_enabled()) { + struct ipa_wigig_pipe_setup_info_smmu *pipe_smmu; + struct ipa_wigig_rx_pipe_data_buffer_info_smmu *rx_dbuff_smmu; + struct ipa_wigig_tx_pipe_data_buffer_info_smmu *tx_dbuff_smmu; + + if (client == IPA_CLIENT_WIGIG_PROD) { + ret = ipa_wigig_get_rx_smmu_info(&pipe_smmu, + &rx_dbuff_smmu); + if (ret) + return ret; + + ret = ipa_disconn_wigig_pipe_i(client, + pipe_smmu, + rx_dbuff_smmu); + } else { + ret = ipa_wigig_get_tx_smmu_info(&pipe_smmu, + &tx_dbuff_smmu, client); + if (ret) + return ret; + + ret = ipa_disconn_wigig_pipe_i(client, + pipe_smmu, + tx_dbuff_smmu); + } + + } else { + ret = ipa_disconn_wigig_pipe_i(client, NULL, NULL); + } + + if (ret) { + IPA_WIGIG_ERR("couldn't disconnect client %d\n", client); + return ret; + } + + /* RX will be disconnected last, deinit uC msi config */ + if (client == IPA_CLIENT_WIGIG_PROD) { + ret = ipa_wigig_uc_msi_init(false, + ipa_wigig_ctx->periph_baddr_pa, + ipa_wigig_ctx->pseudo_cause_pa, + ipa_wigig_ctx->int_gen_tx_pa, + ipa_wigig_ctx->int_gen_rx_pa, + ipa_wigig_ctx->dma_ep_misc_pa); + if (ret) { + IPA_WIGIG_ERR("failed unmapping msi regs\n"); + WARN_ON(1); + } + } + if (ipa_wigig_is_smmu_enabled()) + ipa_wigig_clean_smmu_info(client); + + IPA_WIGIG_DBG("exit\n"); + return 0; +} +EXPORT_SYMBOL(ipa_wigig_disconn_pipe); + +int ipa_wigig_enable_pipe(enum ipa_client_type client) +{ + int ret; + + IPA_WIGIG_DBG("\n"); + + ret = ipa_wigig_validate_client_type(client); + if (ret) + return ret; + + IPA_WIGIG_DBG("enabling pipe %d\n", client); + + ret = ipa_enable_wigig_pipe_i(client); + if (ret) + return ret; + + /* do only when Rx pipe is enabled */ + if (client == IPA_CLIENT_WIGIG_PROD) { + ret = ipa_pm_activate_sync(ipa_wigig_ctx->ipa_pm_hdl); + if (ret) { + IPA_WIGIG_ERR("fail to activate ipa pm\n"); + ret = -EFAULT; + goto fail_pm_active; + } + } + + IPA_WIGIG_DBG("exit\n"); + return 0; + +fail_pm_active: + ipa_disable_wigig_pipe_i(client); + return ret; +} +EXPORT_SYMBOL(ipa_wigig_enable_pipe); + +int ipa_wigig_disable_pipe(enum ipa_client_type client) +{ + int ret; + + IPA_WIGIG_DBG("\n"); + + ret = ipa_wigig_validate_client_type(client); + if (ret) + return ret; + + ret = ipa_disable_wigig_pipe_i(client); + if (ret) + return ret; + + /* do only when Rx pipe is disabled */ + if (client == IPA_CLIENT_WIGIG_PROD) { + ret = ipa_pm_deactivate_sync(ipa_wigig_ctx->ipa_pm_hdl); + if (ret) { + IPA_WIGIG_ERR("fail to deactivate ipa pm\n"); + return -EFAULT; + } + } + + IPA_WIGIG_DBG("exit\n"); + return 0; +} +EXPORT_SYMBOL(ipa_wigig_disable_pipe); + +int ipa_wigig_tx_dp(enum ipa_client_type dst, struct sk_buff *skb) +{ + int ret; + + IPA_WIGIG_DBG_LOW("\n"); + + ret = ipa_wigig_validate_client_type(dst); + if (unlikely(ret)) + return ret; + + ret = ipa_tx_dp(dst, skb, NULL); + if (unlikely(ret)) + return ret; + + IPA_WIGIG_DBG_LOW("exit\n"); + return 0; +} +EXPORT_SYMBOL(ipa_wigig_tx_dp); diff --git a/drivers/platform/msm/ipa/ipa_common_i.h b/drivers/platform/msm/ipa/ipa_common_i.h index 530aa545a328..80433ac52deb 100644 --- a/drivers/platform/msm/ipa/ipa_common_i.h +++ b/drivers/platform/msm/ipa/ipa_common_i.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #define WARNON_RATELIMIT_BURST 1 @@ -445,4 +446,29 @@ int ipa_smmu_free_sgt(struct sg_table **out_sgt_ptr); int ipa_ut_module_init(void); void ipa_ut_module_exit(void); +int ipa_wigig_uc_init( + struct ipa_wdi_uc_ready_params *inout, + ipa_wigig_misc_int_cb int_notify, + phys_addr_t *uc_db_pa); + +int ipa_conn_wigig_rx_pipe_i(void *in, struct ipa_wigig_conn_out_params *out); + +int ipa_conn_wigig_client_i(void *in, struct ipa_wigig_conn_out_params *out); + +int ipa_wigig_uc_msi_init( + bool init, + phys_addr_t periph_baddr_pa, + phys_addr_t pseudo_cause_pa, + phys_addr_t int_gen_tx_pa, + phys_addr_t int_gen_rx_pa, + phys_addr_t dma_ep_misc_pa); + +int ipa_disconn_wigig_pipe_i(enum ipa_client_type client, + struct ipa_wigig_pipe_setup_info_smmu *pipe_smmu, + void *dbuff); + +int ipa_enable_wigig_pipe_i(enum ipa_client_type client); + +int ipa_disable_wigig_pipe_i(enum ipa_client_type client); + #endif /* _IPA_COMMON_I_H_ */ diff --git a/drivers/platform/msm/ipa/ipa_v3/Makefile b/drivers/platform/msm/ipa/ipa_v3/Makefile index 275b33f8dabb..453b8a4e279b 100644 --- a/drivers/platform/msm/ipa/ipa_v3/Makefile +++ b/drivers/platform/msm/ipa/ipa_v3/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_IPA3) += ipat.o ipat-y := ipa.o ipa_debugfs.o ipa_hdr.o ipa_flt.o ipa_rt.o ipa_dp.o ipa_client.o \ ipa_utils.o ipa_nat.o ipa_intf.o teth_bridge.o ipa_interrupts.o \ ipa_uc.o ipa_uc_wdi.o ipa_dma.o ipa_uc_mhi.o ipa_mhi.o ipa_uc_ntn.o \ - ipa_hw_stats.o ipa_pm.o ipa_wdi3_i.o ipa_odl.o + ipa_hw_stats.o ipa_pm.o ipa_wdi3_i.o ipa_odl.o ipa_wigig_i.o ipat-$(CONFIG_IPA_EMULATION) += ipa_dt_replacement.o diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c index 129a4e8af8cf..1b9ab60563cf 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c @@ -4855,6 +4855,12 @@ static int ipa3_post_init(const struct ipa3_plat_drv_res *resource_p, else IPADBG(":wdi init ok\n"); + result = ipa3_wigig_init_i(); + if (result) + IPAERR(":wigig init failed (%d)\n", -result); + else + IPADBG(":wigig init ok\n"); + result = ipa3_ntn_init(); if (result) IPAERR(":ntn init failed (%d)\n", -result); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c index c0d48942cd67..d5c2a703126b 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c @@ -55,8 +55,6 @@ /* less 1 nominal MTU (1500 bytes) rounded to units of KB */ #define IPA_ADJUST_AGGR_BYTE_LIMIT(X) (((X) - IPA_MTU)/1000) -#define IPA_ADJUST_AGGR_BYTE_HARD_LIMIT(X) (X/1000) - #define IPA_RX_BUFF_CLIENT_HEADROOM 256 #define IPA_WLAN_RX_POOL_SZ 100 diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h index a3ec13d56586..1e332a5b0a6b 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h @@ -254,6 +254,8 @@ enum { #define IPA_AGGR_STR_IN_BYTES(str) \ (strnlen((str), IPA_AGGR_MAX_STR_LENGTH - 1) + 1) +#define IPA_ADJUST_AGGR_BYTE_HARD_LIMIT(X) (X/1000) + #define IPA_TRANSPORT_PROD_TIMEOUT_MSEC 100 #define IPA3_ACTIVE_CLIENTS_TABLE_BUF_SIZE 2048 @@ -1286,6 +1288,18 @@ struct ipa3_uc_wdi_ctx { #endif }; +/** +* struct ipa3_uc_wigig_ctx +* @priv: wigig driver private data +* @uc_ready_cb: wigig driver uc ready callback +* @int_notify: wigig driver misc interrupt callback +*/ +struct ipa3_uc_wigig_ctx { + void *priv; + ipa_uc_ready_cb uc_ready_cb; + ipa_wigig_misc_int_cb misc_notify_cb; +}; + /** * struct ipa3_wdi2_ctx - IPA wdi2 context */ @@ -1499,6 +1513,7 @@ struct ipa3_char_device_context { * @wcstats: wlan common buffer stats * @uc_ctx: uC interface context * @uc_wdi_ctx: WDI specific fields for uC interface + * @uc_wigig_ctx: WIGIG specific fields for uC interface * @ipa_num_pipes: The number of pipes used by IPA HW * @skip_uc_pipe_reset: Indicates whether pipe reset via uC needs to be avoided * @ipa_client_apps_wan_cons_agg_gro: RMNET_IOCTL_INGRESS_FORMAT_AGG_DATA @@ -1621,6 +1636,7 @@ struct ipa3_context { struct ipa3_uc_wdi_ctx uc_wdi_ctx; struct ipa3_uc_ntn_ctx uc_ntn_ctx; + struct ipa3_uc_wigig_ctx uc_wigig_ctx; u32 wan_rx_ring_size; u32 lan_rx_ring_size; bool skip_uc_pipe_reset; @@ -2217,6 +2233,26 @@ int ipa3_disconn_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx); int ipa3_enable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx); int ipa3_disable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx); +int ipa3_conn_wigig_rx_pipe_i(void *in, + struct ipa_wigig_conn_out_params *out); + +int ipa3_conn_wigig_client_i(void *in, struct ipa_wigig_conn_out_params *out); + +int ipa3_wigig_uc_msi_init(bool init, + phys_addr_t periph_baddr_pa, + phys_addr_t pseudo_cause_pa, + phys_addr_t int_gen_tx_pa, + phys_addr_t int_gen_rx_pa, + phys_addr_t dma_ep_misc_pa); + +int ipa3_disconn_wigig_pipe_i(enum ipa_client_type client, + struct ipa_wigig_pipe_setup_info_smmu *pipe_smmu, + void *dbuff); + +int ipa3_enable_wigig_pipe_i(enum ipa_client_type client); + +int ipa3_disable_wigig_pipe_i(enum ipa_client_type client); + /* * To retrieve doorbell physical address of * wlan pipes @@ -2523,6 +2559,12 @@ void ipa3_tag_destroy_imm(void *user1, int user2); const struct ipa_gsi_ep_config *ipa3_get_gsi_ep_info (enum ipa_client_type client); +int ipa3_wigig_init_i(void); +int ipa3_wigig_uc_init( + struct ipa_wdi_uc_ready_params *inout, + ipa_wigig_misc_int_cb int_notify, + phys_addr_t *uc_db_pa); + /* Hardware stats */ #define IPA_STATS_MAX_PIPE_BIT 32 diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_uc.c b/drivers/platform/msm/ipa/ipa_v3/ipa_uc.c index 36a8a74eb75c..badcd998ede7 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_uc.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_uc.c @@ -524,6 +524,21 @@ static void ipa3_uc_response_hdlr(enum ipa_irq_type interrupt, IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); } +static void ipa3_uc_wigig_misc_int_handler(enum ipa_irq_type interrupt, + void *private_data, + void *interrupt_data) +{ + IPADBG("\n"); + + WARN_ON(private_data != ipa3_ctx); + + if (ipa3_ctx->uc_wigig_ctx.misc_notify_cb) + ipa3_ctx->uc_wigig_ctx.misc_notify_cb( + ipa3_ctx->uc_wigig_ctx.priv); + + IPADBG("exit\n"); +} + static int ipa3_uc_send_cmd_64b_param(u32 cmd_lo, u32 cmd_hi, u32 opcode, u32 expected_status, bool polling_mode, unsigned long timeout_jiffies) { @@ -687,7 +702,7 @@ int ipa3_uc_interface_init(void) ipa3_uc_event_handler, true, ipa3_ctx); if (result) { - IPAERR("Fail to register for UC_IRQ0 rsp interrupt\n"); + IPAERR("Fail to register for UC_IRQ0 event interrupt\n"); result = -EFAULT; goto irq_fail0; } @@ -701,11 +716,21 @@ int ipa3_uc_interface_init(void) goto irq_fail1; } + result = ipa3_add_interrupt_handler(IPA_UC_IRQ_2, + ipa3_uc_wigig_misc_int_handler, true, + ipa3_ctx); + if (result) { + IPAERR("fail to register for UC_IRQ2 wigig misc interrupt\n"); + result = -EFAULT; + goto irq_fail2; + } + ipa3_ctx->uc_ctx.uc_inited = true; IPADBG("IPA uC interface is initialized\n"); return 0; - +irq_fail2: + ipa3_remove_interrupt_handler(IPA_UC_IRQ_1); irq_fail1: ipa3_remove_interrupt_handler(IPA_UC_IRQ_0); irq_fail0: diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c index 453730f6a047..e4f88af5bd09 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c @@ -6197,6 +6197,13 @@ int ipa3_bind_api_controller(enum ipa_hw_type ipa_hw_type, api_ctrl->ipa_get_smmu_params = ipa3_get_smmu_params; api_ctrl->ipa_is_vlan_mode = ipa3_is_vlan_mode; api_ctrl->ipa_pm_is_used = ipa3_pm_is_used; + api_ctrl->ipa_wigig_uc_init = ipa3_wigig_uc_init; + api_ctrl->ipa_conn_wigig_rx_pipe_i = ipa3_conn_wigig_rx_pipe_i; + api_ctrl->ipa_conn_wigig_client_i = ipa3_conn_wigig_client_i; + api_ctrl->ipa_disconn_wigig_pipe_i = ipa3_disconn_wigig_pipe_i; + api_ctrl->ipa_wigig_uc_msi_init = ipa3_wigig_uc_msi_init; + api_ctrl->ipa_enable_wigig_pipe_i = ipa3_enable_wigig_pipe_i; + api_ctrl->ipa_disable_wigig_pipe_i = ipa3_disable_wigig_pipe_i; return 0; } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_wigig_i.c b/drivers/platform/msm/ipa/ipa_v3/ipa_wigig_i.c new file mode 100644 index 000000000000..c6db5f398d6d --- /dev/null +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_wigig_i.c @@ -0,0 +1,1507 @@ +/* Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "ipa_i.h" +#include +#include +#include + +#define IPA_WIGIG_DESC_RING_EL_SIZE 32 + +#define GSI_STOP_MAX_RETRY_CNT 10 + +#define IPA_WIGIG_CONNECTED BIT(0) +#define IPA_WIGIG_ENABLED BIT(1) +#define IPA_WIGIG_MSB_MASK 0xFFFFFFFF00000000 +#define IPA_WIGIG_LSB_MASK 0x00000000FFFFFFFF +#define IPA_WIGIG_MSB(num) ((u32)((num & IPA_WIGIG_MSB_MASK) >> 32)) +#define IPA_WIGIG_LSB(num) ((u32)(num & IPA_WIGIG_LSB_MASK)) +#define W11AD_RX 0 +#define W11AD_TX 1 +#define W11AD_TO_GSI_DB_m 1 +#define W11AD_TO_GSI_DB_n 1 + + +static int ipa3_wigig_uc_loaded_handler(struct notifier_block *self, + unsigned long val, void *data) +{ + IPADBG("val %d\n", val); + + if (!ipa3_ctx) { + IPAERR("IPA ctx is null\n"); + return -EINVAL; + } + + WARN_ON(data != ipa3_ctx); + + if (ipa3_ctx->uc_wigig_ctx.uc_ready_cb) { + ipa3_ctx->uc_wigig_ctx.uc_ready_cb( + ipa3_ctx->uc_wigig_ctx.priv); + + ipa3_ctx->uc_wigig_ctx.uc_ready_cb = + NULL; + ipa3_ctx->uc_wigig_ctx.priv = NULL; + } + + IPADBG("exit\n"); + return 0; +} + +static struct notifier_block uc_loaded_notifier = { + .notifier_call = ipa3_wigig_uc_loaded_handler, +}; + +int ipa3_wigig_init_i(void) +{ + IPADBG("\n"); + + ipa3_uc_register_ready_cb(&uc_loaded_notifier); + + IPADBG("exit\n"); + + return 0; +} + +int ipa3_wigig_uc_init( + struct ipa_wdi_uc_ready_params *inout, + ipa_wigig_misc_int_cb int_notify, + phys_addr_t *uc_db_pa) +{ + int result = 0; + + IPADBG("\n"); + + if (inout == NULL) { + IPAERR("inout is NULL"); + return -EINVAL; + } + + if (int_notify == NULL) { + IPAERR("int_notify is NULL"); + return -EINVAL; + } + + result = ipa3_uc_state_check(); + if (result) { + inout->is_uC_ready = false; + ipa3_ctx->uc_wigig_ctx.uc_ready_cb = inout->notify; + ipa3_ctx->uc_wigig_ctx.priv = inout->priv; + } else { + inout->is_uC_ready = true; + } + + ipa3_ctx->uc_wigig_ctx.misc_notify_cb = int_notify; + + *uc_db_pa = ipa3_ctx->ipa_wrapper_base + + ipahal_get_reg_base() + + ipahal_get_reg_mn_ofst( + IPA_UC_MAILBOX_m_n, + W11AD_TO_GSI_DB_m, + W11AD_TO_GSI_DB_n); + + IPADBG("exit\n"); + + return 0; +} + +static int ipa3_wigig_tx_bit_to_ep( + const u8 tx_bit_num, + enum ipa_client_type *type) +{ + IPADBG("tx_bit_num %d\n", tx_bit_num); + + switch (tx_bit_num) { + case 2: + *type = IPA_CLIENT_WIGIG1_CONS; + break; + case 3: + *type = IPA_CLIENT_WIGIG2_CONS; + break; + case 4: + *type = IPA_CLIENT_WIGIG3_CONS; + break; + case 5: + *type = IPA_CLIENT_WIGIG4_CONS; + break; + default: + IPAERR("invalid tx_bit_num %d\n", tx_bit_num); + return -EINVAL; + } + + IPADBG("exit\n"); + return 0; +} + +static int ipa3_wigig_smmu_map_channel(bool Rx, + struct ipa_wigig_pipe_setup_info_smmu *pipe_smmu, + void *buff, + bool map) +{ + int result = 0; + + IPADBG("\n"); + + /* + * -------------------------------------------------------------------- + * entity |HWHEAD|HWTAIL|HWHEAD|HWTAIL| misc | buffers| rings| + * |Sring |Sring |Dring |Dring | regs | | | + * -------------------------------------------------------------------- + * GSI (apps CB) | TX |RX, TX| |RX, TX| | |Rx, TX| + * -------------------------------------------------------------------- + * IPA (WLAN CB) | | | | | | RX, TX | | + * -------------------------------------------------------------------- + * uc (uC CB) | RX | | TX | |always| | | + * -------------------------------------------------------------------- + */ + + if (Rx) { + result = ipa3_smmu_map_peer_reg( + pipe_smmu->status_ring_HWHEAD_pa, + map, + IPA_SMMU_CB_UC); + if (result) { + IPAERR( + "failed to %s status_ring_HWAHEAD %d\n", + map ? "map" : "unmap", + result); + goto fail_status_HWHEAD; + } + } else { + + result = ipa3_smmu_map_peer_reg( + pipe_smmu->status_ring_HWHEAD_pa, + map, + IPA_SMMU_CB_AP); + if (result) { + IPAERR( + "failed to %s status_ring_HWAHEAD %d\n", + map ? "map" : "unmap", + result); + goto fail_status_HWHEAD; + } + + result = ipa3_smmu_map_peer_reg( + pipe_smmu->desc_ring_HWHEAD_pa, + map, + IPA_SMMU_CB_UC); + if (result) { + IPAERR("failed to %s desc_ring_HWHEAD %d\n", + map ? "map" : "unmap", + result); + goto fail; + } + } + + result = ipa3_smmu_map_peer_reg( + pipe_smmu->status_ring_HWTAIL_pa, + map, + IPA_SMMU_CB_AP); + if (result) { + IPAERR( + "failed to %s status_ring_HWTAIL %d\n", + map ? "map" : "unmap", + result); + goto fail_status_HWTAIL; + } + + result = ipa3_smmu_map_peer_reg( + pipe_smmu->desc_ring_HWTAIL_pa, + map, + IPA_SMMU_CB_AP); + if (result) { + IPAERR("failed to %s desc_ring_HWTAIL %d\n", + map ? "map" : "unmap", + result); + goto fail_desc_HWTAIL; + } + + result = ipa3_smmu_map_peer_buff( + pipe_smmu->desc_ring_base_iova, + pipe_smmu->desc_ring_size, + map, + &pipe_smmu->desc_ring_base, + IPA_SMMU_CB_AP); + if (result) { + IPAERR("failed to %s desc_ring_base %d\n", + map ? "map" : "unmap", + result); + goto fail_desc_ring; + } + + result = ipa3_smmu_map_peer_buff( + pipe_smmu->status_ring_base_iova, + pipe_smmu->status_ring_size, + map, + &pipe_smmu->status_ring_base, + IPA_SMMU_CB_AP); + if (result) { + IPAERR("failed to %s status_ring_base %d\n", + map ? "map" : "unmap", + result); + goto fail_status_ring; + } + + if (Rx) { + struct ipa_wigig_rx_pipe_data_buffer_info_smmu *dbuff_smmu = + (struct ipa_wigig_rx_pipe_data_buffer_info_smmu *)buff; + + int num_elem = + pipe_smmu->desc_ring_size / + IPA_WIGIG_DESC_RING_EL_SIZE; + + result = ipa3_smmu_map_peer_buff( + dbuff_smmu->data_buffer_base_iova, + dbuff_smmu->data_buffer_size * num_elem, + map, + &dbuff_smmu->data_buffer_base, + IPA_SMMU_CB_WLAN); + if (result) { + IPAERR( + "failed to %s rx data_buffer %d, num elem %d\n" + , map ? "map" : "unmap", + result, num_elem); + goto fail_map_buff; + } + + } else { + int i; + struct ipa_wigig_tx_pipe_data_buffer_info_smmu *dbuff_smmu = + (struct ipa_wigig_tx_pipe_data_buffer_info_smmu *)buff; + + for (i = 0; i < dbuff_smmu->num_buffers; i++) { + result = ipa3_smmu_map_peer_buff( + *(dbuff_smmu->data_buffer_base_iova + i), + dbuff_smmu->data_buffer_size, + map, + (dbuff_smmu->data_buffer_base + i), + IPA_SMMU_CB_WLAN); + if (result) { + IPAERR( + "%d: failed to %s tx data buffer %d\n" + , i, map ? "map" : "unmap", + result); + for (i--; i >= 0; i--) { + result = ipa3_smmu_map_peer_buff( + *(dbuff_smmu->data_buffer_base_iova + + i), + dbuff_smmu->data_buffer_size, + !map, + (dbuff_smmu->data_buffer_base + + i), + IPA_SMMU_CB_WLAN); + } + goto fail_map_buff; + } + } + } + + IPADBG("exit\n"); + + return 0; +fail_map_buff: + result = ipa3_smmu_map_peer_buff( + pipe_smmu->status_ring_base_iova, pipe_smmu->status_ring_size, + !map, &pipe_smmu->status_ring_base, + IPA_SMMU_CB_AP); +fail_status_ring: + ipa3_smmu_map_peer_buff( + pipe_smmu->desc_ring_base_iova, pipe_smmu->desc_ring_size, + !map, &pipe_smmu->desc_ring_base, + IPA_SMMU_CB_AP); +fail_desc_ring: + ipa3_smmu_map_peer_reg( + pipe_smmu->status_ring_HWTAIL_pa, !map, IPA_SMMU_CB_AP); +fail_status_HWTAIL: + if (Rx) + ipa3_smmu_map_peer_reg(pipe_smmu->status_ring_HWHEAD_pa, + !map, IPA_SMMU_CB_UC); +fail_status_HWHEAD: + ipa3_smmu_map_peer_reg( + pipe_smmu->desc_ring_HWTAIL_pa, !map, IPA_SMMU_CB_AP); +fail_desc_HWTAIL: + ipa3_smmu_map_peer_reg( + pipe_smmu->desc_ring_HWHEAD_pa, !map, IPA_SMMU_CB_UC); +fail: + return result; +} + +static void ipa_gsi_chan_err_cb(struct gsi_chan_err_notify *notify) +{ + switch (notify->evt_id) { + case GSI_CHAN_INVALID_TRE_ERR: + IPAERR("Got GSI_CHAN_INVALID_TRE_ERR\n"); + break; + case GSI_CHAN_NON_ALLOCATED_EVT_ACCESS_ERR: + IPAERR("Got GSI_CHAN_NON_ALLOCATED_EVT_ACCESS_ERR\n"); + break; + case GSI_CHAN_OUT_OF_BUFFERS_ERR: + IPAERR("Got GSI_CHAN_OUT_OF_BUFFERS_ERR\n"); + break; + case GSI_CHAN_OUT_OF_RESOURCES_ERR: + IPAERR("Got GSI_CHAN_OUT_OF_RESOURCES_ERR\n"); + break; + case GSI_CHAN_UNSUPPORTED_INTER_EE_OP_ERR: + IPAERR("Got GSI_CHAN_UNSUPPORTED_INTER_EE_OP_ERR\n"); + break; + case GSI_CHAN_HWO_1_ERR: + IPAERR("Got GSI_CHAN_HWO_1_ERR\n"); + break; + default: + IPAERR("Unexpected err evt: %d\n", notify->evt_id); + } + ipa_assert(); +} + +static void ipa_gsi_evt_ring_err_cb(struct gsi_evt_err_notify *notify) +{ + switch (notify->evt_id) { + case GSI_EVT_OUT_OF_BUFFERS_ERR: + IPAERR("Got GSI_EVT_OUT_OF_BUFFERS_ERR\n"); + break; + case GSI_EVT_OUT_OF_RESOURCES_ERR: + IPAERR("Got GSI_EVT_OUT_OF_RESOURCES_ERR\n"); + break; + case GSI_EVT_UNSUPPORTED_INTER_EE_OP_ERR: + IPAERR("Got GSI_EVT_UNSUPPORTED_INTER_EE_OP_ERR\n"); + break; + case GSI_EVT_EVT_RING_EMPTY_ERR: + IPAERR("Got GSI_EVT_EVT_RING_EMPTY_ERR\n"); + break; + default: + IPAERR("Unexpected err evt: %d\n", notify->evt_id); + } + ipa_assert(); +} + +static int ipa3_wigig_config_gsi(bool Rx, + bool smmu_en, + void *pipe_info, + void *buff, + const struct ipa_gsi_ep_config *ep_gsi, + struct ipa3_ep_context *ep) +{ + struct gsi_evt_ring_props evt_props; + struct gsi_chan_props channel_props; + union __packed gsi_channel_scratch gsi_scratch; + int gsi_res; + struct ipa_wigig_pipe_setup_info_smmu *pipe_smmu; + struct ipa_wigig_pipe_setup_info *pipe; + struct ipa_wigig_rx_pipe_data_buffer_info *rx_dbuff; + struct ipa_wigig_rx_pipe_data_buffer_info_smmu *rx_dbuff_smmu; + struct ipa_wigig_tx_pipe_data_buffer_info *tx_dbuff; + struct ipa_wigig_tx_pipe_data_buffer_info_smmu *tx_dbuff_smmu; + + /* alloc event ring */ + memset(&evt_props, 0, sizeof(evt_props)); + evt_props.intf = GSI_EVT_CHTYPE_11AD_EV; + evt_props.re_size = GSI_EVT_RING_RE_SIZE_16B; + evt_props.intr = GSI_INTR_MSI; + evt_props.intvec = 0; + evt_props.exclusive = true; + evt_props.err_cb = ipa_gsi_evt_ring_err_cb; + evt_props.user_data = NULL; + evt_props.int_modc = 1; + evt_props.int_modt = 1; + evt_props.ring_base_vaddr = NULL; + + if (smmu_en) { + pipe_smmu = (struct ipa_wigig_pipe_setup_info_smmu *)pipe_info; + evt_props.ring_base_addr = + pipe_smmu->desc_ring_base_iova; + evt_props.ring_len = pipe_smmu->desc_ring_size; + evt_props.msi_addr = pipe_smmu->desc_ring_HWTAIL_pa; + } else { + pipe = (struct ipa_wigig_pipe_setup_info *)pipe_info; + evt_props.ring_base_addr = pipe->desc_ring_base_pa; + evt_props.ring_len = pipe->desc_ring_size; + evt_props.msi_addr = pipe->desc_ring_HWTAIL_pa; + } + + gsi_res = gsi_alloc_evt_ring(&evt_props, + ipa3_ctx->gsi_dev_hdl, + &ep->gsi_evt_ring_hdl); + if (gsi_res != GSI_STATUS_SUCCESS) { + IPAERR("Error allocating event ring: %d\n", gsi_res); + return -EFAULT; + } + + /* event scratch not configured by SW for TX channels */ + if (Rx) { + union __packed gsi_evt_scratch evt_scratch; + + memset(&evt_scratch, 0, sizeof(evt_scratch)); + evt_scratch.w11ad.update_status_hwtail_mod_threshold = 1; + gsi_res = gsi_write_evt_ring_scratch(ep->gsi_evt_ring_hdl, + evt_scratch); + if (gsi_res != GSI_STATUS_SUCCESS) { + IPAERR("Error writing WIGIG event ring scratch: %d\n", + gsi_res); + goto fail_write_evt_scratch; + } + } + + ep->gsi_mem_info.evt_ring_len = evt_props.ring_len; + ep->gsi_mem_info.evt_ring_base_addr = evt_props.ring_base_addr; + ep->gsi_mem_info.evt_ring_base_vaddr = evt_props.ring_base_vaddr; + + /* alloc channel ring */ + memset(&channel_props, 0, sizeof(channel_props)); + memset(&gsi_scratch, 0, sizeof(gsi_scratch)); + + if (Rx) + channel_props.dir = GSI_CHAN_DIR_TO_GSI; + else + channel_props.dir = GSI_CHAN_DIR_FROM_GSI; + + channel_props.re_size = GSI_CHAN_RE_SIZE_16B; + channel_props.prot - GSI_CHAN_PROT_11AD; + channel_props.ch_id = ep_gsi->ipa_gsi_chan_num; + channel_props.evt_ring_hdl = ep->gsi_evt_ring_hdl; + channel_props.xfer_cb = NULL; + + channel_props.use_db_eng = GSI_CHAN_DB_MODE; + channel_props.max_prefetch = GSI_ONE_PREFETCH_SEG; + channel_props.prefetch_mode = ep_gsi->prefetch_mode; + channel_props.low_weight = 1; + channel_props.err_cb = ipa_gsi_chan_err_cb; + + channel_props.ring_base_vaddr = NULL; + + if (Rx) { + if (smmu_en) { + rx_dbuff_smmu = + (struct ipa_wigig_rx_pipe_data_buffer_info_smmu *)buff; + + channel_props.ring_base_addr = + pipe_smmu->status_ring_base_iova; + channel_props.ring_len = + pipe_smmu->status_ring_size; + + gsi_scratch.rx_11ad.status_ring_hwtail_address_lsb = + IPA_WIGIG_LSB( + pipe_smmu->status_ring_HWTAIL_pa); + gsi_scratch.rx_11ad.status_ring_hwtail_address_msb = + IPA_WIGIG_MSB( + pipe_smmu->status_ring_HWTAIL_pa); + + gsi_scratch.rx_11ad.data_buffers_base_address_lsb = + IPA_WIGIG_LSB( + rx_dbuff_smmu->data_buffer_base_iova); + gsi_scratch.rx_11ad.data_buffers_base_address_msb = + IPA_WIGIG_MSB( + rx_dbuff_smmu->data_buffer_base_iova); + gsi_scratch.rx_11ad.fixed_data_buffer_size_pow_2 = + ilog2(rx_dbuff_smmu->data_buffer_size); + } else { + rx_dbuff = + (struct ipa_wigig_rx_pipe_data_buffer_info *)buff; + + channel_props.ring_base_addr = + pipe->status_ring_base_pa; + channel_props.ring_len = pipe->status_ring_size; + + gsi_scratch.rx_11ad.status_ring_hwtail_address_lsb = + IPA_WIGIG_LSB(pipe->status_ring_HWTAIL_pa); + gsi_scratch.rx_11ad.status_ring_hwtail_address_msb = + IPA_WIGIG_MSB(pipe->status_ring_HWTAIL_pa); + + gsi_scratch.rx_11ad.data_buffers_base_address_lsb = + IPA_WIGIG_LSB(rx_dbuff->data_buffer_base_pa); + gsi_scratch.rx_11ad.data_buffers_base_address_msb = + IPA_WIGIG_MSB(rx_dbuff->data_buffer_base_pa); + gsi_scratch.rx_11ad.fixed_data_buffer_size_pow_2 = + ilog2(rx_dbuff->data_buffer_size); + } + IPADBG("fixed_data_buffer_size_pow_2 %d\n", + gsi_scratch.rx_11ad.fixed_data_buffer_size_pow_2); + } else { + if (smmu_en) { + tx_dbuff_smmu = + (struct ipa_wigig_tx_pipe_data_buffer_info_smmu *)buff; + channel_props.ring_base_addr = + pipe_smmu->desc_ring_base_iova; + channel_props.ring_len = + pipe_smmu->desc_ring_size; + + gsi_scratch.tx_11ad.status_ring_hwtail_address_lsb = + IPA_WIGIG_LSB( + pipe_smmu->status_ring_HWTAIL_pa); + gsi_scratch.tx_11ad.status_ring_hwtail_address_msb = + IPA_WIGIG_MSB( + pipe_smmu->status_ring_HWTAIL_pa); + + gsi_scratch.tx_11ad.fixed_data_buffer_size_pow_2 = + ilog2(tx_dbuff_smmu->data_buffer_size); + } else { + tx_dbuff = + (struct ipa_wigig_tx_pipe_data_buffer_info *)buff; + + channel_props.ring_base_addr = pipe->desc_ring_base_pa; + channel_props.ring_len = pipe->desc_ring_size; + + gsi_scratch.tx_11ad.status_ring_hwtail_address_lsb = + IPA_WIGIG_LSB( + pipe->status_ring_HWTAIL_pa); + gsi_scratch.tx_11ad.status_ring_hwtail_address_msb = + IPA_WIGIG_MSB( + pipe->status_ring_HWTAIL_pa); + + gsi_scratch.tx_11ad.fixed_data_buffer_size_pow_2 = + ilog2(tx_dbuff->data_buffer_size); + } + IPADBG("fixed_data_buffer_size_pow_2 %d\n", + gsi_scratch.tx_11ad.fixed_data_buffer_size_pow_2); + } + + IPADBG("ch_id: %d\n", channel_props.ch_id); + IPADBG("evt_ring_hdl: %ld\n", channel_props.evt_ring_hdl); + IPADBG("re_size: %d\n", channel_props.re_size); + IPADBG("GSI channel ring len: %d\n", channel_props.ring_len); + IPADBG("channel ring base addr = 0x%llX\n", + (unsigned long long)channel_props.ring_base_addr); + + IPADBG("Allocating GSI channel\n"); + gsi_res = gsi_alloc_channel(&channel_props, + ipa3_ctx->gsi_dev_hdl, + &ep->gsi_chan_hdl); + if (gsi_res != GSI_STATUS_SUCCESS) + goto fail_alloc_channel; + + ep->gsi_mem_info.chan_ring_len = channel_props.ring_len; + ep->gsi_mem_info.chan_ring_base_addr = channel_props.ring_base_addr; + ep->gsi_mem_info.chan_ring_base_vaddr = + channel_props.ring_base_vaddr; + + gsi_res = gsi_write_channel_scratch(ep->gsi_chan_hdl, + gsi_scratch); + if (gsi_res != GSI_STATUS_SUCCESS) { + IPAERR("gsi_write_channel_scratch failed %d\n", + gsi_res); + goto fail_write_channel_scratch; + } + +fail_write_channel_scratch: + gsi_dealloc_channel(ep->gsi_chan_hdl); +fail_alloc_channel: +fail_write_evt_scratch: + gsi_dealloc_evt_ring(ep->gsi_evt_ring_hdl); + return -EFAULT; +} + +static int ipa3_wigig_config_uc(bool init, + bool Rx, + u8 wifi_ch, + u8 gsi_ch, + phys_addr_t HWHEAD) +{ + struct ipa_mem_buffer cmd; + enum ipa_cpu_2_hw_offload_commands command; + int result; + + IPADBG("%s\n", init ? "init" : "Deinit"); + if (init) { + struct IpaHwOffloadSetUpCmdData_t_v4_0 *cmd_data; + + cmd.size = sizeof(*cmd_data); + cmd.base = dma_alloc_coherent(ipa3_ctx->uc_pdev, cmd.size, + &cmd.phys_base, GFP_KERNEL); + if (cmd.base == NULL) { + IPAERR("fail to get DMA memory.\n"); + return -ENOMEM; + } + + cmd_data = + (struct IpaHwOffloadSetUpCmdData_t_v4_0 *)cmd.base; + + cmd_data->protocol = IPA_HW_PROTOCOL_11ad; + cmd_data->SetupCh_params.W11AdSetupCh_params.dir = + Rx ? W11AD_RX : W11AD_TX; + cmd_data->SetupCh_params.W11AdSetupCh_params.gsi_ch = gsi_ch; + cmd_data->SetupCh_params.W11AdSetupCh_params.wifi_ch = wifi_ch; + cmd_data->SetupCh_params.W11AdSetupCh_params.wifi_hp_addr_msb = + IPA_WIGIG_MSB(HWHEAD); + cmd_data->SetupCh_params.W11AdSetupCh_params.wifi_hp_addr_lsb = + IPA_WIGIG_LSB(HWHEAD); + command = IPA_CPU_2_HW_CMD_OFFLOAD_CHANNEL_SET_UP; + + } else { + struct IpaHwOffloadCommonChCmdData_t_v4_0 *cmd_data; + + cmd.size = sizeof(*cmd_data); + cmd.base = dma_alloc_coherent(ipa3_ctx->uc_pdev, cmd.size, + &cmd.phys_base, GFP_KERNEL); + if (cmd.base == NULL) { + IPAERR("fail to get DMA memory.\n"); + return -ENOMEM; + } + + cmd_data = + (struct IpaHwOffloadCommonChCmdData_t_v4_0 *)cmd.base; + + cmd_data->protocol = IPA_HW_PROTOCOL_11ad; + cmd_data->CommonCh_params.W11AdCommonCh_params.gsi_ch = gsi_ch; + command = IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN; + } + + IPA_ACTIVE_CLIENTS_INC_SIMPLE(); + + result = ipa3_uc_send_cmd((u32)(cmd.phys_base), + command, + IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS, + false, 10 * HZ); + if (result) { + IPAERR("fail to %s uc for %s gsi channel %d\n", + init ? "init" : "deinit", + Rx ? "Rx" : "Tx", gsi_ch); + } + + dma_free_coherent(ipa3_ctx->uc_pdev, + cmd.size, cmd.base, cmd.phys_base); + IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); + + IPADBG("exit\n"); + return result; +} + +int ipa3_conn_wigig_rx_pipe_i(void *in, struct ipa_wigig_conn_out_params *out) +{ + int ipa_ep_idx; + struct ipa3_ep_context *ep; + struct ipa_ep_cfg ep_cfg; + enum ipa_client_type rx_client = IPA_CLIENT_WIGIG_PROD; + bool is_smmu_enabled; + struct ipa_wigig_conn_rx_in_params_smmu *input_smmu = NULL; + struct ipa_wigig_conn_rx_in_params *input = NULL; + const struct ipa_gsi_ep_config *ep_gsi; + void *pipe_info; + void *buff; + phys_addr_t status_ring_HWHEAD_pa; + int result; + + IPADBG("\n"); + + ipa_ep_idx = ipa_get_ep_mapping(rx_client); + if (ipa_ep_idx == IPA_EP_NOT_ALLOCATED || + ipa_ep_idx >= IPA3_MAX_NUM_PIPES) { + IPAERR("fail to get ep (IPA_CLIENT_WIGIG_PROD) %d.\n", + ipa_ep_idx); + return -EFAULT; + } + + ep = &ipa3_ctx->ep[ipa_ep_idx]; + if (ep->valid) { + IPAERR("EP %d already allocated.\n", ipa_ep_idx); + return -EFAULT; + } + + if (ep->gsi_offload_state) { + IPAERR("WIGIG channel bad state 0x%X\n", + ep->gsi_offload_state); + return -EFAULT; + } + + ep_gsi = ipa3_get_gsi_ep_info(rx_client); + if (!ep_gsi) { + IPAERR("Failed getting GSI EP info for client=%d\n", + rx_client); + return -EPERM; + } + + memset(ep, 0, offsetof(struct ipa3_ep_context, sys)); + + IPA_ACTIVE_CLIENTS_INC_SIMPLE(); + + /* setup rx ep cfg */ + ep->valid = 1; + ep->client = rx_client; + result = ipa3_disable_data_path(ipa_ep_idx); + if (result) { + IPAERR("disable data path failed res=%d clnt=%d.\n", result, + ipa_ep_idx); + IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); + return -EFAULT; + } + + is_smmu_enabled = !ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_WLAN]; + if (is_smmu_enabled) { + struct ipa_wigig_rx_pipe_data_buffer_info_smmu *dbuff_smmu; + + input_smmu = (struct ipa_wigig_conn_rx_in_params_smmu *)in; + dbuff_smmu = &input_smmu->dbuff_smmu; + ep->client_notify = input_smmu->notify; + ep->priv = input_smmu->priv; + + if (IPA_WIGIG_MSB( + dbuff_smmu->data_buffer_base_iova) & + 0xFFFFFF00) { + IPAERR( + "data_buffers_base_address_msb is over the 8 bit limit (0xpa)\n" + , &dbuff_smmu->data_buffer_base_iova); + IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); + return -EFAULT; + } + if (dbuff_smmu->data_buffer_size >> 16) { + IPAERR( + "data_buffer_size is over the 16 bit limit (0x%X)\n" + , dbuff_smmu->data_buffer_size); + IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); + return -EFAULT; + } + } else { + input = (struct ipa_wigig_conn_rx_in_params *)in; + ep->client_notify = input->notify; + ep->priv = input->priv; + + if ( + IPA_WIGIG_MSB(input->dbuff.data_buffer_base_pa) & 0xFFFFFF00) { + IPAERR( + "data_buffers_base_address_msb is over the 8 bit limit (0xpa)\n" + , &input->dbuff.data_buffer_base_pa); + IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); + return -EFAULT; + } + if (input->dbuff.data_buffer_size >> 16) { + IPAERR( + "data_buffer_size is over the 16 bit limit (0x%X)\n" + , input->dbuff.data_buffer_size); + IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); + return -EFAULT; + } + } + + memset(&ep_cfg, 0, sizeof(ep_cfg)); + ep_cfg.nat.nat_en = IPA_SRC_NAT; + ep_cfg.hdr.hdr_len = ETH_HLEN; + ep_cfg.hdr.hdr_ofst_pkt_size_valid = 0; + ep_cfg.hdr.hdr_ofst_pkt_size = 0; + ep_cfg.hdr.hdr_additional_const_len = 0; + ep_cfg.hdr_ext.hdr_little_endian = true; + ep_cfg.hdr.hdr_ofst_metadata_valid = 0; + ep_cfg.hdr.hdr_metadata_reg_valid = 1; + ep_cfg.mode.mode = IPA_BASIC; + + + if (ipa3_cfg_ep(ipa_ep_idx, &ep->cfg)) { + IPAERR("fail to setup rx pipe cfg\n"); + result = -EFAULT; + goto fail; + } + + if (is_smmu_enabled) { + result = ipa3_wigig_smmu_map_channel(true, + &input_smmu->pipe_smmu, + &input_smmu->dbuff_smmu, + true); + if (result) { + IPAERR("failed to setup rx pipe smmu map\n"); + result = -EFAULT; + goto fail; + } + + pipe_info = &input_smmu->pipe_smmu; + buff = &input_smmu->dbuff_smmu; + status_ring_HWHEAD_pa = + input_smmu->pipe_smmu.status_ring_HWHEAD_pa; + } else { + pipe_info = &input->pipe; + buff = &input->dbuff; + status_ring_HWHEAD_pa = + input->pipe.status_ring_HWHEAD_pa; + } + + result = ipa3_wigig_config_uc( + true, true, 0, + ep_gsi->ipa_gsi_chan_num, + status_ring_HWHEAD_pa); + if (result) + goto fail_uc_config; + + result = ipa3_wigig_config_gsi(true, + is_smmu_enabled, + pipe_info, + buff, + ep_gsi, ep); + if (result) + goto fail_gsi; + + ipa3_install_dflt_flt_rules(ipa_ep_idx); + + out->client = IPA_CLIENT_WIGIG_PROD; + ep->gsi_offload_state |= IPA_WIGIG_CONNECTED; + + IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); + + IPADBG("wigig rx pipe connected successfully\n"); + IPADBG("exit\n"); + + return 0; + +fail_gsi: + ipa3_wigig_config_uc( + false, true, 0, + ep_gsi->ipa_gsi_chan_num, + status_ring_HWHEAD_pa); +fail_uc_config: + if (input_smmu) + ipa3_wigig_smmu_map_channel(true, &input_smmu->pipe_smmu, + &input_smmu->dbuff_smmu, false); +fail: + IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); + return result; +} + +int ipa3_conn_wigig_client_i(void *in, struct ipa_wigig_conn_out_params *out) +{ + int ipa_ep_idx; + struct ipa3_ep_context *ep; + struct ipa_ep_cfg ep_cfg; + enum ipa_client_type tx_client; + bool is_smmu_enabled; + struct ipa_wigig_conn_tx_in_params_smmu *input_smmu = NULL; + struct ipa_wigig_conn_tx_in_params *input = NULL; + const struct ipa_gsi_ep_config *ep_gsi; + u32 aggr_byte_limit; + int result; + void *pipe_info; + void *buff; + phys_addr_t desc_ring_HWHEAD_pa; + u8 wifi_ch; + + IPADBG("\n"); + + is_smmu_enabled = !ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_WLAN]; + if (is_smmu_enabled) { + input_smmu = (struct ipa_wigig_conn_tx_in_params_smmu *)in; + if (ipa3_wigig_tx_bit_to_ep(input_smmu->int_gen_tx_bit_num, + &tx_client)) { + return -EINVAL; + } + wifi_ch = input_smmu->int_gen_tx_bit_num; + if (input_smmu->dbuff_smmu.data_buffer_size >> 16) { + IPAERR( + "data_buffer_size is over the 16 bit limit (0x%X)\n" + , input_smmu->dbuff_smmu.data_buffer_size); + return -EFAULT; + } + + /* convert to kBytes */ + aggr_byte_limit = IPA_ADJUST_AGGR_BYTE_HARD_LIMIT( + input_smmu->dbuff_smmu.data_buffer_size); + } else { + input = (struct ipa_wigig_conn_tx_in_params *)in; + if (ipa3_wigig_tx_bit_to_ep(input->int_gen_tx_bit_num, + &tx_client)) { + return -EINVAL; + } + wifi_ch = input->int_gen_tx_bit_num; + + if (input->dbuff.data_buffer_size >> 16) { + IPAERR( + "data_buffer_size is over the 16 bit limit (0x%X)\n" + , input->dbuff.data_buffer_size); + return -EFAULT; + } + + /* convert to kBytes */ + aggr_byte_limit = IPA_ADJUST_AGGR_BYTE_HARD_LIMIT( + input->dbuff.data_buffer_size); + } + IPADBG("client type is %d\n", tx_client); + + ipa_ep_idx = ipa_get_ep_mapping(tx_client); + if (ipa_ep_idx == IPA_EP_NOT_ALLOCATED || + ipa_ep_idx >= IPA3_MAX_NUM_PIPES) { + IPAERR("fail to get ep (%d) %d.\n", + tx_client, ipa_ep_idx); + return -EFAULT; + } + + ep = &ipa3_ctx->ep[ipa_ep_idx]; + if (ep->valid) { + IPAERR("EP %d already allocated.\n", ipa_ep_idx); + return -EFAULT; + } + + if (ep->gsi_offload_state) { + IPAERR("WIGIG channel bad state 0x%X\n", + ep->gsi_offload_state); + return -EFAULT; + } + + ep_gsi = ipa3_get_gsi_ep_info(tx_client); + if (!ep_gsi) { + IPAERR("Failed getting GSI EP info for client=%d\n", + tx_client); + return -EFAULT; + } + + memset(ep, 0, offsetof(struct ipa3_ep_context, sys)); + IPA_ACTIVE_CLIENTS_INC_SIMPLE(); + + /* setup tx ep cfg */ + ep->valid = 1; + ep->client = tx_client; + result = ipa3_disable_data_path(ipa_ep_idx); + if (result) { + IPAERR("disable data path failed res=%d clnt=%d.\n", result, + ipa_ep_idx); + goto fail; + } + + ep->client_notify = NULL; + ep->priv = NULL; + + memset(&ep_cfg, 0, sizeof(ep_cfg)); + ep_cfg.nat.nat_en = IPA_DST_NAT; + ep_cfg.hdr.hdr_len = ETH_HLEN; + ep_cfg.hdr.hdr_ofst_pkt_size_valid = 0; + ep_cfg.hdr.hdr_ofst_pkt_size = 0; + ep_cfg.hdr.hdr_additional_const_len = 0; + ep_cfg.hdr_ext.hdr_little_endian = true; + ep_cfg.mode.mode = IPA_BASIC; + + /* config hard byte limit, max is the buffer size (in kB)*/ + ep_cfg.aggr.aggr_en = IPA_ENABLE_AGGR; + ep_cfg.aggr.aggr = IPA_GENERIC; + ep_cfg.aggr.aggr_pkt_limit = 1; + ep_cfg.aggr.aggr_byte_limit = aggr_byte_limit; + ep_cfg.aggr.aggr_hard_byte_limit_en = IPA_ENABLE_AGGR; + + if (ipa3_cfg_ep(ipa_ep_idx, &ep_cfg)) { + IPAERR("fail to setup rx pipe cfg\n"); + result = -EFAULT; + goto fail; + } + + if (is_smmu_enabled) { + result = ipa3_wigig_smmu_map_channel(false, + &input_smmu->pipe_smmu, + &input_smmu->dbuff_smmu, + true); + if (result) { + IPAERR( + "failed to setup tx pipe smmu map client %d (ep %d)\n" + , tx_client, ipa_ep_idx); + result = -EFAULT; + goto fail; + } + + pipe_info = &input_smmu->pipe_smmu; + buff = &input_smmu->dbuff_smmu; + desc_ring_HWHEAD_pa = + input_smmu->pipe_smmu.desc_ring_HWHEAD_pa; + } else { + pipe_info = &input->pipe; + buff = &input->dbuff; + desc_ring_HWHEAD_pa = + input->pipe.desc_ring_HWHEAD_pa; + } + + result = ipa3_wigig_config_uc( + true, false, wifi_ch, + ep_gsi->ipa_gsi_chan_num, + desc_ring_HWHEAD_pa); + if (result) + goto fail_uc_config; + + result = ipa3_wigig_config_gsi(false, + is_smmu_enabled, + pipe_info, + buff, + ep_gsi, ep); + if (result) + goto fail_gsi; + + out->client = tx_client; + ep->gsi_offload_state |= IPA_WIGIG_CONNECTED; + + IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); + + IPADBG("wigig client %d (ep %d) connected successfully\n", tx_client, + ipa_ep_idx); + return 0; + +fail_gsi: + ipa3_wigig_config_uc( + false, false, wifi_ch, + ep_gsi->ipa_gsi_chan_num, + desc_ring_HWHEAD_pa); +fail_uc_config: + if (input_smmu) + ipa3_wigig_smmu_map_channel(false, &input_smmu->pipe_smmu, + &input_smmu->dbuff_smmu, false); +fail: + ep->valid = 0; + IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); + return result; +} + +int ipa3_disconn_wigig_pipe_i(enum ipa_client_type client, + struct ipa_wigig_pipe_setup_info_smmu *pipe_smmu, + void *dbuff) +{ + bool is_smmu_enabled; + int ipa_ep_idx; + struct ipa3_ep_context *ep; + const struct ipa_gsi_ep_config *ep_gsi; + int result; + bool rx = false; + + IPADBG("\n"); + + ipa_ep_idx = ipa_get_ep_mapping(client); + if (ipa_ep_idx == IPA_EP_NOT_ALLOCATED || + ipa_ep_idx >= IPA3_MAX_NUM_PIPES) { + IPAERR("fail to get ep (%d) %d.\n", + client, ipa_ep_idx); + return -EFAULT; + } + + ep = &ipa3_ctx->ep[ipa_ep_idx]; + if (!ep->valid) { + IPAERR("Invalid EP\n"); + return -EFAULT; + } + + ep_gsi = ipa3_get_gsi_ep_info(client); + if (!ep_gsi) { + IPAERR("Failed getting GSI EP info for client=%d\n", + client); + return -EFAULT; + } + + if (ep->gsi_offload_state != IPA_WIGIG_CONNECTED) { + IPAERR("client in bad state(client %d) 0x%X\n", + client, ep->gsi_offload_state); + return -EFAULT; + } + + if (client == IPA_CLIENT_WIGIG_PROD) + rx = true; + + IPA_ACTIVE_CLIENTS_INC_SIMPLE(); + + /* Release channel and evt*/ + result = ipa3_release_gsi_channel(ipa_ep_idx); + if (result) { + IPAERR("failed to deallocate channel\n"); + goto fail; + } + + is_smmu_enabled = !ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_WLAN]; + if (is_smmu_enabled) { + if (!pipe_smmu || !dbuff) { + IPAERR("smmu input is null %pK %pK\n", + pipe_smmu, dbuff); + WARN_ON(1); + } else { + result = ipa3_wigig_smmu_map_channel(rx, + pipe_smmu, + dbuff, + false); + if (result) { + IPAERR( + "failed to unmap pipe smmu %d (ep %d)\n" + , client, ipa_ep_idx); + result = -EFAULT; + goto fail; + } + } + } else if (pipe_smmu || dbuff) { + IPAERR("smmu input is not null %pK %pK\n", + pipe_smmu, dbuff); + WARN_ON(1); + } + + /* only gsi ch number and dir are necessary */ + result = ipa3_wigig_config_uc( + false, rx, 0, + ep_gsi->ipa_gsi_chan_num, 0); + if (result) { + IPAERR("failed uC channel teardown %d\n", result); + WARN_ON(1); + } + + memset(ep, 0, sizeof(struct ipa3_ep_context)); + + ep->gsi_offload_state = 0; + + IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); + + IPADBG("client (ep: %d) disconnected\n", ipa_ep_idx); + + IPADBG("exit\n"); + return 0; + +fail: + IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); + return result; +} + +int ipa3_wigig_uc_msi_init(bool init, + phys_addr_t periph_baddr_pa, + phys_addr_t pseudo_cause_pa, + phys_addr_t int_gen_tx_pa, + phys_addr_t int_gen_rx_pa, + phys_addr_t dma_ep_misc_pa) +{ + int result; + struct ipa_mem_buffer cmd; + enum ipa_cpu_2_hw_offload_commands command; + bool map = false; + + IPADBG("\n"); + + /* first make sure registers are SMMU mapped if necessary*/ + if ((!ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_UC])) { + if (init) + map = true; + + IPADBG("SMMU enabled, map %d\n", map); + + result = ipa3_smmu_map_peer_reg( + pseudo_cause_pa, + map, + IPA_SMMU_CB_UC); + if (result) { + IPAERR( + "failed to %s pseudo_cause reg %d\n", + map ? "map" : "unmap", + result); + goto fail; + } + + result = ipa3_smmu_map_peer_reg( + int_gen_tx_pa, + map, + IPA_SMMU_CB_UC); + if (result) { + IPAERR( + "failed to %s int_gen_tx reg %d\n", + map ? "map" : "unmap", + result); + goto fail_gen_tx; + } + + result = ipa3_smmu_map_peer_reg( + int_gen_rx_pa, + map, + IPA_SMMU_CB_UC); + if (result) { + IPAERR( + "failed to %s int_gen_rx reg %d\n", + map ? "map" : "unmap", + result); + goto fail_gen_rx; + } + + result = ipa3_smmu_map_peer_reg( + dma_ep_misc_pa, + map, + IPA_SMMU_CB_UC); + if (result) { + IPAERR( + "failed to %s dma_ep_misc reg %d\n", + map ? "map" : "unmap", + result); + goto fail_dma_ep_misc; + } + } + + /* now send the wigig hw base address to uC*/ + if (init) { + struct IpaHwPeripheralInitCmdData_t *cmd_data; + + cmd.size = sizeof(*cmd_data); + cmd.base = dma_alloc_coherent(ipa3_ctx->uc_pdev, cmd.size, + &cmd.phys_base, GFP_KERNEL); + if (cmd.base == NULL) { + IPAERR("fail to get DMA memory.\n"); + result = -ENOMEM; + if (map) + goto fail_alloc; + return result; + } + cmd_data = (struct IpaHwPeripheralInitCmdData_t *)cmd.base; + cmd_data->protocol = IPA_HW_PROTOCOL_11ad; + cmd_data->Init_params.W11AdInit_params.periph_baddr_msb = + IPA_WIGIG_MSB(periph_baddr_pa); + cmd_data->Init_params.W11AdInit_params.periph_baddr_lsb = + IPA_WIGIG_LSB(periph_baddr_pa); + command = IPA_CPU_2_HW_CMD_PERIPHERAL_INIT; + } else { + struct IpaHwPeripheralDeinitCmdData_t *cmd_data; + + cmd.size = sizeof(*cmd_data); + cmd.base = dma_alloc_coherent(ipa3_ctx->uc_pdev, cmd.size, + &cmd.phys_base, GFP_KERNEL); + if (cmd.base == NULL) { + IPAERR("fail to get DMA memory.\n"); + result = -ENOMEM; + if (map) + goto fail_alloc; + return result; + } + cmd_data = (struct IpaHwPeripheralDeinitCmdData_t *)cmd.base; + cmd_data->protocol = IPA_HW_PROTOCOL_11ad; + command = IPA_CPU_2_HW_CMD_PERIPHERAL_DEINIT; + } + + IPA_ACTIVE_CLIENTS_INC_SIMPLE(); + + result = ipa3_uc_send_cmd((u32)(cmd.phys_base), + command, + IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS, + false, 10 * HZ); + if (result) { + IPAERR("fail to %s uc MSI config\n", init ? "init" : "deinit"); + goto fail_command; + } + + dma_free_coherent(ipa3_ctx->uc_pdev, cmd.size, + cmd.base, cmd.phys_base); + IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); + + IPADBG("exit\n"); + + return 0; +fail_command: + dma_free_coherent(ipa3_ctx->uc_pdev, + cmd.size, + cmd.base, cmd.phys_base); + IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); +fail_alloc: + ipa3_smmu_map_peer_reg(dma_ep_misc_pa, !map, IPA_SMMU_CB_UC); +fail_dma_ep_misc: + ipa3_smmu_map_peer_reg(int_gen_rx_pa, !map, IPA_SMMU_CB_UC); +fail_gen_rx: + ipa3_smmu_map_peer_reg(int_gen_tx_pa, !map, IPA_SMMU_CB_UC); +fail_gen_tx: + ipa3_smmu_map_peer_reg(pseudo_cause_pa, !map, IPA_SMMU_CB_UC); +fail: + return result; +} + +int ipa3_enable_wigig_pipe_i(enum ipa_client_type client) +{ + int ipa_ep_idx, res; + struct ipa3_ep_context *ep; + struct ipa_ep_cfg_ctrl ep_cfg_ctrl; + int retry_cnt = 0; + uint64_t val; + + IPADBG("\n"); + + ipa_ep_idx = ipa_get_ep_mapping(client); + if (ipa_ep_idx == IPA_EP_NOT_ALLOCATED || + ipa_ep_idx >= IPA3_MAX_NUM_PIPES) { + IPAERR("fail to get ep (%d) %d.\n", + client, ipa_ep_idx); + return -EFAULT; + } + + ep = &ipa3_ctx->ep[ipa_ep_idx]; + + if (!ep->valid) { + IPAERR("Invalid EP\n"); + return -EFAULT; + } + + if (ep->gsi_offload_state != IPA_WIGIG_CONNECTED) { + IPAERR("WIGIG channel bad state 0x%X\n", + ep->gsi_offload_state); + return -EFAULT; + } + + IPA_ACTIVE_CLIENTS_INC_EP(client); + + res = ipa3_enable_data_path(ipa_ep_idx); + if (res) + goto fail_enable_datapath; + + memset(&ep_cfg_ctrl, 0, sizeof(struct ipa_ep_cfg_ctrl)); + ipa3_cfg_ep_ctrl(ipa_ep_idx, &ep_cfg_ctrl); + + /* ring the event db (outside the ring boundary)*/ + val = ep->gsi_mem_info.evt_ring_base_addr + + ep->gsi_mem_info.evt_ring_len; + res = gsi_ring_evt_ring_db(ep->gsi_evt_ring_hdl, val); + if (res) { + IPAERR( + "fail to ring evt ring db %d. hdl=%lu wp=0x%llx\n" + , res, ep->gsi_evt_ring_hdl, + (unsigned long long)val); + res = -EFAULT; + goto fail_ring_evt; + } + + res = gsi_start_channel(ep->gsi_chan_hdl); + if (res != GSI_STATUS_SUCCESS) { + IPAERR("gsi_start_channel failed %d\n", res); + WARN_ON(1); + res = -EFAULT; + goto fail_gsi_start; + } + + /* for TX we have to ring the channel db (last desc in the ring) */ + if (client != IPA_CLIENT_WIGIG_PROD) { + uint64_t val; + + val = ep->gsi_mem_info.chan_ring_base_addr + + ep->gsi_mem_info.chan_ring_len - + IPA_WIGIG_DESC_RING_EL_SIZE; + + res = gsi_ring_ch_ring_db(ep->gsi_chan_hdl, val); + if (res) { + IPAERR( + "fail to ring channel db %d. hdl=%lu wp=0x%llx\n" + , res, ep->gsi_chan_hdl, + (unsigned long long)val); + res = -EFAULT; + goto fail_ring_ch; + } + } + + ep->gsi_offload_state |= IPA_WIGIG_ENABLED; + + IPADBG("exit\n"); + + return 0; + +fail_ring_ch: + res = ipa3_stop_gsi_channel(ipa_ep_idx); + if (res != 0 && res != -GSI_STATUS_AGAIN && + res != -GSI_STATUS_TIMED_OUT) { + IPAERR("failed to stop channel res = %d\n", res); + } else if (res == -GSI_STATUS_AGAIN) { + IPADBG("GSI stop channel failed retry cnt = %d\n", + retry_cnt); + retry_cnt++; + if (retry_cnt < GSI_STOP_MAX_RETRY_CNT) + goto fail_ring_ch; + } else { + IPADBG("GSI channel %ld STOP\n", ep->gsi_chan_hdl); + } + res = -EFAULT; +fail_gsi_start: +fail_ring_evt: + ipa3_disable_data_path(ipa_ep_idx); +fail_enable_datapath: + IPA_ACTIVE_CLIENTS_DEC_EP(client); + return res; +} + +int ipa3_disable_wigig_pipe_i(enum ipa_client_type client) +{ + int ipa_ep_idx, res; + struct ipa3_ep_context *ep; + struct ipahal_ep_cfg_ctrl_scnd ep_ctrl_scnd = { 0 }; + struct ipa_ep_cfg_ctrl ep_cfg_ctrl; + bool disable_force_clear = false; + u32 source_pipe_bitmask = 0; + int retry_cnt = 0; + + IPADBG("\n"); + + ipa_ep_idx = ipa_get_ep_mapping(client); + if (ipa_ep_idx == IPA_EP_NOT_ALLOCATED || + ipa_ep_idx >= IPA3_MAX_NUM_PIPES) { + IPAERR("fail to get ep (%d) %d.\n", + client, ipa_ep_idx); + return -EFAULT; + } + if (ipa_ep_idx >= IPA3_MAX_NUM_PIPES) { + IPAERR("ep %d out of range.\n", ipa_ep_idx); + return -EFAULT; + } + + ep = &ipa3_ctx->ep[ipa_ep_idx]; + + if (!ep->valid) { + IPAERR("Invalid EP\n"); + return -EFAULT; + } + + if (ep->gsi_offload_state != + (IPA_WIGIG_CONNECTED | IPA_WIGIG_ENABLED)) { + IPAERR("WIGIG channel bad state 0x%X\n", + ep->gsi_offload_state); + return -EFAULT; + } + + IPADBG("pipe %d\n", ipa_ep_idx); + source_pipe_bitmask = 1 << ipa_ep_idx; + res = ipa3_enable_force_clear(ipa_ep_idx, + false, source_pipe_bitmask); + if (res) { + /* + * assuming here modem SSR, AP can remove + * the delay in this case + */ + IPAERR("failed to force clear %d\n", res); + IPAERR("remove delay from SCND reg\n"); + ep_ctrl_scnd.endp_delay = false; + ipahal_write_reg_n_fields( + IPA_ENDP_INIT_CTRL_SCND_n, ipa_ep_idx, + &ep_ctrl_scnd); + } else { + disable_force_clear = true; + } +retry_gsi_stop: + res = ipa3_stop_gsi_channel(ipa_ep_idx); + if (res != 0 && res != -GSI_STATUS_AGAIN && + res != -GSI_STATUS_TIMED_OUT) { + IPAERR("failed to stop channel res = %d\n", res); + goto fail_stop_channel; + } else if (res == -GSI_STATUS_AGAIN) { + IPADBG("GSI stop channel failed retry cnt = %d\n", + retry_cnt); + retry_cnt++; + if (retry_cnt >= GSI_STOP_MAX_RETRY_CNT) + goto fail_stop_channel; + goto retry_gsi_stop; + } else { + IPADBG("GSI channel %ld STOP\n", ep->gsi_chan_hdl); + } + + res = ipa3_reset_gsi_channel(ipa_ep_idx); + if (res != GSI_STATUS_SUCCESS) { + IPAERR("Failed to reset chan: %d.\n", res); + goto fail_stop_channel; + } + + if (disable_force_clear) + ipa3_disable_force_clear(ipa_ep_idx); + + res = ipa3_disable_data_path(ipa_ep_idx); + if (res) { + WARN_ON(1); + return res; + } + + /* Set the delay after disabling IPA Producer pipe */ + if (IPA_CLIENT_IS_PROD(ep->client)) { + memset(&ep_cfg_ctrl, 0, sizeof(struct ipa_ep_cfg_ctrl)); + ep_cfg_ctrl.ipa_ep_delay = true; + ipa3_cfg_ep_ctrl(ipa_ep_idx, &ep_cfg_ctrl); + } + + ep->gsi_offload_state &= ~IPA_WIGIG_ENABLED; + + IPA_ACTIVE_CLIENTS_DEC_EP(ipa3_get_client_mapping(ipa_ep_idx)); + IPADBG("exit\n"); + return 0; + +fail_stop_channel: + ipa_assert(); + return res; +} diff --git a/include/linux/ipa_wigig.h b/include/linux/ipa_wigig.h new file mode 100644 index 000000000000..f190fe862750 --- /dev/null +++ b/include/linux/ipa_wigig.h @@ -0,0 +1,491 @@ +/* Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _IPA_WIGIG_H_ +#define _IPA_WIGIG_H_ + +#include +#include + +typedef void (*ipa_wigig_misc_int_cb)(void *priv); + +/* + * struct ipa_wigig_init_in_params - wigig init input parameters + * + * @periph_baddr_pa: physical address of wigig HW base + * @pseudo_cause_pa: physical address of wigig HW pseudo_cause register + * @int_gen_tx_pa: physical address of wigig HW int_gen_tx register + * @int_gen_rx_pa: physical address of wigig HW int_gen_rx register + * @dma_ep_misc_pa: physical address of wigig HW dma_ep_misc register + * @notify: uc ready callback + * @int_notify: wigig misc interrupt callback + * @priv: uc ready callback cookie + */ +struct ipa_wigig_init_in_params { + phys_addr_t periph_baddr_pa; + phys_addr_t pseudo_cause_pa; + phys_addr_t int_gen_tx_pa; + phys_addr_t int_gen_rx_pa; + phys_addr_t dma_ep_misc_pa; + ipa_uc_ready_cb notify; + ipa_wigig_misc_int_cb int_notify; + void *priv; +}; + +/* + * struct ipa_wigig_init_out_params - wigig init output parameters + * + * @is_uC_ready: is uC ready. No API should be called until uC is ready. + * @uc_db_pa: physical address of IPA uC doorbell + */ +struct ipa_wigig_init_out_params { + bool is_uc_ready; + phys_addr_t uc_db_pa; +}; + +/* + * struct ipa_wigig_hdr_info - Header to install on IPA HW + * + * @hdr: header to install on IPA HW + * @hdr_len: length of header + * @dst_mac_addr_offset: destination mac address offset + * @hdr_type: layer two header type + */ +struct ipa_wigig_hdr_info { + u8 *hdr; + u8 hdr_len; + u8 dst_mac_addr_offset; + enum ipa_hdr_l2_type hdr_type; +}; + +/* + * struct ipa_wigig_reg_intf_in_params - parameters for offload interface + * registration + * + * @netdev_name: network interface name + * @netdev_mac: netdev mac address + * @hdr_info: header information + */ +struct ipa_wigig_reg_intf_in_params { + const char *netdev_name; + u8 netdev_mac[IPA_MAC_ADDR_SIZE]; + struct ipa_wigig_hdr_info hdr_info[IPA_IP_MAX]; +}; + +/* + * struct ipa_wigig_pipe_setup_info - WIGIG TX/Rx configuration + * @desc_ring_base_pa: physical address of the base of the descriptor ring + * @desc_ring_size: size of the descriptor ring + * @desc_ring_HWHEAD_pa: physical address of the wigig descriptor ring HWHEAD + * @desc_ring_HWTAIL_pa: physical address of the wigig descriptor ring HWTAIL + * @status_ring_base_pa: physical address of the base of the status ring + * @status_ring_size: status ring size + * @desc_ring_HWHEAD_pa: physical address of the wigig descriptor ring HWHEAD + * @desc_ring_HWTAIL_pa: physical address of the wigig descriptor ring HWTAIL + */ +struct ipa_wigig_pipe_setup_info { + phys_addr_t desc_ring_base_pa; + u32 desc_ring_size; + phys_addr_t desc_ring_HWHEAD_pa; + phys_addr_t desc_ring_HWTAIL_pa; + + phys_addr_t status_ring_base_pa; + u32 status_ring_size; + phys_addr_t status_ring_HWHEAD_pa; + phys_addr_t status_ring_HWTAIL_pa; +}; + +/* + * struct ipa_wigig_pipe_setup_info_smmu - WIGIG TX/Rx configuration smmu mode + * @desc_ring_base: sg_table of the base of the descriptor ring + * @desc_ring_base_iova: IO virtual address mapped to physical base address + * @desc_ring_size: size of the descriptor ring + * @desc_ring_HWHEAD_pa: physical address of the wigig descriptor ring HWHEAD + * @desc_ring_HWTAIL_pa: physical address of the wigig descriptor ring HWTAIL + * @status_ring_base: sg_table of the base of the status ring + * @status_ring_base_iova: IO virtual address mapped to physical base address + * @status_ring_size: status ring size + * @desc_ring_HWHEAD_pa: physical address of the wigig descriptor ring HWHEAD + * @desc_ring_HWTAIL_pa: physical address of the wigig descriptor ring HWTAIL + */ +struct ipa_wigig_pipe_setup_info_smmu { + struct sg_table desc_ring_base; + u64 desc_ring_base_iova; + u32 desc_ring_size; + phys_addr_t desc_ring_HWHEAD_pa; + phys_addr_t desc_ring_HWTAIL_pa; + + struct sg_table status_ring_base; + u64 status_ring_base_iova; + u32 status_ring_size; + phys_addr_t status_ring_HWHEAD_pa; + phys_addr_t status_ring_HWTAIL_pa; +}; + +/* + * struct ipa_wigig_rx_pipe_data_buffer_info - WIGIG Rx data buffer + * configuration + * @data_buffer_base_pa: physical address of the physically contiguous + * Rx data buffer + * @data_buffer_size: size of the data buffer + */ +struct ipa_wigig_rx_pipe_data_buffer_info { + phys_addr_t data_buffer_base_pa; + u32 data_buffer_size; +}; + +/* + * struct ipa_wigig_rx_pipe_data_buffer_info_smmu - WIGIG Rx data buffer + * configuration smmu mode + * @data_buffer_base: sg_table of the physically contiguous + * Rx data buffer + * @data_buffer_base_iova: IO virtual address mapped to physical base address + * @data_buffer_size: size of the data buffer + */ +struct ipa_wigig_rx_pipe_data_buffer_info_smmu { + struct sg_table data_buffer_base; + u64 data_buffer_base_iova; + u32 data_buffer_size; +}; + +/* + * struct ipa_wigig_conn_rx_in_params - information provided by + * WIGIG offload client for Rx pipe + * @notify: client callback function + * @priv: client cookie + * @pipe: parameters to connect Rx pipe (WIGIG to IPA) + * @dbuff: Rx data buffer info + */ +struct ipa_wigig_conn_rx_in_params { + ipa_notify_cb notify; + void *priv; + struct ipa_wigig_pipe_setup_info pipe; + struct ipa_wigig_rx_pipe_data_buffer_info dbuff; +}; + +/* + * struct ipa_wigig_conn_rx_in_params_smmu - information provided by + * WIGIG offload client for Rx pipe + * @notify: client callback function + * @priv: client cookie + * @pipe_smmu: parameters to connect Rx pipe (WIGIG to IPA) smmu mode + * @dbuff_smmu: Rx data buffer info smmu mode + */ +struct ipa_wigig_conn_rx_in_params_smmu { + ipa_notify_cb notify; + void *priv; + struct ipa_wigig_pipe_setup_info_smmu pipe_smmu; + struct ipa_wigig_rx_pipe_data_buffer_info_smmu dbuff_smmu; +}; + +/* + * struct ipa_wigig_conn_out_params - information provided + * to WIGIG driver + * @client: client type allocated by IPA driver + */ +struct ipa_wigig_conn_out_params { + enum ipa_client_type client; +}; + +/* + * struct ipa_wigig_tx_pipe_data_buffer_info - WIGIG Tx data buffer + * configuration + * @data_buffer_size: size of a single data buffer + */ +struct ipa_wigig_tx_pipe_data_buffer_info { + u32 data_buffer_size; +}; + +/* + * struct ipa_wigig_tx_pipe_data_buffer_info_smmu - WIGIG Tx data buffer + * configuration smmu mode + * @data_buffer_base_pa: sg_tables of the Tx data buffers + * @data_buffer_base_iova: IO virtual address mapped to physical base address + * @num_buffers: number of buffers + * @data_buffer_size: size of a single data buffer + */ +struct ipa_wigig_tx_pipe_data_buffer_info_smmu { + struct sg_table *data_buffer_base; + u64 *data_buffer_base_iova; + u32 num_buffers; + u32 data_buffer_size; +}; + +/* + * struct ipa_wigig_conn_tx_in_params - information provided by + * wigig offload client for Tx pipe + * @pipe: parameters to connect Tx pipe (IPA to WIGIG) + * @dbuff: Tx data buffer info + * @int_gen_tx_bit_num: bit in int_gen_tx register associated with this client + * @client_mac: MAC address of client to be connected + */ +struct ipa_wigig_conn_tx_in_params { + struct ipa_wigig_pipe_setup_info pipe; + struct ipa_wigig_tx_pipe_data_buffer_info dbuff; + u8 int_gen_tx_bit_num; + u8 client_mac[IPA_MAC_ADDR_SIZE]; +}; + +/* + * struct ipa_wigig_conn_tx_in_params_smmu - information provided by + * wigig offload client for Tx pipe + * @pipe_smmu: parameters to connect Tx pipe (IPA to WIGIG) smmu mode + * @dbuff_smmu: Tx data buffer info smmu mode + * @int_gen_tx_bit_num: bit in int_gen_tx register associated with this client + * @client_mac: MAC address of client to be connected + */ +struct ipa_wigig_conn_tx_in_params_smmu { + struct ipa_wigig_pipe_setup_info_smmu pipe_smmu; + struct ipa_wigig_tx_pipe_data_buffer_info_smmu dbuff_smmu; + u8 int_gen_tx_bit_num; + u8 client_mac[IPA_MAC_ADDR_SIZE]; +}; + +#if defined CONFIG_IPA || defined CONFIG_IPA3 + +/* + * ipa_wigig_init - Client should call this function to + * init WIGIG IPA offload data path + * + * Note: Should not be called from atomic context + * + * @Return 0 on success, negative on failure + */ +int ipa_wigig_init(struct ipa_wigig_init_in_params *in, + struct ipa_wigig_init_out_params *out); + +/* + * ipa_wigig_cleanup - Client should call this function to + * clean up WIGIG IPA offload data path + * + * @Return 0 on success, negative on failure + */ +int ipa_wigig_cleanup(void); + +/* + * ipa_wigig_is_smmu_enabled - get smmu state + * + * @Return true if smmu is enabled, false if disabled + */ +bool ipa_wigig_is_smmu_enabled(void); + +/* + * ipa_wigig_reg_intf - Client should call this function to + * register interface + * + * Note: Should not be called from atomic context + * + * @Return 0 on success, negative on failure + */ +int ipa_wigig_reg_intf(struct ipa_wigig_reg_intf_in_params *in); + +/* + * ipa_wigig_dereg_intf - Client Driver should call this + * function to deregister before unload and after disconnect + * + * @Return 0 on success, negative on failure + */ +int ipa_wigig_dereg_intf(const char *netdev_name); + +/* + * ipa_wigig_conn_rx_pipe - Client should call this + * function to connect the rx (UL) pipe + * + * @in: [in] input parameters from client + * @out: [out] output params to client + * + * Note: Non SMMU mode only, Should not be called from atomic context + * + * @Return 0 on success, negative on failure + */ +int ipa_wigig_conn_rx_pipe(struct ipa_wigig_conn_rx_in_params *in, + struct ipa_wigig_conn_out_params *out); + +/* + * ipa_wigig_conn_rx_pipe_smmu - Client should call this + * function to connect the rx (UL) pipe + * + * @in: [in] input parameters from client + * @out: [out] output params to client + * + * Note: SMMU mode only, Should not be called from atomic context + * + * @Return 0 on success, negative on failure + */ +int ipa_wigig_conn_rx_pipe_smmu(struct ipa_wigig_conn_rx_in_params_smmu *in, + struct ipa_wigig_conn_out_params *out); + +/* + * ipa_wigig_conn_client - Client should call this + * function to connect one of the tx (DL) pipes when a WIGIG client connects + * + * @in: [in] input parameters from client + * @out: [out] output params to client + * + * Note: Non SMMU mode only, Should not be called from atomic context + * + * @Return 0 on success, negative on failure + */ +int ipa_wigig_conn_client(struct ipa_wigig_conn_tx_in_params *in, + struct ipa_wigig_conn_out_params *out); + +/* + * ipa_wigig_conn_client_smmu - Client should call this + * function to connect one of the tx (DL) pipes when a WIGIG client connects + * + * @in: [in] input parameters from client + * @out: [out] output params to client + * + * Note: SMMU mode only, Should not be called from atomic context + * + * @Return 0 on success, negative on failure + */ +int ipa_wigig_conn_client_smmu(struct ipa_wigig_conn_tx_in_params_smmu *in, + struct ipa_wigig_conn_out_params *out); + +/* + * ipa_wigig_disconn_pipe() - Client should call this + * function to disconnect a pipe + * + * @client: [in] pipe to be disconnected + * + * Note: Should not be called from atomic context + * + * Returns: 0 on success, negative on failure + */ +int ipa_wigig_disconn_pipe(enum ipa_client_type client); + +/* + * ipa_wigig_enable_pipe() - Client should call this + * function to enable IPA offload data path + * + * @client: [in] pipe to be enabled + * Note: Should not be called from atomic context + * + * Returns: 0 on success, negative on failure + */ + +int ipa_wigig_enable_pipe(enum ipa_client_type client); + +/* + * ipa_wigig_disable_pipe() - Client should call this + * function to disable IPA offload data path + * + * @client: [in] pipe to be disabled + * Note: Should not be called from atomic context + * + * Returns: 0 on success, negative on failure + */ +int ipa_wigig_disable_pipe(enum ipa_client_type client); + +/* + * ipa_wigig_tx_dp() - transmit tx packet through IPA to 11ad HW + * + * @dst: [in] destination ipa client pipe to be used + * @skb: [in] skb to be transmitted + * + * Returns: 0 on success, negative on failure + */ +int ipa_wigig_tx_dp(enum ipa_client_type dst, struct sk_buff *skb); + +/** + * ipa_wigig_set_perf_profile() - Client should call this function to + * set IPA clock bandwidth based on data rates + * + * @max_supported_bw_mbps: [in] maximum bandwidth needed (in Mbps) + * + * Returns: 0 on success, negative on failure + */ +int ipa_wigig_set_perf_profile(u32 max_supported_bw_mbps); + +#else /* (CONFIG_IPA || CONFIG_IPA3) */ +static inline int ipa_wigig_init(struct ipa_wigig_init_in_params *in, + struct ipa_wigig_init_out_params *out) +{ + return -EPERM; +} + +static inline int ipa_wigig_cleanup(void) +{ + return -EPERM; +} + +static inline bool ipa_wigig_is_smmu_enabled(void) +{ + return -EPERM; +} + +static inline int ipa_wigig_reg_intf(struct ipa_wigig_reg_intf_in_params *in) +{ + return -EPERM; +} + +static inline int ipa_wigig_dereg_intf(const char *netdev_name) +{ + return -EPERM; +} + +static inline int ipa_wigig_conn_rx_pipe( + struct ipa_wigig_conn_rx_in_params *in, + struct ipa_wigig_conn_out_params *out) +{ + return -EPERM; +} + +static inline int ipa_wigig_conn_rx_pipe_smmu( + struct ipa_wigig_conn_rx_in_params_smmu *in, + struct ipa_wigig_conn_out_params *out) +{ + return -EPERM; +} + +static inline int ipa_wigig_conn_client( + struct ipa_wigig_conn_tx_in_params *in, + struct ipa_wigig_conn_out_params *out) +{ + return -EPERM; +} + +static inline int ipa_wigig_conn_client_smmu( + struct ipa_wigig_conn_tx_in_params_smmu *in, + struct ipa_wigig_conn_out_params *out) +{ + return -EPERM; +} + +static inline int ipa_wigig_disconn_pipe(enum ipa_client_type client) +{ + return -EPERM; +} + +static inline int ipa_wigig_enable_pipe(enum ipa_client_type client) +{ + return -EPERM; +} + +static inline int ipa_wigig_disable_pipe(enum ipa_client_type client) +{ + return -EPERM; +} + +static inline int ipa_wigig_tx_dp(enum ipa_client_type dst, + struct sk_buff *skb) +{ + return -EPERM; +} + +int ipa_wigig_set_perf_profile(u32 max_supported_bw_mbps) +{ + return -EPERM; +} +#endif /* CONFIG_IPA3 */ +#endif /* _IPA_WIGIG_H_ */ diff --git a/include/linux/msm_gsi.h b/include/linux/msm_gsi.h index d3c07838965a..006bab5c6b92 100644 --- a/include/linux/msm_gsi.h +++ b/include/linux/msm_gsi.h @@ -711,7 +711,7 @@ struct __packed gsi_wdi_channel_scratch { * @status_ring_hwtail_address_msb: High 32 bits of status ring hwtail address. * @data_buffers_base_address_lsb: Low 32 bits of the data buffers address. * @data_buffers_base_address_msb: High 32 bits of the data buffers address. -* @fixed_data_buffer_size: the fixed buffer size (> MTU). +* @fixed_data_buffer_size_pow_2: the fixed buffer size power of 2 (> MTU). * @resv1: reserved bits. */ struct __packed gsi_11ad_rx_channel_scratch { @@ -719,7 +719,7 @@ struct __packed gsi_11ad_rx_channel_scratch { uint32_t status_ring_hwtail_address_msb; uint32_t data_buffers_base_address_lsb; uint32_t data_buffers_base_address_msb:8; - uint32_t fixed_data_buffer_size:16; + uint32_t fixed_data_buffer_size_pow_2:16; uint32_t resv1:8; }; @@ -733,7 +733,7 @@ struct __packed gsi_11ad_rx_channel_scratch { * updating descriptor ring 11ad HWTAIL pointer moderation. * @resv1: reserved bits. * @resv2: reserved bit. - * @fixed_data_buffer_size: the fixed buffer size (> MTU). + * @fixed_data_buffer_size_pow_2: the fixed buffer size power of 2 (> MTU). * @resv3: reserved bits. */ struct __packed gsi_11ad_tx_channel_scratch { @@ -742,7 +742,7 @@ struct __packed gsi_11ad_tx_channel_scratch { uint32_t update_status_hwtail_mod_threshold:8; uint32_t resv1:24; uint32_t resv2:8; - uint32_t fixed_data_buffer_size:16; + uint32_t fixed_data_buffer_size_pow_2:16; uint32_t resv3:8; }; @@ -843,7 +843,7 @@ union __packed gsi_evt_scratch { struct __packed gsi_mhi_evt_scratch mhi; struct __packed gsi_xdci_evt_scratch xdci; struct __packed gsi_wdi_evt_scratch wdi; - struct __packed gsi_11ad_evt_scratch ad11; + struct __packed gsi_11ad_evt_scratch w11ad; struct __packed { uint32_t word1; uint32_t word2; diff --git a/include/uapi/linux/msm_ipa.h b/include/uapi/linux/msm_ipa.h index d48464cba323..9ec8b192575c 100644 --- a/include/uapi/linux/msm_ipa.h +++ b/include/uapi/linux/msm_ipa.h @@ -328,9 +328,17 @@ enum ipa_client_type { /* RESERVED PROD = 86, */ IPA_CLIENT_APPS_WAN_COAL_CONS = 87, + IPA_CLIENT_WIGIG_PROD = 88, + IPA_CLIENT_WIGIG1_CONS = 89, + /* RESERVERD PROD = 90, */ + IPA_CLIENT_WIGIG2_CONS = 91, + /* RESERVERD PROD = 92, */ + IPA_CLIENT_WIGIG3_CONS = 93, + /* RESERVERD PROD = 94, */ + IPA_CLIENT_WIGIG4_CONS = 95, }; -#define IPA_CLIENT_MAX (IPA_CLIENT_APPS_WAN_COAL_CONS + 1) +#define IPA_CLIENT_MAX (IPA_CLIENT_WIGIG4_CONS + 1) #define IPA_CLIENT_Q6_DL_NLO_DATA_PROD IPA_CLIENT_Q6_DL_NLO_DATA_PROD #define IPA_CLIENT_Q6_UL_NLO_ACK_CONS IPA_CLIENT_Q6_UL_NLO_ACK_CONS @@ -338,6 +346,11 @@ enum ipa_client_type { #define IPA_CLIENT_MHI_DPL_CONS IPA_CLIENT_MHI_DPL_CONS #define IPA_CLIENT_Q6_AUDIO_DMA_MHI_PROD IPA_CLIENT_Q6_AUDIO_DMA_MHI_PROD #define IPA_CLIENT_Q6_AUDIO_DMA_MHI_CONS IPA_CLIENT_Q6_AUDIO_DMA_MHI_CONS +#define IPA_CLIENT_WIGIG_PROD IPA_CLIENT_WIGIG_PROD +#define IPA_CLIENT_WIGIG1_CONS IPA_CLIENT_WIGIG1_CONS +#define IPA_CLIENT_WIGIG2_CONS IPA_CLIENT_WIGIG2_CONS +#define IPA_CLIENT_WIGIG3_CONS IPA_CLIENT_WIGIG3_CONS +#define IPA_CLIENT_WIGIG4_CONS IPA_CLIENT_WIGIG4_CONS #define IPA_CLIENT_IS_APPS_CONS(client) \ ((client) == IPA_CLIENT_APPS_LAN_CONS || \