Esempio n. 1
0
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);
}
Esempio n. 2
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;
}
Esempio n. 3
0
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;
}
Esempio n. 4
0
static int wcn36xx_rx_handle_packets(struct wcn36xx *wcn,
                                     struct wcn36xx_dxe_ch *ch)
{
    struct wcn36xx_dxe_ctl *ctl = ch->head_blk_ctl;
    struct wcn36xx_dxe_desc *dxe = ctl->desc;
    dma_addr_t  dma_addr;
    struct sk_buff *skb;

    while (!(dxe->ctrl & WCN36XX_DXE_CTRL_VALID_MASK)) {
        skb = ctl->skb;
        dma_addr = dxe->dst_addr_l;
        wcn36xx_dxe_fill_skb(ctl);

        switch (ch->ch_type) {
        case WCN36XX_DXE_CH_RX_L:
            dxe->ctrl = WCN36XX_DXE_CTRL_RX_L;
            wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR,
                                       WCN36XX_DXE_INT_CH1_MASK);
            break;
        case WCN36XX_DXE_CH_RX_H:
            dxe->ctrl = WCN36XX_DXE_CTRL_RX_H;
            wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR,
                                       WCN36XX_DXE_INT_CH3_MASK);
            break;
        default:
            wcn36xx_warn("Unknown channel\n");
        }

        dma_unmap_single(NULL, dma_addr, WCN36XX_PKT_SIZE,
                         DMA_FROM_DEVICE);
        wcn36xx_rx_skb(wcn, skb);
        ctl = ctl->next;
        dxe = ctl->desc;
    }

    ch->head_blk_ctl = ctl;

    return 0;
}
Esempio n. 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");
}
Esempio 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;
}
Esempio n. 7
0
int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn,
                         struct wcn36xx_vif *vif_priv,
                         struct sk_buff *skb,
                         bool is_low)
{
    struct wcn36xx_dxe_ctl *ctl = NULL;
    struct wcn36xx_dxe_desc *desc = NULL;
    struct wcn36xx_dxe_ch *ch = NULL;
    unsigned long flags;

    ch = is_low ? &wcn->dxe_tx_l_ch : &wcn->dxe_tx_h_ch;

    ctl = ch->head_blk_ctl;

    spin_lock_irqsave(&ctl->next->skb_lock, flags);

    /*
     * If skb is not null that means that we reached the tail of the ring
     * hence ring is full. Stop queues to let mac80211 back off until ring
     * has an empty slot again.
     */
    if (NULL != ctl->next->skb) {
        ieee80211_stop_queues(wcn->hw);
        wcn->queues_stopped = true;
        spin_unlock_irqrestore(&ctl->next->skb_lock, flags);
        return -EBUSY;
    }
    spin_unlock_irqrestore(&ctl->next->skb_lock, flags);

    ctl->skb = NULL;
    desc = ctl->desc;

    /* Set source address of the BD we send */
    desc->src_addr_l = ctl->bd_phy_addr;

    desc->dst_addr_l = ch->dxe_wq;
    desc->fr_len = sizeof(struct wcn36xx_tx_bd);
    desc->ctrl = ch->ctrl_bd;

    wcn36xx_dbg(WCN36XX_DBG_DXE, "DXE TX\n");

    wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "DESC1 >>> ",
                     (char *)desc, sizeof(*desc));
    wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP,
                     "BD   >>> ", (char *)ctl->bd_cpu_addr,
                     sizeof(struct wcn36xx_tx_bd));

    /* Set source address of the SKB we send */
    ctl = ctl->next;
    ctl->skb = skb;
    desc = ctl->desc;
    if (ctl->bd_cpu_addr) {
        wcn36xx_err("bd_cpu_addr cannot be NULL for skb DXE\n");
        return -EINVAL;
    }

    desc->src_addr_l = dma_map_single(NULL,
                                      ctl->skb->data,
                                      ctl->skb->len,
                                      DMA_TO_DEVICE);

    desc->dst_addr_l = ch->dxe_wq;
    desc->fr_len = ctl->skb->len;

    /* set dxe descriptor to VALID */
    desc->ctrl = ch->ctrl_skb;

    wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "DESC2 >>> ",
                     (char *)desc, sizeof(*desc));
    wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "SKB   >>> ",
                     (char *)ctl->skb->data, ctl->skb->len);

    /* Move the head of the ring to the next empty descriptor */
    ch->head_blk_ctl = ctl->next;

    /*
     * When connected and trying to send data frame chip can be in sleep
     * mode and writing to the register will not wake up the chip. Instead
     * notify chip about new frame through SMSM bus.
     */
    if (is_low &&  vif_priv->pw_state == WCN36XX_BMPS) {
        wcn->ctrl_ops->smsm_change_state(
            0,
            WCN36XX_SMSM_WLAN_TX_ENABLE);
    } else {
        /* indicate End Of Packet and generate interrupt on descriptor
         * done.
         */
        wcn36xx_dxe_write_register(wcn,
                                   ch->reg_ctrl, ch->def_ctrl);
    }

    return 0;
}
Esempio n. 8
0
int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn,
			 struct sk_buff *skb,
			 bool is_low)
{
	struct wcn36xx_dxe_ctl *ctl = NULL;
	struct wcn36xx_dxe_desc *desc = NULL;
	struct wcn36xx_dxe_ch *ch = NULL;

	ch = is_low ? &wcn->dxe_tx_l_ch : &wcn->dxe_tx_h_ch;

	ctl = ch->head_blk_ctl;
	ctl->skb = NULL;
	desc = ctl->desc;

	/* Set source address of the BD we send */
	desc->src_addr_l = ctl->bd_phy_addr;

	desc->dst_addr_l = ch->dxe_wq;
	desc->fr_len = sizeof(struct wcn36xx_tx_bd);
	desc->ctrl = ch->ctrl_bd;

	wcn36xx_dbg(WCN36XX_DBG_DXE, "DXE TX");

	wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "DESC1 >>> ",
			 (char *)desc, sizeof(*desc));
	wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP,
			 "BD   >>> ", (char *)ctl->bd_cpu_addr,
			 sizeof(struct wcn36xx_tx_bd));

	/* Set source address of the SKB we send */
	ctl = ctl->next;
	ctl->skb = skb;
	desc = ctl->desc;
	if (ctl->bd_cpu_addr) {
		/* TODO: Recover from this situation */
		wcn36xx_error("bd_cpu_addr cannot be NULL for skb DXE");
		return -EINVAL;
	}

	desc->src_addr_l = dma_map_single(NULL,
					  ctl->skb->data,
					  ctl->skb->len,
					  DMA_TO_DEVICE);

	desc->dst_addr_l = ch->dxe_wq;
	desc->fr_len = ctl->skb->len;

	/* set dxe descriptor to VALID */
	desc->ctrl = ch->ctrl_skb;

	wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "DESC2 >>> ",
			 (char *)desc, sizeof(*desc));
	wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "SKB   >>> ",
			 (char *)ctl->skb->data, ctl->skb->len);

	/* Move the head of the ring to the next empty descriptor */
	 ch->head_blk_ctl = ctl->next;

	/*
	 * When connected and trying to send data frame chip can be in sleep
	 * mode and writing to the register will not wake up the chip. Instead
	 * notify chip about new frame through SMSM bus.
	 */
	if (wcn->pw_state == WCN36XX_BMPS) {
		smsm_change_state(SMSM_APPS_STATE,
				  0,
				  WCN36XX_SMSM_WLAN_TX_ENABLE);
	} else {
		/* indicate End Of Packet and generate interrupt on descriptor
		 * done.
		 */
		wcn36xx_dxe_write_register(wcn,
			ch->reg_ctrl, ch->def_ctrl);
	}
	return 0;
}
Esempio n. 9
0
File: dxe.c Progetto: 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;
}
Esempio n. 10
0
File: dxe.c Progetto: Lyude/linux
int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn,
			 struct wcn36xx_vif *vif_priv,
			 struct wcn36xx_tx_bd *bd,
			 struct sk_buff *skb,
			 bool is_low)
{
	struct wcn36xx_dxe_desc *desc_bd, *desc_skb;
	struct wcn36xx_dxe_ctl *ctl_bd, *ctl_skb;
	struct wcn36xx_dxe_ch *ch = NULL;
	unsigned long flags;
	int ret;

	ch = is_low ? &wcn->dxe_tx_l_ch : &wcn->dxe_tx_h_ch;

	spin_lock_irqsave(&ch->lock, flags);
	ctl_bd = ch->head_blk_ctl;
	ctl_skb = ctl_bd->next;

	/*
	 * If skb is not null that means that we reached the tail of the ring
	 * hence ring is full. Stop queues to let mac80211 back off until ring
	 * has an empty slot again.
	 */
	if (NULL != ctl_skb->skb) {
		ieee80211_stop_queues(wcn->hw);
		wcn->queues_stopped = true;
		spin_unlock_irqrestore(&ch->lock, flags);
		return -EBUSY;
	}

	if (unlikely(ctl_skb->bd_cpu_addr)) {
		wcn36xx_err("bd_cpu_addr cannot be NULL for skb DXE\n");
		ret = -EINVAL;
		goto unlock;
	}

	desc_bd = ctl_bd->desc;
	desc_skb = ctl_skb->desc;

	ctl_bd->skb = NULL;

	/* write buffer descriptor */
	memcpy(ctl_bd->bd_cpu_addr, bd, sizeof(*bd));

	/* Set source address of the BD we send */
	desc_bd->src_addr_l = ctl_bd->bd_phy_addr;
	desc_bd->dst_addr_l = ch->dxe_wq;
	desc_bd->fr_len = sizeof(struct wcn36xx_tx_bd);

	wcn36xx_dbg(WCN36XX_DBG_DXE, "DXE TX\n");

	wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "DESC1 >>> ",
			 (char *)desc_bd, sizeof(*desc_bd));
	wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP,
			 "BD   >>> ", (char *)ctl_bd->bd_cpu_addr,
			 sizeof(struct wcn36xx_tx_bd));

	desc_skb->src_addr_l = dma_map_single(wcn->dev,
					      skb->data,
					      skb->len,
					      DMA_TO_DEVICE);
	if (dma_mapping_error(wcn->dev, desc_skb->src_addr_l)) {
		dev_err(wcn->dev, "unable to DMA map src_addr_l\n");
		ret = -ENOMEM;
		goto unlock;
	}

	ctl_skb->skb = skb;
	desc_skb->dst_addr_l = ch->dxe_wq;
	desc_skb->fr_len = ctl_skb->skb->len;

	wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "DESC2 >>> ",
			 (char *)desc_skb, sizeof(*desc_skb));
	wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "SKB   >>> ",
			 (char *)ctl_skb->skb->data, ctl_skb->skb->len);

	/* Move the head of the ring to the next empty descriptor */
	 ch->head_blk_ctl = ctl_skb->next;

	/* Commit all previous writes and set descriptors to VALID */
	wmb();
	desc_skb->ctrl = ch->ctrl_skb;
	wmb();
	desc_bd->ctrl = ch->ctrl_bd;

	/*
	 * When connected and trying to send data frame chip can be in sleep
	 * mode and writing to the register will not wake up the chip. Instead
	 * notify chip about new frame through SMSM bus.
	 */
	if (is_low &&  vif_priv->pw_state == WCN36XX_BMPS) {
		qcom_smem_state_update_bits(wcn->tx_rings_empty_state,
					    WCN36XX_SMSM_WLAN_TX_ENABLE,
					    WCN36XX_SMSM_WLAN_TX_ENABLE);
	} else {
		/* indicate End Of Packet and generate interrupt on descriptor
		 * done.
		 */
		wcn36xx_dxe_write_register(wcn,
			ch->reg_ctrl, ch->def_ctrl);
	}

	ret = 0;
unlock:
	spin_unlock_irqrestore(&ch->lock, flags);
	return ret;
}
Esempio n. 11
0
File: dxe.c Progetto: 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;
}
Esempio n. 12
0
File: dxe.c Progetto: 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;
}