diff options
| -rw-r--r-- | core/hdd/src/wlan_hdd_ipa.c | 3314 |
1 files changed, 1930 insertions, 1384 deletions
diff --git a/core/hdd/src/wlan_hdd_ipa.c b/core/hdd/src/wlan_hdd_ipa.c index 2e3420219b88..1938d8b27bee 100644 --- a/core/hdd/src/wlan_hdd_ipa.c +++ b/core/hdd/src/wlan_hdd_ipa.c @@ -35,7 +35,12 @@ #ifdef IPA_OFFLOAD /* Include Files */ +#ifdef CONFIG_IPA_WDI_UNIFIED_API +#include <linux/ipa_wdi3.h> +#else #include <linux/ipa.h> +#endif + #include <wlan_hdd_includes.h> #include <wlan_hdd_ipa.h> @@ -92,6 +97,8 @@ #define IPA_WLAN_RX_SOFTIRQ_THRESH 16 +#define HDD_IPA_MAX_BANDWIDTH 800 + enum hdd_ipa_uc_op_code { HDD_IPA_UC_OPCODE_TX_SUSPEND = 0, HDD_IPA_UC_OPCODE_TX_RESUME = 1, @@ -502,6 +509,9 @@ struct hdd_ipa_priv { struct completion ipa_uc_set_quota_comp; #endif struct completion ipa_resource_comp; + + uint32_t wdi_version; + bool is_smmu_enabled; /* IPA caps returned from ipa_wdi_init */ }; #define HDD_IPA_WLAN_FRAG_HEADER sizeof(struct frag_header) @@ -623,22 +633,6 @@ struct hdd_ipa_uc_tx_hdr ipa_uc_tx_hdr = { }; #endif -/* For Tx pipes, use 802.3 Header format */ -static struct hdd_ipa_tx_hdr ipa_tx_hdr = { - { - {0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xFF}, - {0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xFF}, - 0x00 /* length can be zero */ - }, - { - /* LLC SNAP header 8 bytes */ - 0xaa, 0xaa, - {0x03, 0x00, 0x00, 0x00}, - 0x0008 /* type value(2 bytes) ,filled by wlan */ - /* 0x0800 - IPV4, 0x86dd - IPV6 */ - } -}; - #ifdef FEATURE_METERING #define IPA_UC_SHARING_STATES_WAIT_TIME 500 #define IPA_UC_SET_QUOTA_WAIT_TIME 500 @@ -658,30 +652,8 @@ static void hdd_ipa_msg_free_fn(void *buff, uint32_t len, uint32_t type); static void hdd_ipa_cleanup_iface(struct hdd_ipa_iface_context *iface_context); static void hdd_ipa_uc_proc_pending_event(struct hdd_ipa_priv *hdd_ipa, bool is_loading); - -#if ((defined(QCA_WIFI_3_0) && defined(CONFIG_IPA3)) || \ - defined(IPA_CLIENT_IS_MHI_CONS)) -/** - * hdd_ipa_uc_get_db_paddr() - Get Doorbell physical address - * @db_paddr: Doorbell physical address should be given bu IPA - * @client: IPA client type - * - * Query doorbell physical address from IPA - * IPA will give physical address for TX COMP and RX READY - * - * Return: None - */ -static void hdd_ipa_uc_get_db_paddr(qdf_dma_addr_t *db_paddr, - enum ipa_client_type client) -{ - struct ipa_wdi_db_params dbpa; - - dbpa.client = client; - ipa_uc_wdi_get_dbpa(&dbpa); - *db_paddr = dbpa.uc_door_bell_pa; - HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "PROD DB get dbpa 0x%x", - (unsigned int)dbpa.uc_door_bell_pa); -} +static int hdd_ipa_uc_enable_pipes(struct hdd_ipa_priv *hdd_ipa); +static int hdd_ipa_wdi_init(struct hdd_ipa_priv *hdd_ipa); /** * hdd_ipa_uc_loaded_uc_cb() - IPA UC loaded event callback @@ -775,58 +747,6 @@ static int hdd_ipa_uc_send_wdi_control_msg(bool ctrl) } /** - * hdd_ipa_uc_register_uc_ready() - Register UC ready callback function to IPA - * @hdd_ipa: HDD IPA local context - * - * Register IPA UC ready callback function to IPA kernel driver - * Even IPA UC loaded later than WLAN kernel driver, WLAN kernel driver will - * open WDI pipe after WLAN driver loading finished - * - * Return: 0 Success - * -EPERM Registration fail - */ -static int hdd_ipa_uc_register_uc_ready(struct hdd_ipa_priv *hdd_ipa) -{ - struct ipa_wdi_uc_ready_params uc_ready_param; - int ret = 0; - - hdd_ipa->uc_loaded = false; - uc_ready_param.priv = (void *)hdd_ipa; - uc_ready_param.notify = hdd_ipa_uc_loaded_uc_cb; - if (ipa_uc_reg_rdyCB(&uc_ready_param)) { - HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, - "UC Ready CB register fail"); - return -EPERM; - } - if (true == uc_ready_param.is_uC_ready) { - HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "UC Ready"); - hdd_ipa->uc_loaded = true; - } else { - ret = hdd_ipa_uc_send_wdi_control_msg(false); - } - - return ret; -} -#else -static void hdd_ipa_uc_get_db_paddr(qdf_dma_addr_t *db_paddr, - enum ipa_client_type client) -{ - /* Do nothing */ -} - -static int hdd_ipa_uc_register_uc_ready(struct hdd_ipa_priv *hdd_ipa) -{ - hdd_ipa->uc_loaded = true; - return 0; -} - -static int hdd_ipa_uc_send_wdi_control_msg(bool ctrl) -{ - return 0; -} -#endif - -/** * hdd_ipa_is_enabled() - Is IPA enabled? * @hdd_ctx: Global HDD context * @@ -947,6 +867,1790 @@ bool hdd_ipa_is_fw_wdi_actived(hdd_context_t *hdd_ctx) return (HDD_IPA_UC_NUM_WDI_PIPE == hdd_ipa->activated_fw_pipe); } +#ifdef FEATURE_METERING +/** + * __hdd_ipa_wdi_meter_notifier_cb() - WLAN to IPA callback handler. + * IPA calls to get WLAN stats or set quota limit. + * @priv: pointer to private data registered with IPA (we register a + *» pointer to the global IPA context) + * @evt: the IPA event which triggered the callback + * @data: data associated with the event + * + * Return: None + */ +static void __hdd_ipa_wdi_meter_notifier_cb(enum ipa_wdi_meter_evt_type evt, + void *data) +{ + struct hdd_ipa_priv *hdd_ipa = ghdd_ipa; + hdd_adapter_t *adapter = NULL; + struct ipa_get_wdi_sap_stats *wdi_sap_stats; + struct ipa_set_wifi_quota *ipa_set_quota; + int ret = 0; + + if (wlan_hdd_validate_context(hdd_ipa->hdd_ctx)) + return; + + adapter = hdd_get_adapter(hdd_ipa->hdd_ctx, QDF_STA_MODE); + + HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "event=%d", evt); + + switch (evt) { + case IPA_GET_WDI_SAP_STATS: + /* fill-up ipa_get_wdi_sap_stats structure after getting + * ipa_uc_fw_stats from FW + */ + wdi_sap_stats = data; + + if (!adapter) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "IPA uC share stats failed - no adapter"); + wdi_sap_stats->stats_valid = 0; + return; + } + + INIT_COMPLETION(hdd_ipa->ipa_uc_sharing_stats_comp); + hdd_ipa_uc_sharing_stats_request(adapter, + wdi_sap_stats->reset_stats); + ret = wait_for_completion_timeout( + &hdd_ipa->ipa_uc_sharing_stats_comp, + msecs_to_jiffies(IPA_UC_SHARING_STATES_WAIT_TIME)); + if (!ret) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "IPA uC share stats request timed out"); + wdi_sap_stats->stats_valid = 0; + } else { + wdi_sap_stats->stats_valid = 1; + + wdi_sap_stats->ipv4_rx_packets = + hdd_ipa->ipa_sharing_stats.ipv4_rx_packets; + wdi_sap_stats->ipv4_rx_bytes = + hdd_ipa->ipa_sharing_stats.ipv4_rx_bytes; + wdi_sap_stats->ipv6_rx_packets = + hdd_ipa->ipa_sharing_stats.ipv6_rx_packets; + wdi_sap_stats->ipv6_rx_bytes = + hdd_ipa->ipa_sharing_stats.ipv6_rx_bytes; + wdi_sap_stats->ipv4_tx_packets = + hdd_ipa->ipa_sharing_stats.ipv4_tx_packets; + wdi_sap_stats->ipv4_tx_bytes = + hdd_ipa->ipa_sharing_stats.ipv4_tx_bytes; + wdi_sap_stats->ipv6_tx_packets = + hdd_ipa->ipa_sharing_stats.ipv6_tx_packets; + wdi_sap_stats->ipv6_tx_bytes = + hdd_ipa->ipa_sharing_stats.ipv6_tx_bytes; + HDD_IPA_DP_LOG(QDF_TRACE_LEVEL_DEBUG, + "%s:%d,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu", + "IPA_GET_WDI_SAP_STATS", + wdi_sap_stats->stats_valid, + wdi_sap_stats->ipv4_rx_packets, + wdi_sap_stats->ipv4_rx_bytes, + wdi_sap_stats->ipv6_rx_packets, + wdi_sap_stats->ipv6_rx_bytes, + wdi_sap_stats->ipv4_tx_packets, + wdi_sap_stats->ipv4_tx_bytes, + wdi_sap_stats->ipv6_tx_packets, + wdi_sap_stats->ipv6_tx_bytes); + } + break; + case IPA_SET_WIFI_QUOTA: + /* Get ipa_set_wifi_quota structure from IPA and pass to FW + * through quota_exceeded field in ipa_uc_fw_stats + */ + ipa_set_quota = data; + + if (!adapter) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "IPA uC set quota failed - no adapter"); + ipa_set_quota->set_valid = 0; + return; + } + + INIT_COMPLETION(hdd_ipa->ipa_uc_set_quota_comp); + hdd_ipa_uc_set_quota(adapter, ipa_set_quota->set_quota, + ipa_set_quota->quota_bytes); + + ret = wait_for_completion_timeout( + &hdd_ipa->ipa_uc_set_quota_comp, + msecs_to_jiffies(IPA_UC_SET_QUOTA_WAIT_TIME)); + if (!ret) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "IPA uC set quota request timed out"); + ipa_set_quota->set_valid = 0; + } else { + ipa_set_quota->quota_bytes = + ((uint64_t)(hdd_ipa->ipa_quota_rsp.quota_hi) + <<32)|hdd_ipa->ipa_quota_rsp.quota_lo; + ipa_set_quota->set_valid = + hdd_ipa->ipa_quota_rsp.success; + } + + HDD_IPA_DP_LOG(QDF_TRACE_LEVEL_DEBUG, "SET_QUOTA: %llu, %d", + ipa_set_quota->quota_bytes, + ipa_set_quota->set_valid); + break; + } +} + +/** + * hdd_ipa_wdi_meter_notifier_cb() - WLAN to IPA callback handler. + * IPA calls to get WLAN stats or set quota limit. + * @priv: pointer to private data registered with IPA (we register a + *» pointer to the global IPA context) + * @evt: the IPA event which triggered the callback + * @data: data associated with the event + * + * Return: None + */ +static void hdd_ipa_wdi_meter_notifier_cb(enum ipa_wdi_meter_evt_type evt, + void *data) +{ + cds_ssr_protect(__func__); + __hdd_ipa_wdi_meter_notifier_cb(evt, data); + cds_ssr_unprotect(__func__); +} + +#else /* FEATURE_METERING */ +static void hdd_ipa_wdi_init_metering(struct hdd_ipa_priv *ipa_ctxt, void *in) +{ +} +#endif /* FEATURE_METERING */ + +#ifdef CONFIG_IPA_WDI_UNIFIED_API +/* + * TODO: Get WDI version through FW capabilities + */ +#ifdef QCA_WIFI_3_0 +static inline void hdd_ipa_wdi_get_wdi_version(struct hdd_ipa_priv *hdd_ipa) +{ + hdd_ipa->wdi_version = IPA_WDI_2; +} +#else +static inline void hdd_ipa_wdi_get_wdi_version(struct hdd_ipa_priv *hdd_ipa) +{ + hdd_ipa->wdi_version = IPA_WDI_1; +} +#endif + +static bool hdd_ipa_wdi_is_smmu_enabled(struct hdd_ipa_priv *hdd_ipa, + qdf_device_t osdev) +{ + return hdd_ipa->is_smmu_enabled && qdf_mem_smmu_s1_enabled(osdev); +} + +#ifdef QCA_LL_TX_FLOW_CONTROL_V2 +static bool hdd_ipa_wdi_is_mcc_mode_enabled(struct hdd_ipa_priv *hdd_ipa) +{ + return false; +} +#else +static bool hdd_ipa_wdi_is_mcc_mode_enabled(struct hdd_ipa_priv *hdd_ipa) +{ + return hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx); +} +#endif + +#ifdef FEATURE_METERING +static void hdd_ipa_wdi_init_metering(struct hdd_ipa_priv *ipa_ctxt, void *in) +{ + struct ipa_wdi_init_in_params *wdi3_in; + + wdi3_in = (struct ipa_wdi_init_in_params *)in; + wdi3_in->wdi_notify = hdd_ipa_wdi_meter_notifier_cb; + + init_completion(&ipa_ctxt->ipa_uc_sharing_stats_comp); + init_completion(&ipa_ctxt->ipa_uc_set_quota_comp); +} +#endif + +static int hdd_ipa_wdi_init(struct hdd_ipa_priv *hdd_ipa) +{ + struct ipa_wdi_init_in_params in; + struct ipa_wdi_init_out_params out; + int ret; + + hdd_ipa->uc_loaded = false; + + in.wdi_version = hdd_ipa->wdi_version; + in.notify = hdd_ipa_uc_loaded_uc_cb; + in.priv = (void *)hdd_ipa; + hdd_ipa_wdi_init_metering(hdd_ipa, (void *)&in); + + ret = ipa_wdi_init(&in, &out); + if (ret) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "ipa_wdi_init failed with ret=%d", ret); + return -EPERM; + } + + if (out.is_uC_ready) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "IPA uC READY"); + hdd_ipa->uc_loaded = true; + hdd_ipa->is_smmu_enabled = out.is_smmu_enabled; + HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "is_smmu_enabled=%d", + hdd_ipa->is_smmu_enabled); + } else { + ret = -EACCES; + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "IPA uC NOT READY ret=%d", ret); + } + + return ret; +} + +static int hdd_ipa_wdi_cleanup(void) +{ + int ret; + + ret = ipa_wdi_cleanup(); + if (ret) + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "ipa_wdi_cleanup failed ret=%d", ret); + return ret; +} + +static int hdd_ipa_wdi_conn_pipes(struct hdd_ipa_priv *hdd_ipa, + struct ol_txrx_ipa_resources *ipa_res) +{ + hdd_context_t *hdd_ctx = (hdd_context_t *)hdd_ipa->hdd_ctx; + qdf_device_t osdev = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + struct ipa_wdi_conn_in_params *in; + struct ipa_wdi_conn_out_params out; + struct ipa_wdi_pipe_setup_info *info; + struct ipa_wdi_pipe_setup_info_smmu *info_smmu; + struct ipa_ep_cfg *tx_cfg; + struct ipa_ep_cfg *rx_cfg; + int ret; + int i; + + in = qdf_mem_malloc(sizeof(*in)); + if (!in) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "failed to alloc ipa_wdi_conn_in_params"); + return -ENOMEM; + } + + /* IPA RX exception packets callback */ + in->notify = hdd_ipa_w2i_cb; + in->priv = hdd_ctx->hdd_ipa; + + if (hdd_ipa_wdi_is_mcc_mode_enabled(hdd_ipa)) { + in->num_sys_pipe_needed = HDD_IPA_MAX_IFACE; + for (i = 0; i < in->num_sys_pipe_needed; i++) + memcpy(&in->sys_in[i], + &hdd_ipa->sys_pipe[i].ipa_sys_params, + sizeof(struct ipa_sys_connect_params)); + } else { + in->num_sys_pipe_needed = 0; + } + + if (hdd_ipa_wdi_is_smmu_enabled(hdd_ipa, osdev)) + in->is_smmu_enabled = true; + else + in->is_smmu_enabled = false; + + if (in->is_smmu_enabled) { + tx_cfg = &in->u_tx.tx_smmu.ipa_ep_cfg; + rx_cfg = &in->u_rx.rx_smmu.ipa_ep_cfg; + } else { + tx_cfg = &in->u_tx.tx.ipa_ep_cfg; + rx_cfg = &in->u_rx.rx.ipa_ep_cfg; + } + + tx_cfg->nat.nat_en = IPA_BYPASS_NAT; + tx_cfg->hdr.hdr_len = HDD_IPA_UC_WLAN_TX_HDR_LEN; + tx_cfg->hdr.hdr_ofst_pkt_size_valid = 1; + tx_cfg->hdr.hdr_ofst_pkt_size = 0; + tx_cfg->hdr.hdr_additional_const_len = + HDD_IPA_UC_WLAN_8023_HDR_SIZE; + tx_cfg->hdr_ext.hdr_little_endian = true; + tx_cfg->mode.mode = IPA_BASIC; + + rx_cfg->nat.nat_en = IPA_BYPASS_NAT; + rx_cfg->hdr.hdr_len = HDD_IPA_UC_WLAN_RX_HDR_LEN; + rx_cfg->hdr.hdr_ofst_pkt_size_valid = 1; + rx_cfg->hdr.hdr_ofst_pkt_size = 0; + rx_cfg->hdr.hdr_additional_const_len = + HDD_IPA_UC_WLAN_8023_HDR_SIZE; + rx_cfg->hdr_ext.hdr_little_endian = true; + rx_cfg->hdr.hdr_ofst_metadata_valid = 0; + rx_cfg->hdr.hdr_metadata_reg_valid = 1; + rx_cfg->mode.mode = IPA_BASIC; + + if (in->is_smmu_enabled) { + /* TX */ + info_smmu = &in->u_tx.tx_smmu; + info_smmu->client = IPA_CLIENT_WLAN1_CONS; + + qdf_mem_copy(&info_smmu->transfer_ring_base, + &ipa_res->tx_comp_ring->sgtable, + sizeof(sgtable_t)); + info_smmu->transfer_ring_size = + ipa_res->tx_comp_ring->mem_info.size; + + qdf_mem_copy(&info_smmu->event_ring_base, + &ipa_res->ce_sr->sgtable, sizeof(sgtable_t)); + info_smmu->event_ring_size = ipa_res->ce_sr_ring_size; + info_smmu->event_ring_doorbell_pa = ipa_res->ce_reg_paddr; + info_smmu->num_pkt_buffers = ipa_res->tx_num_alloc_buffer; + + /* RX */ + info_smmu = &in->u_rx.rx_smmu; + info_smmu->client = IPA_CLIENT_WLAN1_PROD; + + qdf_mem_copy(&info_smmu->transfer_ring_base, + &ipa_res->rx_rdy_ring->sgtable, + sizeof(sgtable_t)); + info_smmu->transfer_ring_size = + ipa_res->rx_rdy_ring->mem_info.size; + info_smmu->transfer_ring_doorbell_pa = + ipa_res->rx_proc_done_idx->mem_info.pa; + + qdf_mem_copy(&info_smmu->event_ring_base, + &ipa_res->rx2_rdy_ring->sgtable, + sizeof(sgtable_t)); + info_smmu->event_ring_size = + ipa_res->rx2_rdy_ring->mem_info.size; + info_smmu->event_ring_doorbell_pa = + ipa_res->rx2_proc_done_idx->mem_info.pa; + } else { + /* TX */ + info = &in->u_tx.tx; + + info->client = IPA_CLIENT_WLAN1_CONS; + + info->transfer_ring_base_pa = qdf_mem_get_dma_addr(osdev, + &ipa_res->tx_comp_ring->mem_info); + info->transfer_ring_size = + ipa_res->tx_comp_ring->mem_info.size; + + info->event_ring_base_pa = qdf_mem_get_dma_addr(osdev, + &ipa_res->ce_sr->mem_info); + info->event_ring_size = ipa_res->ce_sr_ring_size; + info->event_ring_doorbell_pa = ipa_res->ce_reg_paddr; + info->num_pkt_buffers = ipa_res->tx_num_alloc_buffer; + + /* RX */ + info = &in->u_rx.rx; + + info->client = IPA_CLIENT_WLAN1_PROD; + + info->transfer_ring_base_pa = + ipa_res->rx_rdy_ring->mem_info.pa; + info->transfer_ring_size = + ipa_res->rx_rdy_ring->mem_info.size; + info->transfer_ring_doorbell_pa = + ipa_res->rx_proc_done_idx->mem_info.pa; + + if (hdd_ipa->wdi_version == IPA_WDI_2) { + info->event_ring_base_pa = qdf_mem_get_dma_addr(osdev, + &ipa_res->rx2_rdy_ring->mem_info); + info->event_ring_size = + ipa_res->rx2_rdy_ring->mem_info.size; + info->event_ring_doorbell_pa = + qdf_mem_get_dma_addr(osdev, + &ipa_res->rx2_proc_done_idx->mem_info); + } + } + + ret = ipa_wdi_conn_pipes(in, &out); + if (ret) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "ipa_wdi_conn_pipes failed ret=%d", ret); + qdf_mem_free(in); + return ret; + } + + HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, + "out.tx_uc_db_pa 0x%x out.rx_uc_db_pa 0x%x", + out.tx_uc_db_pa, out.rx_uc_db_pa); + + hdd_ipa->tx_comp_doorbell_dmaaddr = out.tx_uc_db_pa; + hdd_ipa->rx_ready_doorbell_dmaaddr = out.rx_uc_db_pa; + + qdf_mem_free(in); + + return 0; +} + +static int hdd_ipa_wdi_disconn_pipes(struct hdd_ipa_priv *hdd_ipa) +{ + int ret; + + ret = ipa_wdi_disconn_pipes(); + if (ret) + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "ipa_wdi_disconn_pipes failed ret=%d", ret); + return ret; +} + +static int hdd_ipa_wdi_reg_intf(struct hdd_ipa_priv *hdd_ipa, + struct hdd_ipa_iface_context *iface_context) +{ + hdd_adapter_t *adapter = iface_context->adapter; + struct ipa_wdi_reg_intf_in_params in; + struct hdd_ipa_uc_tx_hdr uc_tx_hdr; + struct hdd_ipa_uc_tx_hdr uc_tx_hdr_v6; + int ret; + + memcpy(&uc_tx_hdr, &ipa_uc_tx_hdr, HDD_IPA_UC_WLAN_TX_HDR_LEN); + memcpy(&uc_tx_hdr.eth.h_source, adapter->dev->dev_addr, ETH_ALEN); + uc_tx_hdr.ipa_hd.vdev_id = iface_context->adapter->sessionId; + + in.netdev_name = adapter->dev->name; + in.hdr_info[IPA_IP_v4].hdr = (u8 *)&uc_tx_hdr; + in.hdr_info[IPA_IP_v4].hdr_len = HDD_IPA_UC_WLAN_TX_HDR_LEN; + in.hdr_info[IPA_IP_v4].dst_mac_addr_offset = + HDD_IPA_UC_WLAN_HDR_DES_MAC_OFFSET; + in.hdr_info[IPA_IP_v4].hdr_type = IPA_HDR_L2_ETHERNET_II; + + if (hdd_ipa_is_ipv6_enabled(hdd_ipa->hdd_ctx)) { + memcpy(&uc_tx_hdr_v6, &ipa_uc_tx_hdr, + HDD_IPA_UC_WLAN_TX_HDR_LEN); + memcpy(&uc_tx_hdr_v6.eth.h_source, adapter->dev->dev_addr, + ETH_ALEN); + uc_tx_hdr_v6.ipa_hd.vdev_id = iface_context->adapter->sessionId; + uc_tx_hdr_v6.eth.h_proto = cpu_to_be16(ETH_P_IPV6); + + in.netdev_name = adapter->dev->name; + in.hdr_info[IPA_IP_v6].hdr = (u8 *)&uc_tx_hdr_v6; + in.hdr_info[IPA_IP_v6].hdr_len = HDD_IPA_UC_WLAN_TX_HDR_LEN; + in.hdr_info[IPA_IP_v6].dst_mac_addr_offset = + HDD_IPA_UC_WLAN_HDR_DES_MAC_OFFSET; + in.hdr_info[IPA_IP_v6].hdr_type = IPA_HDR_L2_ETHERNET_II; + } + + in.alt_dst_pipe = iface_context->cons_client; + in.is_meta_data_valid = 1; + in.meta_data = htonl(iface_context->adapter->sessionId << 16); + in.meta_data_mask = htonl(0x00FF0000); + + ret = ipa_wdi_reg_intf(&in); + if (ret) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "ipa_wdi_reg_intf failed ret=%d", ret); + return ret; + } + + return 0; +} + +static int hdd_ipa_wdi_dereg_intf(struct hdd_ipa_priv *hdd_ipa, + const char *devname) +{ + int ret; + + ret = ipa_wdi_dereg_intf(devname); + if (ret) + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "ipa_wdi_dereg_intf failed ret=%d", ret); + return ret; +} + +static int hdd_ipa_wdi_enable_pipes(struct hdd_ipa_priv *hdd_ipa) +{ + int ret; + + ret = ipa_wdi_enable_pipes(); + if (ret) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "ipa_wdi_enable_pipes failed ret=%d", ret); + return ret; + } + + return 0; +} + +static int hdd_ipa_wdi_disable_pipes(struct hdd_ipa_priv *hdd_ipa) +{ + int ret; + + ret = ipa_wdi_disable_pipes(); + if (ret) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "ipa_wdi_disable_pipes failed ret=%d", ret); + return ret; + } + + return 0; +} + +static inline int hdd_ipa_wdi_setup_sys_pipe(struct hdd_ipa_priv *hdd_ipa, + struct ipa_sys_connect_params *sys, uint32_t *handle) +{ + return 0; +} + +static inline int hdd_ipa_wdi_teardown_sys_pipe(struct hdd_ipa_priv *hdd_ipa, + uint32_t handle) +{ + return 0; +} + +static inline int hdd_ipa_wdi_rm_set_perf_profile(struct hdd_ipa_priv *hdd_ipa, + enum ipa_rm_resource_name resource_name, + struct ipa_rm_perf_profile *profile) +{ + return 0; +} + +static inline int hdd_ipa_wdi_rm_request_resource(struct hdd_ipa_priv *hdd_ipa, + enum ipa_rm_resource_name res_name) +{ + return 0; +} + +static inline int hdd_ipa_wdi_rm_release_resource(struct hdd_ipa_priv *hdd_ipa, + enum ipa_rm_resource_name res_name) +{ + return 0; +} + +static inline int hdd_ipa_wdi_setup_rm(struct hdd_ipa_priv *hdd_ipa) +{ + return 0; +} + +static inline int hdd_ipa_wdi_destroy_rm(struct hdd_ipa_priv *hdd_ipa) +{ + return 0; +} + +static inline int hdd_ipa_wdi_rm_request(struct hdd_ipa_priv *hdd_ipa) +{ + return 0; +} + +static inline int hdd_ipa_wdi_rm_try_release(struct hdd_ipa_priv *hdd_ipa) +{ + return 0; +} + +static inline int hdd_ipa_wdi_rm_notify_completion(enum ipa_rm_event event, + enum ipa_rm_resource_name resource_name) +{ + return 0; +} +#else /* CONFIG_IPA_WDI_UNIFIED_API */ +static inline void hdd_ipa_wdi_get_wdi_version(struct hdd_ipa_priv *hdd_ipa) +{ +} + +static int hdd_ipa_wdi_is_smmu_enabled(struct hdd_ipa_priv *hdd_ipa, + qdf_device_t osdev) +{ + return qdf_mem_smmu_s1_enabled(osdev); +} + +#ifdef FEATURE_METERING +static void hdd_ipa_wdi_init_metering(struct hdd_ipa_priv *ipa_ctxt, void *in) +{ + struct ipa_wdi_in_params *wdi_in; + + wdi_in = (struct ipa_wdi_in_params *)in; + wdi_in->wdi_notify = hdd_ipa_wdi_meter_notifier_cb; + + init_completion(&ipa_ctxt->ipa_uc_sharing_stats_comp); + init_completion(&ipa_ctxt->ipa_uc_set_quota_comp); +} +#endif + +static int hdd_ipa_wdi_init(struct hdd_ipa_priv *hdd_ipa) +{ + struct ipa_wdi_uc_ready_params uc_ready_param; + int ret = 0; + + hdd_ipa->uc_loaded = false; + uc_ready_param.priv = (void *)hdd_ipa; + uc_ready_param.notify = hdd_ipa_uc_loaded_uc_cb; + if (ipa_uc_reg_rdyCB(&uc_ready_param)) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "UC Ready CB register fail"); + return -EPERM; + } + + if (true == uc_ready_param.is_uC_ready) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "UC Ready"); + hdd_ipa->uc_loaded = true; + } else { + ret = -EACCES; + } + + return ret; +} + +static int hdd_ipa_wdi_cleanup(void) +{ + int ret; + + ret = ipa_uc_dereg_rdyCB(); + if (ret) + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "UC Ready CB deregister fail"); + return ret; +} + +static int hdd_ipa_wdi_conn_pipes(struct hdd_ipa_priv *hdd_ipa, + struct ol_txrx_ipa_resources *ipa_res) +{ + hdd_context_t *hdd_ctx = (hdd_context_t *)hdd_ipa->hdd_ctx; + struct ipa_wdi_in_params pipe_in; + struct ipa_wdi_out_params pipe_out; + QDF_STATUS stat = QDF_STATUS_SUCCESS; + qdf_device_t osdev = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + int ret; + + qdf_mem_zero(&hdd_ipa->cons_pipe_in, sizeof(struct ipa_wdi_in_params)); + qdf_mem_zero(&hdd_ipa->prod_pipe_in, sizeof(struct ipa_wdi_in_params)); + qdf_mem_zero(&pipe_in, sizeof(struct ipa_wdi_in_params)); + qdf_mem_zero(&pipe_out, sizeof(struct ipa_wdi_out_params)); + + /* TX PIPE */ + pipe_in.sys.ipa_ep_cfg.nat.nat_en = IPA_BYPASS_NAT; + pipe_in.sys.ipa_ep_cfg.hdr.hdr_len = HDD_IPA_UC_WLAN_TX_HDR_LEN; + pipe_in.sys.ipa_ep_cfg.hdr.hdr_ofst_pkt_size_valid = 1; + pipe_in.sys.ipa_ep_cfg.hdr.hdr_ofst_pkt_size = 0; + pipe_in.sys.ipa_ep_cfg.hdr.hdr_additional_const_len = + HDD_IPA_UC_WLAN_8023_HDR_SIZE; + pipe_in.sys.ipa_ep_cfg.mode.mode = IPA_BASIC; + pipe_in.sys.client = IPA_CLIENT_WLAN1_CONS; + pipe_in.sys.desc_fifo_sz = hdd_ctx->config->IpaDescSize; + pipe_in.sys.priv = hdd_ctx->hdd_ipa; + pipe_in.sys.ipa_ep_cfg.hdr_ext.hdr_little_endian = true; + pipe_in.sys.notify = hdd_ipa_i2w_cb; + if (!hdd_ipa_is_rm_enabled(hdd_ctx)) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, + "IPA RM DISABLED, IPA AWAKE"); + pipe_in.sys.keep_ipa_awake = true; + } + + pipe_in.smmu_enabled = hdd_ipa_wdi_is_smmu_enabled(hdd_ipa, osdev); + if (pipe_in.smmu_enabled) { + qdf_mem_copy(&pipe_in.u.dl_smmu.comp_ring, + &ipa_res->tx_comp_ring->sgtable, + sizeof(sgtable_t)); + + qdf_mem_copy(&pipe_in.u.dl_smmu.ce_ring, + &ipa_res->ce_sr->sgtable, + sizeof(sgtable_t)); + pipe_in.u.dl_smmu.comp_ring_size = + ipa_res->tx_comp_ring->mem_info.size; + pipe_in.u.dl_smmu.ce_ring_size = + ipa_res->ce_sr_ring_size; + pipe_in.u.dl_smmu.ce_door_bell_pa = + ipa_res->ce_reg_paddr; + pipe_in.u.dl_smmu.num_tx_buffers = + ipa_res->tx_num_alloc_buffer; + } else { + pipe_in.u.dl.comp_ring_base_pa = + qdf_mem_get_dma_addr(osdev, + &ipa_res->tx_comp_ring->mem_info); + pipe_in.u.dl.ce_ring_base_pa = + qdf_mem_get_dma_addr(osdev, + &ipa_res->ce_sr->mem_info); + pipe_in.u.dl.comp_ring_size = + ipa_res->tx_comp_ring->mem_info.size; + pipe_in.u.dl.ce_door_bell_pa = ipa_res->ce_reg_paddr; + pipe_in.u.dl.ce_ring_size = + ipa_res->ce_sr_ring_size; + pipe_in.u.dl.num_tx_buffers = + ipa_res->tx_num_alloc_buffer; + } + + qdf_mem_copy(&hdd_ipa->cons_pipe_in, &pipe_in, + sizeof(struct ipa_wdi_in_params)); + + /* Connect WDI IPA PIPE */ + ret = ipa_connect_wdi_pipe(&hdd_ipa->cons_pipe_in, &pipe_out); + if (ret) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "ipa_connect_wdi_pipe failed for Tx: ret=%d", + ret); + stat = QDF_STATUS_E_FAILURE; + goto fail_return; + } + + /* Micro Controller Doorbell register */ + hdd_ipa->tx_comp_doorbell_dmaaddr = pipe_out.uc_door_bell_pa; + + /* WLAN TX PIPE Handle */ + hdd_ipa->tx_pipe_handle = pipe_out.clnt_hdl; + + if (hdd_ipa->tx_pipe_handle == 0) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "TX Handle zero"); + QDF_BUG(0); + } + + HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, + "CONS DB pipe out 0x%x TX PIPE Handle 0x%x", + (unsigned int)pipe_out.uc_door_bell_pa, + hdd_ipa->tx_pipe_handle); + HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, + "TX : CRBPA 0x%x, CRS %d, CERBPA 0x%x, CEDPA 0x%x," + " CERZ %d, NB %d, CDBPAD 0x%x", + (unsigned int)pipe_in.u.dl.comp_ring_base_pa, + pipe_in.u.dl.comp_ring_size, + (unsigned int)pipe_in.u.dl.ce_ring_base_pa, + (unsigned int)pipe_in.u.dl.ce_door_bell_pa, + pipe_in.u.dl.ce_ring_size, + pipe_in.u.dl.num_tx_buffers, + (unsigned int)hdd_ipa->tx_comp_doorbell_dmaaddr); + + /* RX PIPE */ + pipe_in.sys.ipa_ep_cfg.nat.nat_en = IPA_BYPASS_NAT; + pipe_in.sys.ipa_ep_cfg.hdr.hdr_len = HDD_IPA_UC_WLAN_RX_HDR_LEN; + pipe_in.sys.ipa_ep_cfg.hdr.hdr_ofst_metadata_valid = 0; + pipe_in.sys.ipa_ep_cfg.hdr.hdr_metadata_reg_valid = 1; + pipe_in.sys.ipa_ep_cfg.mode.mode = IPA_BASIC; + pipe_in.sys.client = IPA_CLIENT_WLAN1_PROD; + pipe_in.sys.desc_fifo_sz = hdd_ctx->config->IpaDescSize + + sizeof(struct sps_iovec); + pipe_in.sys.notify = hdd_ipa_w2i_cb; + if (!hdd_ipa_is_rm_enabled(hdd_ctx)) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, + "IPA RM DISABLED, IPA AWAKE"); + pipe_in.sys.keep_ipa_awake = true; + } + + pipe_in.smmu_enabled = hdd_ipa_wdi_is_smmu_enabled(hdd_ipa, osdev); + if (pipe_in.smmu_enabled) { + qdf_mem_copy(&pipe_in.u.ul_smmu.rdy_ring, + &ipa_res->rx_rdy_ring->sgtable, + sizeof(sgtable_t)); + pipe_in.u.ul_smmu.rdy_ring_size = + ipa_res->rx_rdy_ring->mem_info.size; + pipe_in.u.ul_smmu.rdy_ring_rp_pa = + ipa_res->rx_proc_done_idx->mem_info.pa; + + pipe_in.u.ul_smmu.rdy_ring_rp_va = + ipa_res->rx_proc_done_idx->vaddr; + + qdf_mem_copy(&pipe_in.u.ul_smmu.rdy_comp_ring, + &ipa_res->rx2_rdy_ring->sgtable, + sizeof(sgtable_t)); + + pipe_in.u.ul_smmu.rdy_comp_ring_size = + ipa_res->rx2_rdy_ring->mem_info.size; + + pipe_in.u.ul_smmu.rdy_comp_ring_wp_pa = + ipa_res->rx2_proc_done_idx->mem_info.pa; + + pipe_in.u.ul_smmu.rdy_comp_ring_wp_va = + ipa_res->rx2_proc_done_idx->vaddr; + } else { + pipe_in.u.ul.rdy_ring_base_pa = + ipa_res->rx_rdy_ring->mem_info.pa; + pipe_in.u.ul.rdy_ring_size = + ipa_res->rx_rdy_ring->mem_info.size; + pipe_in.u.ul.rdy_ring_rp_pa = + ipa_res->rx_proc_done_idx->mem_info.pa; + HDD_IPA_WDI2_SET(pipe_in, hdd_ipa, osdev); + } + + hdd_ipa_wdi_init_metering(hdd_ipa, (void *)&pipe_in); + + qdf_mem_copy(&hdd_ipa->prod_pipe_in, &pipe_in, + sizeof(struct ipa_wdi_in_params)); + + ret = ipa_connect_wdi_pipe(&hdd_ipa->prod_pipe_in, &pipe_out); + if (ret) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "ipa_connect_wdi_pipe failed for Rx: ret=%d", + ret); + stat = QDF_STATUS_E_FAILURE; + ret = ipa_disconnect_wdi_pipe(hdd_ipa->tx_pipe_handle); + if (ret) + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "disconnect failed for TX: ret=%d", + ret); + goto fail_return; + } + hdd_ipa->rx_ready_doorbell_dmaaddr = pipe_out.uc_door_bell_pa; + hdd_ipa->rx_pipe_handle = pipe_out.clnt_hdl; + if (hdd_ipa->rx_pipe_handle == 0) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "RX Handle zero"); + QDF_BUG(0); + } + + HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, + "PROD DB pipe out 0x%x RX PIPE Handle 0x%x", + (unsigned int)pipe_out.uc_door_bell_pa, + hdd_ipa->rx_pipe_handle); + HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, + "RX : RRBPA 0x%x, RRS %d, PDIPA 0x%x, RDY_DB_PAD 0x%x", + (unsigned int)pipe_in.u.ul.rdy_ring_base_pa, + pipe_in.u.ul.rdy_ring_size, + (unsigned int)pipe_in.u.ul.rdy_ring_rp_pa, + (unsigned int)hdd_ipa->rx_ready_doorbell_dmaaddr); + +fail_return: + HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "exit: stat=%d", stat); + return stat; +} + +static int hdd_ipa_wdi_disconn_pipes(struct hdd_ipa_priv *hdd_ipa) +{ + int ret; + + HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, + "Disconnect TX PIPE tx_pipe_handle=0x%x", + hdd_ipa->tx_pipe_handle); + ret = ipa_disconnect_wdi_pipe(hdd_ipa->tx_pipe_handle); + HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, + "Disconnect RX PIPE rx_pipe_handle=0x%x", + hdd_ipa->rx_pipe_handle); + ret = ipa_disconnect_wdi_pipe(hdd_ipa->rx_pipe_handle); + + return ret; +} + +/** + * hdd_remove_ipa_header() - Remove a specific header from IPA + * @name: Name of the header to be removed + * + * Return: None + */ +static void hdd_ipa_remove_header(char *name) +{ + struct ipa_ioc_get_hdr hdrlookup; + int ret = 0, len; + struct ipa_ioc_del_hdr *ipa_hdr; + + qdf_mem_zero(&hdrlookup, sizeof(hdrlookup)); + strlcpy(hdrlookup.name, name, sizeof(hdrlookup.name)); + ret = ipa_get_hdr(&hdrlookup); + if (ret) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "Hdr deleted already %s, %d", + name, ret); + return; + } + + HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "hdl: 0x%x", hdrlookup.hdl); + len = sizeof(struct ipa_ioc_del_hdr) + sizeof(struct ipa_hdr_del) * 1; + ipa_hdr = (struct ipa_ioc_del_hdr *)qdf_mem_malloc(len); + if (ipa_hdr == NULL) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "ipa_hdr allocation failed"); + return; + } + ipa_hdr->num_hdls = 1; + ipa_hdr->commit = 0; + ipa_hdr->hdl[0].hdl = hdrlookup.hdl; + ipa_hdr->hdl[0].status = -1; + ret = ipa_del_hdr(ipa_hdr); + if (ret != 0) + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "Delete header failed: %d", + ret); + + qdf_mem_free(ipa_hdr); +} + +/** + * wlan_ipa_add_hdr() - Add IPA Tx header + * @ipa_hdr: pointer to IPA header addition parameters + * + * Call IPA API to add IPA Tx header descriptor + * and dump Tx header struct + * + * Return: 0 for success, non-zero for failure + */ +static int wlan_ipa_add_hdr(struct ipa_ioc_add_hdr *ipa_hdr) +{ + int ret; + + hdd_debug("==== IPA Tx Header ====\n" + "name: %s\n" + "hdr_len: %d\n" + "type: %d\n" + "is_partial: %d\n" + "hdr_hdl: 0x%x\n" + "status: %d\n" + "is_eth2_ofst_valid: %d\n" + "eth2_ofst: %d\n", + ipa_hdr->hdr[0].name, + ipa_hdr->hdr[0].hdr_len, + ipa_hdr->hdr[0].type, + ipa_hdr->hdr[0].is_partial, + ipa_hdr->hdr[0].hdr_hdl, + ipa_hdr->hdr[0].status, + ipa_hdr->hdr[0].is_eth2_ofst_valid, + ipa_hdr->hdr[0].eth2_ofst); + + HDD_IPA_DBG_DUMP(QDF_TRACE_LEVEL_DEBUG, "hdr:", + ipa_hdr->hdr[0].hdr, HDD_IPA_UC_WLAN_TX_HDR_LEN); + + ret = ipa_add_hdr(ipa_hdr); + return ret; +} + +/* For Tx pipes, use 802.3 Header format */ +static struct hdd_ipa_tx_hdr ipa_tx_hdr = { + { + {0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xFF}, + {0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xFF}, + 0x00 /* length can be zero */ + }, + { + /* LLC SNAP header 8 bytes */ + 0xaa, 0xaa, + {0x03, 0x00, 0x00, 0x00}, + 0x0008 /* type value(2 bytes) ,filled by wlan */ + /* 0x0800 - IPV4, 0x86dd - IPV6 */ + } +}; + +/** + * hdd_ipa_add_header_info() - Add IPA header for a given interface + * @hdd_ipa: Global HDD IPA context + * @iface_context: Interface-specific HDD IPA context + * @mac_addr: Interface MAC address + * + * Return: 0 on success, negativer errno value on error + */ +static int hdd_ipa_add_header_info(struct hdd_ipa_priv *hdd_ipa, + struct hdd_ipa_iface_context *iface_context, + uint8_t *mac_addr) +{ + hdd_adapter_t *adapter = iface_context->adapter; + char *ifname; + struct ipa_ioc_add_hdr *ipa_hdr = NULL; + int ret = -EINVAL; + struct hdd_ipa_tx_hdr *tx_hdr = NULL; + struct hdd_ipa_uc_tx_hdr *uc_tx_hdr = NULL; + + ifname = adapter->dev->name; + + HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "Add Partial hdr: %s, %pM", + ifname, mac_addr); + + /* dynamically allocate the memory to add the hdrs */ + ipa_hdr = qdf_mem_malloc(sizeof(struct ipa_ioc_add_hdr) + + sizeof(struct ipa_hdr_add)); + if (!ipa_hdr) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "%s: ipa_hdr allocation failed", ifname); + ret = -ENOMEM; + goto end; + } + + ipa_hdr->commit = 0; + ipa_hdr->num_hdrs = 1; + + if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) { + uc_tx_hdr = (struct hdd_ipa_uc_tx_hdr *)ipa_hdr->hdr[0].hdr; + memcpy(uc_tx_hdr, &ipa_uc_tx_hdr, HDD_IPA_UC_WLAN_TX_HDR_LEN); + memcpy(uc_tx_hdr->eth.h_source, mac_addr, ETH_ALEN); + uc_tx_hdr->ipa_hd.vdev_id = iface_context->adapter->sessionId; + HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, + "ifname=%s, vdev_id=%d", + ifname, uc_tx_hdr->ipa_hd.vdev_id); + snprintf(ipa_hdr->hdr[0].name, IPA_RESOURCE_NAME_MAX, "%s%s", + ifname, HDD_IPA_IPV4_NAME_EXT); + ipa_hdr->hdr[0].hdr_len = HDD_IPA_UC_WLAN_TX_HDR_LEN; + ipa_hdr->hdr[0].type = IPA_HDR_L2_ETHERNET_II; + ipa_hdr->hdr[0].is_partial = 1; + ipa_hdr->hdr[0].hdr_hdl = 0; + ipa_hdr->hdr[0].is_eth2_ofst_valid = 1; + ipa_hdr->hdr[0].eth2_ofst = HDD_IPA_UC_WLAN_HDR_DES_MAC_OFFSET; + + ret = wlan_ipa_add_hdr(ipa_hdr); + } else { + tx_hdr = (struct hdd_ipa_tx_hdr *)ipa_hdr->hdr[0].hdr; + + /* Set the Source MAC */ + memcpy(tx_hdr, &ipa_tx_hdr, HDD_IPA_WLAN_TX_HDR_LEN); + memcpy(tx_hdr->eth.h_source, mac_addr, ETH_ALEN); + + snprintf(ipa_hdr->hdr[0].name, IPA_RESOURCE_NAME_MAX, "%s%s", + ifname, HDD_IPA_IPV4_NAME_EXT); + ipa_hdr->hdr[0].hdr_len = HDD_IPA_WLAN_TX_HDR_LEN; + ipa_hdr->hdr[0].is_partial = 1; + ipa_hdr->hdr[0].hdr_hdl = 0; + ipa_hdr->hdr[0].is_eth2_ofst_valid = 1; + ipa_hdr->hdr[0].eth2_ofst = HDD_IPA_WLAN_HDR_DES_MAC_OFFSET; + + /* Set the type to IPV4 in the header */ + tx_hdr->llc_snap.eth_type = cpu_to_be16(ETH_P_IP); + + ret = ipa_add_hdr(ipa_hdr); + } + if (ret) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "%s: IPv4 add hdr failed: %d", ifname, ret); + goto end; + } + + HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "%s: IPv4 hdr_hdl: 0x%x", + ipa_hdr->hdr[0].name, ipa_hdr->hdr[0].hdr_hdl); + + if (hdd_ipa_is_ipv6_enabled(hdd_ipa->hdd_ctx)) { + snprintf(ipa_hdr->hdr[0].name, IPA_RESOURCE_NAME_MAX, "%s%s", + ifname, HDD_IPA_IPV6_NAME_EXT); + + if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) { + uc_tx_hdr = + (struct hdd_ipa_uc_tx_hdr *)ipa_hdr->hdr[0].hdr; + uc_tx_hdr->eth.h_proto = cpu_to_be16(ETH_P_IPV6); + ret = wlan_ipa_add_hdr(ipa_hdr); + } else { + /* Set the type to IPV6 in the header */ + tx_hdr = (struct hdd_ipa_tx_hdr *)ipa_hdr->hdr[0].hdr; + tx_hdr->llc_snap.eth_type = cpu_to_be16(ETH_P_IPV6); + ret = ipa_add_hdr(ipa_hdr); + } + + if (ret) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "%s: IPv6 add hdr failed: %d", ifname, ret); + goto clean_ipv4_hdr; + } + + HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "%s: IPv6 hdr_hdl: 0x%x", + ipa_hdr->hdr[0].name, ipa_hdr->hdr[0].hdr_hdl); + } + + qdf_mem_free(ipa_hdr); + + return ret; + +clean_ipv4_hdr: + snprintf(ipa_hdr->hdr[0].name, IPA_RESOURCE_NAME_MAX, "%s%s", + ifname, HDD_IPA_IPV4_NAME_EXT); + hdd_ipa_remove_header(ipa_hdr->hdr[0].name); +end: + if (ipa_hdr) + qdf_mem_free(ipa_hdr); + + return ret; +} + +/** + * hdd_ipa_register_interface() - register IPA interface + * @hdd_ipa: Global IPA context + * @iface_context: Per-interface IPA context + * + * Return: 0 on success, negative errno on error + */ +static int hdd_ipa_register_interface(struct hdd_ipa_priv *hdd_ipa, + struct hdd_ipa_iface_context + *iface_context) +{ + struct ipa_tx_intf tx_intf; + struct ipa_rx_intf rx_intf; + struct ipa_ioc_tx_intf_prop *tx_prop = NULL; + struct ipa_ioc_rx_intf_prop *rx_prop = NULL; + char *ifname = iface_context->adapter->dev->name; + + char ipv4_hdr_name[IPA_RESOURCE_NAME_MAX]; + char ipv6_hdr_name[IPA_RESOURCE_NAME_MAX]; + + int num_prop = 1; + int ret = 0; + + if (hdd_ipa_is_ipv6_enabled(hdd_ipa->hdd_ctx)) + num_prop++; + + /* Allocate TX properties for TOS categories, 1 each for IPv4 & IPv6 */ + tx_prop = + qdf_mem_malloc(sizeof(struct ipa_ioc_tx_intf_prop) * num_prop); + if (!tx_prop) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "tx_prop allocation failed"); + goto register_interface_fail; + } + + /* Allocate RX properties, 1 each for IPv4 & IPv6 */ + rx_prop = + qdf_mem_malloc(sizeof(struct ipa_ioc_rx_intf_prop) * num_prop); + if (!rx_prop) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "rx_prop allocation failed"); + goto register_interface_fail; + } + + qdf_mem_zero(&tx_intf, sizeof(tx_intf)); + qdf_mem_zero(&rx_intf, sizeof(rx_intf)); + + snprintf(ipv4_hdr_name, IPA_RESOURCE_NAME_MAX, "%s%s", + ifname, HDD_IPA_IPV4_NAME_EXT); + snprintf(ipv6_hdr_name, IPA_RESOURCE_NAME_MAX, "%s%s", + ifname, HDD_IPA_IPV6_NAME_EXT); + + rx_prop[IPA_IP_v4].ip = IPA_IP_v4; + rx_prop[IPA_IP_v4].src_pipe = iface_context->prod_client; + rx_prop[IPA_IP_v4].hdr_l2_type = IPA_HDR_L2_ETHERNET_II; + rx_prop[IPA_IP_v4].attrib.attrib_mask = IPA_FLT_META_DATA; + + /* + * Interface ID is 3rd byte in the CLD header. Add the meta data and + * mask to identify the interface in IPA hardware + */ + rx_prop[IPA_IP_v4].attrib.meta_data = + htonl(iface_context->adapter->sessionId << 16); + rx_prop[IPA_IP_v4].attrib.meta_data_mask = htonl(0x00FF0000); + + rx_intf.num_props++; + if (hdd_ipa_is_ipv6_enabled(hdd_ipa->hdd_ctx)) { + rx_prop[IPA_IP_v6].ip = IPA_IP_v6; + rx_prop[IPA_IP_v6].src_pipe = iface_context->prod_client; + rx_prop[IPA_IP_v6].hdr_l2_type = IPA_HDR_L2_ETHERNET_II; + rx_prop[IPA_IP_v4].attrib.attrib_mask = IPA_FLT_META_DATA; + rx_prop[IPA_IP_v4].attrib.meta_data = + htonl(iface_context->adapter->sessionId << 16); + rx_prop[IPA_IP_v4].attrib.meta_data_mask = htonl(0x00FF0000); + + rx_intf.num_props++; + } + + tx_prop[IPA_IP_v4].ip = IPA_IP_v4; + tx_prop[IPA_IP_v4].hdr_l2_type = IPA_HDR_L2_ETHERNET_II; + tx_prop[IPA_IP_v4].dst_pipe = IPA_CLIENT_WLAN1_CONS; + tx_prop[IPA_IP_v4].alt_dst_pipe = iface_context->cons_client; + strlcpy(tx_prop[IPA_IP_v4].hdr_name, ipv4_hdr_name, + IPA_RESOURCE_NAME_MAX); + tx_intf.num_props++; + + if (hdd_ipa_is_ipv6_enabled(hdd_ipa->hdd_ctx)) { + tx_prop[IPA_IP_v6].ip = IPA_IP_v6; + tx_prop[IPA_IP_v6].hdr_l2_type = IPA_HDR_L2_ETHERNET_II; + tx_prop[IPA_IP_v6].dst_pipe = IPA_CLIENT_WLAN1_CONS; + tx_prop[IPA_IP_v6].alt_dst_pipe = iface_context->cons_client; + strlcpy(tx_prop[IPA_IP_v6].hdr_name, ipv6_hdr_name, + IPA_RESOURCE_NAME_MAX); + tx_intf.num_props++; + } + + tx_intf.prop = tx_prop; + rx_intf.prop = rx_prop; + + /* Call the ipa api to register interface */ + ret = ipa_register_intf(ifname, &tx_intf, &rx_intf); + +register_interface_fail: + qdf_mem_free(tx_prop); + qdf_mem_free(rx_prop); + return ret; +} + +static int hdd_ipa_wdi_reg_intf(struct hdd_ipa_priv *hdd_ipa, + struct hdd_ipa_iface_context *iface_context) +{ + int ret; + + ret = hdd_ipa_add_header_info(hdd_ipa, iface_context, + iface_context->adapter->dev->dev_addr); + + if (ret) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "ipa add header failed ret=%d", ret); + return ret; + } + + /* Configure the TX and RX pipes filter rules */ + ret = hdd_ipa_register_interface(hdd_ipa, iface_context); + if (ret) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "ipa register interface failed ret=%d", ret); + return ret; + } + + return 0; +} + +static int hdd_ipa_wdi_dereg_intf(struct hdd_ipa_priv *hdd_ipa, + const char *devname) +{ + char name_ipa[IPA_RESOURCE_NAME_MAX]; + int ret; + + /* Remove the headers */ + snprintf(name_ipa, IPA_RESOURCE_NAME_MAX, "%s%s", devname, + HDD_IPA_IPV4_NAME_EXT); + hdd_ipa_remove_header(name_ipa); + + if (hdd_ipa_is_ipv6_enabled(hdd_ipa->hdd_ctx)) { + snprintf(name_ipa, IPA_RESOURCE_NAME_MAX, "%s%s", devname, + HDD_IPA_IPV6_NAME_EXT); + hdd_ipa_remove_header(name_ipa); + } + + /* unregister the interface with IPA */ + ret = ipa_deregister_intf(devname); + if (ret) + HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, + "%s: ipa_deregister_intf fail: %d", devname, ret); + return ret; +} + +static int hdd_ipa_wdi_enable_pipes(struct hdd_ipa_priv *hdd_ipa) +{ + int result; + + /* ACTIVATE TX PIPE */ + HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, + "Enable TX PIPE(tx_pipe_handle=%d)", + hdd_ipa->tx_pipe_handle); + result = ipa_enable_wdi_pipe(hdd_ipa->tx_pipe_handle); + if (result) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "Enable TX PIPE fail, code %d", + result); + return result; + } + + result = ipa_resume_wdi_pipe(hdd_ipa->tx_pipe_handle); + if (result) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "Resume TX PIPE fail, code %d", + result); + return result; + } + + /* ACTIVATE RX PIPE */ + HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, + "Enable RX PIPE(rx_pipe_handle=%d)", + hdd_ipa->rx_pipe_handle); + result = ipa_enable_wdi_pipe(hdd_ipa->rx_pipe_handle); + if (result) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "Enable RX PIPE fail, code %d", + result); + return result; + } + + result = ipa_resume_wdi_pipe(hdd_ipa->rx_pipe_handle); + if (result) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "Resume RX PIPE fail, code %d", + result); + return result; + } + + return 0; +} + +static int hdd_ipa_wdi_disable_pipes(struct hdd_ipa_priv *hdd_ipa) +{ + int result; + + HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "Disable RX PIPE"); + result = ipa_suspend_wdi_pipe(hdd_ipa->rx_pipe_handle); + if (result) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "Suspend RX PIPE fail, code %d", result); + return result; + } + + result = ipa_disable_wdi_pipe(hdd_ipa->rx_pipe_handle); + if (result) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "Disable RX PIPE fail, code %d", result); + return result; + } + + HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "Disable TX PIPE"); + result = ipa_suspend_wdi_pipe(hdd_ipa->tx_pipe_handle); + if (result) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "Suspend TX PIPE fail, code %d", result); + return result; + } + + result = ipa_disable_wdi_pipe(hdd_ipa->tx_pipe_handle); + if (result) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "Disable TX PIPE fail, code %d", result); + return result; + } + + return 0; +} + +static int hdd_ipa_wdi_setup_sys_pipe(struct hdd_ipa_priv *hdd_ipa, + struct ipa_sys_connect_params *sys, uint32_t *handle) +{ + return ipa_setup_sys_pipe(sys, handle); +} + +static int hdd_ipa_wdi_teardown_sys_pipe(struct hdd_ipa_priv *hdd_ipa, + uint32_t handle) +{ + return ipa_teardown_sys_pipe(handle); +} + +static int hdd_ipa_wdi_rm_set_perf_profile(struct hdd_ipa_priv *hdd_ipa, + enum ipa_rm_resource_name resource_name, + struct ipa_rm_perf_profile *profile) +{ + return ipa_rm_set_perf_profile(resource_name, profile); +} + +static int hdd_ipa_wdi_rm_request_resource(struct hdd_ipa_priv *hdd_ipa, + enum ipa_rm_resource_name res_name) +{ + return ipa_rm_request_resource(res_name); +} + +static int hdd_ipa_wdi_rm_release_resource(struct hdd_ipa_priv *hdd_ipa, + enum ipa_rm_resource_name res_name) +{ + return ipa_rm_release_resource(res_name); +} + +/** + * hdd_ipa_init_uc_rm_work - init ipa uc resource manager work + * @work: struct work_struct + * @work_handler: work_handler + * + * Return: none + */ +static void hdd_ipa_init_uc_rm_work(struct work_struct *work, + work_func_t work_handler) +{ + INIT_WORK(work, work_handler); +} + +/** + * hdd_ipa_wake_lock_timer_func() - Wake lock work handler + * @work: scheduled work + * + * When IPA resources are released in hdd_ipa_wdi_rm_try_release() we do + * not want to immediately release the wake lock since the system + * would then potentially try to suspend when there is a healthy data + * rate. Deferred work is scheduled and this function handles the + * work. When this function is called, if the IPA resource is still + * released then we release the wake lock. + * + * Return: None + */ +static void hdd_ipa_wake_lock_timer_func(struct work_struct *work) +{ + struct hdd_ipa_priv *hdd_ipa = container_of(to_delayed_work(work), + struct hdd_ipa_priv, + wake_lock_work); + + qdf_spin_lock_bh(&hdd_ipa->rm_lock); + + if (hdd_ipa->rm_state != HDD_IPA_RM_RELEASED) + goto end; + + hdd_ipa->wake_lock_released = true; + qdf_wake_lock_release(&hdd_ipa->wake_lock, + WIFI_POWER_EVENT_WAKELOCK_IPA); + +end: + qdf_spin_unlock_bh(&hdd_ipa->rm_lock); +} + +/** + * hdd_ipa_wdi_rm_request() - Request resource from IPA + * @hdd_ipa: Global HDD IPA context + * + * Return: 0 on success, negative errno on error + */ +static int hdd_ipa_wdi_rm_request(struct hdd_ipa_priv *hdd_ipa) +{ + int ret = 0; + + if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx)) + return 0; + + qdf_spin_lock_bh(&hdd_ipa->rm_lock); + + switch (hdd_ipa->rm_state) { + case HDD_IPA_RM_GRANTED: + qdf_spin_unlock_bh(&hdd_ipa->rm_lock); + return 0; + case HDD_IPA_RM_GRANT_PENDING: + qdf_spin_unlock_bh(&hdd_ipa->rm_lock); + return -EINPROGRESS; + case HDD_IPA_RM_RELEASED: + hdd_ipa->rm_state = HDD_IPA_RM_GRANT_PENDING; + break; + } + + qdf_spin_unlock_bh(&hdd_ipa->rm_lock); + + ret = ipa_rm_inactivity_timer_request_resource( + IPA_RM_RESOURCE_WLAN_PROD); + + qdf_spin_lock_bh(&hdd_ipa->rm_lock); + if (ret == 0) { + hdd_ipa->rm_state = HDD_IPA_RM_GRANTED; + hdd_ipa->stats.num_rm_grant_imm++; + } + + cancel_delayed_work(&hdd_ipa->wake_lock_work); + if (hdd_ipa->wake_lock_released) { + qdf_wake_lock_acquire(&hdd_ipa->wake_lock, + WIFI_POWER_EVENT_WAKELOCK_IPA); + hdd_ipa->wake_lock_released = false; + } + qdf_spin_unlock_bh(&hdd_ipa->rm_lock); + + return ret; +} + +/** + * hdd_ipa_wdi_rm_try_release() - Attempt to release IPA resource + * @hdd_ipa: Global HDD IPA context + * + * Return: 0 if resources released, negative errno otherwise + */ +static int hdd_ipa_wdi_rm_try_release(struct hdd_ipa_priv *hdd_ipa) +{ + int ret = 0; + + if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx)) + return 0; + + if (atomic_read(&hdd_ipa->tx_ref_cnt)) + return -EAGAIN; + + qdf_spin_lock_bh(&hdd_ipa->pm_lock); + + if (!qdf_nbuf_is_queue_empty(&hdd_ipa->pm_queue_head)) { + qdf_spin_unlock_bh(&hdd_ipa->pm_lock); + return -EAGAIN; + } + qdf_spin_unlock_bh(&hdd_ipa->pm_lock); + + qdf_spin_lock_bh(&hdd_ipa->rm_lock); + switch (hdd_ipa->rm_state) { + case HDD_IPA_RM_GRANTED: + break; + case HDD_IPA_RM_GRANT_PENDING: + qdf_spin_unlock_bh(&hdd_ipa->rm_lock); + return -EINPROGRESS; + case HDD_IPA_RM_RELEASED: + qdf_spin_unlock_bh(&hdd_ipa->rm_lock); + return 0; + } + + /* IPA driver returns immediately so set the state here to avoid any + * race condition. + */ + hdd_ipa->rm_state = HDD_IPA_RM_RELEASED; + hdd_ipa->stats.num_rm_release++; + qdf_spin_unlock_bh(&hdd_ipa->rm_lock); + + ret = ipa_rm_inactivity_timer_release_resource( + IPA_RM_RESOURCE_WLAN_PROD); + + qdf_spin_lock_bh(&hdd_ipa->rm_lock); + if (unlikely(ret != 0)) { + hdd_ipa->rm_state = HDD_IPA_RM_GRANTED; + WARN_ON(1); + HDD_IPA_LOG(QDF_TRACE_LEVEL_WARN, + "ipa_rm_inactivity_timer_release_resource returnied fail"); + } + + /* + * If wake_lock is released immediately, kernel would try to suspend + * immediately as well, Just avoid ping-pong between suspend-resume + * while there is healthy amount of data transfer going on by + * releasing the wake_lock after some delay. + */ + schedule_delayed_work(&hdd_ipa->wake_lock_work, + msecs_to_jiffies + (HDD_IPA_RX_INACTIVITY_MSEC_DELAY)); + + qdf_spin_unlock_bh(&hdd_ipa->rm_lock); + + return ret; +} + +/** + * hdd_ipa_rm_notify() - IPA resource manager notifier callback + * @user_data: user data registered with IPA + * @event: the IPA resource manager event that occurred + * @data: the data associated with the event + * + * Return: None + */ +static void hdd_ipa_rm_notify(void *user_data, enum ipa_rm_event event, + unsigned long data) +{ + struct hdd_ipa_priv *hdd_ipa = user_data; + + if (unlikely(!hdd_ipa)) + return; + + if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx)) + return; + + HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "Evt: %d", event); + + switch (event) { + case IPA_RM_RESOURCE_GRANTED: + if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) { + /* RM Notification comes with ISR context + * it should be serialized into work queue to avoid + * ISR sleep problem + */ + hdd_ipa->uc_rm_work.event = event; + schedule_work(&hdd_ipa->uc_rm_work.work); + break; + } + qdf_spin_lock_bh(&hdd_ipa->rm_lock); + hdd_ipa->rm_state = HDD_IPA_RM_GRANTED; + qdf_spin_unlock_bh(&hdd_ipa->rm_lock); + hdd_ipa->stats.num_rm_grant++; + break; + + case IPA_RM_RESOURCE_RELEASED: + HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "RM Release"); + hdd_ipa->resource_unloading = false; + break; + + default: + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "Unknown RM Evt: %d", event); + break; + } +} + +/** + * hdd_ipa_rm_cons_release() - WLAN consumer resource release handler + * + * Callback function registered with IPA that is called when IPA wants + * to release the WLAN consumer resource + * + * Return: 0 if the request is granted, negative errno otherwise + */ +static int hdd_ipa_rm_cons_release(void) +{ + return 0; +} + +/** + * hdd_ipa_rm_cons_request() - WLAN consumer resource request handler + * + * Callback function registered with IPA that is called when IPA wants + * to access the WLAN consumer resource + * + * Return: 0 if the request is granted, negative errno otherwise + */ +static int hdd_ipa_rm_cons_request(void) +{ + int ret = 0; + + if (ghdd_ipa->resource_loading) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_FATAL, + "IPA resource loading in progress"); + ghdd_ipa->pending_cons_req = true; + ret = -EINPROGRESS; + } else if (ghdd_ipa->resource_unloading) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_FATAL, + "IPA resource unloading in progress"); + ghdd_ipa->pending_cons_req = true; + ret = -EPERM; + } + + return ret; +} + +/** + * hdd_ipa_uc_rm_notify_handler() - IPA uC resource notification handler + * @context: User context registered with TL (the IPA Global context is + * registered + * @rxpkt: Packet containing the notification + * @staid: ID of the station associated with the packet + * + * Return: None + */ +static void +hdd_ipa_uc_rm_notify_handler(void *context, enum ipa_rm_event event) +{ + struct hdd_ipa_priv *hdd_ipa = context; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + /* + * When SSR is going on or driver is unloading, just return. + */ + status = wlan_hdd_validate_context(hdd_ipa->hdd_ctx); + if (status) + return; + + if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx)) + return; + + HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "event code %d", + event); + + switch (event) { + case IPA_RM_RESOURCE_GRANTED: + /* Differed RM Granted */ + qdf_mutex_acquire(&hdd_ipa->ipa_lock); + if ((false == hdd_ipa->resource_unloading) && + (!hdd_ipa->activated_fw_pipe)) { + hdd_ipa_uc_enable_pipes(hdd_ipa); + hdd_ipa->resource_loading = false; + } + qdf_mutex_release(&hdd_ipa->ipa_lock); + break; + + case IPA_RM_RESOURCE_RELEASED: + /* Differed RM Released */ + hdd_ipa->resource_unloading = false; + break; + + default: + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "invalid event code %d", event); + break; + } +} + +/** + * hdd_ipa_uc_rm_notify_defer() - Defer IPA uC notification + * @hdd_ipa: Global HDD IPA context + * @event: IPA resource manager event to be deferred + * + * This function is called when a resource manager event is received + * from firmware in interrupt context. This function will defer the + * handling to the OL RX thread + * + * Return: None + */ +static void hdd_ipa_uc_rm_notify_defer(struct work_struct *work) +{ + enum ipa_rm_event event; + struct uc_rm_work_struct *uc_rm_work = container_of(work, + struct uc_rm_work_struct, work); + struct hdd_ipa_priv *hdd_ipa = container_of(uc_rm_work, + struct hdd_ipa_priv, uc_rm_work); + + cds_ssr_protect(__func__); + event = uc_rm_work->event; + HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, + "posted event %d", event); + + hdd_ipa_uc_rm_notify_handler(hdd_ipa, event); + cds_ssr_unprotect(__func__); +} + +/** + * hdd_ipa_wdi_setup_rm() - Setup IPA resource management + * @hdd_ipa: Global HDD IPA context + * + * Return: 0 on success, negative errno on error + */ +static int hdd_ipa_wdi_setup_rm(struct hdd_ipa_priv *hdd_ipa) +{ + struct ipa_rm_create_params create_params = { 0 }; + int ret; + + if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx)) + return 0; + + hdd_ipa_init_uc_rm_work(&hdd_ipa->uc_rm_work.work, + hdd_ipa_uc_rm_notify_defer); + memset(&create_params, 0, sizeof(create_params)); + create_params.name = IPA_RM_RESOURCE_WLAN_PROD; + create_params.reg_params.user_data = hdd_ipa; + create_params.reg_params.notify_cb = hdd_ipa_rm_notify; + create_params.floor_voltage = IPA_VOLTAGE_SVS; + + ret = ipa_rm_create_resource(&create_params); + if (ret) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "Create RM resource failed: %d", ret); + goto setup_rm_fail; + } + + memset(&create_params, 0, sizeof(create_params)); + create_params.name = IPA_RM_RESOURCE_WLAN_CONS; + create_params.request_resource = hdd_ipa_rm_cons_request; + create_params.release_resource = hdd_ipa_rm_cons_release; + create_params.floor_voltage = IPA_VOLTAGE_SVS; + + ret = ipa_rm_create_resource(&create_params); + if (ret) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "Create RM CONS resource failed: %d", ret); + goto delete_prod; + } + + ipa_rm_add_dependency(IPA_RM_RESOURCE_WLAN_PROD, + IPA_RM_RESOURCE_APPS_CONS); + + ret = ipa_rm_inactivity_timer_init(IPA_RM_RESOURCE_WLAN_PROD, + HDD_IPA_RX_INACTIVITY_MSEC_DELAY); + if (ret) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "Timer init failed: %d", + ret); + goto timer_init_failed; + } + + /* Set the lowest bandwidth to start with */ + ret = hdd_ipa_set_perf_level(hdd_ipa->hdd_ctx, 0, 0); + + if (ret) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "Set perf level failed: %d", ret); + goto set_perf_failed; + } + + qdf_wake_lock_create(&hdd_ipa->wake_lock, "wlan_ipa"); + INIT_DELAYED_WORK(&hdd_ipa->wake_lock_work, + hdd_ipa_wake_lock_timer_func); + qdf_spinlock_create(&hdd_ipa->rm_lock); + hdd_ipa->rm_state = HDD_IPA_RM_RELEASED; + hdd_ipa->wake_lock_released = true; + atomic_set(&hdd_ipa->tx_ref_cnt, 0); + + return ret; + +set_perf_failed: + ipa_rm_inactivity_timer_destroy(IPA_RM_RESOURCE_WLAN_PROD); + +timer_init_failed: + ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_CONS); + +delete_prod: + ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_PROD); + +setup_rm_fail: + return ret; +} + +/** + * hdd_ipa_wdi_destroy_rm() - Destroy IPA resources + * @hdd_ipa: Global HDD IPA context + * + * Destroys all resources associated with the IPA resource manager + * + * Return: None + */ +static void hdd_ipa_wdi_destroy_rm(struct hdd_ipa_priv *hdd_ipa) +{ + int ret; + + if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx)) + return; + + cancel_delayed_work_sync(&hdd_ipa->wake_lock_work); + qdf_wake_lock_destroy(&hdd_ipa->wake_lock); + cancel_work_sync(&hdd_ipa->uc_rm_work.work); + qdf_spinlock_destroy(&hdd_ipa->rm_lock); + + ipa_rm_inactivity_timer_destroy(IPA_RM_RESOURCE_WLAN_PROD); + + ret = ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_PROD); + if (ret) + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "RM PROD resource delete failed %d", ret); + + ret = ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_CONS); + if (ret) + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "RM CONS resource delete failed %d", ret); +} + +static int hdd_ipa_wdi_rm_notify_completion(enum ipa_rm_event event, + enum ipa_rm_resource_name resource_name) +{ + return ipa_rm_notify_completion(event, resource_name); +} +#endif /* CONFIG_IPA_WDI_UNIFIED_API */ + /** * hdd_ipa_uc_rt_debug_host_fill - fill rt debug buffer * @ctext: pointer to hdd context. @@ -1749,8 +3453,8 @@ static bool hdd_ipa_uc_find_add_assoc_sta(struct hdd_ipa_priv *hdd_ipa, */ static int hdd_ipa_uc_enable_pipes(struct hdd_ipa_priv *hdd_ipa) { + struct ol_txrx_pdev_t *pdev = cds_get_context(QDF_MODULE_ID_TXRX); int result = 0; - p_cds_contextType cds_ctx = hdd_ipa->hdd_ctx->pcds_context; HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "enter"); @@ -1765,46 +3469,17 @@ static int hdd_ipa_uc_enable_pipes(struct hdd_ipa_priv *hdd_ipa) goto end; } - /* ACTIVATE TX PIPE */ - HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, - "Enable TX PIPE(tx_pipe_handle=%d)", - hdd_ipa->tx_pipe_handle); - result = ipa_enable_wdi_pipe(hdd_ipa->tx_pipe_handle); - if (result) { - HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, - "Enable TX PIPE fail, code %d", - result); - goto end; - } - result = ipa_resume_wdi_pipe(hdd_ipa->tx_pipe_handle); + result = hdd_ipa_wdi_enable_pipes(hdd_ipa); if (result) { HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, - "Resume TX PIPE fail, code %d", - result); + "Enable IPA WDI pipes failed ret=%d", result); goto end; } - ol_txrx_ipa_uc_set_active(cds_ctx->pdev_txrx_ctx, true, true); - /* ACTIVATE RX PIPE */ - HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, - "Enable RX PIPE(rx_pipe_handle=%d)", - hdd_ipa->rx_pipe_handle); - result = ipa_enable_wdi_pipe(hdd_ipa->rx_pipe_handle); - if (result) { - HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, - "Enable RX PIPE fail, code %d", - result); - goto end; - } - result = ipa_resume_wdi_pipe(hdd_ipa->rx_pipe_handle); - if (result) { - HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, - "Resume RX PIPE fail, code %d", - result); - goto end; - } + ol_txrx_ipa_uc_set_active(pdev, true, true); + ol_txrx_ipa_uc_set_active(pdev, true, false); + INIT_COMPLETION(hdd_ipa->ipa_resource_comp); - ol_txrx_ipa_uc_set_active(cds_ctx->pdev_txrx_ctx, true, false); hdd_ipa->ipa_pipes_down = false; end: @@ -1836,35 +3511,10 @@ static int hdd_ipa_uc_disable_pipes(struct hdd_ipa_priv *hdd_ipa) goto end; } - HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "Disable RX PIPE"); - result = ipa_suspend_wdi_pipe(hdd_ipa->rx_pipe_handle); + result = hdd_ipa_wdi_disable_pipes(hdd_ipa); if (result) { HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, - "Suspend RX PIPE fail, code %d", - result); - goto end; - } - result = ipa_disable_wdi_pipe(hdd_ipa->rx_pipe_handle); - if (result) { - HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, - "Disable RX PIPE fail, code %d", - result); - goto end; - } - - HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "Disable TX PIPE"); - result = ipa_suspend_wdi_pipe(hdd_ipa->tx_pipe_handle); - if (result) { - HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, - "Suspend TX PIPE fail, code %d", - result); - goto end; - } - result = ipa_disable_wdi_pipe(hdd_ipa->tx_pipe_handle); - if (result) { - HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, - "Disable TX PIPE fail, code %d", - result); + "Disable IPA WDI pipes failed ret=%d", result); goto end; } @@ -1894,7 +3544,8 @@ static int hdd_ipa_uc_handle_first_con(struct hdd_ipa_priv *hdd_ipa) * PROD resource may return sync or async manners */ if (hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx)) { - if (!ipa_rm_request_resource(IPA_RM_RESOURCE_WLAN_PROD)) { + if (!hdd_ipa_wdi_rm_request_resource(hdd_ipa, + IPA_RM_RESOURCE_WLAN_PROD)) { /* RM PROD request sync return * enable pipe immediately */ @@ -1933,11 +3584,11 @@ static int hdd_ipa_uc_handle_first_con(struct hdd_ipa_priv *hdd_ipa) */ static void hdd_ipa_uc_handle_last_discon(struct hdd_ipa_priv *hdd_ipa) { - p_cds_contextType cds_ctx = hdd_ipa->hdd_ctx->pcds_context; + struct ol_txrx_pdev_t *pdev = cds_get_context(QDF_MODULE_ID_TXRX); HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "enter"); - if (!cds_ctx || !cds_ctx->pdev_txrx_ctx) { + if (!pdev) { HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "txrx context is NULL"); QDF_ASSERT(0); return; @@ -1946,90 +3597,11 @@ static void hdd_ipa_uc_handle_last_discon(struct hdd_ipa_priv *hdd_ipa) hdd_ipa->resource_unloading = true; INIT_COMPLETION(hdd_ipa->ipa_resource_comp); HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "Disable FW RX PIPE"); - ol_txrx_ipa_uc_set_active(cds_ctx->pdev_txrx_ctx, false, false); + ol_txrx_ipa_uc_set_active(pdev, false, false); HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "exit: IPA WDI Pipes deactivated"); } -/** - * hdd_ipa_uc_rm_notify_handler() - IPA uC resource notification handler - * @context: User context registered with TL (the IPA Global context is - * registered - * @rxpkt: Packet containing the notification - * @staid: ID of the station associated with the packet - * - * Return: None - */ -static void -hdd_ipa_uc_rm_notify_handler(void *context, enum ipa_rm_event event) -{ - struct hdd_ipa_priv *hdd_ipa = context; - QDF_STATUS status = QDF_STATUS_SUCCESS; - - /* - * When SSR is going on or driver is unloading, just return. - */ - status = wlan_hdd_validate_context(hdd_ipa->hdd_ctx); - if (status) - return; - - if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx)) - return; - - HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "event code %d", - event); - - switch (event) { - case IPA_RM_RESOURCE_GRANTED: - /* Differed RM Granted */ - qdf_mutex_acquire(&hdd_ipa->ipa_lock); - if ((false == hdd_ipa->resource_unloading) && - (!hdd_ipa->activated_fw_pipe)) { - hdd_ipa_uc_enable_pipes(hdd_ipa); - hdd_ipa->resource_loading = false; - } - qdf_mutex_release(&hdd_ipa->ipa_lock); - break; - - case IPA_RM_RESOURCE_RELEASED: - /* Differed RM Released */ - hdd_ipa->resource_unloading = false; - break; - - default: - HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, - "invalid event code %d", event); - break; - } -} - -/** - * hdd_ipa_uc_rm_notify_defer() - Defer IPA uC notification - * @hdd_ipa: Global HDD IPA context - * @event: IPA resource manager event to be deferred - * - * This function is called when a resource manager event is received - * from firmware in interrupt context. This function will defer the - * handling to the OL RX thread - * - * Return: None - */ -static void hdd_ipa_uc_rm_notify_defer(struct work_struct *work) -{ - enum ipa_rm_event event; - struct uc_rm_work_struct *uc_rm_work = container_of(work, - struct uc_rm_work_struct, work); - struct hdd_ipa_priv *hdd_ipa = container_of(uc_rm_work, - struct hdd_ipa_priv, uc_rm_work); - - cds_ssr_protect(__func__); - event = uc_rm_work->event; - HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, - "posted event %d", event); - - hdd_ipa_uc_rm_notify_handler(hdd_ipa, event); - cds_ssr_unprotect(__func__); -} /** * hdd_ipa_uc_op_metering() - IPA uC operation for stats and quota limit @@ -2132,7 +3704,12 @@ static QDF_STATUS hdd_ipa_uc_op_metering(hdd_context_t *hdd_ctx, */ static void hdd_ipa_uc_loaded_handler(struct hdd_ipa_priv *ipa_ctxt) { - struct ipa_wdi_out_params pipe_out; + struct ol_txrx_ipa_resources *ipa_res = &ipa_ctxt->ipa_resource; + qdf_device_t osdev = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + struct ol_txrx_pdev_t *pdev; + uint32_t tx_comp_db_dmaaddr = 0; + uint32_t rx_rdy_db_dmaaddr = 0; + int ret; HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "UC READY"); if (true == ipa_ctxt->uc_loaded) { @@ -2140,22 +3717,42 @@ static void hdd_ipa_uc_loaded_handler(struct hdd_ipa_priv *ipa_ctxt) return; } + if (!osdev) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_FATAL, "invalid qdf dev context"); + return; + } + + pdev = cds_get_context(QDF_MODULE_ID_TXRX); + if (!pdev) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_FATAL, "invalid txrx context"); + return; + } + /* Connect pipe */ - ipa_connect_wdi_pipe(&ipa_ctxt->cons_pipe_in, &pipe_out); - ipa_ctxt->tx_pipe_handle = pipe_out.clnt_hdl; - ipa_ctxt->tx_comp_doorbell_dmaaddr = pipe_out.uc_door_bell_pa; - HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, - "TX PIPE Handle %d, DBPA 0x%llx", - ipa_ctxt->tx_pipe_handle, - (unsigned long long) pipe_out.uc_door_bell_pa); + ret = hdd_ipa_wdi_conn_pipes(ipa_ctxt, ipa_res); + if (ret) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "ipa wdi conn pipes failed ret=%d", ret); + return; + } - ipa_connect_wdi_pipe(&ipa_ctxt->prod_pipe_in, &pipe_out); - ipa_ctxt->rx_pipe_handle = pipe_out.clnt_hdl; - ipa_ctxt->rx_ready_doorbell_dmaaddr = pipe_out.uc_door_bell_pa; - HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, - "RX PIPE Handle %d, DBPA 0x%llx", - ipa_ctxt->rx_pipe_handle, - (unsigned long long) pipe_out.uc_door_bell_pa); + if (hdd_ipa_wdi_is_smmu_enabled(ipa_ctxt, osdev)) { + pld_smmu_map(osdev->dev, + ipa_ctxt->tx_comp_doorbell_dmaaddr, + &tx_comp_db_dmaaddr, + sizeof(uint32_t)); + ipa_ctxt->tx_comp_doorbell_dmaaddr = tx_comp_db_dmaaddr; + + pld_smmu_map(osdev->dev, + ipa_ctxt->rx_ready_doorbell_dmaaddr, + &rx_rdy_db_dmaaddr, + sizeof(uint32_t)); + ipa_ctxt->rx_ready_doorbell_dmaaddr = rx_rdy_db_dmaaddr; + } + + ol_txrx_ipa_uc_set_doorbell_paddr(pdev, + ipa_ctxt->tx_comp_doorbell_dmaaddr, + ipa_ctxt->rx_ready_doorbell_dmaaddr); /* If already any STA connected, enable IPA/FW PIPEs */ if (ipa_ctxt->sap_num_connected_sta) { @@ -2678,7 +4275,7 @@ static void hdd_ipa_uc_op_cb(struct op_msg_type *op_msg, void *usr_ctxt) } hdd_ipa_uc_proc_pending_event(hdd_ipa, true); if (hdd_ipa->pending_cons_req) - ipa_rm_notify_completion( + hdd_ipa_wdi_rm_notify_completion( IPA_RM_RESOURCE_GRANTED, IPA_RM_RESOURCE_WLAN_CONS); hdd_ipa->pending_cons_req = false; @@ -2704,7 +4301,7 @@ static void hdd_ipa_uc_op_cb(struct op_msg_type *op_msg, void *usr_ctxt) hdd_ipa->resource_unloading = false; complete(&hdd_ipa->ipa_resource_comp); if (hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx)) - ipa_rm_release_resource( + hdd_ipa_wdi_rm_release_resource(hdd_ipa, IPA_RM_RESOURCE_WLAN_PROD); hdd_ipa_uc_proc_pending_event(hdd_ipa, false); hdd_ipa->pending_cons_req = false; @@ -2911,162 +4508,6 @@ static void hdd_ipa_init_uc_op_work(struct work_struct *work, INIT_WORK(work, work_handler); } -#ifdef FEATURE_METERING -/** - * __hdd_ipa_wdi_meter_notifier_cb() - WLAN to IPA callback handler. - * IPA calls to get WLAN stats or set quota limit. - * @priv: pointer to private data registered with IPA (we register a - *» pointer to the global IPA context) - * @evt: the IPA event which triggered the callback - * @data: data associated with the event - * - * Return: None - */ -static void __hdd_ipa_wdi_meter_notifier_cb(enum ipa_wdi_meter_evt_type evt, - void *data) -{ - struct hdd_ipa_priv *hdd_ipa = ghdd_ipa; - hdd_adapter_t *adapter = NULL; - struct ipa_get_wdi_sap_stats *wdi_sap_stats; - struct ipa_set_wifi_quota *ipa_set_quota; - int ret = 0; - - if (wlan_hdd_validate_context(hdd_ipa->hdd_ctx)) - return; - - adapter = hdd_get_adapter(hdd_ipa->hdd_ctx, QDF_STA_MODE); - - HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "event=%d", evt); - - switch (evt) { - case IPA_GET_WDI_SAP_STATS: - /* fill-up ipa_get_wdi_sap_stats structure after getting - * ipa_uc_fw_stats from FW - */ - wdi_sap_stats = data; - - if (!adapter) { - HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, - "IPA uC share stats failed - no adapter"); - wdi_sap_stats->stats_valid = 0; - return; - } - - INIT_COMPLETION(hdd_ipa->ipa_uc_sharing_stats_comp); - hdd_ipa_uc_sharing_stats_request(adapter, - wdi_sap_stats->reset_stats); - ret = wait_for_completion_timeout( - &hdd_ipa->ipa_uc_sharing_stats_comp, - msecs_to_jiffies(IPA_UC_SHARING_STATES_WAIT_TIME)); - if (!ret) { - HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, - "IPA uC share stats request timed out"); - wdi_sap_stats->stats_valid = 0; - } else { - wdi_sap_stats->stats_valid = 1; - - wdi_sap_stats->ipv4_rx_packets = - hdd_ipa->ipa_sharing_stats.ipv4_rx_packets; - wdi_sap_stats->ipv4_rx_bytes = - hdd_ipa->ipa_sharing_stats.ipv4_rx_bytes; - wdi_sap_stats->ipv6_rx_packets = - hdd_ipa->ipa_sharing_stats.ipv6_rx_packets; - wdi_sap_stats->ipv6_rx_bytes = - hdd_ipa->ipa_sharing_stats.ipv6_rx_bytes; - wdi_sap_stats->ipv4_tx_packets = - hdd_ipa->ipa_sharing_stats.ipv4_tx_packets; - wdi_sap_stats->ipv4_tx_bytes = - hdd_ipa->ipa_sharing_stats.ipv4_tx_bytes; - wdi_sap_stats->ipv6_tx_packets = - hdd_ipa->ipa_sharing_stats.ipv6_tx_packets; - wdi_sap_stats->ipv6_tx_bytes = - hdd_ipa->ipa_sharing_stats.ipv6_tx_bytes; - HDD_IPA_DP_LOG(QDF_TRACE_LEVEL_DEBUG, - "%s:%d,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu", - "IPA_GET_WDI_SAP_STATS", - wdi_sap_stats->stats_valid, - wdi_sap_stats->ipv4_rx_packets, - wdi_sap_stats->ipv4_rx_bytes, - wdi_sap_stats->ipv6_rx_packets, - wdi_sap_stats->ipv6_rx_bytes, - wdi_sap_stats->ipv4_tx_packets, - wdi_sap_stats->ipv4_tx_bytes, - wdi_sap_stats->ipv6_tx_packets, - wdi_sap_stats->ipv6_tx_bytes); - } - break; - case IPA_SET_WIFI_QUOTA: - /* Get ipa_set_wifi_quota structure from IPA and pass to FW - * through quota_exceeded field in ipa_uc_fw_stats - */ - ipa_set_quota = data; - - if (!adapter) { - HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, - "IPA uC set quota failed - no adapter"); - ipa_set_quota->set_valid = 0; - return; - } - - INIT_COMPLETION(hdd_ipa->ipa_uc_set_quota_comp); - hdd_ipa_uc_set_quota(adapter, ipa_set_quota->set_quota, - ipa_set_quota->quota_bytes); - - ret = wait_for_completion_timeout( - &hdd_ipa->ipa_uc_set_quota_comp, - msecs_to_jiffies(IPA_UC_SET_QUOTA_WAIT_TIME)); - if (!ret) { - HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, - "IPA uC set quota request timed out"); - ipa_set_quota->set_valid = 0; - } else { - ipa_set_quota->quota_bytes = - ((uint64_t)(hdd_ipa->ipa_quota_rsp.quota_hi) - <<32)|hdd_ipa->ipa_quota_rsp.quota_lo; - ipa_set_quota->set_valid = - hdd_ipa->ipa_quota_rsp.success; - } - - HDD_IPA_DP_LOG(QDF_TRACE_LEVEL_DEBUG, "SET_QUOTA: %llu, %d", - ipa_set_quota->quota_bytes, - ipa_set_quota->set_valid); - break; - } -} - -/** - * hdd_ipa_wdi_meter_notifier_cb() - WLAN to IPA callback handler. - * IPA calls to get WLAN stats or set quota limit. - * @priv: pointer to private data registered with IPA (we register a - *» pointer to the global IPA context) - * @evt: the IPA event which triggered the callback - * @data: data associated with the event - * - * Return: None - */ -static void hdd_ipa_wdi_meter_notifier_cb(enum ipa_wdi_meter_evt_type evt, - void *data) -{ - cds_ssr_protect(__func__); - __hdd_ipa_wdi_meter_notifier_cb(evt, data); - cds_ssr_unprotect(__func__); -} - -static void hdd_ipa_init_metering(struct hdd_ipa_priv *ipa_ctxt, - struct ipa_wdi_in_params *pipe_in) -{ - pipe_in->wdi_notify = hdd_ipa_wdi_meter_notifier_cb; - - init_completion(&ipa_ctxt->ipa_uc_sharing_stats_comp); - init_completion(&ipa_ctxt->ipa_uc_set_quota_comp); -} -#else -static void hdd_ipa_init_metering(struct hdd_ipa_priv *ipa_ctxt, - struct ipa_wdi_in_params *pipe_in) -{ -} -#endif - /** * hdd_ipa_uc_ol_init() - Initialize IPA uC offload * @hdd_ctx: Global HDD context @@ -3079,12 +4520,10 @@ static void hdd_ipa_init_metering(struct hdd_ipa_priv *ipa_ctxt, */ QDF_STATUS hdd_ipa_uc_ol_init(hdd_context_t *hdd_ctx) { - struct ipa_wdi_in_params pipe_in; - struct ipa_wdi_out_params pipe_out; struct hdd_ipa_priv *ipa_ctxt = (struct hdd_ipa_priv *)hdd_ctx->hdd_ipa; struct ol_txrx_ipa_resources *ipa_res = &ipa_ctxt->ipa_resource; struct ol_txrx_pdev_t *pdev = NULL; - int i, ret; + int i; QDF_STATUS stat = QDF_STATUS_SUCCESS; qdf_device_t osdev = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); uint32_t tx_comp_db_dmaaddr = 0, rx_rdy_db_dmaaddr = 0; @@ -3124,207 +4563,32 @@ QDF_STATUS hdd_ipa_uc_ol_init(hdd_context_t *hdd_ctx) stat = QDF_STATUS_E_FAILURE; goto fail_return; } - qdf_mem_zero(&ipa_ctxt->cons_pipe_in, sizeof(struct ipa_wdi_in_params)); - qdf_mem_zero(&ipa_ctxt->prod_pipe_in, sizeof(struct ipa_wdi_in_params)); - qdf_mem_zero(&pipe_in, sizeof(struct ipa_wdi_in_params)); - qdf_mem_zero(&pipe_out, sizeof(struct ipa_wdi_out_params)); - - /* TX PIPE */ - pipe_in.sys.ipa_ep_cfg.nat.nat_en = IPA_BYPASS_NAT; - pipe_in.sys.ipa_ep_cfg.hdr.hdr_len = HDD_IPA_UC_WLAN_TX_HDR_LEN; - pipe_in.sys.ipa_ep_cfg.hdr.hdr_ofst_pkt_size_valid = 1; - pipe_in.sys.ipa_ep_cfg.hdr.hdr_ofst_pkt_size = 0; - pipe_in.sys.ipa_ep_cfg.hdr.hdr_additional_const_len = - HDD_IPA_UC_WLAN_8023_HDR_SIZE; - pipe_in.sys.ipa_ep_cfg.mode.mode = IPA_BASIC; - pipe_in.sys.client = IPA_CLIENT_WLAN1_CONS; - pipe_in.sys.desc_fifo_sz = hdd_ctx->config->IpaDescSize; - pipe_in.sys.priv = hdd_ctx->hdd_ipa; - pipe_in.sys.ipa_ep_cfg.hdr_ext.hdr_little_endian = true; - pipe_in.sys.notify = hdd_ipa_i2w_cb; - if (!hdd_ipa_is_rm_enabled(hdd_ctx)) { - HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, - "IPA RM DISABLED, IPA AWAKE"); - pipe_in.sys.keep_ipa_awake = true; - } - - pipe_in.smmu_enabled = qdf_mem_smmu_s1_enabled(osdev); - if (pipe_in.smmu_enabled) { - qdf_mem_copy(&pipe_in.u.dl_smmu.comp_ring, - &ipa_res->tx_comp_ring->sgtable, - sizeof(sgtable_t)); - - qdf_mem_copy(&pipe_in.u.dl_smmu.ce_ring, - &ipa_res->ce_sr->sgtable, - sizeof(sgtable_t)); - pipe_in.u.dl_smmu.comp_ring_size = - ipa_res->tx_comp_ring->mem_info.size; - pipe_in.u.dl_smmu.ce_ring_size = - ipa_res->ce_sr_ring_size; - pipe_in.u.dl_smmu.ce_door_bell_pa = - ipa_res->ce_reg_paddr; - pipe_in.u.dl_smmu.num_tx_buffers = - ipa_res->tx_num_alloc_buffer; - } else { - pipe_in.u.dl.comp_ring_base_pa = - qdf_mem_get_dma_addr(osdev, - &ipa_res->tx_comp_ring->mem_info); - pipe_in.u.dl.ce_ring_base_pa = - qdf_mem_get_dma_addr(osdev, - &ipa_res->ce_sr->mem_info); - pipe_in.u.dl.comp_ring_size = - ipa_res->tx_comp_ring->mem_info.size; - pipe_in.u.dl.ce_door_bell_pa = ipa_res->ce_reg_paddr; - pipe_in.u.dl.ce_ring_size = - ipa_res->ce_sr_ring_size; - pipe_in.u.dl.num_tx_buffers = - ipa_res->tx_num_alloc_buffer; - } - - qdf_mem_copy(&ipa_ctxt->cons_pipe_in, &pipe_in, - sizeof(struct ipa_wdi_in_params)); - hdd_ipa_uc_get_db_paddr(&ipa_ctxt->tx_comp_doorbell_dmaaddr, - IPA_CLIENT_WLAN1_CONS); - if (true == ipa_ctxt->uc_loaded) { - /* Connect WDI IPA PIPE */ - ret = ipa_connect_wdi_pipe(&ipa_ctxt->cons_pipe_in, &pipe_out); - if (ret) { - HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, - "ipa_connect_wdi_pipe failed for Tx: ret=%d", - ret); + if (ipa_ctxt->uc_loaded) { + if (hdd_ipa_wdi_conn_pipes(ipa_ctxt, ipa_res)) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_FATAL, + "IPA CONN PIPES failed"); stat = QDF_STATUS_E_FAILURE; goto fail_return; } - /* Micro Controller Doorbell register */ - ipa_ctxt->tx_comp_doorbell_dmaaddr = pipe_out.uc_door_bell_pa; - - /* WLAN TX PIPE Handle */ - ipa_ctxt->tx_pipe_handle = pipe_out.clnt_hdl; - - if (ipa_ctxt->tx_pipe_handle == 0) { - HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, - "TX Handle zero"); - QDF_BUG(0); + if (hdd_ipa_wdi_is_smmu_enabled(ipa_ctxt, osdev)) { + pld_smmu_map(osdev->dev, + ipa_ctxt->tx_comp_doorbell_dmaaddr, + &tx_comp_db_dmaaddr, + sizeof(uint32_t)); + ipa_ctxt->tx_comp_doorbell_dmaaddr = tx_comp_db_dmaaddr; + + pld_smmu_map(osdev->dev, + ipa_ctxt->rx_ready_doorbell_dmaaddr, + &rx_rdy_db_dmaaddr, + sizeof(uint32_t)); + ipa_ctxt->rx_ready_doorbell_dmaaddr = rx_rdy_db_dmaaddr; } - HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, - "CONS DB pipe out 0x%x TX PIPE Handle 0x%x", - (unsigned int)pipe_out.uc_door_bell_pa, - ipa_ctxt->tx_pipe_handle); - HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, - "TX : CRBPA 0x%x, CRS %d, CERBPA 0x%x, CEDPA 0x%x," - " CERZ %d, NB %d, CDBPAD 0x%x", - (unsigned int)pipe_in.u.dl.comp_ring_base_pa, - pipe_in.u.dl.comp_ring_size, - (unsigned int)pipe_in.u.dl.ce_ring_base_pa, - (unsigned int)pipe_in.u.dl.ce_door_bell_pa, - pipe_in.u.dl.ce_ring_size, - pipe_in.u.dl.num_tx_buffers, - (unsigned int)ipa_ctxt->tx_comp_doorbell_dmaaddr); - } - - /* RX PIPE */ - pipe_in.sys.ipa_ep_cfg.nat.nat_en = IPA_BYPASS_NAT; - pipe_in.sys.ipa_ep_cfg.hdr.hdr_len = HDD_IPA_UC_WLAN_RX_HDR_LEN; - pipe_in.sys.ipa_ep_cfg.hdr.hdr_ofst_metadata_valid = 0; - pipe_in.sys.ipa_ep_cfg.hdr.hdr_metadata_reg_valid = 1; - pipe_in.sys.ipa_ep_cfg.mode.mode = IPA_BASIC; - pipe_in.sys.client = IPA_CLIENT_WLAN1_PROD; - pipe_in.sys.desc_fifo_sz = hdd_ctx->config->IpaDescSize + - sizeof(struct sps_iovec); - pipe_in.sys.notify = hdd_ipa_w2i_cb; - if (!hdd_ipa_is_rm_enabled(hdd_ctx)) { - HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, - "IPA RM DISABLED, IPA AWAKE"); - pipe_in.sys.keep_ipa_awake = true; - } - - pipe_in.smmu_enabled = qdf_mem_smmu_s1_enabled(osdev); - if (pipe_in.smmu_enabled) { - qdf_mem_copy(&pipe_in.u.ul_smmu.rdy_ring, - &ipa_res->rx_rdy_ring->sgtable, - sizeof(sgtable_t)); - pipe_in.u.ul_smmu.rdy_ring_size = - ipa_res->rx_rdy_ring->mem_info.size; - pipe_in.u.ul_smmu.rdy_ring_rp_pa = - ipa_res->rx_proc_done_idx->mem_info.pa; - - pipe_in.u.ul_smmu.rdy_ring_rp_va = - ipa_res->rx_proc_done_idx->vaddr; - - qdf_mem_copy(&pipe_in.u.ul_smmu.rdy_comp_ring, - &ipa_res->rx2_rdy_ring->sgtable, - sizeof(sgtable_t)); - - pipe_in.u.ul_smmu.rdy_comp_ring_size = - ipa_res->rx2_rdy_ring->mem_info.size; - - pipe_in.u.ul_smmu.rdy_comp_ring_wp_pa = - ipa_res->rx2_proc_done_idx->mem_info.pa; - - pipe_in.u.ul_smmu.rdy_comp_ring_wp_va = - ipa_res->rx2_proc_done_idx->vaddr; - } else { - pipe_in.u.ul.rdy_ring_base_pa = - ipa_res->rx_rdy_ring->mem_info.pa; - pipe_in.u.ul.rdy_ring_size = - ipa_res->rx_rdy_ring->mem_info.size; - pipe_in.u.ul.rdy_ring_rp_pa = - ipa_res->rx_proc_done_idx->mem_info.pa; - HDD_IPA_WDI2_SET(pipe_in, ipa_ctxt, osdev); - } - - hdd_ipa_init_metering(ipa_ctxt, &pipe_in); - - qdf_mem_copy(&ipa_ctxt->prod_pipe_in, &pipe_in, - sizeof(struct ipa_wdi_in_params)); - hdd_ipa_uc_get_db_paddr(&ipa_ctxt->rx_ready_doorbell_dmaaddr, - IPA_CLIENT_WLAN1_PROD); - - if (true == ipa_ctxt->uc_loaded) { - ret = ipa_connect_wdi_pipe(&ipa_ctxt->prod_pipe_in, &pipe_out); - if (ret) { - HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, - "ipa_connect_wdi_pipe failed for Rx: ret=%d", - ret); - stat = QDF_STATUS_E_FAILURE; - ret = ipa_disconnect_wdi_pipe(ipa_ctxt->tx_pipe_handle); - if (ret) - HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, - "disconnect failed for TX: ret=%d", - ret); - goto fail_return; - } - ipa_ctxt->rx_ready_doorbell_dmaaddr = pipe_out.uc_door_bell_pa; - ipa_ctxt->rx_pipe_handle = pipe_out.clnt_hdl; - if (ipa_ctxt->rx_pipe_handle == 0) { - HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, - "RX Handle zero"); - QDF_BUG(0); - } - - HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, - "PROD DB pipe out 0x%x RX PIPE Handle 0x%x", - (unsigned int)pipe_out.uc_door_bell_pa, - ipa_ctxt->rx_pipe_handle); - HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, - "RX : RRBPA 0x%x, RRS %d, PDIPA 0x%x, RDY_DB_PAD 0x%x", - (unsigned int)pipe_in.u.ul.rdy_ring_base_pa, - pipe_in.u.ul.rdy_ring_size, - (unsigned int)pipe_in.u.ul.rdy_ring_rp_pa, - (unsigned int)ipa_ctxt->rx_ready_doorbell_dmaaddr); - } - - if (qdf_mem_smmu_s1_enabled(osdev)) { - pld_smmu_map(osdev->dev, ipa_ctxt->tx_comp_doorbell_dmaaddr, - &tx_comp_db_dmaaddr, sizeof(uint32_t)); - ipa_ctxt->tx_comp_doorbell_dmaaddr = tx_comp_db_dmaaddr; - - pld_smmu_map(osdev->dev, ipa_ctxt->rx_ready_doorbell_dmaaddr, - &rx_rdy_db_dmaaddr, sizeof(uint32_t)); - ipa_ctxt->rx_ready_doorbell_dmaaddr = rx_rdy_db_dmaaddr; + ol_txrx_ipa_uc_set_doorbell_paddr(pdev, + ipa_ctxt->tx_comp_doorbell_dmaaddr, + ipa_ctxt->rx_ready_doorbell_dmaaddr); } for (i = 0; i < HDD_IPA_UC_OPCODE_MAX; i++) { @@ -3333,10 +4597,6 @@ QDF_STATUS hdd_ipa_uc_ol_init(hdd_context_t *hdd_ctx) ipa_ctxt->uc_op_work[i].msg = NULL; } - ol_txrx_ipa_uc_set_doorbell_paddr(pdev, - ipa_ctxt->tx_comp_doorbell_dmaaddr, - ipa_ctxt->rx_ready_doorbell_dmaaddr); - ol_txrx_ipa_uc_register_op_cb(pdev, hdd_ipa_uc_op_event_handler, (void *)hdd_ctx); @@ -3386,16 +4646,8 @@ int hdd_ipa_uc_ol_deinit(hdd_context_t *hdd_ctx) if (!hdd_ipa->ipa_pipes_down) hdd_ipa_uc_disable_pipes(hdd_ipa); - if (true == hdd_ipa->uc_loaded) { - HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, - "Disconnect TX PIPE tx_pipe_handle=0x%x", - hdd_ipa->tx_pipe_handle); - ret = ipa_disconnect_wdi_pipe(hdd_ipa->tx_pipe_handle); - HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, - "Disconnect RX PIPE rx_pipe_handle=0x%x", - hdd_ipa->rx_pipe_handle); - ret = ipa_disconnect_wdi_pipe(hdd_ipa->rx_pipe_handle); - } + if (true == hdd_ipa->uc_loaded) + ret = hdd_ipa_wdi_disconn_pipes(hdd_ipa); qdf_mutex_acquire(&hdd_ipa->ipa_lock); hdd_ipa_cleanup_pending_event(hdd_ipa); @@ -3871,245 +5123,6 @@ struct sk_buff *hdd_ipa_tx_packet_ipa(hdd_context_t *hdd_ctx, } /** - * hdd_ipa_wake_lock_timer_func() - Wake lock work handler - * @work: scheduled work - * - * When IPA resources are released in hdd_ipa_rm_try_release() we do - * not want to immediately release the wake lock since the system - * would then potentially try to suspend when there is a healthy data - * rate. Deferred work is scheduled and this function handles the - * work. When this function is called, if the IPA resource is still - * released then we release the wake lock. - * - * Return: None - */ -static void hdd_ipa_wake_lock_timer_func(struct work_struct *work) -{ - struct hdd_ipa_priv *hdd_ipa = container_of(to_delayed_work(work), - struct hdd_ipa_priv, - wake_lock_work); - - qdf_spin_lock_bh(&hdd_ipa->rm_lock); - - if (hdd_ipa->rm_state != HDD_IPA_RM_RELEASED) - goto end; - - hdd_ipa->wake_lock_released = true; - qdf_wake_lock_release(&hdd_ipa->wake_lock, - WIFI_POWER_EVENT_WAKELOCK_IPA); - -end: - qdf_spin_unlock_bh(&hdd_ipa->rm_lock); -} - -/** - * hdd_ipa_rm_request() - Request resource from IPA - * @hdd_ipa: Global HDD IPA context - * - * Return: 0 on success, negative errno on error - */ -static int hdd_ipa_rm_request(struct hdd_ipa_priv *hdd_ipa) -{ - int ret = 0; - - if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx)) - return 0; - - qdf_spin_lock_bh(&hdd_ipa->rm_lock); - - switch (hdd_ipa->rm_state) { - case HDD_IPA_RM_GRANTED: - qdf_spin_unlock_bh(&hdd_ipa->rm_lock); - return 0; - case HDD_IPA_RM_GRANT_PENDING: - qdf_spin_unlock_bh(&hdd_ipa->rm_lock); - return -EINPROGRESS; - case HDD_IPA_RM_RELEASED: - hdd_ipa->rm_state = HDD_IPA_RM_GRANT_PENDING; - break; - } - - qdf_spin_unlock_bh(&hdd_ipa->rm_lock); - - ret = ipa_rm_inactivity_timer_request_resource( - IPA_RM_RESOURCE_WLAN_PROD); - - qdf_spin_lock_bh(&hdd_ipa->rm_lock); - if (ret == 0) { - hdd_ipa->rm_state = HDD_IPA_RM_GRANTED; - hdd_ipa->stats.num_rm_grant_imm++; - } - - cancel_delayed_work(&hdd_ipa->wake_lock_work); - if (hdd_ipa->wake_lock_released) { - qdf_wake_lock_acquire(&hdd_ipa->wake_lock, - WIFI_POWER_EVENT_WAKELOCK_IPA); - hdd_ipa->wake_lock_released = false; - } - qdf_spin_unlock_bh(&hdd_ipa->rm_lock); - - return ret; -} - -/** - * hdd_ipa_rm_try_release() - Attempt to release IPA resource - * @hdd_ipa: Global HDD IPA context - * - * Return: 0 if resources released, negative errno otherwise - */ -static int hdd_ipa_rm_try_release(struct hdd_ipa_priv *hdd_ipa) -{ - int ret = 0; - - if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx)) - return 0; - - if (atomic_read(&hdd_ipa->tx_ref_cnt)) - return -EAGAIN; - - qdf_spin_lock_bh(&hdd_ipa->pm_lock); - - if (!qdf_nbuf_is_queue_empty(&hdd_ipa->pm_queue_head)) { - qdf_spin_unlock_bh(&hdd_ipa->pm_lock); - return -EAGAIN; - } - qdf_spin_unlock_bh(&hdd_ipa->pm_lock); - - qdf_spin_lock_bh(&hdd_ipa->rm_lock); - switch (hdd_ipa->rm_state) { - case HDD_IPA_RM_GRANTED: - break; - case HDD_IPA_RM_GRANT_PENDING: - qdf_spin_unlock_bh(&hdd_ipa->rm_lock); - return -EINPROGRESS; - case HDD_IPA_RM_RELEASED: - qdf_spin_unlock_bh(&hdd_ipa->rm_lock); - return 0; - } - - /* IPA driver returns immediately so set the state here to avoid any - * race condition. - */ - hdd_ipa->rm_state = HDD_IPA_RM_RELEASED; - hdd_ipa->stats.num_rm_release++; - qdf_spin_unlock_bh(&hdd_ipa->rm_lock); - - ret = ipa_rm_inactivity_timer_release_resource( - IPA_RM_RESOURCE_WLAN_PROD); - - qdf_spin_lock_bh(&hdd_ipa->rm_lock); - if (unlikely(ret != 0)) { - hdd_ipa->rm_state = HDD_IPA_RM_GRANTED; - WARN_ON(1); - HDD_IPA_LOG(QDF_TRACE_LEVEL_WARN, - "ipa_rm_inactivity_timer_release_resource returnied fail"); - } - - /* - * If wake_lock is released immediately, kernel would try to suspend - * immediately as well, Just avoid ping-pong between suspend-resume - * while there is healthy amount of data transfer going on by - * releasing the wake_lock after some delay. - */ - schedule_delayed_work(&hdd_ipa->wake_lock_work, - msecs_to_jiffies - (HDD_IPA_RX_INACTIVITY_MSEC_DELAY)); - - qdf_spin_unlock_bh(&hdd_ipa->rm_lock); - - return ret; -} - -/** - * hdd_ipa_rm_notify() - IPA resource manager notifier callback - * @user_data: user data registered with IPA - * @event: the IPA resource manager event that occurred - * @data: the data associated with the event - * - * Return: None - */ -static void hdd_ipa_rm_notify(void *user_data, enum ipa_rm_event event, - unsigned long data) -{ - struct hdd_ipa_priv *hdd_ipa = user_data; - - if (unlikely(!hdd_ipa)) - return; - - if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx)) - return; - - HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "Evt: %d", event); - - switch (event) { - case IPA_RM_RESOURCE_GRANTED: - if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) { - /* RM Notification comes with ISR context - * it should be serialized into work queue to avoid - * ISR sleep problem - */ - hdd_ipa->uc_rm_work.event = event; - schedule_work(&hdd_ipa->uc_rm_work.work); - break; - } - qdf_spin_lock_bh(&hdd_ipa->rm_lock); - hdd_ipa->rm_state = HDD_IPA_RM_GRANTED; - qdf_spin_unlock_bh(&hdd_ipa->rm_lock); - hdd_ipa->stats.num_rm_grant++; - break; - - case IPA_RM_RESOURCE_RELEASED: - HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "RM Release"); - hdd_ipa->resource_unloading = false; - break; - - default: - HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "Unknown RM Evt: %d", event); - break; - } -} - -/** - * hdd_ipa_rm_cons_release() - WLAN consumer resource release handler - * - * Callback function registered with IPA that is called when IPA wants - * to release the WLAN consumer resource - * - * Return: 0 if the request is granted, negative errno otherwise - */ -static int hdd_ipa_rm_cons_release(void) -{ - return 0; -} - -/** - * hdd_ipa_rm_cons_request() - WLAN consumer resource request handler - * - * Callback function registered with IPA that is called when IPA wants - * to access the WLAN consumer resource - * - * Return: 0 if the request is granted, negative errno otherwise - */ -static int hdd_ipa_rm_cons_request(void) -{ - int ret = 0; - - if (ghdd_ipa->resource_loading) { - HDD_IPA_LOG(QDF_TRACE_LEVEL_FATAL, - "IPA resource loading in progress"); - ghdd_ipa->pending_cons_req = true; - ret = -EINPROGRESS; - } else if (ghdd_ipa->resource_unloading) { - HDD_IPA_LOG(QDF_TRACE_LEVEL_FATAL, - "IPA resource unloading in progress"); - ghdd_ipa->pending_cons_req = true; - ret = -EPERM; - } - - return ret; -} - -/** * __hdd_ipa_set_perf_level() - Set IPA performance level * @hdd_ctx: Global HDD context * @tx_packets: Number of packets transmitted in the last sample period @@ -4163,8 +5176,8 @@ static int __hdd_ipa_set_perf_level(hdd_context_t *hdd_ctx, uint64_t tx_packets, hdd_debug("Requesting CONS perf curr: %d, next: %d", hdd_ipa->curr_cons_bw, next_cons_bw); profile.max_supported_bandwidth_mbps = next_cons_bw; - ret = ipa_rm_set_perf_profile(IPA_RM_RESOURCE_WLAN_CONS, - &profile); + ret = hdd_ipa_wdi_rm_set_perf_profile(hdd_ipa, + IPA_RM_RESOURCE_WLAN_CONS, &profile); if (ret) { hdd_err("RM CONS set perf profile failed: %d", ret); @@ -4178,8 +5191,8 @@ static int __hdd_ipa_set_perf_level(hdd_context_t *hdd_ctx, uint64_t tx_packets, hdd_debug("Requesting PROD perf curr: %d, next: %d", hdd_ipa->curr_prod_bw, next_prod_bw); profile.max_supported_bandwidth_mbps = next_prod_bw; - ret = ipa_rm_set_perf_profile(IPA_RM_RESOURCE_WLAN_PROD, - &profile); + ret = hdd_ipa_wdi_rm_set_perf_profile(hdd_ipa, + IPA_RM_RESOURCE_WLAN_PROD, &profile); if (ret) { hdd_err("RM PROD set perf profile failed: %d", ret); return ret; @@ -4211,138 +5224,7 @@ int hdd_ipa_set_perf_level(hdd_context_t *hdd_ctx, uint64_t tx_packets, return ret; } -/** - * hdd_ipa_init_uc_rm_work - init ipa uc resource manager work - * @work: struct work_struct - * @work_handler: work_handler - * - * Return: none - */ -static void hdd_ipa_init_uc_rm_work(struct work_struct *work, - work_func_t work_handler) -{ - INIT_WORK(work, work_handler); -} - -/** - * hdd_ipa_setup_rm() - Setup IPA resource management - * @hdd_ipa: Global HDD IPA context - * - * Return: 0 on success, negative errno on error - */ -static int hdd_ipa_setup_rm(struct hdd_ipa_priv *hdd_ipa) -{ - struct ipa_rm_create_params create_params = { 0 }; - int ret; - - if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx)) - return 0; - - hdd_ipa_init_uc_rm_work(&hdd_ipa->uc_rm_work.work, - hdd_ipa_uc_rm_notify_defer); - memset(&create_params, 0, sizeof(create_params)); - create_params.name = IPA_RM_RESOURCE_WLAN_PROD; - create_params.reg_params.user_data = hdd_ipa; - create_params.reg_params.notify_cb = hdd_ipa_rm_notify; - create_params.floor_voltage = IPA_VOLTAGE_SVS; - - ret = ipa_rm_create_resource(&create_params); - if (ret) { - HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, - "Create RM resource failed: %d", ret); - goto setup_rm_fail; - } - - memset(&create_params, 0, sizeof(create_params)); - create_params.name = IPA_RM_RESOURCE_WLAN_CONS; - create_params.request_resource = hdd_ipa_rm_cons_request; - create_params.release_resource = hdd_ipa_rm_cons_release; - create_params.floor_voltage = IPA_VOLTAGE_SVS; - - ret = ipa_rm_create_resource(&create_params); - if (ret) { - HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, - "Create RM CONS resource failed: %d", ret); - goto delete_prod; - } - - ipa_rm_add_dependency(IPA_RM_RESOURCE_WLAN_PROD, - IPA_RM_RESOURCE_APPS_CONS); - - ret = ipa_rm_inactivity_timer_init(IPA_RM_RESOURCE_WLAN_PROD, - HDD_IPA_RX_INACTIVITY_MSEC_DELAY); - if (ret) { - HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "Timer init failed: %d", - ret); - goto timer_init_failed; - } - - /* Set the lowest bandwidth to start with */ - ret = hdd_ipa_set_perf_level(hdd_ipa->hdd_ctx, 0, 0); - - if (ret) { - HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, - "Set perf level failed: %d", ret); - goto set_perf_failed; - } - - qdf_wake_lock_create(&hdd_ipa->wake_lock, "wlan_ipa"); - INIT_DELAYED_WORK(&hdd_ipa->wake_lock_work, - hdd_ipa_wake_lock_timer_func); - qdf_spinlock_create(&hdd_ipa->rm_lock); - hdd_ipa->rm_state = HDD_IPA_RM_RELEASED; - hdd_ipa->wake_lock_released = true; - atomic_set(&hdd_ipa->tx_ref_cnt, 0); - - return ret; - -set_perf_failed: - ipa_rm_inactivity_timer_destroy(IPA_RM_RESOURCE_WLAN_PROD); - -timer_init_failed: - ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_CONS); - -delete_prod: - ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_PROD); - -setup_rm_fail: - return ret; -} - -/** - * hdd_ipa_destroy_rm_resource() - Destroy IPA resources - * @hdd_ipa: Global HDD IPA context - * - * Destroys all resources associated with the IPA resource manager - * - * Return: None - */ -static void hdd_ipa_destroy_rm_resource(struct hdd_ipa_priv *hdd_ipa) -{ - int ret; - - if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx)) - return; - - cancel_delayed_work_sync(&hdd_ipa->wake_lock_work); - qdf_wake_lock_destroy(&hdd_ipa->wake_lock); - cancel_work_sync(&hdd_ipa->uc_rm_work.work); - qdf_spinlock_destroy(&hdd_ipa->rm_lock); - - ipa_rm_inactivity_timer_destroy(IPA_RM_RESOURCE_WLAN_PROD); - - ret = ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_PROD); - if (ret) - HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, - "RM PROD resource delete failed %d", ret); - - ret = ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_CONS); - if (ret) - HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, - "RM CONS resource delete failed %d", ret); -} - -#ifdef PF_WAKE_UP_IDLE +#ifdef QCA_CONFIG_SMP /** * hdd_ipa_get_wake_up_idle() - Get PF_WAKE_UP_IDLE flag in the task structure * @@ -4368,7 +5250,12 @@ static void hdd_ipa_set_wake_up_idle(bool wake_up_idle) sched_set_wake_up_idle(current, wake_up_idle); } -#else + +static int hdd_ipa_aggregated_rx_ind(qdf_nbuf_t skb) +{ + return netif_rx_ni(skb); +} +#else /* QCA_CONFIG_SMP */ static uint32_t hdd_ipa_get_wake_up_idle(void) { return 0; @@ -4377,14 +5264,7 @@ static uint32_t hdd_ipa_get_wake_up_idle(void) static void hdd_ipa_set_wake_up_idle(bool wake_up_idle) { } -#endif -#ifdef QCA_CONFIG_SMP -static int hdd_ipa_aggregated_rx_ind(qdf_nbuf_t skb) -{ - return netif_rx_ni(skb); -} -#else static int hdd_ipa_aggregated_rx_ind(qdf_nbuf_t skb) { struct iphdr *ip_h; @@ -4411,7 +5291,7 @@ static int hdd_ipa_aggregated_rx_ind(qdf_nbuf_t skb) return result; } -#endif +#endif /* QCA_CONFIG_SMP */ /** * hdd_ipa_send_skb_to_network() - Send skb to kernel @@ -4757,7 +5637,7 @@ void hdd_ipa_nbuf_cb(qdf_nbuf_t skb) atomic_dec(&hdd_ipa->tx_ref_cnt); - hdd_ipa_rm_try_release(hdd_ipa); + hdd_ipa_wdi_rm_try_release(hdd_ipa); } /** @@ -4783,7 +5663,7 @@ static void hdd_ipa_send_pkt_to_tl( ipa_free_skb(ipa_tx_desc); iface_context->stats.num_tx_drop++; qdf_spin_unlock_bh(&iface_context->interface_lock); - hdd_ipa_rm_try_release(hdd_ipa); + hdd_ipa_wdi_rm_try_release(hdd_ipa); return; } @@ -4797,7 +5677,7 @@ static void hdd_ipa_send_pkt_to_tl( ipa_free_skb(ipa_tx_desc); qdf_spin_unlock_bh(&iface_context->interface_lock); iface_context->stats.num_tx_cac_drop++; - hdd_ipa_rm_try_release(hdd_ipa); + hdd_ipa_wdi_rm_try_release(hdd_ipa); return; } } @@ -4837,7 +5717,7 @@ static void hdd_ipa_send_pkt_to_tl( hdd_ipa->stats.num_tx_desc_error++; qdf_spin_unlock_bh(&hdd_ipa->q_lock); ipa_free_skb(ipa_tx_desc); - hdd_ipa_rm_try_release(hdd_ipa); + hdd_ipa_wdi_rm_try_release(hdd_ipa); return; } @@ -4979,7 +5859,7 @@ static void __hdd_ipa_i2w_cb(void *priv, enum ipa_dp_evt_type evt, * workaround to request PROD resource while data is going over CONS * pipe to prevent the IPA hardware clockdown. */ - hdd_ipa_rm_request(hdd_ipa); + hdd_ipa_wdi_rm_request(hdd_ipa); qdf_spin_lock_bh(&hdd_ipa->pm_lock); /* @@ -5233,7 +6113,8 @@ static int hdd_ipa_setup_sys_pipe(struct hdd_ipa_priv *hdd_ipa) if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx)) ipa->keep_ipa_awake = 1; - ret = ipa_setup_sys_pipe(ipa, &(hdd_ipa->sys_pipe[i].conn_hdl)); + ret = hdd_ipa_wdi_setup_sys_pipe(hdd_ipa, ipa, + &(hdd_ipa->sys_pipe[i].conn_hdl)); if (ret) { HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "Failed for pipe %d ret: %d", i, ret); @@ -5270,7 +6151,8 @@ static int hdd_ipa_setup_sys_pipe(struct hdd_ipa_priv *hdd_ipa) if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx)) ipa->keep_ipa_awake = 1; - ret = ipa_setup_sys_pipe(ipa, &(hdd_ipa->sys_pipe[i].conn_hdl)); + ret = hdd_ipa_wdi_setup_sys_pipe(hdd_ipa, ipa, + &(hdd_ipa->sys_pipe[i].conn_hdl)); if (ret) { HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "Failed for RX pipe: %d", ret); @@ -5293,7 +6175,8 @@ static int hdd_ipa_setup_sys_pipe(struct hdd_ipa_priv *hdd_ipa) setup_sys_pipe_fail: while (--i >= 0) { - ipa_teardown_sys_pipe(hdd_ipa->sys_pipe[i].conn_hdl); + hdd_ipa_wdi_teardown_sys_pipe(hdd_ipa, + hdd_ipa->sys_pipe[i].conn_hdl); qdf_mem_zero(&hdd_ipa->sys_pipe[i], sizeof(struct hdd_ipa_sys_pipe)); } @@ -5315,9 +6198,8 @@ static void hdd_ipa_teardown_sys_pipe(struct hdd_ipa_priv *hdd_ipa) for (i = 0; i < HDD_IPA_MAX_SYSBAM_PIPE; i++) { if (hdd_ipa->sys_pipe[i].conn_hdl_valid) { - ret = - ipa_teardown_sys_pipe(hdd_ipa->sys_pipe[i]. - conn_hdl); + ret = hdd_ipa_wdi_teardown_sys_pipe(hdd_ipa, + hdd_ipa->sys_pipe[i].conn_hdl); if (ret) HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "Failed: %d", ret); @@ -5344,348 +6226,6 @@ static void hdd_ipa_teardown_sys_pipe(struct hdd_ipa_priv *hdd_ipa) } /** - * hdd_ipa_register_interface() - register IPA interface - * @hdd_ipa: Global IPA context - * @iface_context: Per-interface IPA context - * - * Return: 0 on success, negative errno on error - */ -static int hdd_ipa_register_interface(struct hdd_ipa_priv *hdd_ipa, - struct hdd_ipa_iface_context - *iface_context) -{ - struct ipa_tx_intf tx_intf; - struct ipa_rx_intf rx_intf; - struct ipa_ioc_tx_intf_prop *tx_prop = NULL; - struct ipa_ioc_rx_intf_prop *rx_prop = NULL; - char *ifname = iface_context->adapter->dev->name; - - char ipv4_hdr_name[IPA_RESOURCE_NAME_MAX]; - char ipv6_hdr_name[IPA_RESOURCE_NAME_MAX]; - - int num_prop = 1; - int ret = 0; - - if (hdd_ipa_is_ipv6_enabled(hdd_ipa->hdd_ctx)) - num_prop++; - - /* Allocate TX properties for TOS categories, 1 each for IPv4 & IPv6 */ - tx_prop = - qdf_mem_malloc(sizeof(struct ipa_ioc_tx_intf_prop) * num_prop); - if (!tx_prop) { - HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "tx_prop allocation failed"); - goto register_interface_fail; - } - - /* Allocate RX properties, 1 each for IPv4 & IPv6 */ - rx_prop = - qdf_mem_malloc(sizeof(struct ipa_ioc_rx_intf_prop) * num_prop); - if (!rx_prop) { - HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "rx_prop allocation failed"); - goto register_interface_fail; - } - - qdf_mem_zero(&tx_intf, sizeof(tx_intf)); - qdf_mem_zero(&rx_intf, sizeof(rx_intf)); - - snprintf(ipv4_hdr_name, IPA_RESOURCE_NAME_MAX, "%s%s", - ifname, HDD_IPA_IPV4_NAME_EXT); - snprintf(ipv6_hdr_name, IPA_RESOURCE_NAME_MAX, "%s%s", - ifname, HDD_IPA_IPV6_NAME_EXT); - - rx_prop[IPA_IP_v4].ip = IPA_IP_v4; - rx_prop[IPA_IP_v4].src_pipe = iface_context->prod_client; - rx_prop[IPA_IP_v4].hdr_l2_type = IPA_HDR_L2_ETHERNET_II; - rx_prop[IPA_IP_v4].attrib.attrib_mask = IPA_FLT_META_DATA; - - /* - * Interface ID is 3rd byte in the CLD header. Add the meta data and - * mask to identify the interface in IPA hardware - */ - rx_prop[IPA_IP_v4].attrib.meta_data = - htonl(iface_context->adapter->sessionId << 16); - rx_prop[IPA_IP_v4].attrib.meta_data_mask = htonl(0x00FF0000); - - rx_intf.num_props++; - if (hdd_ipa_is_ipv6_enabled(hdd_ipa->hdd_ctx)) { - rx_prop[IPA_IP_v6].ip = IPA_IP_v6; - rx_prop[IPA_IP_v6].src_pipe = iface_context->prod_client; - rx_prop[IPA_IP_v6].hdr_l2_type = IPA_HDR_L2_ETHERNET_II; - rx_prop[IPA_IP_v4].attrib.attrib_mask = IPA_FLT_META_DATA; - rx_prop[IPA_IP_v4].attrib.meta_data = - htonl(iface_context->adapter->sessionId << 16); - rx_prop[IPA_IP_v4].attrib.meta_data_mask = htonl(0x00FF0000); - - rx_intf.num_props++; - } - - tx_prop[IPA_IP_v4].ip = IPA_IP_v4; - tx_prop[IPA_IP_v4].hdr_l2_type = IPA_HDR_L2_ETHERNET_II; - tx_prop[IPA_IP_v4].dst_pipe = IPA_CLIENT_WLAN1_CONS; - tx_prop[IPA_IP_v4].alt_dst_pipe = iface_context->cons_client; - strlcpy(tx_prop[IPA_IP_v4].hdr_name, ipv4_hdr_name, - IPA_RESOURCE_NAME_MAX); - tx_intf.num_props++; - - if (hdd_ipa_is_ipv6_enabled(hdd_ipa->hdd_ctx)) { - tx_prop[IPA_IP_v6].ip = IPA_IP_v6; - tx_prop[IPA_IP_v6].hdr_l2_type = IPA_HDR_L2_ETHERNET_II; - tx_prop[IPA_IP_v6].dst_pipe = IPA_CLIENT_WLAN1_CONS; - tx_prop[IPA_IP_v6].alt_dst_pipe = iface_context->cons_client; - strlcpy(tx_prop[IPA_IP_v6].hdr_name, ipv6_hdr_name, - IPA_RESOURCE_NAME_MAX); - tx_intf.num_props++; - } - - tx_intf.prop = tx_prop; - rx_intf.prop = rx_prop; - - /* Call the ipa api to register interface */ - ret = ipa_register_intf(ifname, &tx_intf, &rx_intf); - - /* Register IPA Tx desc free callback */ - qdf_nbuf_reg_free_cb(hdd_ipa_nbuf_cb); - -register_interface_fail: - qdf_mem_free(tx_prop); - qdf_mem_free(rx_prop); - return ret; -} - -/** - * hdd_remove_ipa_header() - Remove a specific header from IPA - * @name: Name of the header to be removed - * - * Return: None - */ -static void hdd_ipa_remove_header(char *name) -{ - struct ipa_ioc_get_hdr hdrlookup; - int ret = 0, len; - struct ipa_ioc_del_hdr *ipa_hdr; - - qdf_mem_zero(&hdrlookup, sizeof(hdrlookup)); - strlcpy(hdrlookup.name, name, sizeof(hdrlookup.name)); - ret = ipa_get_hdr(&hdrlookup); - if (ret) { - HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "Hdr deleted already %s, %d", - name, ret); - return; - } - - HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "hdl: 0x%x", hdrlookup.hdl); - len = sizeof(struct ipa_ioc_del_hdr) + sizeof(struct ipa_hdr_del) * 1; - ipa_hdr = (struct ipa_ioc_del_hdr *)qdf_mem_malloc(len); - if (ipa_hdr == NULL) { - HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "ipa_hdr allocation failed"); - return; - } - ipa_hdr->num_hdls = 1; - ipa_hdr->commit = 0; - ipa_hdr->hdl[0].hdl = hdrlookup.hdl; - ipa_hdr->hdl[0].status = -1; - ret = ipa_del_hdr(ipa_hdr); - if (ret != 0) - HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "Delete header failed: %d", - ret); - - qdf_mem_free(ipa_hdr); -} - -/** - * wlan_ipa_add_hdr() - Add IPA Tx header - * @ipa_hdr: pointer to IPA header addition parameters - * - * Call IPA API to add IPA Tx header descriptor - * and dump Tx header struct - * - * Return: 0 for success, non-zero for failure - */ -static int wlan_ipa_add_hdr(struct ipa_ioc_add_hdr *ipa_hdr) -{ - int ret; - - hdd_debug("==== IPA Tx Header ====\n" - "name: %s\n" - "hdr_len: %d\n" - "type: %d\n" - "is_partial: %d\n" - "hdr_hdl: 0x%x\n" - "status: %d\n" - "is_eth2_ofst_valid: %d\n" - "eth2_ofst: %d\n", - ipa_hdr->hdr[0].name, - ipa_hdr->hdr[0].hdr_len, - ipa_hdr->hdr[0].type, - ipa_hdr->hdr[0].is_partial, - ipa_hdr->hdr[0].hdr_hdl, - ipa_hdr->hdr[0].status, - ipa_hdr->hdr[0].is_eth2_ofst_valid, - ipa_hdr->hdr[0].eth2_ofst); - - HDD_IPA_DBG_DUMP(QDF_TRACE_LEVEL_DEBUG, "hdr:", - ipa_hdr->hdr[0].hdr, HDD_IPA_UC_WLAN_TX_HDR_LEN); - - ret = ipa_add_hdr(ipa_hdr); - return ret; -} - -/** - * hdd_ipa_add_header_info() - Add IPA header for a given interface - * @hdd_ipa: Global HDD IPA context - * @iface_context: Interface-specific HDD IPA context - * @mac_addr: Interface MAC address - * - * Return: 0 on success, negativer errno value on error - */ -static int hdd_ipa_add_header_info(struct hdd_ipa_priv *hdd_ipa, - struct hdd_ipa_iface_context *iface_context, - uint8_t *mac_addr) -{ - hdd_adapter_t *adapter = iface_context->adapter; - char *ifname; - struct ipa_ioc_add_hdr *ipa_hdr = NULL; - int ret = -EINVAL; - struct hdd_ipa_tx_hdr *tx_hdr = NULL; - struct hdd_ipa_uc_tx_hdr *uc_tx_hdr = NULL; - - ifname = adapter->dev->name; - - HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "Add Partial hdr: %s, %pM", - ifname, mac_addr); - - /* dynamically allocate the memory to add the hdrs */ - ipa_hdr = qdf_mem_malloc(sizeof(struct ipa_ioc_add_hdr) - + sizeof(struct ipa_hdr_add)); - if (!ipa_hdr) { - HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, - "%s: ipa_hdr allocation failed", ifname); - ret = -ENOMEM; - goto end; - } - - ipa_hdr->commit = 0; - ipa_hdr->num_hdrs = 1; - - if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) { - uc_tx_hdr = (struct hdd_ipa_uc_tx_hdr *)ipa_hdr->hdr[0].hdr; - memcpy(uc_tx_hdr, &ipa_uc_tx_hdr, HDD_IPA_UC_WLAN_TX_HDR_LEN); - memcpy(uc_tx_hdr->eth.h_source, mac_addr, ETH_ALEN); - uc_tx_hdr->ipa_hd.vdev_id = iface_context->adapter->sessionId; - HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, - "ifname=%s, vdev_id=%d", - ifname, uc_tx_hdr->ipa_hd.vdev_id); - snprintf(ipa_hdr->hdr[0].name, IPA_RESOURCE_NAME_MAX, "%s%s", - ifname, HDD_IPA_IPV4_NAME_EXT); - ipa_hdr->hdr[0].hdr_len = HDD_IPA_UC_WLAN_TX_HDR_LEN; - ipa_hdr->hdr[0].type = IPA_HDR_L2_ETHERNET_II; - ipa_hdr->hdr[0].is_partial = 1; - ipa_hdr->hdr[0].hdr_hdl = 0; - ipa_hdr->hdr[0].is_eth2_ofst_valid = 1; - ipa_hdr->hdr[0].eth2_ofst = HDD_IPA_UC_WLAN_HDR_DES_MAC_OFFSET; - - ret = wlan_ipa_add_hdr(ipa_hdr); - } else { - tx_hdr = (struct hdd_ipa_tx_hdr *)ipa_hdr->hdr[0].hdr; - - /* Set the Source MAC */ - memcpy(tx_hdr, &ipa_tx_hdr, HDD_IPA_WLAN_TX_HDR_LEN); - memcpy(tx_hdr->eth.h_source, mac_addr, ETH_ALEN); - - snprintf(ipa_hdr->hdr[0].name, IPA_RESOURCE_NAME_MAX, "%s%s", - ifname, HDD_IPA_IPV4_NAME_EXT); - ipa_hdr->hdr[0].hdr_len = HDD_IPA_WLAN_TX_HDR_LEN; - ipa_hdr->hdr[0].is_partial = 1; - ipa_hdr->hdr[0].hdr_hdl = 0; - ipa_hdr->hdr[0].is_eth2_ofst_valid = 1; - ipa_hdr->hdr[0].eth2_ofst = HDD_IPA_WLAN_HDR_DES_MAC_OFFSET; - - /* Set the type to IPV4 in the header */ - tx_hdr->llc_snap.eth_type = cpu_to_be16(ETH_P_IP); - - ret = ipa_add_hdr(ipa_hdr); - } - if (ret) { - HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, - "%s: IPv4 add hdr failed: %d", ifname, ret); - goto end; - } - - HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "%s: IPv4 hdr_hdl: 0x%x", - ipa_hdr->hdr[0].name, ipa_hdr->hdr[0].hdr_hdl); - - if (hdd_ipa_is_ipv6_enabled(hdd_ipa->hdd_ctx)) { - snprintf(ipa_hdr->hdr[0].name, IPA_RESOURCE_NAME_MAX, "%s%s", - ifname, HDD_IPA_IPV6_NAME_EXT); - - if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) { - uc_tx_hdr = - (struct hdd_ipa_uc_tx_hdr *)ipa_hdr->hdr[0].hdr; - uc_tx_hdr->eth.h_proto = cpu_to_be16(ETH_P_IPV6); - ret = wlan_ipa_add_hdr(ipa_hdr); - } else { - /* Set the type to IPV6 in the header */ - tx_hdr = (struct hdd_ipa_tx_hdr *)ipa_hdr->hdr[0].hdr; - tx_hdr->llc_snap.eth_type = cpu_to_be16(ETH_P_IPV6); - ret = ipa_add_hdr(ipa_hdr); - } - - if (ret) { - HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, - "%s: IPv6 add hdr failed: %d", ifname, ret); - goto clean_ipv4_hdr; - } - - HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "%s: IPv6 hdr_hdl: 0x%x", - ipa_hdr->hdr[0].name, ipa_hdr->hdr[0].hdr_hdl); - } - - qdf_mem_free(ipa_hdr); - - return ret; - -clean_ipv4_hdr: - snprintf(ipa_hdr->hdr[0].name, IPA_RESOURCE_NAME_MAX, "%s%s", - ifname, HDD_IPA_IPV4_NAME_EXT); - hdd_ipa_remove_header(ipa_hdr->hdr[0].name); -end: - if (ipa_hdr) - qdf_mem_free(ipa_hdr); - - return ret; -} - -/** - * hdd_ipa_clean_hdr() - Cleanup IPA on a given adapter - * @adapter: Adapter upon which IPA was previously configured - * - * Return: None - */ -static void hdd_ipa_clean_hdr(hdd_adapter_t *adapter) -{ - struct hdd_ipa_priv *hdd_ipa = ghdd_ipa; - int ret; - char name_ipa[IPA_RESOURCE_NAME_MAX]; - - /* Remove the headers */ - snprintf(name_ipa, IPA_RESOURCE_NAME_MAX, "%s%s", - adapter->dev->name, HDD_IPA_IPV4_NAME_EXT); - hdd_ipa_remove_header(name_ipa); - - if (hdd_ipa_is_ipv6_enabled(hdd_ipa->hdd_ctx)) { - snprintf(name_ipa, IPA_RESOURCE_NAME_MAX, "%s%s", - adapter->dev->name, HDD_IPA_IPV6_NAME_EXT); - hdd_ipa_remove_header(name_ipa); - } - /* unregister the interface with IPA */ - ret = ipa_deregister_intf(adapter->dev->name); - if (ret) - HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, - "%s: ipa_deregister_intf fail: %d", - adapter->dev->name, ret); -} - -/** * hdd_ipa_cleanup_iface() - Cleanup IPA on a given interface * @iface_context: interface-specific IPA context * @@ -5705,7 +6245,8 @@ static void hdd_ipa_cleanup_iface(struct hdd_ipa_iface_context *iface_context) return; } - hdd_ipa_clean_hdr(iface_context->adapter); + hdd_ipa_wdi_dereg_intf(iface_context->hdd_ipa, + iface_context->adapter->dev->name); qdf_spin_lock_bh(&iface_context->interface_lock); /* @@ -5789,16 +6330,15 @@ static int hdd_ipa_setup_iface(struct hdd_ipa_priv *hdd_ipa, iface_context->tl_context = tl_context; - ret = hdd_ipa_add_header_info(hdd_ipa, iface_context, - adapter->dev->dev_addr); - - if (ret) + ret = hdd_ipa_wdi_reg_intf(hdd_ipa, iface_context); + if (ret) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "IPA WDI reg intf failed ret=%d", ret); goto end; + } - /* Configure the TX and RX pipes filter rules */ - ret = hdd_ipa_register_interface(hdd_ipa, iface_context); - if (ret) - goto cleanup_header; + /* Register IPA Tx desc free callback */ + qdf_nbuf_reg_free_cb(hdd_ipa_nbuf_cb); hdd_ipa->num_iface++; @@ -5806,9 +6346,6 @@ static int hdd_ipa_setup_iface(struct hdd_ipa_priv *hdd_ipa, hdd_ipa->num_iface); return ret; -cleanup_header: - - hdd_ipa_clean_hdr(adapter); end: if (iface_context) hdd_ipa_cleanup_iface(iface_context); @@ -6563,6 +7100,8 @@ static QDF_STATUS __hdd_ipa_init(hdd_context_t *hdd_ctx) hdd_ipa->hdd_ctx = hdd_ctx; hdd_ipa->num_iface = 0; + hdd_ipa_wdi_get_wdi_version(hdd_ipa); + /* Create the interface context */ for (i = 0; i < HDD_IPA_MAX_IFACE; i++) { iface_context = &hdd_ipa->iface_context[i]; @@ -6584,7 +7123,7 @@ static QDF_STATUS __hdd_ipa_init(hdd_context_t *hdd_ctx) qdf_mutex_create(&hdd_ipa->event_lock); qdf_mutex_create(&hdd_ipa->ipa_lock); - ret = hdd_ipa_setup_rm(hdd_ipa); + ret = hdd_ipa_wdi_setup_rm(hdd_ipa); if (ret) goto fail_setup_rm; @@ -6609,8 +7148,18 @@ static QDF_STATUS __hdd_ipa_init(hdd_context_t *hdd_ctx) INIT_WORK(&hdd_ipa->mcc_work, hdd_ipa_mcc_work_handler); } - if (hdd_ipa_uc_register_uc_ready(hdd_ipa)) - goto fail_create_sys_pipe; + + ret = hdd_ipa_wdi_init(hdd_ipa); + if (ret) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "ipa wdi init failed ret=%d", ret); + if (ret == -EACCES) { + if (hdd_ipa_uc_send_wdi_control_msg(false)) + goto fail_create_sys_pipe; + } else { + goto fail_create_sys_pipe; + } + } } else { ret = hdd_ipa_setup_sys_pipe(hdd_ipa); if (ret) @@ -6619,19 +7168,19 @@ static QDF_STATUS __hdd_ipa_init(hdd_context_t *hdd_ctx) /* When IPA clock scaling is disabled, initialze maximum clock */ if (!hdd_ipa_is_clk_scaling_enabled(hdd_ctx)) { - profile.max_supported_bandwidth_mbps = 800; + profile.max_supported_bandwidth_mbps = HDD_IPA_MAX_BANDWIDTH; hdd_debug("IPA clock scaling is disabled."); hdd_debug("Set initial CONS/PROD perf: %d", profile.max_supported_bandwidth_mbps); - ret = ipa_rm_set_perf_profile(IPA_RM_RESOURCE_WLAN_CONS, - &profile); + ret = hdd_ipa_wdi_rm_set_perf_profile(hdd_ipa, + IPA_RM_RESOURCE_WLAN_CONS, &profile); if (ret) { hdd_err("RM CONS set perf profile failed: %d", ret); goto fail_create_sys_pipe; } - ret = ipa_rm_set_perf_profile(IPA_RM_RESOURCE_WLAN_PROD, - &profile); + ret = hdd_ipa_wdi_rm_set_perf_profile(hdd_ipa, + IPA_RM_RESOURCE_WLAN_PROD, &profile); if (ret) { hdd_err("RM PROD set perf profile failed: %d", ret); goto fail_create_sys_pipe; @@ -6644,7 +7193,7 @@ static QDF_STATUS __hdd_ipa_init(hdd_context_t *hdd_ctx) return QDF_STATUS_SUCCESS; fail_create_sys_pipe: - hdd_ipa_destroy_rm_resource(hdd_ipa); + hdd_ipa_wdi_destroy_rm(hdd_ipa); fail_setup_rm: qdf_spinlock_destroy(&hdd_ipa->pm_lock); qdf_mem_free(hdd_ipa); @@ -6734,8 +7283,7 @@ static QDF_STATUS __hdd_ipa_cleanup(hdd_context_t *hdd_ctx) cancel_work_sync(&hdd_ipa->mcc_work); } - hdd_ipa_destroy_rm_resource(hdd_ipa); - + hdd_ipa_wdi_destroy_rm(hdd_ipa); __hdd_ipa_flush(hdd_ctx); @@ -6749,9 +7297,7 @@ static QDF_STATUS __hdd_ipa_cleanup(hdd_context_t *hdd_ctx) } if (hdd_ipa_uc_is_enabled(hdd_ctx)) { - if (ipa_uc_dereg_rdyCB()) - HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, - "UC Ready CB deregister fail"); + hdd_ipa_wdi_cleanup(); hdd_ipa_uc_rt_debug_deinit(hdd_ctx); qdf_mutex_destroy(&hdd_ipa->event_lock); qdf_mutex_destroy(&hdd_ipa->ipa_lock); |
