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); }
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"); }
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; }
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, ®_data); reg_data |= wcn_ch; wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_INT_MASK_REG, (int)reg_data); return 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"); }
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, ®_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, ®_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; }
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, ®_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, ®_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; }
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; }
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; }