/*
 * Copyright (c) 2013-2017 The Linux Foundation. All rights reserved.
 *
 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
 *
 *
 * Permission to use, copy, modify, and/or distribute this software for
 * any purpose with or without fee is hereby granted, provided that the
 * above copyright notice and this permission notice appear in all
 * copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * This file was originally distributed by Qualcomm Atheros, Inc.
 * under proprietary terms before Copyright ownership was assigned
 * to the Linux Foundation.
 */

/**
 * DOC: target_if_wifi_pos.c
 * This file defines the functions pertinent to wifi positioning component's
 * target if layer.
 */
#include "../../../../umac/wifi_pos/src/wifi_pos_utils_i.h"
#include "wmi_unified_api.h"
#include "wlan_lmac_if_def.h"
#include "target_if_wifi_pos.h"
#include "../../../../umac/wifi_pos/src/wifi_pos_main_i.h"
#include "target_if.h"
#ifdef WLAN_FEATURE_CIF_CFR
#include "hal_api.h"

#define RING_BASE_ALIGN 8

static void *target_if_wifi_pos_vaddr_lookup(
				struct wifi_pos_psoc_priv_obj *priv,
				void *paddr, uint8_t ring_num, uint32_t cookie)
{
	if (priv->dma_buf_pool[ring_num][cookie].paddr == paddr) {
		return priv->dma_buf_pool[ring_num][cookie].vaddr +
				priv->dma_buf_pool[ring_num][cookie].offset;
	} else {
		target_if_err("incorrect paddr found on cookie slot");
		return NULL;
	}
}

static QDF_STATUS target_if_wifi_pos_replenish_ring(
			struct wifi_pos_psoc_priv_obj *priv, uint8_t ring_idx,
			void *alinged_vaddr, uint32_t cookie)
{
	uint64_t *ring_entry;
	uint32_t dw_lo, dw_hi = 0, map_status;
	void *hal_soc = priv->hal_soc;
	void *srng = priv->dma_cfg[ring_idx].srng;
	void *paddr;

	if (!alinged_vaddr) {
		target_if_debug("NULL alinged_vaddr provided");
		return QDF_STATUS_SUCCESS;
	}

	map_status = qdf_mem_map_nbytes_single(NULL, alinged_vaddr,
			QDF_DMA_FROM_DEVICE,
			priv->dma_cap[ring_idx].min_buf_size,
			(qdf_dma_addr_t *)&paddr);
	if (map_status) {
		target_if_err("mem map failed status: %d", map_status);
		return QDF_STATUS_E_FAILURE;
	}
	QDF_ASSERT(!((uint64_t)paddr % priv->dma_cap[ring_idx].min_buf_align));
	priv->dma_buf_pool[ring_idx][cookie].paddr = paddr;

	hal_srng_access_start(hal_soc, srng);
	ring_entry = hal_srng_src_get_next(hal_soc, srng);
	dw_lo = (uint64_t)paddr & 0xFFFFFFFF;
	WMI_OEM_DMA_DATA_ADDR_HI_SET(dw_hi, (uint64_t)paddr >> 32);
	WMI_OEM_DMA_DATA_ADDR_HI_HOST_DATA_SET(dw_hi, cookie);
	*ring_entry = (uint64_t)dw_hi << 32 | dw_lo;
	hal_srng_access_end(hal_soc, srng);

	return QDF_STATUS_SUCCESS;
}

static QDF_STATUS target_if_wifi_pos_get_indirect_data(
		struct wifi_pos_psoc_priv_obj *priv_obj,
		wmi_oem_indirect_data *indirect,
		struct oem_data_rsp *rsp, uint32_t *cookie)
{
	void *paddr = NULL;
	uint32_t addr_hi;
	uint8_t ring_idx = 0, num_rings;

	if (!indirect) {
		target_if_debug("no indirect data. regular event received");
		return QDF_STATUS_SUCCESS;
	}

	ring_idx = indirect->pdev_id - 1;
	num_rings = priv_obj->num_rings;
	if (ring_idx >= num_rings) {
		target_if_err("incorrect pdev_id: %d", indirect->pdev_id);
		return QDF_STATUS_E_INVAL;
	}
	addr_hi = (uint64_t)WMI_OEM_DMA_DATA_ADDR_HI_GET(
						indirect->addr_hi);
	paddr = (void *)((uint64_t)addr_hi << 32 | indirect->addr_lo);
	*cookie = WMI_OEM_DMA_DATA_ADDR_HI_HOST_DATA_GET(
						indirect->addr_hi);
	rsp->vaddr = target_if_wifi_pos_vaddr_lookup(priv_obj,
					paddr, ring_idx, *cookie);
	rsp->dma_len = indirect->len;
	qdf_mem_unmap_nbytes_single(NULL, (qdf_dma_addr_t)paddr,
			QDF_DMA_FROM_DEVICE,
			priv_obj->dma_cap[ring_idx].min_buf_size);

	return QDF_STATUS_SUCCESS;
}

#else
static QDF_STATUS target_if_wifi_pos_replenish_ring(
			struct wifi_pos_psoc_priv_obj *priv, uint8_t ring_idx,
			void *vaddr, uint32_t cookie)
{
	return QDF_STATUS_SUCCESS;
}

static QDF_STATUS target_if_wifi_pos_get_indirect_data(
		struct wifi_pos_psoc_priv_obj *priv_obj,
		wmi_oem_indirect_data *indirect,
		struct oem_data_rsp *rsp, uint32_t *cookie)
{
	return QDF_STATUS_SUCCESS;
}
#endif

/**
 * target_if_wifi_pos_oem_rsp_ev_handler: handler registered with
 * WMI_OEM_RESPONSE_EVENTID
 * @scn: scn handle
 * @data_buf: event buffer
 * @data_len: event buffer length
 *
 * Return: status of operation
 */
static int target_if_wifi_pos_oem_rsp_ev_handler(ol_scn_t scn,
					uint8_t *data_buf,
					uint32_t data_len)
{
	int ret;
	uint8_t ring_idx = 0;
	QDF_STATUS status;
	uint32_t cookie = 0;
	wmi_oem_indirect_data *indirect;
	struct oem_data_rsp oem_rsp = {0};
	struct wifi_pos_psoc_priv_obj *priv_obj;
	struct wlan_objmgr_psoc *psoc = wifi_pos_get_psoc();
	struct wlan_lmac_if_wifi_pos_rx_ops *wifi_pos_rx_ops = NULL;
	WMI_OEM_RESPONSE_EVENTID_param_tlvs *param_buf =
		(WMI_OEM_RESPONSE_EVENTID_param_tlvs *)data_buf;

	if (!psoc) {
		target_if_err("psoc is null");
		return QDF_STATUS_NOT_INITIALIZED;
	}

	wlan_objmgr_psoc_get_ref(psoc, WLAN_WIFI_POS_ID);

	priv_obj = wifi_pos_get_psoc_priv_obj(psoc);
	if (!priv_obj) {
		target_if_err("priv_obj is null");
		wlan_objmgr_psoc_release_ref(psoc, WLAN_WIFI_POS_ID);
		return QDF_STATUS_NOT_INITIALIZED;
	}

	wifi_pos_rx_ops = target_if_wifi_pos_get_rxops(psoc);
	if (!wifi_pos_rx_ops || !wifi_pos_rx_ops->oem_rsp_event_rx) {
		wlan_objmgr_psoc_release_ref(psoc, WLAN_WIFI_POS_ID);
		target_if_err("lmac callbacks not registered");
		return QDF_STATUS_NOT_INITIALIZED;
	}

	oem_rsp.rsp_len_1 = param_buf->num_data;
	oem_rsp.data_1 = param_buf->data;

	if (param_buf->num_data2) {
		oem_rsp.rsp_len_2 = param_buf->num_data2;
		oem_rsp.data_2 = param_buf->data2;
	}

	indirect = (wmi_oem_indirect_data *)param_buf->indirect_data;
	status = target_if_wifi_pos_get_indirect_data(priv_obj, indirect,
						      &oem_rsp, &cookie);
	if (QDF_IS_STATUS_ERROR(status)) {
		target_if_err("get indirect data failed status: %d", status);
		wlan_objmgr_psoc_release_ref(psoc, WLAN_WIFI_POS_ID);
		return QDF_STATUS_E_INVAL;
	}

	ret = wifi_pos_rx_ops->oem_rsp_event_rx(psoc, &oem_rsp);
	if (indirect)
		ring_idx = indirect->pdev_id - 1;
	status = target_if_wifi_pos_replenish_ring(priv_obj, ring_idx,
					oem_rsp.vaddr, cookie);
	if (QDF_IS_STATUS_ERROR(status)) {
		target_if_err("replenish failed status: %d", status);
		ret = QDF_STATUS_E_FAILURE;
	}

	wlan_objmgr_psoc_release_ref(psoc, WLAN_WIFI_POS_ID);

	return ret;
}

/**
 * wifi_pos_oem_cap_ev_handler: handler registered with wmi_oem_cap_event_id
 * @scn: scn handle
 * @buf: event buffer
 * @len: event buffer length
 *
 * Return: status of operation
 */
static int wifi_pos_oem_cap_ev_handler(ol_scn_t scn, uint8_t *buf, uint32_t len)
{
	/* TBD */
	return 0;
}

/**
 * wifi_pos_oem_meas_rpt_ev_handler: handler registered with
 * wmi_oem_meas_report_event_id
 * @scn: scn handle
 * @buf: event buffer
 * @len: event buffer length
 *
 * Return: status of operation
 */
static int wifi_pos_oem_meas_rpt_ev_handler(ol_scn_t scn, uint8_t *buf,
					    uint32_t len)
{
	/* TBD */
	return 0;
}

/**
 * wifi_pos_oem_err_rpt_ev_handler: handler registered with
 * wmi_oem_err_report_event_id
 * @scn: scn handle
 * @buf: event buffer
 * @len: event buffer length
 *
 * Return: status of operation
 */
static int wifi_pos_oem_err_rpt_ev_handler(ol_scn_t scn, uint8_t *buf,
					    uint32_t len)
{
	/* TBD */
	return 0;
}

/**
 * wifi_pos_oem_data_req() - start OEM data request to target
 * @wma_handle: wma handle
 * @req: start request params
 *
 * Return: QDF_STATUS
 */
static QDF_STATUS wifi_pos_oem_data_req(struct wlan_objmgr_psoc *psoc,
					struct oem_data_req *req)
{
	QDF_STATUS status;
	void *wmi_hdl = GET_WMI_HDL_FROM_PSOC(psoc);

	target_if_debug("Send oem data req to target");

	if (!req || !req->data) {
		target_if_err("oem_data_req is null");
		return QDF_STATUS_E_INVAL;
	}

	if (!wmi_hdl) {
		target_if_err("WMA closed, can't send oem data req cmd");
		return QDF_STATUS_E_INVAL;
	}

	status = wmi_unified_start_oem_data_cmd(wmi_hdl, req->data_len,
						req->data);

	if (!QDF_IS_STATUS_SUCCESS(status))
		target_if_err("wmi cmd send failed");

	return status;
}

void target_if_wifi_pos_register_tx_ops(struct wlan_lmac_if_tx_ops *tx_ops)
{
	struct wlan_lmac_if_wifi_pos_tx_ops *wifi_pos_tx_ops;
	wifi_pos_tx_ops = &tx_ops->wifi_pos_tx_ops;
	wifi_pos_tx_ops->data_req_tx = wifi_pos_oem_data_req;
}

void target_if_wifi_pos_register_rx_ops(struct wlan_lmac_if_rx_ops *rx_ops)
{
	struct wlan_lmac_if_wifi_pos_rx_ops *wifi_pos_rx_ops;
	wifi_pos_rx_ops = &rx_ops->wifi_pos_rx_ops;
	wifi_pos_rx_ops->oem_rsp_event_rx = wifi_pos_oem_rsp_handler;
}

inline struct wlan_lmac_if_wifi_pos_tx_ops *target_if_wifi_pos_get_txops(
						struct wlan_objmgr_psoc *psoc)
{
	if (!psoc) {
		target_if_err("passed psoc is NULL");
		return NULL;
	}

	return &psoc->soc_cb.tx_ops.wifi_pos_tx_ops;
}

inline struct wlan_lmac_if_wifi_pos_rx_ops *target_if_wifi_pos_get_rxops(
						struct wlan_objmgr_psoc *psoc)
{
	if (!psoc) {
		target_if_err("passed psoc is NULL");
		return NULL;
	}

	return &psoc->soc_cb.rx_ops.wifi_pos_rx_ops;
}

QDF_STATUS target_if_wifi_pos_register_events(struct wlan_objmgr_psoc *psoc)
{
	int ret;

	if (!psoc || !GET_WMI_HDL_FROM_PSOC(psoc)) {
		target_if_err("psoc or psoc->tgt_if_handle is null");
		return QDF_STATUS_E_INVAL;
	}

	ret = wmi_unified_register_event_handler(GET_WMI_HDL_FROM_PSOC(psoc),
					wmi_oem_response_event_id,
					target_if_wifi_pos_oem_rsp_ev_handler,
					WMI_RX_WORK_CTX);
	if (ret) {
		target_if_err("register_event_handler failed: err %d", ret);
		return QDF_STATUS_E_INVAL;
	}

	ret = wmi_unified_register_event_handler(GET_WMI_HDL_FROM_PSOC(psoc),
					wmi_oem_cap_event_id,
					wifi_pos_oem_cap_ev_handler,
					WMI_RX_WORK_CTX);
	if (ret) {
		target_if_err("register_event_handler failed: err %d", ret);
		return QDF_STATUS_E_INVAL;
	}

	ret = wmi_unified_register_event_handler(GET_WMI_HDL_FROM_PSOC(psoc),
					wmi_oem_meas_report_event_id,
					wifi_pos_oem_meas_rpt_ev_handler,
					WMI_RX_WORK_CTX);
	if (ret) {
		target_if_err("register_event_handler failed: err %d", ret);
		return QDF_STATUS_E_INVAL;
	}

	ret = wmi_unified_register_event_handler(GET_WMI_HDL_FROM_PSOC(psoc),
					wmi_oem_report_event_id,
					wifi_pos_oem_err_rpt_ev_handler,
					WMI_RX_WORK_CTX);
	if (ret) {
		target_if_err("register_event_handler failed: err %d", ret);
		return QDF_STATUS_E_INVAL;
	}

	return QDF_STATUS_SUCCESS;
}

QDF_STATUS target_if_wifi_pos_deregister_events(struct wlan_objmgr_psoc *psoc)
{
	if (!psoc || !GET_WMI_HDL_FROM_PSOC(psoc)) {
		target_if_err("psoc or psoc->tgt_if_handle is null");
		return QDF_STATUS_E_INVAL;
	}

	wmi_unified_unregister_event_handler(GET_WMI_HDL_FROM_PSOC(psoc),
					wmi_oem_response_event_id);
	wmi_unified_unregister_event_handler(GET_WMI_HDL_FROM_PSOC(psoc),
					wmi_oem_cap_event_id);
	wmi_unified_unregister_event_handler(GET_WMI_HDL_FROM_PSOC(psoc),
					wmi_oem_meas_report_event_id);
	wmi_unified_unregister_event_handler(GET_WMI_HDL_FROM_PSOC(psoc),
					wmi_oem_report_event_id);

	return QDF_STATUS_SUCCESS;
}

#ifdef WLAN_FEATURE_CIF_CFR
static QDF_STATUS target_if_wifi_pos_fill_ring(uint8_t ring_idx,
					struct hal_srng *srng,
					struct wifi_pos_psoc_priv_obj *priv)
{
	uint32_t i;
	void *buf, *buf_aligned;

	for (i = 0; i < priv->dma_cfg[ring_idx].num_ptr; i++) {
		buf = qdf_mem_malloc(priv->dma_cap[ring_idx].min_buf_size +
				priv->dma_cap[ring_idx].min_buf_align - 1);
		if (!buf) {
			target_if_err("malloc failed");
			return QDF_STATUS_E_NOMEM;
		}
		priv->dma_buf_pool[ring_idx][i].vaddr = buf;
		buf_aligned = (void *)qdf_roundup((uint64_t)buf,
				priv->dma_cap[ring_idx].min_buf_align);
		priv->dma_buf_pool[ring_idx][i].offset = buf_aligned - buf;
		priv->dma_buf_pool[ring_idx][i].cookie = i;
		target_if_wifi_pos_replenish_ring(priv, ring_idx,
						  buf_aligned, i);
	}

	return QDF_STATUS_SUCCESS;
}

static QDF_STATUS target_if_wifi_pos_empty_ring(uint8_t ring_idx,
					struct wifi_pos_psoc_priv_obj *priv)
{
	uint32_t i;

	for (i = 0; i < priv->dma_cfg[ring_idx].num_ptr; i++) {
		qdf_mem_unmap_nbytes_single(NULL,
			(qdf_dma_addr_t)priv->dma_buf_pool[ring_idx][i].vaddr,
			QDF_DMA_FROM_DEVICE,
			priv->dma_cap[ring_idx].min_buf_size);
		qdf_mem_free(priv->dma_buf_pool[ring_idx][i].vaddr);
	}

	return QDF_STATUS_SUCCESS;
}

static QDF_STATUS target_if_wifi_pos_init_ring(uint8_t ring_idx,
					struct wifi_pos_psoc_priv_obj *priv)
{
	void *srng;
	uint32_t num_entries;
	qdf_dma_addr_t paddr;
	uint32_t ring_alloc_size;
	void *hal_soc = priv->hal_soc;
	struct hal_srng_params ring_params = {0};
	uint32_t max_entries = hal_srng_max_entries(hal_soc, WIFI_POS_SRC);
	uint32_t entry_size = hal_srng_get_entrysize(hal_soc, WIFI_POS_SRC);

	num_entries = priv->dma_cap[ring_idx].min_num_ptr > max_entries ?
			max_entries : priv->dma_cap[ring_idx].min_num_ptr;
	priv->dma_cfg[ring_idx].num_ptr = num_entries;
	priv->dma_buf_pool[ring_idx] = qdf_mem_malloc(num_entries *
					sizeof(struct wifi_pos_dma_buf_info));
	if (!priv->dma_buf_pool[ring_idx]) {
		target_if_err("malloc failed");
		return QDF_STATUS_E_NOMEM;
	}

	ring_alloc_size = (num_entries * entry_size) + RING_BASE_ALIGN - 1;
	priv->dma_cfg[ring_idx].ring_alloc_size = ring_alloc_size;
	priv->dma_cfg[ring_idx].base_vaddr_unaligned =
		qdf_mem_alloc_consistent(NULL, NULL, ring_alloc_size, &paddr);
	priv->dma_cfg[ring_idx].base_paddr_unaligned = (void *)paddr;
	if (!priv->dma_cfg[ring_idx].base_vaddr_unaligned) {
		target_if_err("malloc failed");
		return QDF_STATUS_E_NOMEM;
	}

	priv->dma_cfg[ring_idx].base_vaddr_aligned = (void *)qdf_roundup(
		(uint64_t)priv->dma_cfg[ring_idx].base_vaddr_unaligned,
		RING_BASE_ALIGN);
	ring_params.ring_base_vaddr =
		priv->dma_cfg[ring_idx].base_vaddr_aligned;
	priv->dma_cfg[ring_idx].base_paddr_aligned = (void *)qdf_roundup(
		(uint64_t)priv->dma_cfg[ring_idx].base_paddr_unaligned,
		RING_BASE_ALIGN);
	ring_params.ring_base_paddr =
		(qdf_dma_addr_t)priv->dma_cfg[ring_idx].base_paddr_aligned;
	ring_params.num_entries = num_entries;
	srng = hal_srng_setup(hal_soc, WIFI_POS_SRC, 0,
				priv->dma_cap[ring_idx].pdev_id, &ring_params);
	if (!srng) {
		target_if_err("srng setup failed");
		return QDF_STATUS_E_FAILURE;
	}
	priv->dma_cfg[ring_idx].srng = srng;
	priv->dma_cfg[ring_idx].tail_idx_addr =
			(void *)hal_srng_get_tp_addr(hal_soc, srng);
	priv->dma_cfg[ring_idx].head_idx_addr =
			(void *)hal_srng_get_tp_addr(hal_soc, srng);

	return target_if_wifi_pos_fill_ring(ring_idx, srng, priv);
}

static QDF_STATUS target_if_wifi_pos_deinit_ring(uint8_t ring_idx,
					struct wifi_pos_psoc_priv_obj *priv)
{
	target_if_wifi_pos_empty_ring(ring_idx, priv);
	priv->dma_buf_pool[ring_idx] = NULL;
	hal_srng_cleanup(priv->hal_soc, priv->dma_cfg[ring_idx].srng);
	qdf_mem_free_consistent(NULL, NULL,
		priv->dma_cfg[ring_idx].ring_alloc_size,
		priv->dma_cfg[ring_idx].base_vaddr_unaligned,
		(qdf_dma_addr_t)priv->dma_cfg[ring_idx].base_paddr_unaligned,
		0);
	qdf_mem_free(priv->dma_buf_pool[ring_idx]);

	return QDF_STATUS_SUCCESS;
}

static QDF_STATUS target_if_wifi_pos_init_srngs(
					struct wifi_pos_psoc_priv_obj *priv)
{
	uint8_t i;
	QDF_STATUS status;

	/* allocate memory for num_rings pointers */
	priv->dma_cfg = qdf_mem_malloc(priv->num_rings *
				sizeof(struct wifi_pos_dma_rings_cap));
	if (!priv->dma_cfg) {
		target_if_err("malloc failed");
		return QDF_STATUS_E_NOMEM;
	}

	priv->dma_buf_pool = qdf_mem_malloc(priv->num_rings *
				sizeof(struct wifi_pos_dma_buf_info *));
	if (!priv->dma_buf_pool) {
		target_if_err("malloc failed");
		return QDF_STATUS_E_NOMEM;
	}

	for (i = 0; i < priv->num_rings; i++) {
		status = target_if_wifi_pos_init_ring(i, priv);
		if (QDF_IS_STATUS_ERROR(status)) {
			target_if_err("init for ring[%d] failed", i);
			return status;
		}
	}

	return QDF_STATUS_SUCCESS;
}

static QDF_STATUS target_if_wifi_pos_deinit_srngs(
					struct wifi_pos_psoc_priv_obj *priv)
{
	uint8_t i;

	for (i = 0; i < priv->num_rings; i++)
		target_if_wifi_pos_deinit_ring(i, priv);

	qdf_mem_free(priv->dma_buf_pool);
	priv->dma_buf_pool = NULL;

	return QDF_STATUS_SUCCESS;
}

static QDF_STATUS target_if_wifi_pos_cfg_fw(struct wlan_objmgr_psoc *psoc,
					struct wifi_pos_psoc_priv_obj *priv)
{
	uint8_t i;
	QDF_STATUS status;
	void *wmi_hdl = GET_WMI_HDL_FROM_PSOC(psoc);
	wmi_oem_dma_ring_cfg_req_fixed_param cfg = {0};

	if (!wmi_hdl) {
		target_if_err("WMA closed, can't send oem data req cmd");
		return QDF_STATUS_E_INVAL;
	}

	target_if_debug("Sending oem dma ring cfg to target");

	for (i = 0; i < priv->num_rings; i++) {
		cfg.pdev_id = priv->dma_cfg[i].pdev_id;
		cfg.base_addr_lo = (uint64_t)priv->dma_cfg[i].base_paddr_aligned
						& 0xFFFFFFFF;
		cfg.base_addr_hi = (uint64_t)priv->dma_cfg[i].base_paddr_aligned
						& 0xFFFFFFFF00000000;
		cfg.head_idx_addr_lo = (uint64_t)priv->dma_cfg[i].head_idx_addr
						& 0xFFFFFFFF;
		cfg.head_idx_addr_hi = (uint64_t)priv->dma_cfg[i].head_idx_addr
						& 0xFFFFFFFF00000000;
		cfg.tail_idx_addr_lo = (uint64_t)priv->dma_cfg[i].tail_idx_addr
						& 0xFFFFFFFF;
		cfg.tail_idx_addr_hi = (uint64_t)priv->dma_cfg[i].tail_idx_addr
						& 0xFFFFFFFF00000000;
		cfg.num_ptr = priv->dma_cfg[i].num_ptr;
		status = wmi_unified_oem_dma_ring_cfg(wmi_hdl, &cfg);
		if (!QDF_IS_STATUS_SUCCESS(status)) {
			target_if_err("wmi cmd send failed");
			return status;
		}
	}

	return status;
}

QDF_STATUS target_if_wifi_pos_deinit_dma_rings(struct wlan_objmgr_psoc *psoc)
{
	struct wifi_pos_psoc_priv_obj *priv = wifi_pos_get_psoc_priv_obj(psoc);

	target_if_wifi_pos_deinit_srngs(priv);
	qdf_mem_free(priv->dma_cap);
	priv->dma_cap = NULL;

	return QDF_STATUS_SUCCESS;
}

QDF_STATUS target_if_wifi_pos_init_cir_cfr_rings(struct wlan_objmgr_psoc *psoc,
					     void *hal_soc, uint8_t num_mac,
					     void *buf)
{
	uint8_t i;
	QDF_STATUS status = QDF_STATUS_SUCCESS;
	WMI_OEM_DMA_RING_CAPABILITIES *dma_cap = buf;
	struct wifi_pos_psoc_priv_obj *priv = wifi_pos_get_psoc_priv_obj(psoc);

	if (!priv) {
		target_if_err("unable to get wifi_pos psoc obj");
		return QDF_STATUS_E_NULL_VALUE;
	}

	priv->hal_soc = hal_soc;
	priv->num_rings = num_mac;
	priv->dma_cap = qdf_mem_malloc(priv->num_rings *
					sizeof(struct wifi_pos_dma_rings_cap));
	if (!priv->dma_cap) {
		target_if_err("unable to get wifi_pos psoc obj");
		return QDF_STATUS_E_NOMEM;
	}

	for (i = 0; i < num_mac; i++) {
		priv->dma_cap[i].pdev_id = dma_cap[i].pdev_id;
		priv->dma_cap[i].min_num_ptr = dma_cap[i].min_num_ptr;
		priv->dma_cap[i].min_buf_size = dma_cap[i].min_buf_size;
		priv->dma_cap[i].min_buf_align = dma_cap[i].min_buf_align;
	}

	/* initialize DMA rings now */
	status = target_if_wifi_pos_init_srngs(priv);
	if (QDF_IS_STATUS_ERROR(status)) {
		target_if_err("dma init failed: %d", status);
		goto dma_init_failed;
	}

	/* send cfg req cmd to firmware */
	status = target_if_wifi_pos_cfg_fw(psoc, priv);
	if (QDF_IS_STATUS_ERROR(status)) {
		target_if_err("configure to FW failed: %d", status);
		goto dma_init_failed;
	}

	return QDF_STATUS_SUCCESS;

dma_init_failed:
	target_if_wifi_pos_deinit_dma_rings(psoc);
	return status;
}

#endif
