18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any
58c2ecf20Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above
68c2ecf20Sopenharmony_ci * copyright notice and this permission notice appear in all copies.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
98c2ecf20Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
108c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
118c2ecf20Sopenharmony_ci * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
128c2ecf20Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
138c2ecf20Sopenharmony_ci * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
148c2ecf20Sopenharmony_ci * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci/* DXE - DMA transfer engine
188c2ecf20Sopenharmony_ci * we have 2 channels(High prio and Low prio) for TX and 2 channels for RX.
198c2ecf20Sopenharmony_ci * through low channels data packets are transfered
208c2ecf20Sopenharmony_ci * through high channels managment packets are transfered
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
268c2ecf20Sopenharmony_ci#include <linux/soc/qcom/smem_state.h>
278c2ecf20Sopenharmony_ci#include "wcn36xx.h"
288c2ecf20Sopenharmony_ci#include "txrx.h"
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic void wcn36xx_ccu_write_register(struct wcn36xx *wcn, int addr, int data)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	wcn36xx_dbg(WCN36XX_DBG_DXE,
338c2ecf20Sopenharmony_ci		    "wcn36xx_ccu_write_register: addr=%x, data=%x\n",
348c2ecf20Sopenharmony_ci		    addr, data);
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	writel(data, wcn->ccu_base + addr);
378c2ecf20Sopenharmony_ci}
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic void wcn36xx_dxe_write_register(struct wcn36xx *wcn, int addr, int data)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	wcn36xx_dbg(WCN36XX_DBG_DXE,
428c2ecf20Sopenharmony_ci		    "wcn36xx_dxe_write_register: addr=%x, data=%x\n",
438c2ecf20Sopenharmony_ci		    addr, data);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	writel(data, wcn->dxe_base + addr);
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic void wcn36xx_dxe_read_register(struct wcn36xx *wcn, int addr, int *data)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	*data = readl(wcn->dxe_base + addr);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	wcn36xx_dbg(WCN36XX_DBG_DXE,
538c2ecf20Sopenharmony_ci		    "wcn36xx_dxe_read_register: addr=%x, data=%x\n",
548c2ecf20Sopenharmony_ci		    addr, *data);
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic void wcn36xx_dxe_free_ctl_block(struct wcn36xx_dxe_ch *ch)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	struct wcn36xx_dxe_ctl *ctl = ch->head_blk_ctl, *next;
608c2ecf20Sopenharmony_ci	int i;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	for (i = 0; i < ch->desc_num && ctl; i++) {
638c2ecf20Sopenharmony_ci		next = ctl->next;
648c2ecf20Sopenharmony_ci		kfree(ctl);
658c2ecf20Sopenharmony_ci		ctl = next;
668c2ecf20Sopenharmony_ci	}
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic int wcn36xx_dxe_allocate_ctl_block(struct wcn36xx_dxe_ch *ch)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	struct wcn36xx_dxe_ctl *prev_ctl = NULL;
728c2ecf20Sopenharmony_ci	struct wcn36xx_dxe_ctl *cur_ctl = NULL;
738c2ecf20Sopenharmony_ci	int i;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	spin_lock_init(&ch->lock);
768c2ecf20Sopenharmony_ci	for (i = 0; i < ch->desc_num; i++) {
778c2ecf20Sopenharmony_ci		cur_ctl = kzalloc(sizeof(*cur_ctl), GFP_KERNEL);
788c2ecf20Sopenharmony_ci		if (!cur_ctl)
798c2ecf20Sopenharmony_ci			goto out_fail;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci		cur_ctl->ctl_blk_order = i;
828c2ecf20Sopenharmony_ci		if (i == 0) {
838c2ecf20Sopenharmony_ci			ch->head_blk_ctl = cur_ctl;
848c2ecf20Sopenharmony_ci			ch->tail_blk_ctl = cur_ctl;
858c2ecf20Sopenharmony_ci		} else if (ch->desc_num - 1 == i) {
868c2ecf20Sopenharmony_ci			prev_ctl->next = cur_ctl;
878c2ecf20Sopenharmony_ci			cur_ctl->next = ch->head_blk_ctl;
888c2ecf20Sopenharmony_ci		} else {
898c2ecf20Sopenharmony_ci			prev_ctl->next = cur_ctl;
908c2ecf20Sopenharmony_ci		}
918c2ecf20Sopenharmony_ci		prev_ctl = cur_ctl;
928c2ecf20Sopenharmony_ci	}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	return 0;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ciout_fail:
978c2ecf20Sopenharmony_ci	wcn36xx_dxe_free_ctl_block(ch);
988c2ecf20Sopenharmony_ci	return -ENOMEM;
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ciint wcn36xx_dxe_alloc_ctl_blks(struct wcn36xx *wcn)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	int ret;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	wcn->dxe_tx_l_ch.ch_type = WCN36XX_DXE_CH_TX_L;
1068c2ecf20Sopenharmony_ci	wcn->dxe_tx_h_ch.ch_type = WCN36XX_DXE_CH_TX_H;
1078c2ecf20Sopenharmony_ci	wcn->dxe_rx_l_ch.ch_type = WCN36XX_DXE_CH_RX_L;
1088c2ecf20Sopenharmony_ci	wcn->dxe_rx_h_ch.ch_type = WCN36XX_DXE_CH_RX_H;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	wcn->dxe_tx_l_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_TX_L;
1118c2ecf20Sopenharmony_ci	wcn->dxe_tx_h_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_TX_H;
1128c2ecf20Sopenharmony_ci	wcn->dxe_rx_l_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_RX_L;
1138c2ecf20Sopenharmony_ci	wcn->dxe_rx_h_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_RX_H;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	wcn->dxe_tx_l_ch.dxe_wq =  WCN36XX_DXE_WQ_TX_L;
1168c2ecf20Sopenharmony_ci	wcn->dxe_tx_h_ch.dxe_wq =  WCN36XX_DXE_WQ_TX_H;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	wcn->dxe_tx_l_ch.ctrl_bd = WCN36XX_DXE_CTRL_TX_L_BD;
1198c2ecf20Sopenharmony_ci	wcn->dxe_tx_h_ch.ctrl_bd = WCN36XX_DXE_CTRL_TX_H_BD;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	wcn->dxe_tx_l_ch.ctrl_skb = WCN36XX_DXE_CTRL_TX_L_SKB;
1228c2ecf20Sopenharmony_ci	wcn->dxe_tx_h_ch.ctrl_skb = WCN36XX_DXE_CTRL_TX_H_SKB;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	wcn->dxe_tx_l_ch.reg_ctrl = WCN36XX_DXE_REG_CTL_TX_L;
1258c2ecf20Sopenharmony_ci	wcn->dxe_tx_h_ch.reg_ctrl = WCN36XX_DXE_REG_CTL_TX_H;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	wcn->dxe_tx_l_ch.def_ctrl = WCN36XX_DXE_CH_DEFAULT_CTL_TX_L;
1288c2ecf20Sopenharmony_ci	wcn->dxe_tx_h_ch.def_ctrl = WCN36XX_DXE_CH_DEFAULT_CTL_TX_H;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	/* DXE control block allocation */
1318c2ecf20Sopenharmony_ci	ret = wcn36xx_dxe_allocate_ctl_block(&wcn->dxe_tx_l_ch);
1328c2ecf20Sopenharmony_ci	if (ret)
1338c2ecf20Sopenharmony_ci		goto out_err;
1348c2ecf20Sopenharmony_ci	ret = wcn36xx_dxe_allocate_ctl_block(&wcn->dxe_tx_h_ch);
1358c2ecf20Sopenharmony_ci	if (ret)
1368c2ecf20Sopenharmony_ci		goto out_err;
1378c2ecf20Sopenharmony_ci	ret = wcn36xx_dxe_allocate_ctl_block(&wcn->dxe_rx_l_ch);
1388c2ecf20Sopenharmony_ci	if (ret)
1398c2ecf20Sopenharmony_ci		goto out_err;
1408c2ecf20Sopenharmony_ci	ret = wcn36xx_dxe_allocate_ctl_block(&wcn->dxe_rx_h_ch);
1418c2ecf20Sopenharmony_ci	if (ret)
1428c2ecf20Sopenharmony_ci		goto out_err;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	/* Initialize SMSM state  Clear TX Enable RING EMPTY STATE */
1458c2ecf20Sopenharmony_ci	ret = qcom_smem_state_update_bits(wcn->tx_enable_state,
1468c2ecf20Sopenharmony_ci					  WCN36XX_SMSM_WLAN_TX_ENABLE |
1478c2ecf20Sopenharmony_ci					  WCN36XX_SMSM_WLAN_TX_RINGS_EMPTY,
1488c2ecf20Sopenharmony_ci					  WCN36XX_SMSM_WLAN_TX_RINGS_EMPTY);
1498c2ecf20Sopenharmony_ci	if (ret)
1508c2ecf20Sopenharmony_ci		goto out_err;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	return 0;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ciout_err:
1558c2ecf20Sopenharmony_ci	wcn36xx_err("Failed to allocate DXE control blocks\n");
1568c2ecf20Sopenharmony_ci	wcn36xx_dxe_free_ctl_blks(wcn);
1578c2ecf20Sopenharmony_ci	return -ENOMEM;
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_civoid wcn36xx_dxe_free_ctl_blks(struct wcn36xx *wcn)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	wcn36xx_dxe_free_ctl_block(&wcn->dxe_tx_l_ch);
1638c2ecf20Sopenharmony_ci	wcn36xx_dxe_free_ctl_block(&wcn->dxe_tx_h_ch);
1648c2ecf20Sopenharmony_ci	wcn36xx_dxe_free_ctl_block(&wcn->dxe_rx_l_ch);
1658c2ecf20Sopenharmony_ci	wcn36xx_dxe_free_ctl_block(&wcn->dxe_rx_h_ch);
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_cistatic int wcn36xx_dxe_init_descs(struct device *dev, struct wcn36xx_dxe_ch *wcn_ch)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	struct wcn36xx_dxe_desc *cur_dxe = NULL;
1718c2ecf20Sopenharmony_ci	struct wcn36xx_dxe_desc *prev_dxe = NULL;
1728c2ecf20Sopenharmony_ci	struct wcn36xx_dxe_ctl *cur_ctl = NULL;
1738c2ecf20Sopenharmony_ci	size_t size;
1748c2ecf20Sopenharmony_ci	int i;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	size = wcn_ch->desc_num * sizeof(struct wcn36xx_dxe_desc);
1778c2ecf20Sopenharmony_ci	wcn_ch->cpu_addr = dma_alloc_coherent(dev, size, &wcn_ch->dma_addr,
1788c2ecf20Sopenharmony_ci					      GFP_KERNEL);
1798c2ecf20Sopenharmony_ci	if (!wcn_ch->cpu_addr)
1808c2ecf20Sopenharmony_ci		return -ENOMEM;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	cur_dxe = (struct wcn36xx_dxe_desc *)wcn_ch->cpu_addr;
1838c2ecf20Sopenharmony_ci	cur_ctl = wcn_ch->head_blk_ctl;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	for (i = 0; i < wcn_ch->desc_num; i++) {
1868c2ecf20Sopenharmony_ci		cur_ctl->desc = cur_dxe;
1878c2ecf20Sopenharmony_ci		cur_ctl->desc_phy_addr = wcn_ch->dma_addr +
1888c2ecf20Sopenharmony_ci			i * sizeof(struct wcn36xx_dxe_desc);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci		switch (wcn_ch->ch_type) {
1918c2ecf20Sopenharmony_ci		case WCN36XX_DXE_CH_TX_L:
1928c2ecf20Sopenharmony_ci			cur_dxe->ctrl = WCN36XX_DXE_CTRL_TX_L;
1938c2ecf20Sopenharmony_ci			cur_dxe->dst_addr_l = WCN36XX_DXE_WQ_TX_L;
1948c2ecf20Sopenharmony_ci			break;
1958c2ecf20Sopenharmony_ci		case WCN36XX_DXE_CH_TX_H:
1968c2ecf20Sopenharmony_ci			cur_dxe->ctrl = WCN36XX_DXE_CTRL_TX_H;
1978c2ecf20Sopenharmony_ci			cur_dxe->dst_addr_l = WCN36XX_DXE_WQ_TX_H;
1988c2ecf20Sopenharmony_ci			break;
1998c2ecf20Sopenharmony_ci		case WCN36XX_DXE_CH_RX_L:
2008c2ecf20Sopenharmony_ci			cur_dxe->ctrl = WCN36XX_DXE_CTRL_RX_L;
2018c2ecf20Sopenharmony_ci			cur_dxe->src_addr_l = WCN36XX_DXE_WQ_RX_L;
2028c2ecf20Sopenharmony_ci			break;
2038c2ecf20Sopenharmony_ci		case WCN36XX_DXE_CH_RX_H:
2048c2ecf20Sopenharmony_ci			cur_dxe->ctrl = WCN36XX_DXE_CTRL_RX_H;
2058c2ecf20Sopenharmony_ci			cur_dxe->src_addr_l = WCN36XX_DXE_WQ_RX_H;
2068c2ecf20Sopenharmony_ci			break;
2078c2ecf20Sopenharmony_ci		}
2088c2ecf20Sopenharmony_ci		if (0 == i) {
2098c2ecf20Sopenharmony_ci			cur_dxe->phy_next_l = 0;
2108c2ecf20Sopenharmony_ci		} else if ((0 < i) && (i < wcn_ch->desc_num - 1)) {
2118c2ecf20Sopenharmony_ci			prev_dxe->phy_next_l =
2128c2ecf20Sopenharmony_ci				cur_ctl->desc_phy_addr;
2138c2ecf20Sopenharmony_ci		} else if (i == (wcn_ch->desc_num - 1)) {
2148c2ecf20Sopenharmony_ci			prev_dxe->phy_next_l =
2158c2ecf20Sopenharmony_ci				cur_ctl->desc_phy_addr;
2168c2ecf20Sopenharmony_ci			cur_dxe->phy_next_l =
2178c2ecf20Sopenharmony_ci				wcn_ch->head_blk_ctl->desc_phy_addr;
2188c2ecf20Sopenharmony_ci		}
2198c2ecf20Sopenharmony_ci		cur_ctl = cur_ctl->next;
2208c2ecf20Sopenharmony_ci		prev_dxe = cur_dxe;
2218c2ecf20Sopenharmony_ci		cur_dxe++;
2228c2ecf20Sopenharmony_ci	}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	return 0;
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_cistatic void wcn36xx_dxe_deinit_descs(struct device *dev, struct wcn36xx_dxe_ch *wcn_ch)
2288c2ecf20Sopenharmony_ci{
2298c2ecf20Sopenharmony_ci	size_t size;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	size = wcn_ch->desc_num * sizeof(struct wcn36xx_dxe_desc);
2328c2ecf20Sopenharmony_ci	dma_free_coherent(dev, size,wcn_ch->cpu_addr, wcn_ch->dma_addr);
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cistatic void wcn36xx_dxe_init_tx_bd(struct wcn36xx_dxe_ch *ch,
2368c2ecf20Sopenharmony_ci				   struct wcn36xx_dxe_mem_pool *pool)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	int i, chunk_size = pool->chunk_size;
2398c2ecf20Sopenharmony_ci	dma_addr_t bd_phy_addr = pool->phy_addr;
2408c2ecf20Sopenharmony_ci	void *bd_cpu_addr = pool->virt_addr;
2418c2ecf20Sopenharmony_ci	struct wcn36xx_dxe_ctl *cur = ch->head_blk_ctl;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	for (i = 0; i < ch->desc_num; i++) {
2448c2ecf20Sopenharmony_ci		/* Only every second dxe needs a bd pointer,
2458c2ecf20Sopenharmony_ci		   the other will point to the skb data */
2468c2ecf20Sopenharmony_ci		if (!(i & 1)) {
2478c2ecf20Sopenharmony_ci			cur->bd_phy_addr = bd_phy_addr;
2488c2ecf20Sopenharmony_ci			cur->bd_cpu_addr = bd_cpu_addr;
2498c2ecf20Sopenharmony_ci			bd_phy_addr += chunk_size;
2508c2ecf20Sopenharmony_ci			bd_cpu_addr += chunk_size;
2518c2ecf20Sopenharmony_ci		} else {
2528c2ecf20Sopenharmony_ci			cur->bd_phy_addr = 0;
2538c2ecf20Sopenharmony_ci			cur->bd_cpu_addr = NULL;
2548c2ecf20Sopenharmony_ci		}
2558c2ecf20Sopenharmony_ci		cur = cur->next;
2568c2ecf20Sopenharmony_ci	}
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_cistatic int wcn36xx_dxe_enable_ch_int(struct wcn36xx *wcn, u16 wcn_ch)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	int reg_data = 0;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	wcn36xx_dxe_read_register(wcn,
2648c2ecf20Sopenharmony_ci				  WCN36XX_DXE_INT_MASK_REG,
2658c2ecf20Sopenharmony_ci				  &reg_data);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	reg_data |= wcn_ch;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	wcn36xx_dxe_write_register(wcn,
2708c2ecf20Sopenharmony_ci				   WCN36XX_DXE_INT_MASK_REG,
2718c2ecf20Sopenharmony_ci				   (int)reg_data);
2728c2ecf20Sopenharmony_ci	return 0;
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_cistatic void wcn36xx_dxe_disable_ch_int(struct wcn36xx *wcn, u16 wcn_ch)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci	int reg_data = 0;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	wcn36xx_dxe_read_register(wcn,
2808c2ecf20Sopenharmony_ci				  WCN36XX_DXE_INT_MASK_REG,
2818c2ecf20Sopenharmony_ci				  &reg_data);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	reg_data &= ~wcn_ch;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	wcn36xx_dxe_write_register(wcn,
2868c2ecf20Sopenharmony_ci				   WCN36XX_DXE_INT_MASK_REG,
2878c2ecf20Sopenharmony_ci				   (int)reg_data);
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistatic int wcn36xx_dxe_fill_skb(struct device *dev,
2918c2ecf20Sopenharmony_ci				struct wcn36xx_dxe_ctl *ctl,
2928c2ecf20Sopenharmony_ci				gfp_t gfp)
2938c2ecf20Sopenharmony_ci{
2948c2ecf20Sopenharmony_ci	struct wcn36xx_dxe_desc *dxe = ctl->desc;
2958c2ecf20Sopenharmony_ci	struct sk_buff *skb;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	skb = alloc_skb(WCN36XX_PKT_SIZE, gfp);
2988c2ecf20Sopenharmony_ci	if (skb == NULL)
2998c2ecf20Sopenharmony_ci		return -ENOMEM;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	dxe->dst_addr_l = dma_map_single(dev,
3028c2ecf20Sopenharmony_ci					 skb_tail_pointer(skb),
3038c2ecf20Sopenharmony_ci					 WCN36XX_PKT_SIZE,
3048c2ecf20Sopenharmony_ci					 DMA_FROM_DEVICE);
3058c2ecf20Sopenharmony_ci	if (dma_mapping_error(dev, dxe->dst_addr_l)) {
3068c2ecf20Sopenharmony_ci		dev_err(dev, "unable to map skb\n");
3078c2ecf20Sopenharmony_ci		kfree_skb(skb);
3088c2ecf20Sopenharmony_ci		return -ENOMEM;
3098c2ecf20Sopenharmony_ci	}
3108c2ecf20Sopenharmony_ci	ctl->skb = skb;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	return 0;
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_cistatic int wcn36xx_dxe_ch_alloc_skb(struct wcn36xx *wcn,
3168c2ecf20Sopenharmony_ci				    struct wcn36xx_dxe_ch *wcn_ch)
3178c2ecf20Sopenharmony_ci{
3188c2ecf20Sopenharmony_ci	int i;
3198c2ecf20Sopenharmony_ci	struct wcn36xx_dxe_ctl *cur_ctl = NULL;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	cur_ctl = wcn_ch->head_blk_ctl;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	for (i = 0; i < wcn_ch->desc_num; i++) {
3248c2ecf20Sopenharmony_ci		wcn36xx_dxe_fill_skb(wcn->dev, cur_ctl, GFP_KERNEL);
3258c2ecf20Sopenharmony_ci		cur_ctl = cur_ctl->next;
3268c2ecf20Sopenharmony_ci	}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	return 0;
3298c2ecf20Sopenharmony_ci}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_cistatic void wcn36xx_dxe_ch_free_skbs(struct wcn36xx *wcn,
3328c2ecf20Sopenharmony_ci				     struct wcn36xx_dxe_ch *wcn_ch)
3338c2ecf20Sopenharmony_ci{
3348c2ecf20Sopenharmony_ci	struct wcn36xx_dxe_ctl *cur = wcn_ch->head_blk_ctl;
3358c2ecf20Sopenharmony_ci	int i;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	for (i = 0; i < wcn_ch->desc_num; i++) {
3388c2ecf20Sopenharmony_ci		kfree_skb(cur->skb);
3398c2ecf20Sopenharmony_ci		cur = cur->next;
3408c2ecf20Sopenharmony_ci	}
3418c2ecf20Sopenharmony_ci}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_civoid wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	struct ieee80211_tx_info *info;
3468c2ecf20Sopenharmony_ci	struct sk_buff *skb;
3478c2ecf20Sopenharmony_ci	unsigned long flags;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wcn->dxe_lock, flags);
3508c2ecf20Sopenharmony_ci	skb = wcn->tx_ack_skb;
3518c2ecf20Sopenharmony_ci	wcn->tx_ack_skb = NULL;
3528c2ecf20Sopenharmony_ci	del_timer(&wcn->tx_ack_timer);
3538c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wcn->dxe_lock, flags);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	if (!skb) {
3568c2ecf20Sopenharmony_ci		wcn36xx_warn("Spurious TX complete indication\n");
3578c2ecf20Sopenharmony_ci		return;
3588c2ecf20Sopenharmony_ci	}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	info = IEEE80211_SKB_CB(skb);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	if (status == 1)
3638c2ecf20Sopenharmony_ci		info->flags |= IEEE80211_TX_STAT_ACK;
3648c2ecf20Sopenharmony_ci	else
3658c2ecf20Sopenharmony_ci		info->flags &= ~IEEE80211_TX_STAT_ACK;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ack status: %d\n", status);
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	ieee80211_tx_status_irqsafe(wcn->hw, skb);
3708c2ecf20Sopenharmony_ci	ieee80211_wake_queues(wcn->hw);
3718c2ecf20Sopenharmony_ci}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cistatic void wcn36xx_dxe_tx_timer(struct timer_list *t)
3748c2ecf20Sopenharmony_ci{
3758c2ecf20Sopenharmony_ci	struct wcn36xx *wcn = from_timer(wcn, t, tx_ack_timer);
3768c2ecf20Sopenharmony_ci	struct ieee80211_tx_info *info;
3778c2ecf20Sopenharmony_ci	unsigned long flags;
3788c2ecf20Sopenharmony_ci	struct sk_buff *skb;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	/* TX Timeout */
3818c2ecf20Sopenharmony_ci	wcn36xx_dbg(WCN36XX_DBG_DXE, "TX timeout\n");
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wcn->dxe_lock, flags);
3848c2ecf20Sopenharmony_ci	skb = wcn->tx_ack_skb;
3858c2ecf20Sopenharmony_ci	wcn->tx_ack_skb = NULL;
3868c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wcn->dxe_lock, flags);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	if (!skb)
3898c2ecf20Sopenharmony_ci		return;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	info = IEEE80211_SKB_CB(skb);
3928c2ecf20Sopenharmony_ci	info->flags &= ~IEEE80211_TX_STAT_ACK;
3938c2ecf20Sopenharmony_ci	info->flags &= ~IEEE80211_TX_STAT_NOACK_TRANSMITTED;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	ieee80211_tx_status_irqsafe(wcn->hw, skb);
3968c2ecf20Sopenharmony_ci	ieee80211_wake_queues(wcn->hw);
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_cistatic void reap_tx_dxes(struct wcn36xx *wcn, struct wcn36xx_dxe_ch *ch)
4008c2ecf20Sopenharmony_ci{
4018c2ecf20Sopenharmony_ci	struct wcn36xx_dxe_ctl *ctl;
4028c2ecf20Sopenharmony_ci	struct ieee80211_tx_info *info;
4038c2ecf20Sopenharmony_ci	unsigned long flags;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	/*
4068c2ecf20Sopenharmony_ci	 * Make at least one loop of do-while because in case ring is
4078c2ecf20Sopenharmony_ci	 * completely full head and tail are pointing to the same element
4088c2ecf20Sopenharmony_ci	 * and while-do will not make any cycles.
4098c2ecf20Sopenharmony_ci	 */
4108c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ch->lock, flags);
4118c2ecf20Sopenharmony_ci	ctl = ch->tail_blk_ctl;
4128c2ecf20Sopenharmony_ci	do {
4138c2ecf20Sopenharmony_ci		if (READ_ONCE(ctl->desc->ctrl) & WCN36xx_DXE_CTRL_VLD)
4148c2ecf20Sopenharmony_ci			break;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci		if (ctl->skb &&
4178c2ecf20Sopenharmony_ci		    READ_ONCE(ctl->desc->ctrl) & WCN36xx_DXE_CTRL_EOP) {
4188c2ecf20Sopenharmony_ci			dma_unmap_single(wcn->dev, ctl->desc->src_addr_l,
4198c2ecf20Sopenharmony_ci					 ctl->skb->len, DMA_TO_DEVICE);
4208c2ecf20Sopenharmony_ci			info = IEEE80211_SKB_CB(ctl->skb);
4218c2ecf20Sopenharmony_ci			if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) {
4228c2ecf20Sopenharmony_ci				if (info->flags & IEEE80211_TX_CTL_NO_ACK) {
4238c2ecf20Sopenharmony_ci					info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED;
4248c2ecf20Sopenharmony_ci					ieee80211_tx_status_irqsafe(wcn->hw, ctl->skb);
4258c2ecf20Sopenharmony_ci				} else {
4268c2ecf20Sopenharmony_ci					/* Wait for the TX ack indication or timeout... */
4278c2ecf20Sopenharmony_ci					spin_lock(&wcn->dxe_lock);
4288c2ecf20Sopenharmony_ci					if (WARN_ON(wcn->tx_ack_skb))
4298c2ecf20Sopenharmony_ci						ieee80211_free_txskb(wcn->hw, wcn->tx_ack_skb);
4308c2ecf20Sopenharmony_ci					wcn->tx_ack_skb = ctl->skb; /* Tracking ref */
4318c2ecf20Sopenharmony_ci					mod_timer(&wcn->tx_ack_timer, jiffies + HZ / 10);
4328c2ecf20Sopenharmony_ci					spin_unlock(&wcn->dxe_lock);
4338c2ecf20Sopenharmony_ci				}
4348c2ecf20Sopenharmony_ci				/* do not free, ownership transferred to mac80211 status cb */
4358c2ecf20Sopenharmony_ci			} else {
4368c2ecf20Sopenharmony_ci				ieee80211_free_txskb(wcn->hw, ctl->skb);
4378c2ecf20Sopenharmony_ci			}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci			if (wcn->queues_stopped) {
4408c2ecf20Sopenharmony_ci				wcn->queues_stopped = false;
4418c2ecf20Sopenharmony_ci				ieee80211_wake_queues(wcn->hw);
4428c2ecf20Sopenharmony_ci			}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci			ctl->skb = NULL;
4458c2ecf20Sopenharmony_ci		}
4468c2ecf20Sopenharmony_ci		ctl = ctl->next;
4478c2ecf20Sopenharmony_ci	} while (ctl != ch->head_blk_ctl);
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	ch->tail_blk_ctl = ctl;
4508c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ch->lock, flags);
4518c2ecf20Sopenharmony_ci}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_cistatic irqreturn_t wcn36xx_irq_tx_complete(int irq, void *dev)
4548c2ecf20Sopenharmony_ci{
4558c2ecf20Sopenharmony_ci	struct wcn36xx *wcn = (struct wcn36xx *)dev;
4568c2ecf20Sopenharmony_ci	int int_src, int_reason;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_INT_SRC_RAW_REG, &int_src);
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	if (int_src & WCN36XX_INT_MASK_CHAN_TX_H) {
4618c2ecf20Sopenharmony_ci		wcn36xx_dxe_read_register(wcn,
4628c2ecf20Sopenharmony_ci					  WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_H,
4638c2ecf20Sopenharmony_ci					  &int_reason);
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci		wcn36xx_dxe_write_register(wcn,
4668c2ecf20Sopenharmony_ci					   WCN36XX_DXE_0_INT_CLR,
4678c2ecf20Sopenharmony_ci					   WCN36XX_INT_MASK_CHAN_TX_H);
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci		if (int_reason & WCN36XX_CH_STAT_INT_ERR_MASK ) {
4708c2ecf20Sopenharmony_ci			wcn36xx_dxe_write_register(wcn,
4718c2ecf20Sopenharmony_ci						   WCN36XX_DXE_0_INT_ERR_CLR,
4728c2ecf20Sopenharmony_ci						   WCN36XX_INT_MASK_CHAN_TX_H);
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci			wcn36xx_err("DXE IRQ reported error: 0x%x in high TX channel\n",
4758c2ecf20Sopenharmony_ci					int_src);
4768c2ecf20Sopenharmony_ci		}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci		if (int_reason & WCN36XX_CH_STAT_INT_DONE_MASK) {
4798c2ecf20Sopenharmony_ci			wcn36xx_dxe_write_register(wcn,
4808c2ecf20Sopenharmony_ci						   WCN36XX_DXE_0_INT_DONE_CLR,
4818c2ecf20Sopenharmony_ci						   WCN36XX_INT_MASK_CHAN_TX_H);
4828c2ecf20Sopenharmony_ci		}
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci		if (int_reason & WCN36XX_CH_STAT_INT_ED_MASK) {
4858c2ecf20Sopenharmony_ci			wcn36xx_dxe_write_register(wcn,
4868c2ecf20Sopenharmony_ci						   WCN36XX_DXE_0_INT_ED_CLR,
4878c2ecf20Sopenharmony_ci						   WCN36XX_INT_MASK_CHAN_TX_H);
4888c2ecf20Sopenharmony_ci		}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci		wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ready high, reason %08x\n",
4918c2ecf20Sopenharmony_ci			    int_reason);
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci		if (int_reason & (WCN36XX_CH_STAT_INT_DONE_MASK |
4948c2ecf20Sopenharmony_ci				  WCN36XX_CH_STAT_INT_ED_MASK)) {
4958c2ecf20Sopenharmony_ci			reap_tx_dxes(wcn, &wcn->dxe_tx_h_ch);
4968c2ecf20Sopenharmony_ci		}
4978c2ecf20Sopenharmony_ci	}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	if (int_src & WCN36XX_INT_MASK_CHAN_TX_L) {
5008c2ecf20Sopenharmony_ci		wcn36xx_dxe_read_register(wcn,
5018c2ecf20Sopenharmony_ci					  WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_L,
5028c2ecf20Sopenharmony_ci					  &int_reason);
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci		wcn36xx_dxe_write_register(wcn,
5058c2ecf20Sopenharmony_ci					   WCN36XX_DXE_0_INT_CLR,
5068c2ecf20Sopenharmony_ci					   WCN36XX_INT_MASK_CHAN_TX_L);
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci		if (int_reason & WCN36XX_CH_STAT_INT_ERR_MASK ) {
5098c2ecf20Sopenharmony_ci			wcn36xx_dxe_write_register(wcn,
5108c2ecf20Sopenharmony_ci						   WCN36XX_DXE_0_INT_ERR_CLR,
5118c2ecf20Sopenharmony_ci						   WCN36XX_INT_MASK_CHAN_TX_L);
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci			wcn36xx_err("DXE IRQ reported error: 0x%x in low TX channel\n",
5148c2ecf20Sopenharmony_ci					int_src);
5158c2ecf20Sopenharmony_ci		}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci		if (int_reason & WCN36XX_CH_STAT_INT_DONE_MASK) {
5188c2ecf20Sopenharmony_ci			wcn36xx_dxe_write_register(wcn,
5198c2ecf20Sopenharmony_ci						   WCN36XX_DXE_0_INT_DONE_CLR,
5208c2ecf20Sopenharmony_ci						   WCN36XX_INT_MASK_CHAN_TX_L);
5218c2ecf20Sopenharmony_ci		}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci		if (int_reason & WCN36XX_CH_STAT_INT_ED_MASK) {
5248c2ecf20Sopenharmony_ci			wcn36xx_dxe_write_register(wcn,
5258c2ecf20Sopenharmony_ci						   WCN36XX_DXE_0_INT_ED_CLR,
5268c2ecf20Sopenharmony_ci						   WCN36XX_INT_MASK_CHAN_TX_L);
5278c2ecf20Sopenharmony_ci		}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci		wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ready low, reason %08x\n",
5308c2ecf20Sopenharmony_ci			    int_reason);
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci		if (int_reason & (WCN36XX_CH_STAT_INT_DONE_MASK |
5338c2ecf20Sopenharmony_ci				  WCN36XX_CH_STAT_INT_ED_MASK)) {
5348c2ecf20Sopenharmony_ci			reap_tx_dxes(wcn, &wcn->dxe_tx_l_ch);
5358c2ecf20Sopenharmony_ci		}
5368c2ecf20Sopenharmony_ci	}
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
5398c2ecf20Sopenharmony_ci}
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_cistatic irqreturn_t wcn36xx_irq_rx_ready(int irq, void *dev)
5428c2ecf20Sopenharmony_ci{
5438c2ecf20Sopenharmony_ci	struct wcn36xx *wcn = (struct wcn36xx *)dev;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	wcn36xx_dxe_rx_frame(wcn);
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
5488c2ecf20Sopenharmony_ci}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_cistatic int wcn36xx_dxe_request_irqs(struct wcn36xx *wcn)
5518c2ecf20Sopenharmony_ci{
5528c2ecf20Sopenharmony_ci	int ret;
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	ret = request_irq(wcn->tx_irq, wcn36xx_irq_tx_complete,
5558c2ecf20Sopenharmony_ci			  IRQF_TRIGGER_HIGH, "wcn36xx_tx", wcn);
5568c2ecf20Sopenharmony_ci	if (ret) {
5578c2ecf20Sopenharmony_ci		wcn36xx_err("failed to alloc tx irq\n");
5588c2ecf20Sopenharmony_ci		goto out_err;
5598c2ecf20Sopenharmony_ci	}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	ret = request_irq(wcn->rx_irq, wcn36xx_irq_rx_ready, IRQF_TRIGGER_HIGH,
5628c2ecf20Sopenharmony_ci			  "wcn36xx_rx", wcn);
5638c2ecf20Sopenharmony_ci	if (ret) {
5648c2ecf20Sopenharmony_ci		wcn36xx_err("failed to alloc rx irq\n");
5658c2ecf20Sopenharmony_ci		goto out_txirq;
5668c2ecf20Sopenharmony_ci	}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	enable_irq_wake(wcn->rx_irq);
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	return 0;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ciout_txirq:
5738c2ecf20Sopenharmony_ci	free_irq(wcn->tx_irq, wcn);
5748c2ecf20Sopenharmony_ciout_err:
5758c2ecf20Sopenharmony_ci	return ret;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci}
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_cistatic int wcn36xx_rx_handle_packets(struct wcn36xx *wcn,
5808c2ecf20Sopenharmony_ci				     struct wcn36xx_dxe_ch *ch,
5818c2ecf20Sopenharmony_ci				     u32 ctrl,
5828c2ecf20Sopenharmony_ci				     u32 en_mask,
5838c2ecf20Sopenharmony_ci				     u32 int_mask,
5848c2ecf20Sopenharmony_ci				     u32 status_reg)
5858c2ecf20Sopenharmony_ci{
5868c2ecf20Sopenharmony_ci	struct wcn36xx_dxe_desc *dxe;
5878c2ecf20Sopenharmony_ci	struct wcn36xx_dxe_ctl *ctl;
5888c2ecf20Sopenharmony_ci	dma_addr_t  dma_addr;
5898c2ecf20Sopenharmony_ci	struct sk_buff *skb;
5908c2ecf20Sopenharmony_ci	u32 int_reason;
5918c2ecf20Sopenharmony_ci	int ret;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	wcn36xx_dxe_read_register(wcn, status_reg, &int_reason);
5948c2ecf20Sopenharmony_ci	wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_CLR, int_mask);
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	if (int_reason & WCN36XX_CH_STAT_INT_ERR_MASK) {
5978c2ecf20Sopenharmony_ci		wcn36xx_dxe_write_register(wcn,
5988c2ecf20Sopenharmony_ci					   WCN36XX_DXE_0_INT_ERR_CLR,
5998c2ecf20Sopenharmony_ci					   int_mask);
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci		wcn36xx_err("DXE IRQ reported error on RX channel\n");
6028c2ecf20Sopenharmony_ci	}
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	if (int_reason & WCN36XX_CH_STAT_INT_DONE_MASK)
6058c2ecf20Sopenharmony_ci		wcn36xx_dxe_write_register(wcn,
6068c2ecf20Sopenharmony_ci					   WCN36XX_DXE_0_INT_DONE_CLR,
6078c2ecf20Sopenharmony_ci					   int_mask);
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	if (int_reason & WCN36XX_CH_STAT_INT_ED_MASK)
6108c2ecf20Sopenharmony_ci		wcn36xx_dxe_write_register(wcn,
6118c2ecf20Sopenharmony_ci					   WCN36XX_DXE_0_INT_ED_CLR,
6128c2ecf20Sopenharmony_ci					   int_mask);
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	if (!(int_reason & (WCN36XX_CH_STAT_INT_DONE_MASK |
6158c2ecf20Sopenharmony_ci			    WCN36XX_CH_STAT_INT_ED_MASK)))
6168c2ecf20Sopenharmony_ci		return 0;
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	spin_lock(&ch->lock);
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	ctl = ch->head_blk_ctl;
6218c2ecf20Sopenharmony_ci	dxe = ctl->desc;
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	while (!(READ_ONCE(dxe->ctrl) & WCN36xx_DXE_CTRL_VLD)) {
6248c2ecf20Sopenharmony_ci		/* do not read until we own DMA descriptor */
6258c2ecf20Sopenharmony_ci		dma_rmb();
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci		/* read/modify DMA descriptor */
6288c2ecf20Sopenharmony_ci		skb = ctl->skb;
6298c2ecf20Sopenharmony_ci		dma_addr = dxe->dst_addr_l;
6308c2ecf20Sopenharmony_ci		ret = wcn36xx_dxe_fill_skb(wcn->dev, ctl, GFP_ATOMIC);
6318c2ecf20Sopenharmony_ci		if (0 == ret) {
6328c2ecf20Sopenharmony_ci			/* new skb allocation ok. Use the new one and queue
6338c2ecf20Sopenharmony_ci			 * the old one to network system.
6348c2ecf20Sopenharmony_ci			 */
6358c2ecf20Sopenharmony_ci			dma_unmap_single(wcn->dev, dma_addr, WCN36XX_PKT_SIZE,
6368c2ecf20Sopenharmony_ci					DMA_FROM_DEVICE);
6378c2ecf20Sopenharmony_ci			wcn36xx_rx_skb(wcn, skb);
6388c2ecf20Sopenharmony_ci		}
6398c2ecf20Sopenharmony_ci		/* else keep old skb not submitted and reuse it for rx DMA
6408c2ecf20Sopenharmony_ci		 * (dropping the packet that it contained)
6418c2ecf20Sopenharmony_ci		 */
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci		/* flush descriptor changes before re-marking as valid */
6448c2ecf20Sopenharmony_ci		dma_wmb();
6458c2ecf20Sopenharmony_ci		dxe->ctrl = ctrl;
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci		ctl = ctl->next;
6488c2ecf20Sopenharmony_ci		dxe = ctl->desc;
6498c2ecf20Sopenharmony_ci	}
6508c2ecf20Sopenharmony_ci	wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR, en_mask);
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	ch->head_blk_ctl = ctl;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	spin_unlock(&ch->lock);
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	return 0;
6578c2ecf20Sopenharmony_ci}
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_civoid wcn36xx_dxe_rx_frame(struct wcn36xx *wcn)
6608c2ecf20Sopenharmony_ci{
6618c2ecf20Sopenharmony_ci	int int_src;
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_INT_SRC_RAW_REG, &int_src);
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	/* RX_LOW_PRI */
6668c2ecf20Sopenharmony_ci	if (int_src & WCN36XX_DXE_INT_CH1_MASK)
6678c2ecf20Sopenharmony_ci		wcn36xx_rx_handle_packets(wcn, &wcn->dxe_rx_l_ch,
6688c2ecf20Sopenharmony_ci					  WCN36XX_DXE_CTRL_RX_L,
6698c2ecf20Sopenharmony_ci					  WCN36XX_DXE_INT_CH1_MASK,
6708c2ecf20Sopenharmony_ci					  WCN36XX_INT_MASK_CHAN_RX_L,
6718c2ecf20Sopenharmony_ci					  WCN36XX_DXE_CH_STATUS_REG_ADDR_RX_L);
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	/* RX_HIGH_PRI */
6748c2ecf20Sopenharmony_ci	if (int_src & WCN36XX_DXE_INT_CH3_MASK)
6758c2ecf20Sopenharmony_ci		wcn36xx_rx_handle_packets(wcn, &wcn->dxe_rx_h_ch,
6768c2ecf20Sopenharmony_ci					  WCN36XX_DXE_CTRL_RX_H,
6778c2ecf20Sopenharmony_ci					  WCN36XX_DXE_INT_CH3_MASK,
6788c2ecf20Sopenharmony_ci					  WCN36XX_INT_MASK_CHAN_RX_H,
6798c2ecf20Sopenharmony_ci					  WCN36XX_DXE_CH_STATUS_REG_ADDR_RX_H);
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	if (!int_src)
6828c2ecf20Sopenharmony_ci		wcn36xx_warn("No DXE interrupt pending\n");
6838c2ecf20Sopenharmony_ci}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ciint wcn36xx_dxe_allocate_mem_pools(struct wcn36xx *wcn)
6868c2ecf20Sopenharmony_ci{
6878c2ecf20Sopenharmony_ci	size_t s;
6888c2ecf20Sopenharmony_ci	void *cpu_addr;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	/* Allocate BD headers for MGMT frames */
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	/* Where this come from ask QC */
6938c2ecf20Sopenharmony_ci	wcn->mgmt_mem_pool.chunk_size =	WCN36XX_BD_CHUNK_SIZE +
6948c2ecf20Sopenharmony_ci		16 - (WCN36XX_BD_CHUNK_SIZE % 8);
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	s = wcn->mgmt_mem_pool.chunk_size * WCN36XX_DXE_CH_DESC_NUMB_TX_H;
6978c2ecf20Sopenharmony_ci	cpu_addr = dma_alloc_coherent(wcn->dev, s,
6988c2ecf20Sopenharmony_ci				      &wcn->mgmt_mem_pool.phy_addr,
6998c2ecf20Sopenharmony_ci				      GFP_KERNEL);
7008c2ecf20Sopenharmony_ci	if (!cpu_addr)
7018c2ecf20Sopenharmony_ci		goto out_err;
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	wcn->mgmt_mem_pool.virt_addr = cpu_addr;
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	/* Allocate BD headers for DATA frames */
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	/* Where this come from ask QC */
7088c2ecf20Sopenharmony_ci	wcn->data_mem_pool.chunk_size = WCN36XX_BD_CHUNK_SIZE +
7098c2ecf20Sopenharmony_ci		16 - (WCN36XX_BD_CHUNK_SIZE % 8);
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	s = wcn->data_mem_pool.chunk_size * WCN36XX_DXE_CH_DESC_NUMB_TX_L;
7128c2ecf20Sopenharmony_ci	cpu_addr = dma_alloc_coherent(wcn->dev, s,
7138c2ecf20Sopenharmony_ci				      &wcn->data_mem_pool.phy_addr,
7148c2ecf20Sopenharmony_ci				      GFP_KERNEL);
7158c2ecf20Sopenharmony_ci	if (!cpu_addr)
7168c2ecf20Sopenharmony_ci		goto out_err;
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	wcn->data_mem_pool.virt_addr = cpu_addr;
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	return 0;
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ciout_err:
7238c2ecf20Sopenharmony_ci	wcn36xx_dxe_free_mem_pools(wcn);
7248c2ecf20Sopenharmony_ci	wcn36xx_err("Failed to allocate BD mempool\n");
7258c2ecf20Sopenharmony_ci	return -ENOMEM;
7268c2ecf20Sopenharmony_ci}
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_civoid wcn36xx_dxe_free_mem_pools(struct wcn36xx *wcn)
7298c2ecf20Sopenharmony_ci{
7308c2ecf20Sopenharmony_ci	if (wcn->mgmt_mem_pool.virt_addr)
7318c2ecf20Sopenharmony_ci		dma_free_coherent(wcn->dev, wcn->mgmt_mem_pool.chunk_size *
7328c2ecf20Sopenharmony_ci				  WCN36XX_DXE_CH_DESC_NUMB_TX_H,
7338c2ecf20Sopenharmony_ci				  wcn->mgmt_mem_pool.virt_addr,
7348c2ecf20Sopenharmony_ci				  wcn->mgmt_mem_pool.phy_addr);
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	if (wcn->data_mem_pool.virt_addr) {
7378c2ecf20Sopenharmony_ci		dma_free_coherent(wcn->dev, wcn->data_mem_pool.chunk_size *
7388c2ecf20Sopenharmony_ci				  WCN36XX_DXE_CH_DESC_NUMB_TX_L,
7398c2ecf20Sopenharmony_ci				  wcn->data_mem_pool.virt_addr,
7408c2ecf20Sopenharmony_ci				  wcn->data_mem_pool.phy_addr);
7418c2ecf20Sopenharmony_ci	}
7428c2ecf20Sopenharmony_ci}
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ciint wcn36xx_dxe_tx_frame(struct wcn36xx *wcn,
7458c2ecf20Sopenharmony_ci			 struct wcn36xx_vif *vif_priv,
7468c2ecf20Sopenharmony_ci			 struct wcn36xx_tx_bd *bd,
7478c2ecf20Sopenharmony_ci			 struct sk_buff *skb,
7488c2ecf20Sopenharmony_ci			 bool is_low)
7498c2ecf20Sopenharmony_ci{
7508c2ecf20Sopenharmony_ci	struct wcn36xx_dxe_desc *desc_bd, *desc_skb;
7518c2ecf20Sopenharmony_ci	struct wcn36xx_dxe_ctl *ctl_bd, *ctl_skb;
7528c2ecf20Sopenharmony_ci	struct wcn36xx_dxe_ch *ch = NULL;
7538c2ecf20Sopenharmony_ci	unsigned long flags;
7548c2ecf20Sopenharmony_ci	int ret;
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	ch = is_low ? &wcn->dxe_tx_l_ch : &wcn->dxe_tx_h_ch;
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ch->lock, flags);
7598c2ecf20Sopenharmony_ci	ctl_bd = ch->head_blk_ctl;
7608c2ecf20Sopenharmony_ci	ctl_skb = ctl_bd->next;
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	/*
7638c2ecf20Sopenharmony_ci	 * If skb is not null that means that we reached the tail of the ring
7648c2ecf20Sopenharmony_ci	 * hence ring is full. Stop queues to let mac80211 back off until ring
7658c2ecf20Sopenharmony_ci	 * has an empty slot again.
7668c2ecf20Sopenharmony_ci	 */
7678c2ecf20Sopenharmony_ci	if (NULL != ctl_skb->skb) {
7688c2ecf20Sopenharmony_ci		ieee80211_stop_queues(wcn->hw);
7698c2ecf20Sopenharmony_ci		wcn->queues_stopped = true;
7708c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ch->lock, flags);
7718c2ecf20Sopenharmony_ci		return -EBUSY;
7728c2ecf20Sopenharmony_ci	}
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	if (unlikely(ctl_skb->bd_cpu_addr)) {
7758c2ecf20Sopenharmony_ci		wcn36xx_err("bd_cpu_addr cannot be NULL for skb DXE\n");
7768c2ecf20Sopenharmony_ci		ret = -EINVAL;
7778c2ecf20Sopenharmony_ci		goto unlock;
7788c2ecf20Sopenharmony_ci	}
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	desc_bd = ctl_bd->desc;
7818c2ecf20Sopenharmony_ci	desc_skb = ctl_skb->desc;
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	ctl_bd->skb = NULL;
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	/* write buffer descriptor */
7868c2ecf20Sopenharmony_ci	memcpy(ctl_bd->bd_cpu_addr, bd, sizeof(*bd));
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	/* Set source address of the BD we send */
7898c2ecf20Sopenharmony_ci	desc_bd->src_addr_l = ctl_bd->bd_phy_addr;
7908c2ecf20Sopenharmony_ci	desc_bd->dst_addr_l = ch->dxe_wq;
7918c2ecf20Sopenharmony_ci	desc_bd->fr_len = sizeof(struct wcn36xx_tx_bd);
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	wcn36xx_dbg(WCN36XX_DBG_DXE, "DXE TX\n");
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "DESC1 >>> ",
7968c2ecf20Sopenharmony_ci			 (char *)desc_bd, sizeof(*desc_bd));
7978c2ecf20Sopenharmony_ci	wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP,
7988c2ecf20Sopenharmony_ci			 "BD   >>> ", (char *)ctl_bd->bd_cpu_addr,
7998c2ecf20Sopenharmony_ci			 sizeof(struct wcn36xx_tx_bd));
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	desc_skb->src_addr_l = dma_map_single(wcn->dev,
8028c2ecf20Sopenharmony_ci					      skb->data,
8038c2ecf20Sopenharmony_ci					      skb->len,
8048c2ecf20Sopenharmony_ci					      DMA_TO_DEVICE);
8058c2ecf20Sopenharmony_ci	if (dma_mapping_error(wcn->dev, desc_skb->src_addr_l)) {
8068c2ecf20Sopenharmony_ci		dev_err(wcn->dev, "unable to DMA map src_addr_l\n");
8078c2ecf20Sopenharmony_ci		ret = -ENOMEM;
8088c2ecf20Sopenharmony_ci		goto unlock;
8098c2ecf20Sopenharmony_ci	}
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	ctl_skb->skb = skb;
8128c2ecf20Sopenharmony_ci	desc_skb->dst_addr_l = ch->dxe_wq;
8138c2ecf20Sopenharmony_ci	desc_skb->fr_len = ctl_skb->skb->len;
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "DESC2 >>> ",
8168c2ecf20Sopenharmony_ci			 (char *)desc_skb, sizeof(*desc_skb));
8178c2ecf20Sopenharmony_ci	wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "SKB   >>> ",
8188c2ecf20Sopenharmony_ci			 (char *)ctl_skb->skb->data, ctl_skb->skb->len);
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	/* Move the head of the ring to the next empty descriptor */
8218c2ecf20Sopenharmony_ci	 ch->head_blk_ctl = ctl_skb->next;
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	/* Commit all previous writes and set descriptors to VALID */
8248c2ecf20Sopenharmony_ci	wmb();
8258c2ecf20Sopenharmony_ci	desc_skb->ctrl = ch->ctrl_skb;
8268c2ecf20Sopenharmony_ci	wmb();
8278c2ecf20Sopenharmony_ci	desc_bd->ctrl = ch->ctrl_bd;
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	/*
8308c2ecf20Sopenharmony_ci	 * When connected and trying to send data frame chip can be in sleep
8318c2ecf20Sopenharmony_ci	 * mode and writing to the register will not wake up the chip. Instead
8328c2ecf20Sopenharmony_ci	 * notify chip about new frame through SMSM bus.
8338c2ecf20Sopenharmony_ci	 */
8348c2ecf20Sopenharmony_ci	if (is_low &&  vif_priv->pw_state == WCN36XX_BMPS) {
8358c2ecf20Sopenharmony_ci		qcom_smem_state_update_bits(wcn->tx_rings_empty_state,
8368c2ecf20Sopenharmony_ci					    WCN36XX_SMSM_WLAN_TX_ENABLE,
8378c2ecf20Sopenharmony_ci					    WCN36XX_SMSM_WLAN_TX_ENABLE);
8388c2ecf20Sopenharmony_ci	} else {
8398c2ecf20Sopenharmony_ci		/* indicate End Of Packet and generate interrupt on descriptor
8408c2ecf20Sopenharmony_ci		 * done.
8418c2ecf20Sopenharmony_ci		 */
8428c2ecf20Sopenharmony_ci		wcn36xx_dxe_write_register(wcn,
8438c2ecf20Sopenharmony_ci			ch->reg_ctrl, ch->def_ctrl);
8448c2ecf20Sopenharmony_ci	}
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	ret = 0;
8478c2ecf20Sopenharmony_ciunlock:
8488c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ch->lock, flags);
8498c2ecf20Sopenharmony_ci	return ret;
8508c2ecf20Sopenharmony_ci}
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ciint wcn36xx_dxe_init(struct wcn36xx *wcn)
8538c2ecf20Sopenharmony_ci{
8548c2ecf20Sopenharmony_ci	int reg_data = 0, ret;
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	reg_data = WCN36XX_DXE_REG_RESET;
8578c2ecf20Sopenharmony_ci	wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_REG_CSR_RESET, reg_data);
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	/* Select channels for rx avail and xfer done interrupts... */
8608c2ecf20Sopenharmony_ci	reg_data = (WCN36XX_DXE_INT_CH3_MASK | WCN36XX_DXE_INT_CH1_MASK) << 16 |
8618c2ecf20Sopenharmony_ci		    WCN36XX_DXE_INT_CH0_MASK | WCN36XX_DXE_INT_CH4_MASK;
8628c2ecf20Sopenharmony_ci	if (wcn->is_pronto)
8638c2ecf20Sopenharmony_ci		wcn36xx_ccu_write_register(wcn, WCN36XX_CCU_DXE_INT_SELECT_PRONTO, reg_data);
8648c2ecf20Sopenharmony_ci	else
8658c2ecf20Sopenharmony_ci		wcn36xx_ccu_write_register(wcn, WCN36XX_CCU_DXE_INT_SELECT_RIVA, reg_data);
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	/***************************************/
8688c2ecf20Sopenharmony_ci	/* Init descriptors for TX LOW channel */
8698c2ecf20Sopenharmony_ci	/***************************************/
8708c2ecf20Sopenharmony_ci	ret = wcn36xx_dxe_init_descs(wcn->dev, &wcn->dxe_tx_l_ch);
8718c2ecf20Sopenharmony_ci	if (ret) {
8728c2ecf20Sopenharmony_ci		dev_err(wcn->dev, "Error allocating descriptor\n");
8738c2ecf20Sopenharmony_ci		return ret;
8748c2ecf20Sopenharmony_ci	}
8758c2ecf20Sopenharmony_ci	wcn36xx_dxe_init_tx_bd(&wcn->dxe_tx_l_ch, &wcn->data_mem_pool);
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	/* Write channel head to a NEXT register */
8788c2ecf20Sopenharmony_ci	wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_TX_L,
8798c2ecf20Sopenharmony_ci		wcn->dxe_tx_l_ch.head_blk_ctl->desc_phy_addr);
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci	/* Program DMA destination addr for TX LOW */
8828c2ecf20Sopenharmony_ci	wcn36xx_dxe_write_register(wcn,
8838c2ecf20Sopenharmony_ci		WCN36XX_DXE_CH_DEST_ADDR_TX_L,
8848c2ecf20Sopenharmony_ci		WCN36XX_DXE_WQ_TX_L);
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci	wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_REG_CH_EN, &reg_data);
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci	/***************************************/
8898c2ecf20Sopenharmony_ci	/* Init descriptors for TX HIGH channel */
8908c2ecf20Sopenharmony_ci	/***************************************/
8918c2ecf20Sopenharmony_ci	ret = wcn36xx_dxe_init_descs(wcn->dev, &wcn->dxe_tx_h_ch);
8928c2ecf20Sopenharmony_ci	if (ret) {
8938c2ecf20Sopenharmony_ci		dev_err(wcn->dev, "Error allocating descriptor\n");
8948c2ecf20Sopenharmony_ci		goto out_err_txh_ch;
8958c2ecf20Sopenharmony_ci	}
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci	wcn36xx_dxe_init_tx_bd(&wcn->dxe_tx_h_ch, &wcn->mgmt_mem_pool);
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	/* Write channel head to a NEXT register */
9008c2ecf20Sopenharmony_ci	wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_TX_H,
9018c2ecf20Sopenharmony_ci		wcn->dxe_tx_h_ch.head_blk_ctl->desc_phy_addr);
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	/* Program DMA destination addr for TX HIGH */
9048c2ecf20Sopenharmony_ci	wcn36xx_dxe_write_register(wcn,
9058c2ecf20Sopenharmony_ci		WCN36XX_DXE_CH_DEST_ADDR_TX_H,
9068c2ecf20Sopenharmony_ci		WCN36XX_DXE_WQ_TX_H);
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_REG_CH_EN, &reg_data);
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci	/***************************************/
9118c2ecf20Sopenharmony_ci	/* Init descriptors for RX LOW channel */
9128c2ecf20Sopenharmony_ci	/***************************************/
9138c2ecf20Sopenharmony_ci	ret = wcn36xx_dxe_init_descs(wcn->dev, &wcn->dxe_rx_l_ch);
9148c2ecf20Sopenharmony_ci	if (ret) {
9158c2ecf20Sopenharmony_ci		dev_err(wcn->dev, "Error allocating descriptor\n");
9168c2ecf20Sopenharmony_ci		goto out_err_rxl_ch;
9178c2ecf20Sopenharmony_ci	}
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	/* For RX we need to preallocated buffers */
9208c2ecf20Sopenharmony_ci	wcn36xx_dxe_ch_alloc_skb(wcn, &wcn->dxe_rx_l_ch);
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	/* Write channel head to a NEXT register */
9238c2ecf20Sopenharmony_ci	wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_RX_L,
9248c2ecf20Sopenharmony_ci		wcn->dxe_rx_l_ch.head_blk_ctl->desc_phy_addr);
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	/* Write DMA source address */
9278c2ecf20Sopenharmony_ci	wcn36xx_dxe_write_register(wcn,
9288c2ecf20Sopenharmony_ci		WCN36XX_DXE_CH_SRC_ADDR_RX_L,
9298c2ecf20Sopenharmony_ci		WCN36XX_DXE_WQ_RX_L);
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci	/* Program preallocated destination address */
9328c2ecf20Sopenharmony_ci	wcn36xx_dxe_write_register(wcn,
9338c2ecf20Sopenharmony_ci		WCN36XX_DXE_CH_DEST_ADDR_RX_L,
9348c2ecf20Sopenharmony_ci		wcn->dxe_rx_l_ch.head_blk_ctl->desc->phy_next_l);
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	/* Enable default control registers */
9378c2ecf20Sopenharmony_ci	wcn36xx_dxe_write_register(wcn,
9388c2ecf20Sopenharmony_ci		WCN36XX_DXE_REG_CTL_RX_L,
9398c2ecf20Sopenharmony_ci		WCN36XX_DXE_CH_DEFAULT_CTL_RX_L);
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci	/***************************************/
9428c2ecf20Sopenharmony_ci	/* Init descriptors for RX HIGH channel */
9438c2ecf20Sopenharmony_ci	/***************************************/
9448c2ecf20Sopenharmony_ci	ret = wcn36xx_dxe_init_descs(wcn->dev, &wcn->dxe_rx_h_ch);
9458c2ecf20Sopenharmony_ci	if (ret) {
9468c2ecf20Sopenharmony_ci		dev_err(wcn->dev, "Error allocating descriptor\n");
9478c2ecf20Sopenharmony_ci		goto out_err_rxh_ch;
9488c2ecf20Sopenharmony_ci	}
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	/* For RX we need to prealocat buffers */
9518c2ecf20Sopenharmony_ci	wcn36xx_dxe_ch_alloc_skb(wcn, &wcn->dxe_rx_h_ch);
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci	/* Write chanel head to a NEXT register */
9548c2ecf20Sopenharmony_ci	wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_RX_H,
9558c2ecf20Sopenharmony_ci		wcn->dxe_rx_h_ch.head_blk_ctl->desc_phy_addr);
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci	/* Write DMA source address */
9588c2ecf20Sopenharmony_ci	wcn36xx_dxe_write_register(wcn,
9598c2ecf20Sopenharmony_ci		WCN36XX_DXE_CH_SRC_ADDR_RX_H,
9608c2ecf20Sopenharmony_ci		WCN36XX_DXE_WQ_RX_H);
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci	/* Program preallocated destination address */
9638c2ecf20Sopenharmony_ci	wcn36xx_dxe_write_register(wcn,
9648c2ecf20Sopenharmony_ci		WCN36XX_DXE_CH_DEST_ADDR_RX_H,
9658c2ecf20Sopenharmony_ci		 wcn->dxe_rx_h_ch.head_blk_ctl->desc->phy_next_l);
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	/* Enable default control registers */
9688c2ecf20Sopenharmony_ci	wcn36xx_dxe_write_register(wcn,
9698c2ecf20Sopenharmony_ci		WCN36XX_DXE_REG_CTL_RX_H,
9708c2ecf20Sopenharmony_ci		WCN36XX_DXE_CH_DEFAULT_CTL_RX_H);
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci	ret = wcn36xx_dxe_request_irqs(wcn);
9738c2ecf20Sopenharmony_ci	if (ret < 0)
9748c2ecf20Sopenharmony_ci		goto out_err_irq;
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci	timer_setup(&wcn->tx_ack_timer, wcn36xx_dxe_tx_timer, 0);
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	/* Enable channel interrupts */
9798c2ecf20Sopenharmony_ci	wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_TX_L);
9808c2ecf20Sopenharmony_ci	wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_TX_H);
9818c2ecf20Sopenharmony_ci	wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_RX_L);
9828c2ecf20Sopenharmony_ci	wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_RX_H);
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_ci	return 0;
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ciout_err_irq:
9878c2ecf20Sopenharmony_ci	wcn36xx_dxe_deinit_descs(wcn->dev, &wcn->dxe_rx_h_ch);
9888c2ecf20Sopenharmony_ciout_err_rxh_ch:
9898c2ecf20Sopenharmony_ci	wcn36xx_dxe_deinit_descs(wcn->dev, &wcn->dxe_rx_l_ch);
9908c2ecf20Sopenharmony_ciout_err_rxl_ch:
9918c2ecf20Sopenharmony_ci	wcn36xx_dxe_deinit_descs(wcn->dev, &wcn->dxe_tx_h_ch);
9928c2ecf20Sopenharmony_ciout_err_txh_ch:
9938c2ecf20Sopenharmony_ci	wcn36xx_dxe_deinit_descs(wcn->dev, &wcn->dxe_tx_l_ch);
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci	return ret;
9968c2ecf20Sopenharmony_ci}
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_civoid wcn36xx_dxe_deinit(struct wcn36xx *wcn)
9998c2ecf20Sopenharmony_ci{
10008c2ecf20Sopenharmony_ci	int reg_data = 0;
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci	/* Disable channel interrupts */
10038c2ecf20Sopenharmony_ci	wcn36xx_dxe_disable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_RX_H);
10048c2ecf20Sopenharmony_ci	wcn36xx_dxe_disable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_RX_L);
10058c2ecf20Sopenharmony_ci	wcn36xx_dxe_disable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_TX_H);
10068c2ecf20Sopenharmony_ci	wcn36xx_dxe_disable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_TX_L);
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci	free_irq(wcn->tx_irq, wcn);
10098c2ecf20Sopenharmony_ci	free_irq(wcn->rx_irq, wcn);
10108c2ecf20Sopenharmony_ci	del_timer(&wcn->tx_ack_timer);
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci	if (wcn->tx_ack_skb) {
10138c2ecf20Sopenharmony_ci		ieee80211_tx_status_irqsafe(wcn->hw, wcn->tx_ack_skb);
10148c2ecf20Sopenharmony_ci		wcn->tx_ack_skb = NULL;
10158c2ecf20Sopenharmony_ci	}
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	/* Put the DXE block into reset before freeing memory */
10188c2ecf20Sopenharmony_ci	reg_data = WCN36XX_DXE_REG_RESET;
10198c2ecf20Sopenharmony_ci	wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_REG_CSR_RESET, reg_data);
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci	wcn36xx_dxe_ch_free_skbs(wcn, &wcn->dxe_rx_l_ch);
10228c2ecf20Sopenharmony_ci	wcn36xx_dxe_ch_free_skbs(wcn, &wcn->dxe_rx_h_ch);
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	wcn36xx_dxe_deinit_descs(wcn->dev, &wcn->dxe_tx_l_ch);
10258c2ecf20Sopenharmony_ci	wcn36xx_dxe_deinit_descs(wcn->dev, &wcn->dxe_tx_h_ch);
10268c2ecf20Sopenharmony_ci	wcn36xx_dxe_deinit_descs(wcn->dev, &wcn->dxe_rx_l_ch);
10278c2ecf20Sopenharmony_ci	wcn36xx_dxe_deinit_descs(wcn->dev, &wcn->dxe_rx_h_ch);
10288c2ecf20Sopenharmony_ci}
1029