|
@@ -0,0 +1,936 @@
|
|
|
+/*
|
|
|
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
|
|
|
+ *
|
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
|
+ * it under the terms of the GNU General Public License version 2 and
|
|
|
+ * only version 2 as published by the Free Software Foundation.
|
|
|
+ *
|
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
+ * GNU General Public License for more details.
|
|
|
+ */
|
|
|
+
|
|
|
+/*=== includes ===*/
|
|
|
+/* header files for OS primitives */
|
|
|
+#include <osdep.h> /* uint32_t, etc. */
|
|
|
+#include <qdf_mem.h> /* qdf_mem_malloc,free */
|
|
|
+#include <qdf_types.h> /* qdf_device_t, qdf_print */
|
|
|
+#include <qdf_lock.h> /* qdf_spinlock */
|
|
|
+#include <qdf_atomic.h> /* qdf_atomic_read */
|
|
|
+
|
|
|
+#if defined(HIF_PCI) || defined(HIF_SNOC) || defined(HIF_AHB)
|
|
|
+/* Required for WLAN_FEATURE_FASTPATH */
|
|
|
+#include <ce_api.h>
|
|
|
+#endif
|
|
|
+/* header files for utilities */
|
|
|
+#include <cds_queue.h> /* TAILQ */
|
|
|
+
|
|
|
+/* header files for configuration API */
|
|
|
+#include <ol_cfg.h> /* ol_cfg_is_high_latency */
|
|
|
+#include <ol_if_athvar.h>
|
|
|
+
|
|
|
+/* header files for HTT API */
|
|
|
+#include <ol_htt_api.h>
|
|
|
+#include <ol_htt_tx_api.h>
|
|
|
+
|
|
|
+/* header files for our own APIs */
|
|
|
+#include <ol_txrx_api.h>
|
|
|
+#include <ol_txrx_dbg.h>
|
|
|
+#include <cdp_txrx_ocb.h>
|
|
|
+#include <ol_txrx_ctrl_api.h>
|
|
|
+#include <cdp_txrx_stats.h>
|
|
|
+#include <ol_txrx_osif_api.h>
|
|
|
+/* header files for our internal definitions */
|
|
|
+#include <ol_txrx_internal.h> /* TXRX_ASSERT, etc. */
|
|
|
+#include <wdi_event.h> /* WDI events */
|
|
|
+#include <ol_tx.h> /* ol_tx_ll */
|
|
|
+#include <ol_rx.h> /* ol_rx_deliver */
|
|
|
+#include <ol_txrx_peer_find.h> /* ol_txrx_peer_find_attach, etc. */
|
|
|
+#include <ol_rx_pn.h> /* ol_rx_pn_check, etc. */
|
|
|
+#include <ol_rx_fwd.h> /* ol_rx_fwd_check, etc. */
|
|
|
+#include <ol_rx_reorder_timeout.h> /* OL_RX_REORDER_TIMEOUT_INIT, etc. */
|
|
|
+#include <ol_rx_reorder.h>
|
|
|
+#include <ol_tx_send.h> /* ol_tx_discard_target_frms */
|
|
|
+#include <ol_tx_desc.h> /* ol_tx_desc_frame_free */
|
|
|
+#include <ol_tx_queue.h>
|
|
|
+#include <ol_tx_sched.h> /* ol_tx_sched_attach, etc. */
|
|
|
+#include <ol_txrx.h>
|
|
|
+#include <ol_txrx_types.h>
|
|
|
+#include <cdp_txrx_flow_ctrl_legacy.h>
|
|
|
+#include <cdp_txrx_bus.h>
|
|
|
+#include <cdp_txrx_ipa.h>
|
|
|
+#include <cdp_txrx_pmf.h>
|
|
|
+#include "wma.h"
|
|
|
+#include "hif.h"
|
|
|
+#include <cdp_txrx_peer_ops.h>
|
|
|
+#ifndef REMOVE_PKT_LOG
|
|
|
+#include "pktlog_ac.h"
|
|
|
+#endif
|
|
|
+#include "epping_main.h"
|
|
|
+#include <a_types.h>
|
|
|
+
|
|
|
+#ifdef IPA_OFFLOAD
|
|
|
+#include <ol_txrx_ipa.h>
|
|
|
+
|
|
|
+/* For Tx pipes, use Ethernet-II Header format */
|
|
|
+struct ol_txrx_ipa_uc_tx_hdr ipa_uc_tx_hdr = {
|
|
|
+ {
|
|
|
+ 0x0000,
|
|
|
+ 0x00000000,
|
|
|
+ 0x00000000
|
|
|
+ },
|
|
|
+ {
|
|
|
+ 0x00000000
|
|
|
+ },
|
|
|
+ {
|
|
|
+ {0x00, 0x03, 0x7f, 0xaa, 0xbb, 0xcc},
|
|
|
+ {0x00, 0x03, 0x7f, 0xdd, 0xee, 0xff},
|
|
|
+ 0x0008
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * ol_txrx_ipa_uc_get_resource() - Client request resource information
|
|
|
+ * @pdev: handle to the HTT instance
|
|
|
+ *
|
|
|
+ * OL client will reuqest IPA UC related resource information
|
|
|
+ * Resource information will be distributted to IPA module
|
|
|
+ * All of the required resources should be pre-allocated
|
|
|
+ *
|
|
|
+ * Return: QDF_STATUS
|
|
|
+ */
|
|
|
+QDF_STATUS ol_txrx_ipa_uc_get_resource(struct cdp_pdev *ppdev)
|
|
|
+{
|
|
|
+ ol_txrx_pdev_handle pdev = (ol_txrx_pdev_handle)ppdev;
|
|
|
+ struct ol_txrx_ipa_resources *ipa_res = &pdev->ipa_resource;
|
|
|
+
|
|
|
+ htt_ipa_uc_get_resource(pdev->htt_pdev,
|
|
|
+ &ipa_res->ce_sr_base_paddr,
|
|
|
+ &ipa_res->ce_sr_ring_size,
|
|
|
+ &ipa_res->ce_reg_paddr,
|
|
|
+ &ipa_res->tx_comp_ring_base_paddr,
|
|
|
+ &ipa_res->tx_comp_ring_size,
|
|
|
+ &ipa_res->tx_num_alloc_buffer,
|
|
|
+ &ipa_res->rx_rdy_ring_base_paddr,
|
|
|
+ &ipa_res->rx_rdy_ring_size,
|
|
|
+ &ipa_res->rx_proc_done_idx_paddr,
|
|
|
+ &ipa_res->rx_proc_done_idx_vaddr,
|
|
|
+ &ipa_res->rx2_rdy_ring_base_paddr,
|
|
|
+ &ipa_res->rx2_rdy_ring_size,
|
|
|
+ &ipa_res->rx2_proc_done_idx_paddr,
|
|
|
+ &ipa_res->rx2_proc_done_idx_vaddr);
|
|
|
+
|
|
|
+ if ((0 == ipa_res->ce_sr_base_paddr) ||
|
|
|
+ (0 == ipa_res->tx_comp_ring_base_paddr) ||
|
|
|
+ (0 == ipa_res->rx_rdy_ring_base_paddr)
|
|
|
+#if defined(QCA_WIFI_3_0) && defined(CONFIG_IPA3)
|
|
|
+ || (0 == ipa_res->rx2_rdy_ring_base_paddr)
|
|
|
+#endif
|
|
|
+ )
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * ol_txrx_ipa_uc_set_doorbell_paddr() - Client set IPA UC doorbell register
|
|
|
+ * @pdev: handle to the HTT instance
|
|
|
+ *
|
|
|
+ * IPA UC let know doorbell register physical address
|
|
|
+ * WLAN firmware will use this physical address to notify IPA UC
|
|
|
+ *
|
|
|
+ * Return: QDF_STATUS
|
|
|
+ */
|
|
|
+QDF_STATUS ol_txrx_ipa_uc_set_doorbell_paddr(struct cdp_pdev *ppdev)
|
|
|
+{
|
|
|
+ ol_txrx_pdev_handle pdev = (ol_txrx_pdev_handle)ppdev;
|
|
|
+ struct ol_txrx_ipa_resources *ipa_res = &pdev->ipa_resource;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = htt_ipa_uc_set_doorbell_paddr(pdev->htt_pdev,
|
|
|
+ ipa_res->tx_comp_doorbell_paddr,
|
|
|
+ ipa_res->rx_ready_doorbell_paddr);
|
|
|
+
|
|
|
+ if (ret) {
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
|
|
|
+ "htt_ipa_uc_set_doorbell_paddr fail: %d", ret);
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+ }
|
|
|
+
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * ol_txrx_ipa_uc_set_active() - Client notify IPA UC data path active or not
|
|
|
+ * @pdev: handle to the HTT instance
|
|
|
+ * @uc_active: WDI UC path enable or not
|
|
|
+ * @is_tx: TX path or RX path
|
|
|
+ *
|
|
|
+ * IPA UC let know doorbell register physical address
|
|
|
+ * WLAN firmware will use this physical address to notify IPA UC
|
|
|
+ *
|
|
|
+ * Return: QDF_STATUS
|
|
|
+ */
|
|
|
+QDF_STATUS ol_txrx_ipa_uc_set_active(struct cdp_pdev *ppdev, bool uc_active,
|
|
|
+ bool is_tx)
|
|
|
+{
|
|
|
+ ol_txrx_pdev_handle pdev = (ol_txrx_pdev_handle)ppdev;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = htt_h2t_ipa_uc_set_active(pdev->htt_pdev, uc_active, is_tx);
|
|
|
+ if (ret) {
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
|
|
|
+ "htt_h2t_ipa_uc_set_active fail: %d", ret);
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+ }
|
|
|
+
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * ol_txrx_ipa_uc_op_response() - Handle OP command response from firmware
|
|
|
+ * @pdev: handle to the device instance
|
|
|
+ * @op_msg: op response message from firmware
|
|
|
+ *
|
|
|
+ * Return: none
|
|
|
+ */
|
|
|
+QDF_STATUS ol_txrx_ipa_uc_op_response(struct cdp_pdev *ppdev, uint8_t *op_msg)
|
|
|
+{
|
|
|
+ ol_txrx_pdev_handle pdev = (ol_txrx_pdev_handle)ppdev;
|
|
|
+
|
|
|
+ if (pdev->ipa_uc_op_cb) {
|
|
|
+ pdev->ipa_uc_op_cb(op_msg, pdev->usr_ctxt);
|
|
|
+ } else {
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
|
|
|
+ "%s: IPA callback function is not registered", __func__);
|
|
|
+ qdf_mem_free(op_msg);
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+ }
|
|
|
+
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * ol_txrx_ipa_uc_register_op_cb() - Register OP handler function
|
|
|
+ * @pdev: handle to the device instance
|
|
|
+ * @op_cb: handler function pointer
|
|
|
+ *
|
|
|
+ * Return: none
|
|
|
+ */
|
|
|
+QDF_STATUS ol_txrx_ipa_uc_register_op_cb(struct cdp_pdev *ppdev,
|
|
|
+ ipa_uc_op_cb_type op_cb, void *usr_ctxt)
|
|
|
+{
|
|
|
+ ol_txrx_pdev_handle pdev = (ol_txrx_pdev_handle)ppdev;
|
|
|
+
|
|
|
+ pdev->ipa_uc_op_cb = op_cb;
|
|
|
+ pdev->usr_ctxt = usr_ctxt;
|
|
|
+
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * ol_txrx_ipa_uc_get_stat() - Get firmware wdi status
|
|
|
+ * @pdev: handle to the HTT instance
|
|
|
+ *
|
|
|
+ * Return: none
|
|
|
+ */
|
|
|
+QDF_STATUS ol_txrx_ipa_uc_get_stat(struct cdp_pdev *ppdev)
|
|
|
+{
|
|
|
+ ol_txrx_pdev_handle pdev = (ol_txrx_pdev_handle)ppdev;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = htt_h2t_ipa_uc_get_stats(pdev->htt_pdev);
|
|
|
+
|
|
|
+ if (ret) {
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
|
|
|
+ "htt_h2t_ipa_uc_get_stats fail: %d", ret);
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+ }
|
|
|
+
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * ol_txrx_ipa_enable_autonomy() - Enable autonomy RX path
|
|
|
+ * @pdev: handle to the device instance
|
|
|
+ *
|
|
|
+ * Set all RX packet route to IPA
|
|
|
+ * Return: none
|
|
|
+ */
|
|
|
+QDF_STATUS ol_txrx_ipa_enable_autonomy(struct cdp_pdev *ppdev)
|
|
|
+{
|
|
|
+ /* TBD */
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * ol_txrx_ipa_disable_autonomy() - Disable autonomy RX path
|
|
|
+ * @pdev: handle to the device instance
|
|
|
+ *
|
|
|
+ * Disable RX packet route to host
|
|
|
+ * Return: none
|
|
|
+ */
|
|
|
+QDF_STATUS ol_txrx_ipa_disable_autonomy(struct cdp_pdev *ppdev)
|
|
|
+{
|
|
|
+ /* TBD */
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * ol_txrx_ipa_setup() - Setup and connect IPA pipes
|
|
|
+ * @pdev: handle to the device instance
|
|
|
+ * @ipa_i2w_cb: IPA to WLAN callback
|
|
|
+ * @ipa_w2i_cb: WLAN to IPA callback
|
|
|
+ * @ipa_wdi_meter_notifier_cb: IPA WDI metering callback
|
|
|
+ * @ipa_desc_size: IPA descriptor size
|
|
|
+ * @ipa_priv: handle to the HTT instance
|
|
|
+ * @is_rm_enabled: Is IPA RM enabled or not
|
|
|
+ * @p_tx_pipe_handle: pointer to Tx pipe handle
|
|
|
+ * @p_rx_pipe_handle: pointer to Rx pipe handle
|
|
|
+ *
|
|
|
+ * Return: QDF_STATUS
|
|
|
+ */
|
|
|
+QDF_STATUS ol_txrx_ipa_setup(struct cdp_pdev *ppdev, void *ipa_i2w_cb,
|
|
|
+ void *ipa_w2i_cb, void *ipa_wdi_meter_notifier_cb,
|
|
|
+ uint32_t ipa_desc_size, void *ipa_priv,
|
|
|
+ bool is_rm_enabled, uint32_t *p_tx_pipe_handle,
|
|
|
+ uint32_t *p_rx_pipe_handle)
|
|
|
+
|
|
|
+{
|
|
|
+ ol_txrx_pdev_handle pdev = (ol_txrx_pdev_handle)ppdev;
|
|
|
+ struct ol_txrx_ipa_resources *ipa_res = &pdev->ipa_resource;
|
|
|
+ struct ipa_wdi_in_params pipe_in;
|
|
|
+ struct ipa_wdi_out_params pipe_out;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ 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 = OL_TXRX_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 =
|
|
|
+ OL_TXRX_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 = ipa_desc_size;
|
|
|
+ pipe_in.sys.priv = ipa_priv;
|
|
|
+ pipe_in.sys.ipa_ep_cfg.hdr_ext.hdr_little_endian = true;
|
|
|
+ pipe_in.sys.notify = ipa_i2w_cb;
|
|
|
+ if (!is_rm_enabled) {
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
|
|
|
+ "%s: IPA RM DISABLED, IPA AWAKE", __func__);
|
|
|
+ pipe_in.sys.keep_ipa_awake = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ pipe_in.u.dl.comp_ring_base_pa = ipa_res->tx_comp_ring_base_paddr;
|
|
|
+ pipe_in.u.dl.comp_ring_size = ipa_res->tx_comp_ring_size *
|
|
|
+ sizeof(qdf_dma_addr_t);
|
|
|
+ pipe_in.u.dl.ce_ring_base_pa = ipa_res->ce_sr_base_paddr;
|
|
|
+ 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;
|
|
|
+
|
|
|
+ /* Connect WDI IPA PIPE */
|
|
|
+ ret = ipa_connect_wdi_pipe(&pipe_in, &pipe_out);
|
|
|
+ if (ret) {
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
|
|
|
+ "ipa_connect_wdi_pipe: Tx pipe setup failed: ret=%d", ret);
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Micro Controller Doorbell register */
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
|
|
|
+ "%s CONS DB pipe out 0x%x TX PIPE Handle 0x%x",
|
|
|
+ __func__, (unsigned int)pipe_out.uc_door_bell_pa,
|
|
|
+ pipe_out.clnt_hdl);
|
|
|
+ ipa_res->tx_comp_doorbell_paddr = pipe_out.uc_door_bell_pa;
|
|
|
+ /* WLAN TX PIPE Handle */
|
|
|
+ ipa_res->tx_pipe_handle = pipe_out.clnt_hdl;
|
|
|
+ *p_tx_pipe_handle = pipe_out.clnt_hdl;
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
|
|
|
+ "TX: %s 0x%x, %s %d, %s 0x%x, %s 0x%x, %s %d, %sNB %d, %s 0x%x",
|
|
|
+ "comp_ring_base_pa",
|
|
|
+ (unsigned int)pipe_in.u.dl.comp_ring_base_pa,
|
|
|
+ "comp_ring_size",
|
|
|
+ pipe_in.u.dl.comp_ring_size,
|
|
|
+ "ce_ring_base_pa",
|
|
|
+ (unsigned int)pipe_in.u.dl.ce_ring_base_pa,
|
|
|
+ "ce_door_bell_pa",
|
|
|
+ (unsigned int)pipe_in.u.dl.ce_door_bell_pa,
|
|
|
+ "ce_ring_size",
|
|
|
+ pipe_in.u.dl.ce_ring_size,
|
|
|
+ "num_tx_buffers",
|
|
|
+ pipe_in.u.dl.num_tx_buffers,
|
|
|
+ "tx_comp_doorbell_paddr",
|
|
|
+ (unsigned int)ipa_res->tx_comp_doorbell_paddr);
|
|
|
+
|
|
|
+ /* RX PIPE */
|
|
|
+ pipe_in.sys.ipa_ep_cfg.nat.nat_en = IPA_BYPASS_NAT;
|
|
|
+ pipe_in.sys.ipa_ep_cfg.hdr.hdr_len = OL_TXRX_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 = ipa_desc_size + sizeof(struct sps_iovec);
|
|
|
+ pipe_in.sys.notify = ipa_w2i_cb;
|
|
|
+ if (!is_rm_enabled) {
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
|
|
|
+ "%s: IPA RM DISABLED, IPA AWAKE", __func__);
|
|
|
+ pipe_in.sys.keep_ipa_awake = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ pipe_in.u.ul.rdy_ring_base_pa = ipa_res->rx_rdy_ring_base_paddr;
|
|
|
+ pipe_in.u.ul.rdy_ring_size = ipa_res->rx_rdy_ring_size;
|
|
|
+ pipe_in.u.ul.rdy_ring_rp_pa = ipa_res->rx_proc_done_idx_paddr;
|
|
|
+ OL_TXRX_IPA_WDI2_SET(pipe_in, ipa_res);
|
|
|
+
|
|
|
+#ifdef FEATURE_METERING
|
|
|
+ pipe_in.wdi_notify = ipa_wdi_meter_notifier_cb;
|
|
|
+#endif
|
|
|
+
|
|
|
+ ret = ipa_connect_wdi_pipe(&pipe_in, &pipe_out);
|
|
|
+ if (ret) {
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
|
|
|
+ "ipa_connect_wdi_pipe: Rx pipe setup failed: ret=%d", ret);
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+ }
|
|
|
+ ipa_res->rx_ready_doorbell_paddr = pipe_out.uc_door_bell_pa;
|
|
|
+ ipa_res->rx_pipe_handle = pipe_out.clnt_hdl;
|
|
|
+ *p_rx_pipe_handle = pipe_out.clnt_hdl;
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
|
|
|
+ "RX: %s 0x%x, %s %d, %s 0x%x, %s 0x%x",
|
|
|
+ "rdy_ring_base_pa",
|
|
|
+ (unsigned int)pipe_in.u.ul.rdy_ring_base_pa,
|
|
|
+ "rdy_ring_size",
|
|
|
+ pipe_in.u.ul.rdy_ring_size,
|
|
|
+ "rdy_ring_rp_pa",
|
|
|
+ (unsigned int)pipe_in.u.ul.rdy_ring_rp_pa,
|
|
|
+ "rx_ready_doorbell_paddr",
|
|
|
+ (unsigned int)ipa_res->rx_ready_doorbell_paddr);
|
|
|
+
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * ol_txrx_ipa_cleanup() - Disconnect IPA pipes
|
|
|
+ * @tx_pipe_handle: Tx pipe handle
|
|
|
+ * @rx_pipe_handle: Rx pipe handle
|
|
|
+ *
|
|
|
+ * Return: QDF_STATUS
|
|
|
+ */
|
|
|
+QDF_STATUS ol_txrx_ipa_cleanup(uint32_t tx_pipe_handle, uint32_t rx_pipe_handle)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
|
|
|
+ "%s: Disconnect TX PIPE tx_pipe_handle=0x%x",
|
|
|
+ __func__, tx_pipe_handle);
|
|
|
+ ret = ipa_disconnect_wdi_pipe(tx_pipe_handle);
|
|
|
+ if (ret) {
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
|
|
|
+ "ipa_disconnect_wdi_pipe: Tx pipe cleanup failed: ret=%d",
|
|
|
+ ret);
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+ }
|
|
|
+
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
|
|
|
+ "%s: Disconnect RX PIPE rx_pipe_handle=0x%x",
|
|
|
+ __func__, rx_pipe_handle);
|
|
|
+ ret = ipa_disconnect_wdi_pipe(rx_pipe_handle);
|
|
|
+ if (ret) {
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
|
|
|
+ "ipa_disconnect_wdi_pipe: Rx pipe cleanup failed: ret=%d",
|
|
|
+ ret);
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+ }
|
|
|
+
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * ol_txrx_remove_ipa_header() - Remove a specific header from IPA
|
|
|
+ * @name: Name of the header to be removed
|
|
|
+ *
|
|
|
+ * Return: QDF_STATUS
|
|
|
+ */
|
|
|
+static QDF_STATUS ol_txrx_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) {
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
|
|
|
+ "Hdr deleted already %s, %d", name, ret);
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+ }
|
|
|
+
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, 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) {
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
|
|
|
+ "ipa_hdr allocation failed");
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+ }
|
|
|
+ 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) {
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
|
|
|
+ "Delete header failed: %d", ret);
|
|
|
+ qdf_mem_free(ipa_hdr);
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+ }
|
|
|
+
|
|
|
+ qdf_mem_free(ipa_hdr);
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * ol_txrx_ipa_add_header_info() - Add IPA header for a given interface
|
|
|
+ * @ifname: Interface name
|
|
|
+ * @mac_addr: Interface MAC address
|
|
|
+ * @is_ipv6_enabled: Is IPV6 enabled or not
|
|
|
+ *
|
|
|
+ * Return: 0 on success, negativer errno value on error
|
|
|
+ */
|
|
|
+static int ol_txrx_ipa_add_header_info(char *ifname, uint8_t *mac_addr,
|
|
|
+ uint8_t session_id, bool is_ipv6_enabled)
|
|
|
+{
|
|
|
+ struct ipa_ioc_add_hdr *ipa_hdr = NULL;
|
|
|
+ int ret = -EINVAL;
|
|
|
+ struct ol_txrx_ipa_uc_tx_hdr *uc_tx_hdr = NULL;
|
|
|
+
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, 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) {
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
|
|
|
+ "%s: ipa_hdr allocation failed", ifname);
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto end;
|
|
|
+ }
|
|
|
+
|
|
|
+ ipa_hdr->commit = 0;
|
|
|
+ ipa_hdr->num_hdrs = 1;
|
|
|
+
|
|
|
+ uc_tx_hdr = (struct ol_txrx_ipa_uc_tx_hdr *)ipa_hdr->hdr[0].hdr;
|
|
|
+ memcpy(uc_tx_hdr, &ipa_uc_tx_hdr, OL_TXRX_IPA_UC_WLAN_TX_HDR_LEN);
|
|
|
+ memcpy(uc_tx_hdr->eth.h_source, mac_addr, ETH_ALEN);
|
|
|
+ uc_tx_hdr->ipa_hd.vdev_id = session_id;
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, 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, OL_TXRX_IPA_IPV4_NAME_EXT);
|
|
|
+ ipa_hdr->hdr[0].hdr_len = OL_TXRX_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 = OL_TXRX_IPA_UC_WLAN_HDR_DES_MAC_OFFSET;
|
|
|
+
|
|
|
+ ret = ipa_add_hdr(ipa_hdr);
|
|
|
+ if (ret) {
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
|
|
|
+ "%s IPv4 add hdr failed: %d", ifname, ret);
|
|
|
+ goto end;
|
|
|
+ }
|
|
|
+
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
|
|
|
+ "%s: IPv4 hdr_hdl: 0x%x",
|
|
|
+ ipa_hdr->hdr[0].name, ipa_hdr->hdr[0].hdr_hdl);
|
|
|
+
|
|
|
+ if (is_ipv6_enabled) {
|
|
|
+ snprintf(ipa_hdr->hdr[0].name, IPA_RESOURCE_NAME_MAX, "%s%s",
|
|
|
+ ifname, OL_TXRX_IPA_IPV6_NAME_EXT);
|
|
|
+
|
|
|
+ uc_tx_hdr = (struct ol_txrx_ipa_uc_tx_hdr *)ipa_hdr->hdr[0].hdr;
|
|
|
+ uc_tx_hdr->eth.h_proto = cpu_to_be16(ETH_P_IPV6);
|
|
|
+
|
|
|
+ ret = ipa_add_hdr(ipa_hdr);
|
|
|
+ if (ret) {
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
|
|
|
+ "%s: IPv6 add hdr failed: %d", ifname, ret);
|
|
|
+ goto clean_ipv4_hdr;
|
|
|
+ }
|
|
|
+
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, 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, OL_TXRX_IPA_IPV4_NAME_EXT);
|
|
|
+ ol_txrx_ipa_remove_header(ipa_hdr->hdr[0].name);
|
|
|
+end:
|
|
|
+ if (ipa_hdr)
|
|
|
+ qdf_mem_free(ipa_hdr);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * ol_txrx_ipa_register_interface() - register IPA interface
|
|
|
+ * @ifname: Interface name
|
|
|
+ * @prod_client: IPA prod client type
|
|
|
+ * @cons_client: IPA cons client type
|
|
|
+ * @session_id: Session ID
|
|
|
+ * @is_ipv6_enabled: Is IPV6 enabled or not
|
|
|
+ *
|
|
|
+ * Return: 0 on success, negative errno on error
|
|
|
+ */
|
|
|
+static int ol_txrx_ipa_register_interface(char *ifname,
|
|
|
+ enum ipa_client_type prod_client,
|
|
|
+ enum ipa_client_type cons_client,
|
|
|
+ uint8_t session_id,
|
|
|
+ bool is_ipv6_enabled)
|
|
|
+{
|
|
|
+ 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 ipv4_hdr_name[IPA_RESOURCE_NAME_MAX];
|
|
|
+ char ipv6_hdr_name[IPA_RESOURCE_NAME_MAX];
|
|
|
+
|
|
|
+ int num_prop = 1;
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ if (is_ipv6_enabled)
|
|
|
+ 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) {
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, 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) {
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, 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, OL_TXRX_IPA_IPV4_NAME_EXT);
|
|
|
+ snprintf(ipv6_hdr_name, IPA_RESOURCE_NAME_MAX, "%s%s",
|
|
|
+ ifname, OL_TXRX_IPA_IPV6_NAME_EXT);
|
|
|
+
|
|
|
+ rx_prop[IPA_IP_v4].ip = IPA_IP_v4;
|
|
|
+ rx_prop[IPA_IP_v4].src_pipe = 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(session_id << 16);
|
|
|
+ rx_prop[IPA_IP_v4].attrib.meta_data_mask = htonl(0x00FF0000);
|
|
|
+
|
|
|
+ rx_intf.num_props++;
|
|
|
+ if (is_ipv6_enabled) {
|
|
|
+ rx_prop[IPA_IP_v6].ip = IPA_IP_v6;
|
|
|
+ rx_prop[IPA_IP_v6].src_pipe = prod_client;
|
|
|
+ rx_prop[IPA_IP_v6].hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
|
|
|
+ rx_prop[IPA_IP_v6].attrib.attrib_mask = IPA_FLT_META_DATA;
|
|
|
+ rx_prop[IPA_IP_v6].attrib.meta_data =
|
|
|
+ htonl(session_id << 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 = cons_client;
|
|
|
+ strlcpy(tx_prop[IPA_IP_v4].hdr_name, ipv4_hdr_name,
|
|
|
+ IPA_RESOURCE_NAME_MAX);
|
|
|
+ tx_intf.num_props++;
|
|
|
+
|
|
|
+ if (is_ipv6_enabled) {
|
|
|
+ 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 = 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;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * ol_txrx_ipa_setup_iface() - Setup IPA header and register interface
|
|
|
+ * @ifname: Interface name
|
|
|
+ * @mac_addr: Interface MAC address
|
|
|
+ * @prod_client: IPA prod client type
|
|
|
+ * @cons_client: IPA cons client type
|
|
|
+ * @session_id: Session ID
|
|
|
+ * @is_ipv6_enabled: Is IPV6 enabled or not
|
|
|
+ *
|
|
|
+ * Return: QDF_STATUS
|
|
|
+ */
|
|
|
+QDF_STATUS ol_txrx_ipa_setup_iface(char *ifname, uint8_t *mac_addr,
|
|
|
+ enum ipa_client_type prod_client,
|
|
|
+ enum ipa_client_type cons_client,
|
|
|
+ uint8_t session_id, bool is_ipv6_enabled)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = ol_txrx_ipa_add_header_info(ifname, mac_addr, session_id,
|
|
|
+ is_ipv6_enabled);
|
|
|
+ if (ret)
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+
|
|
|
+ /* Configure the TX and RX pipes filter rules */
|
|
|
+ ret = ol_txrx_ipa_register_interface(ifname, prod_client, cons_client,
|
|
|
+ session_id, is_ipv6_enabled);
|
|
|
+ if (ret)
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * ol_txrx_ipa_cleanup_iface() - Cleanup IPA header and deregister interface
|
|
|
+ * @ifname: Interface name
|
|
|
+ * @is_ipv6_enabled: Is IPV6 enabled or not
|
|
|
+ *
|
|
|
+ * Return: QDF_STATUS
|
|
|
+ */
|
|
|
+QDF_STATUS ol_txrx_ipa_cleanup_iface(char *ifname, bool is_ipv6_enabled)
|
|
|
+{
|
|
|
+ char name_ipa[IPA_RESOURCE_NAME_MAX];
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ /* Remove the headers */
|
|
|
+ snprintf(name_ipa, IPA_RESOURCE_NAME_MAX, "%s%s",
|
|
|
+ ifname, OL_TXRX_IPA_IPV4_NAME_EXT);
|
|
|
+ ol_txrx_ipa_remove_header(name_ipa);
|
|
|
+
|
|
|
+ if (is_ipv6_enabled) {
|
|
|
+ snprintf(name_ipa, IPA_RESOURCE_NAME_MAX, "%s%s",
|
|
|
+ ifname, OL_TXRX_IPA_IPV6_NAME_EXT);
|
|
|
+ ol_txrx_ipa_remove_header(name_ipa);
|
|
|
+ }
|
|
|
+ /* unregister the interface with IPA */
|
|
|
+ ret = ipa_deregister_intf(ifname);
|
|
|
+ if (ret) {
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
|
|
|
+ "%s: ipa_deregister_intf fail: %d",
|
|
|
+ ifname, ret);
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+ }
|
|
|
+
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * ol_txrx_ipa_uc_enable_pipes() - Enable and resume traffic on Tx/Rx pipes
|
|
|
+ * @pdev: handle to the device instance
|
|
|
+ *
|
|
|
+ * Return: QDF_STATUS
|
|
|
+ */
|
|
|
+QDF_STATUS ol_txrx_ipa_enable_pipes(struct cdp_pdev *ppdev)
|
|
|
+{
|
|
|
+ ol_txrx_pdev_handle pdev = (ol_txrx_pdev_handle)ppdev;
|
|
|
+ struct ol_txrx_ipa_resources *ipa_res = &pdev->ipa_resource;
|
|
|
+ int result;
|
|
|
+
|
|
|
+ /* ACTIVATE TX PIPE */
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
|
|
|
+ "%s: Enable TX PIPE(tx_pipe_handle=%d)",
|
|
|
+ __func__, ipa_res->tx_pipe_handle);
|
|
|
+ result = ipa_enable_wdi_pipe(ipa_res->tx_pipe_handle);
|
|
|
+ if (result) {
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
|
|
|
+ "%s: Enable TX PIPE fail, code %d",
|
|
|
+ __func__, result);
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+ }
|
|
|
+ result = ipa_resume_wdi_pipe(ipa_res->tx_pipe_handle);
|
|
|
+ if (result) {
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
|
|
|
+ "%s: Resume TX PIPE fail, code %d",
|
|
|
+ __func__, result);
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+ }
|
|
|
+ ol_txrx_ipa_uc_set_active((struct cdp_pdev *)pdev, true, true);
|
|
|
+
|
|
|
+ /* ACTIVATE RX PIPE */
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
|
|
|
+ "%s: Enable RX PIPE(rx_pipe_handle=%d)",
|
|
|
+ __func__, ipa_res->rx_pipe_handle);
|
|
|
+ result = ipa_enable_wdi_pipe(ipa_res->rx_pipe_handle);
|
|
|
+ if (result) {
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
|
|
|
+ "%s: Enable RX PIPE fail, code %d",
|
|
|
+ __func__, result);
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+ }
|
|
|
+ result = ipa_resume_wdi_pipe(ipa_res->rx_pipe_handle);
|
|
|
+ if (result) {
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
|
|
|
+ "%s: Resume RX PIPE fail, code %d",
|
|
|
+ __func__, result);
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+ }
|
|
|
+ ol_txrx_ipa_uc_set_active((struct cdp_pdev *)pdev, true, false);
|
|
|
+
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * ol_txrx_ipa_uc_disable_pipes() – Suspend traffic and disable Tx/Rx pipes
|
|
|
+ * @pdev: handle to the device instance
|
|
|
+ *
|
|
|
+ * Return: QDF_STATUS
|
|
|
+ */
|
|
|
+QDF_STATUS ol_txrx_ipa_disable_pipes(struct cdp_pdev *ppdev)
|
|
|
+{
|
|
|
+ ol_txrx_pdev_handle pdev = (ol_txrx_pdev_handle)ppdev;
|
|
|
+ struct ol_txrx_ipa_resources *ipa_res = &pdev->ipa_resource;
|
|
|
+ int result;
|
|
|
+
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
|
|
|
+ "%s: Disable RX PIPE", __func__);
|
|
|
+ result = ipa_suspend_wdi_pipe(ipa_res->rx_pipe_handle);
|
|
|
+ if (result) {
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
|
|
|
+ "%s: Suspend RX PIPE fail, code %d",
|
|
|
+ __func__, result);
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+ }
|
|
|
+
|
|
|
+ result = ipa_disable_wdi_pipe(ipa_res->rx_pipe_handle);
|
|
|
+ if (result) {
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
|
|
|
+ "%s: Disable RX PIPE fail, code %d",
|
|
|
+ __func__, result);
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+ }
|
|
|
+
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
|
|
|
+ "%s: Disable TX PIPE", __func__);
|
|
|
+ result = ipa_suspend_wdi_pipe(ipa_res->tx_pipe_handle);
|
|
|
+ if (result) {
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
|
|
|
+ "%s: Suspend TX PIPE fail, code %d",
|
|
|
+ __func__, result);
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+ }
|
|
|
+ result = ipa_disable_wdi_pipe(ipa_res->tx_pipe_handle);
|
|
|
+ if (result) {
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
|
|
|
+ "%s: Disable TX PIPE fail, code %d",
|
|
|
+ __func__, result);
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+ }
|
|
|
+
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * ol_txrx_ipa_set_perf_level() - Set IPA clock bandwidth based on data rates
|
|
|
+ * @client: Client type
|
|
|
+ * @max_supported_bw_mbps: Maximum bandwidth needed (in Mbps)
|
|
|
+ *
|
|
|
+ * Return: QDF_STATUS
|
|
|
+ */
|
|
|
+QDF_STATUS ol_txrx_ipa_set_perf_level(int client,
|
|
|
+ uint32_t max_supported_bw_mbps)
|
|
|
+{
|
|
|
+ struct ipa_rm_perf_profile profile;
|
|
|
+ int result;
|
|
|
+
|
|
|
+ profile.max_supported_bandwidth_mbps = max_supported_bw_mbps;
|
|
|
+ result = ipa_rm_set_perf_profile(client, &profile);
|
|
|
+
|
|
|
+ if (result) {
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
|
|
|
+ "Set perf profile failed, code %d", result);
|
|
|
+
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+ }
|
|
|
+
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+#ifdef FEATURE_METERING
|
|
|
+QDF_STATUS ol_txrx_ipa_uc_get_share_stats(struct cdp_pdev *ppdev,
|
|
|
+ uint8_t reset_stats)
|
|
|
+{
|
|
|
+ struct ol_txrx_pdev_t *pdev = (struct ol_txrx_pdev_t *)ppdev;
|
|
|
+ int result;
|
|
|
+
|
|
|
+ result = htt_h2t_ipa_uc_get_share_stats(pdev->htt_pdev, reset_stats);
|
|
|
+
|
|
|
+ if (result) {
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
|
|
|
+ "Get IPA sharing stats failed, code %d", result);
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+ }
|
|
|
+
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+QDF_STATUS ol_txrx_ipa_uc_set_quota(struct cdp_pdev *ppdev,
|
|
|
+ uint64_t quota_bytes)
|
|
|
+{
|
|
|
+ struct ol_txrx_pdev_t *pdev = (struct ol_txrx_pdev_t *)ppdev;
|
|
|
+ int result;
|
|
|
+
|
|
|
+ result = htt_h2t_ipa_uc_set_quota(pdev->htt_pdev, quota_bytes);
|
|
|
+
|
|
|
+ if (result) {
|
|
|
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
|
|
|
+ "Set IPA quota failed, code %d", result);
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+ }
|
|
|
+
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
+}
|
|
|
+#endif
|
|
|
+#endif /* IPA_UC_OFFLOAD */
|