summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/hdd/src/wlan_hdd_ipa.c3314
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);