コード例 #1
0
ファイル: dxe.c プロジェクト: dreamfly281/wcn36xx
void wcn36xx_rx_ready_work(struct work_struct *work)
{
	struct wcn36xx *wcn =
		container_of(work, struct wcn36xx, rx_ready_work);
	int int_src;

	wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_INT_SRC_RAW_REG, &int_src);

	/* RX_LOW_PRI */
	if (int_src & WCN36XX_DXE_INT_CH1_MASK) {
		wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_CLR,
					   WCN36XX_DXE_INT_CH1_MASK);
		wcn36xx_rx_handle_packets(wcn, &(wcn->dxe_rx_l_ch));
	}

	/* RX_HIGH_PRI */
	if (int_src & WCN36XX_DXE_INT_CH3_MASK) {
		/* Clean up all the INT within this channel */
		wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_CLR,
					   WCN36XX_DXE_INT_CH3_MASK);
		wcn36xx_rx_handle_packets(wcn, &(wcn->dxe_rx_h_ch));
	}

	if (!int_src)
		wcn36xx_warn("No DXE interrupt pending");

	enable_irq(wcn->rx_irq);
}
コード例 #2
0
ファイル: dxe.c プロジェクト: Lyude/linux
void wcn36xx_dxe_rx_frame(struct wcn36xx *wcn)
{
	int int_src;

	wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_INT_SRC_RAW_REG, &int_src);

	/* RX_LOW_PRI */
	if (int_src & WCN36XX_DXE_INT_CH1_MASK)
		wcn36xx_rx_handle_packets(wcn, &wcn->dxe_rx_l_ch,
					  WCN36XX_DXE_CTRL_RX_L,
					  WCN36XX_DXE_INT_CH1_MASK,
					  WCN36XX_INT_MASK_CHAN_RX_L,
					  WCN36XX_DXE_CH_STATUS_REG_ADDR_RX_L);

	/* RX_HIGH_PRI */
	if (int_src & WCN36XX_DXE_INT_CH3_MASK)
		wcn36xx_rx_handle_packets(wcn, &wcn->dxe_rx_h_ch,
					  WCN36XX_DXE_CTRL_RX_H,
					  WCN36XX_DXE_INT_CH3_MASK,
					  WCN36XX_INT_MASK_CHAN_RX_H,
					  WCN36XX_DXE_CH_STATUS_REG_ADDR_RX_H);

	if (!int_src)
		wcn36xx_warn("No DXE interrupt pending\n");
}
コード例 #3
0
static irqreturn_t wcn36xx_irq_tx_complete(int irq, void *dev)
{
    struct wcn36xx *wcn = (struct wcn36xx *)dev;
    int int_src, int_reason;

    wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_INT_SRC_RAW_REG, &int_src);

    if (int_src & WCN36XX_INT_MASK_CHAN_TX_H) {
        wcn36xx_dxe_read_register(wcn,
                                  WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_H,
                                  &int_reason);

        /* TODO: Check int_reason */

        wcn36xx_dxe_write_register(wcn,
                                   WCN36XX_DXE_0_INT_CLR,
                                   WCN36XX_INT_MASK_CHAN_TX_H);

        wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_ED_CLR,
                                   WCN36XX_INT_MASK_CHAN_TX_H);
        wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ready high\n");
        reap_tx_dxes(wcn, &wcn->dxe_tx_h_ch);
    }

    if (int_src & WCN36XX_INT_MASK_CHAN_TX_L) {
        wcn36xx_dxe_read_register(wcn,
                                  WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_L,
                                  &int_reason);
        /* TODO: Check int_reason */

        wcn36xx_dxe_write_register(wcn,
                                   WCN36XX_DXE_0_INT_CLR,
                                   WCN36XX_INT_MASK_CHAN_TX_L);

        wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_ED_CLR,
                                   WCN36XX_INT_MASK_CHAN_TX_L);
        wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ready low\n");
        reap_tx_dxes(wcn, &wcn->dxe_tx_l_ch);
    }

    return IRQ_HANDLED;
}
コード例 #4
0
ファイル: dxe.c プロジェクト: dreamfly281/wcn36xx
static int wcn36xx_dxe_enable_ch_int(struct wcn36xx *wcn, u16 wcn_ch)
{
	int reg_data = 0;
	wcn36xx_dxe_read_register(wcn,
		WCN36XX_DXE_INT_MASK_REG,
		&reg_data);
	reg_data |= wcn_ch;
	wcn36xx_dxe_write_register(wcn,
		WCN36XX_DXE_INT_MASK_REG,
		(int)reg_data);
	return 0;
}
コード例 #5
0
void wcn36xx_dxe_rx_frame(struct wcn36xx *wcn)
{
    int int_src;

    wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_INT_SRC_RAW_REG, &int_src);

    /* RX_LOW_PRI */
    if (int_src & WCN36XX_DXE_INT_CH1_MASK) {
        wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_CLR,
                                   WCN36XX_DXE_INT_CH1_MASK);
        wcn36xx_rx_handle_packets(wcn, &(wcn->dxe_rx_l_ch));
    }

    /* RX_HIGH_PRI */
    if (int_src & WCN36XX_DXE_INT_CH3_MASK) {
        /* Clean up all the INT within this channel */
        wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_CLR,
                                   WCN36XX_DXE_INT_CH3_MASK);
        wcn36xx_rx_handle_packets(wcn, &(wcn->dxe_rx_h_ch));
    }

    if (!int_src)
        wcn36xx_warn("No DXE interrupt pending\n");
}
コード例 #6
0
int wcn36xx_dxe_init(struct wcn36xx *wcn)
{
    int reg_data = 0, ret;

    reg_data = WCN36XX_DXE_REG_RESET;
    wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_REG_CSR_RESET, reg_data);

    /* Setting interrupt path */
    reg_data = WCN36XX_DXE_CCU_INT;
    wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_REG_CCU_INT, reg_data);

    /***************************************/
    /* Init descriptors for TX LOW channel */
    /***************************************/
    wcn36xx_dxe_init_descs(&wcn->dxe_tx_l_ch);
    wcn36xx_dxe_init_tx_bd(&wcn->dxe_tx_l_ch, &wcn->data_mem_pool);

    /* Write channel head to a NEXT register */
    wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_TX_L,
                               wcn->dxe_tx_l_ch.head_blk_ctl->desc_phy_addr);

    /* Program DMA destination addr for TX LOW */
    wcn36xx_dxe_write_register(wcn,
                               WCN36XX_DXE_CH_DEST_ADDR_TX_L,
                               WCN36XX_DXE_WQ_TX_L);

    wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_REG_CH_EN, &reg_data);
    wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_TX_L);

    /***************************************/
    /* Init descriptors for TX HIGH channel */
    /***************************************/
    wcn36xx_dxe_init_descs(&wcn->dxe_tx_h_ch);
    wcn36xx_dxe_init_tx_bd(&wcn->dxe_tx_h_ch, &wcn->mgmt_mem_pool);

    /* Write channel head to a NEXT register */
    wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_TX_H,
                               wcn->dxe_tx_h_ch.head_blk_ctl->desc_phy_addr);

    /* Program DMA destination addr for TX HIGH */
    wcn36xx_dxe_write_register(wcn,
                               WCN36XX_DXE_CH_DEST_ADDR_TX_H,
                               WCN36XX_DXE_WQ_TX_H);

    wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_REG_CH_EN, &reg_data);

    /* Enable channel interrupts */
    wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_TX_H);

    /***************************************/
    /* Init descriptors for RX LOW channel */
    /***************************************/
    wcn36xx_dxe_init_descs(&wcn->dxe_rx_l_ch);

    /* For RX we need to preallocated buffers */
    wcn36xx_dxe_ch_alloc_skb(wcn, &wcn->dxe_rx_l_ch);

    /* Write channel head to a NEXT register */
    wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_RX_L,
                               wcn->dxe_rx_l_ch.head_blk_ctl->desc_phy_addr);

    /* Write DMA source address */
    wcn36xx_dxe_write_register(wcn,
                               WCN36XX_DXE_CH_SRC_ADDR_RX_L,
                               WCN36XX_DXE_WQ_RX_L);

    /* Program preallocated destination address */
    wcn36xx_dxe_write_register(wcn,
                               WCN36XX_DXE_CH_DEST_ADDR_RX_L,
                               wcn->dxe_rx_l_ch.head_blk_ctl->desc->phy_next_l);

    /* Enable default control registers */
    wcn36xx_dxe_write_register(wcn,
                               WCN36XX_DXE_REG_CTL_RX_L,
                               WCN36XX_DXE_CH_DEFAULT_CTL_RX_L);

    /* Enable channel interrupts */
    wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_RX_L);

    /***************************************/
    /* Init descriptors for RX HIGH channel */
    /***************************************/
    wcn36xx_dxe_init_descs(&wcn->dxe_rx_h_ch);

    /* For RX we need to prealocat buffers */
    wcn36xx_dxe_ch_alloc_skb(wcn, &wcn->dxe_rx_h_ch);

    /* Write chanel head to a NEXT register */
    wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_RX_H,
                               wcn->dxe_rx_h_ch.head_blk_ctl->desc_phy_addr);

    /* Write DMA source address */
    wcn36xx_dxe_write_register(wcn,
                               WCN36XX_DXE_CH_SRC_ADDR_RX_H,
                               WCN36XX_DXE_WQ_RX_H);

    /* Program preallocated destination address */
    wcn36xx_dxe_write_register(wcn,
                               WCN36XX_DXE_CH_DEST_ADDR_RX_H,
                               wcn->dxe_rx_h_ch.head_blk_ctl->desc->phy_next_l);

    /* Enable default control registers */
    wcn36xx_dxe_write_register(wcn,
                               WCN36XX_DXE_REG_CTL_RX_H,
                               WCN36XX_DXE_CH_DEFAULT_CTL_RX_H);

    /* Enable channel interrupts */
    wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_RX_H);

    ret = wcn36xx_dxe_request_irqs(wcn);
    if (ret < 0)
        goto out_err;

    return 0;

out_err:
    return ret;
}
コード例 #7
0
ファイル: dxe.c プロジェクト: Lyude/linux
int wcn36xx_dxe_init(struct wcn36xx *wcn)
{
	int reg_data = 0, ret;

	reg_data = WCN36XX_DXE_REG_RESET;
	wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_REG_CSR_RESET, reg_data);

	/* Select channels for rx avail and xfer done interrupts... */
	reg_data = (WCN36XX_DXE_INT_CH3_MASK | WCN36XX_DXE_INT_CH1_MASK) << 16 |
		    WCN36XX_DXE_INT_CH0_MASK | WCN36XX_DXE_INT_CH4_MASK;
	if (wcn->is_pronto)
		wcn36xx_ccu_write_register(wcn, WCN36XX_CCU_DXE_INT_SELECT_PRONTO, reg_data);
	else
		wcn36xx_ccu_write_register(wcn, WCN36XX_CCU_DXE_INT_SELECT_RIVA, reg_data);

	/***************************************/
	/* Init descriptors for TX LOW channel */
	/***************************************/
	ret = wcn36xx_dxe_init_descs(wcn->dev, &wcn->dxe_tx_l_ch);
	if (ret) {
		dev_err(wcn->dev, "Error allocating descriptor\n");
		return ret;
	}
	wcn36xx_dxe_init_tx_bd(&wcn->dxe_tx_l_ch, &wcn->data_mem_pool);

	/* Write channel head to a NEXT register */
	wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_TX_L,
		wcn->dxe_tx_l_ch.head_blk_ctl->desc_phy_addr);

	/* Program DMA destination addr for TX LOW */
	wcn36xx_dxe_write_register(wcn,
		WCN36XX_DXE_CH_DEST_ADDR_TX_L,
		WCN36XX_DXE_WQ_TX_L);

	wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_REG_CH_EN, &reg_data);
	wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_TX_L);

	/***************************************/
	/* Init descriptors for TX HIGH channel */
	/***************************************/
	ret = wcn36xx_dxe_init_descs(wcn->dev, &wcn->dxe_tx_h_ch);
	if (ret) {
		dev_err(wcn->dev, "Error allocating descriptor\n");
		goto out_err_txh_ch;
	}

	wcn36xx_dxe_init_tx_bd(&wcn->dxe_tx_h_ch, &wcn->mgmt_mem_pool);

	/* Write channel head to a NEXT register */
	wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_TX_H,
		wcn->dxe_tx_h_ch.head_blk_ctl->desc_phy_addr);

	/* Program DMA destination addr for TX HIGH */
	wcn36xx_dxe_write_register(wcn,
		WCN36XX_DXE_CH_DEST_ADDR_TX_H,
		WCN36XX_DXE_WQ_TX_H);

	wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_REG_CH_EN, &reg_data);

	/* Enable channel interrupts */
	wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_TX_H);

	/***************************************/
	/* Init descriptors for RX LOW channel */
	/***************************************/
	ret = wcn36xx_dxe_init_descs(wcn->dev, &wcn->dxe_rx_l_ch);
	if (ret) {
		dev_err(wcn->dev, "Error allocating descriptor\n");
		goto out_err_rxl_ch;
	}


	/* For RX we need to preallocated buffers */
	wcn36xx_dxe_ch_alloc_skb(wcn, &wcn->dxe_rx_l_ch);

	/* Write channel head to a NEXT register */
	wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_RX_L,
		wcn->dxe_rx_l_ch.head_blk_ctl->desc_phy_addr);

	/* Write DMA source address */
	wcn36xx_dxe_write_register(wcn,
		WCN36XX_DXE_CH_SRC_ADDR_RX_L,
		WCN36XX_DXE_WQ_RX_L);

	/* Program preallocated destination address */
	wcn36xx_dxe_write_register(wcn,
		WCN36XX_DXE_CH_DEST_ADDR_RX_L,
		wcn->dxe_rx_l_ch.head_blk_ctl->desc->phy_next_l);

	/* Enable default control registers */
	wcn36xx_dxe_write_register(wcn,
		WCN36XX_DXE_REG_CTL_RX_L,
		WCN36XX_DXE_CH_DEFAULT_CTL_RX_L);

	/* Enable channel interrupts */
	wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_RX_L);

	/***************************************/
	/* Init descriptors for RX HIGH channel */
	/***************************************/
	ret = wcn36xx_dxe_init_descs(wcn->dev, &wcn->dxe_rx_h_ch);
	if (ret) {
		dev_err(wcn->dev, "Error allocating descriptor\n");
		goto out_err_rxh_ch;
	}

	/* For RX we need to prealocat buffers */
	wcn36xx_dxe_ch_alloc_skb(wcn, &wcn->dxe_rx_h_ch);

	/* Write chanel head to a NEXT register */
	wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_RX_H,
		wcn->dxe_rx_h_ch.head_blk_ctl->desc_phy_addr);

	/* Write DMA source address */
	wcn36xx_dxe_write_register(wcn,
		WCN36XX_DXE_CH_SRC_ADDR_RX_H,
		WCN36XX_DXE_WQ_RX_H);

	/* Program preallocated destination address */
	wcn36xx_dxe_write_register(wcn,
		WCN36XX_DXE_CH_DEST_ADDR_RX_H,
		 wcn->dxe_rx_h_ch.head_blk_ctl->desc->phy_next_l);

	/* Enable default control registers */
	wcn36xx_dxe_write_register(wcn,
		WCN36XX_DXE_REG_CTL_RX_H,
		WCN36XX_DXE_CH_DEFAULT_CTL_RX_H);

	/* Enable channel interrupts */
	wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_RX_H);

	ret = wcn36xx_dxe_request_irqs(wcn);
	if (ret < 0)
		goto out_err_irq;

	return 0;

out_err_irq:
	wcn36xx_dxe_deinit_descs(wcn->dev, &wcn->dxe_rx_h_ch);
out_err_rxh_ch:
	wcn36xx_dxe_deinit_descs(wcn->dev, &wcn->dxe_rx_l_ch);
out_err_rxl_ch:
	wcn36xx_dxe_deinit_descs(wcn->dev, &wcn->dxe_tx_h_ch);
out_err_txh_ch:
	wcn36xx_dxe_deinit_descs(wcn->dev, &wcn->dxe_tx_l_ch);

	return ret;
}
コード例 #8
0
ファイル: dxe.c プロジェクト: Lyude/linux
static int wcn36xx_rx_handle_packets(struct wcn36xx *wcn,
				     struct wcn36xx_dxe_ch *ch,
				     u32 ctrl,
				     u32 en_mask,
				     u32 int_mask,
				     u32 status_reg)
{
	struct wcn36xx_dxe_desc *dxe;
	struct wcn36xx_dxe_ctl *ctl;
	dma_addr_t  dma_addr;
	struct sk_buff *skb;
	u32 int_reason;
	int ret;

	wcn36xx_dxe_read_register(wcn, status_reg, &int_reason);
	wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_CLR, int_mask);

	if (int_reason & WCN36XX_CH_STAT_INT_ERR_MASK) {
		wcn36xx_dxe_write_register(wcn,
					   WCN36XX_DXE_0_INT_ERR_CLR,
					   int_mask);

		wcn36xx_err("DXE IRQ reported error on RX channel\n");
	}

	if (int_reason & WCN36XX_CH_STAT_INT_DONE_MASK)
		wcn36xx_dxe_write_register(wcn,
					   WCN36XX_DXE_0_INT_DONE_CLR,
					   int_mask);

	if (int_reason & WCN36XX_CH_STAT_INT_ED_MASK)
		wcn36xx_dxe_write_register(wcn,
					   WCN36XX_DXE_0_INT_ED_CLR,
					   int_mask);

	if (!(int_reason & (WCN36XX_CH_STAT_INT_DONE_MASK |
			    WCN36XX_CH_STAT_INT_ED_MASK)))
		return 0;

	spin_lock(&ch->lock);

	ctl = ch->head_blk_ctl;
	dxe = ctl->desc;

	while (!(READ_ONCE(dxe->ctrl) & WCN36xx_DXE_CTRL_VLD)) {
		skb = ctl->skb;
		dma_addr = dxe->dst_addr_l;
		ret = wcn36xx_dxe_fill_skb(wcn->dev, ctl, GFP_ATOMIC);
		if (0 == ret) {
			/* new skb allocation ok. Use the new one and queue
			 * the old one to network system.
			 */
			dma_unmap_single(wcn->dev, dma_addr, WCN36XX_PKT_SIZE,
					DMA_FROM_DEVICE);
			wcn36xx_rx_skb(wcn, skb);
		} /* else keep old skb not submitted and use it for rx DMA */

		dxe->ctrl = ctrl;
		ctl = ctl->next;
		dxe = ctl->desc;
	}
	wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR, en_mask);

	ch->head_blk_ctl = ctl;

	spin_unlock(&ch->lock);

	return 0;
}
コード例 #9
0
ファイル: dxe.c プロジェクト: Lyude/linux
static irqreturn_t wcn36xx_irq_tx_complete(int irq, void *dev)
{
	struct wcn36xx *wcn = (struct wcn36xx *)dev;
	int int_src, int_reason;

	wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_INT_SRC_RAW_REG, &int_src);

	if (int_src & WCN36XX_INT_MASK_CHAN_TX_H) {
		wcn36xx_dxe_read_register(wcn,
					  WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_H,
					  &int_reason);

		wcn36xx_dxe_write_register(wcn,
					   WCN36XX_DXE_0_INT_CLR,
					   WCN36XX_INT_MASK_CHAN_TX_H);

		if (int_reason & WCN36XX_CH_STAT_INT_ERR_MASK ) {
			wcn36xx_dxe_write_register(wcn,
						   WCN36XX_DXE_0_INT_ERR_CLR,
						   WCN36XX_INT_MASK_CHAN_TX_H);

			wcn36xx_err("DXE IRQ reported error: 0x%x in high TX channel\n",
					int_src);
		}

		if (int_reason & WCN36XX_CH_STAT_INT_DONE_MASK) {
			wcn36xx_dxe_write_register(wcn,
						   WCN36XX_DXE_0_INT_DONE_CLR,
						   WCN36XX_INT_MASK_CHAN_TX_H);
		}

		if (int_reason & WCN36XX_CH_STAT_INT_ED_MASK) {
			wcn36xx_dxe_write_register(wcn,
						   WCN36XX_DXE_0_INT_ED_CLR,
						   WCN36XX_INT_MASK_CHAN_TX_H);
		}

		wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ready high, reason %08x\n",
			    int_reason);

		if (int_reason & (WCN36XX_CH_STAT_INT_DONE_MASK |
				  WCN36XX_CH_STAT_INT_ED_MASK))
			reap_tx_dxes(wcn, &wcn->dxe_tx_h_ch);
	}

	if (int_src & WCN36XX_INT_MASK_CHAN_TX_L) {
		wcn36xx_dxe_read_register(wcn,
					  WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_L,
					  &int_reason);

		wcn36xx_dxe_write_register(wcn,
					   WCN36XX_DXE_0_INT_CLR,
					   WCN36XX_INT_MASK_CHAN_TX_L);


		if (int_reason & WCN36XX_CH_STAT_INT_ERR_MASK ) {
			wcn36xx_dxe_write_register(wcn,
						   WCN36XX_DXE_0_INT_ERR_CLR,
						   WCN36XX_INT_MASK_CHAN_TX_L);

			wcn36xx_err("DXE IRQ reported error: 0x%x in low TX channel\n",
					int_src);
		}

		if (int_reason & WCN36XX_CH_STAT_INT_DONE_MASK) {
			wcn36xx_dxe_write_register(wcn,
						   WCN36XX_DXE_0_INT_DONE_CLR,
						   WCN36XX_INT_MASK_CHAN_TX_L);
		}

		if (int_reason & WCN36XX_CH_STAT_INT_ED_MASK) {
			wcn36xx_dxe_write_register(wcn,
						   WCN36XX_DXE_0_INT_ED_CLR,
						   WCN36XX_INT_MASK_CHAN_TX_L);
		}

		wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ready low, reason %08x\n",
			    int_reason);

		if (int_reason & (WCN36XX_CH_STAT_INT_DONE_MASK |
				  WCN36XX_CH_STAT_INT_ED_MASK))
			reap_tx_dxes(wcn, &wcn->dxe_tx_l_ch);
	}

	return IRQ_HANDLED;
}