diff options
Diffstat (limited to 'drivers/net/wireless/ath/wcn36xx/dxe.c')
| -rw-r--r-- | drivers/net/wireless/ath/wcn36xx/dxe.c | 195 |
1 files changed, 163 insertions, 32 deletions
diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.c b/drivers/net/wireless/ath/wcn36xx/dxe.c index bab30f7a443c..0f4df5585fd9 100644 --- a/drivers/net/wireless/ath/wcn36xx/dxe.c +++ b/drivers/net/wireless/ath/wcn36xx/dxe.c @@ -112,8 +112,8 @@ int wcn36xx_dxe_alloc_ctl_blks(struct wcn36xx *wcn) wcn->dxe_rx_l_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_RX_L; wcn->dxe_rx_h_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_RX_H; - wcn->dxe_tx_l_ch.dxe_wq = WCN36XX_DXE_WQ_TX_L; - wcn->dxe_tx_h_ch.dxe_wq = WCN36XX_DXE_WQ_TX_H; + wcn->dxe_tx_l_ch.dxe_wq = WCN36XX_DXE_WQ_TX_L(wcn); + wcn->dxe_tx_h_ch.dxe_wq = WCN36XX_DXE_WQ_TX_H(wcn); wcn->dxe_tx_l_ch.ctrl_bd = WCN36XX_DXE_CTRL_TX_L_BD; wcn->dxe_tx_h_ch.ctrl_bd = WCN36XX_DXE_CTRL_TX_H_BD; @@ -165,8 +165,9 @@ void wcn36xx_dxe_free_ctl_blks(struct wcn36xx *wcn) wcn36xx_dxe_free_ctl_block(&wcn->dxe_rx_h_ch); } -static int wcn36xx_dxe_init_descs(struct device *dev, struct wcn36xx_dxe_ch *wcn_ch) +static int wcn36xx_dxe_init_descs(struct wcn36xx *wcn, struct wcn36xx_dxe_ch *wcn_ch) { + struct device *dev = wcn->dev; struct wcn36xx_dxe_desc *cur_dxe = NULL; struct wcn36xx_dxe_desc *prev_dxe = NULL; struct wcn36xx_dxe_ctl *cur_ctl = NULL; @@ -179,7 +180,7 @@ static int wcn36xx_dxe_init_descs(struct device *dev, struct wcn36xx_dxe_ch *wcn if (!wcn_ch->cpu_addr) return -ENOMEM; - cur_dxe = (struct wcn36xx_dxe_desc *)wcn_ch->cpu_addr; + cur_dxe = wcn_ch->cpu_addr; cur_ctl = wcn_ch->head_blk_ctl; for (i = 0; i < wcn_ch->desc_num; i++) { @@ -190,11 +191,11 @@ static int wcn36xx_dxe_init_descs(struct device *dev, struct wcn36xx_dxe_ch *wcn switch (wcn_ch->ch_type) { case WCN36XX_DXE_CH_TX_L: cur_dxe->ctrl = WCN36XX_DXE_CTRL_TX_L; - cur_dxe->dst_addr_l = WCN36XX_DXE_WQ_TX_L; + cur_dxe->dst_addr_l = WCN36XX_DXE_WQ_TX_L(wcn); break; case WCN36XX_DXE_CH_TX_H: cur_dxe->ctrl = WCN36XX_DXE_CTRL_TX_H; - cur_dxe->dst_addr_l = WCN36XX_DXE_WQ_TX_H; + cur_dxe->dst_addr_l = WCN36XX_DXE_WQ_TX_H(wcn); break; case WCN36XX_DXE_CH_RX_L: cur_dxe->ctrl = WCN36XX_DXE_CTRL_RX_L; @@ -272,6 +273,21 @@ static int wcn36xx_dxe_enable_ch_int(struct wcn36xx *wcn, u16 wcn_ch) return 0; } +static void wcn36xx_dxe_disable_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); +} + static int wcn36xx_dxe_fill_skb(struct device *dev, struct wcn36xx_dxe_ctl *ctl, gfp_t gfp) @@ -334,6 +350,7 @@ void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status) spin_lock_irqsave(&wcn->dxe_lock, flags); skb = wcn->tx_ack_skb; wcn->tx_ack_skb = NULL; + timer_delete(&wcn->tx_ack_timer); spin_unlock_irqrestore(&wcn->dxe_lock, flags); if (!skb) { @@ -345,6 +362,8 @@ void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status) if (status == 1) info->flags |= IEEE80211_TX_STAT_ACK; + else + info->flags &= ~IEEE80211_TX_STAT_ACK; wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ack status: %d\n", status); @@ -352,6 +371,32 @@ void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status) ieee80211_wake_queues(wcn->hw); } +static void wcn36xx_dxe_tx_timer(struct timer_list *t) +{ + struct wcn36xx *wcn = timer_container_of(wcn, t, tx_ack_timer); + struct ieee80211_tx_info *info; + unsigned long flags; + struct sk_buff *skb; + + /* TX Timeout */ + wcn36xx_dbg(WCN36XX_DBG_DXE, "TX timeout\n"); + + spin_lock_irqsave(&wcn->dxe_lock, flags); + skb = wcn->tx_ack_skb; + wcn->tx_ack_skb = NULL; + spin_unlock_irqrestore(&wcn->dxe_lock, flags); + + if (!skb) + return; + + info = IEEE80211_SKB_CB(skb); + info->flags &= ~IEEE80211_TX_STAT_ACK; + info->flags &= ~IEEE80211_TX_STAT_NOACK_TRANSMITTED; + + ieee80211_tx_status_irqsafe(wcn->hw, skb); + ieee80211_wake_queues(wcn->hw); +} + static void reap_tx_dxes(struct wcn36xx *wcn, struct wcn36xx_dxe_ch *ch) { struct wcn36xx_dxe_ctl *ctl; @@ -374,8 +419,21 @@ static void reap_tx_dxes(struct wcn36xx *wcn, struct wcn36xx_dxe_ch *ch) dma_unmap_single(wcn->dev, ctl->desc->src_addr_l, ctl->skb->len, DMA_TO_DEVICE); info = IEEE80211_SKB_CB(ctl->skb); - if (!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)) { - /* Keep frame until TX status comes */ + if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) { + if (info->flags & IEEE80211_TX_CTL_NO_ACK) { + info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED; + ieee80211_tx_status_irqsafe(wcn->hw, ctl->skb); + } else { + /* Wait for the TX ack indication or timeout... */ + spin_lock(&wcn->dxe_lock); + if (WARN_ON(wcn->tx_ack_skb)) + ieee80211_free_txskb(wcn->hw, wcn->tx_ack_skb); + wcn->tx_ack_skb = ctl->skb; /* Tracking ref */ + mod_timer(&wcn->tx_ack_timer, jiffies + HZ / 10); + spin_unlock(&wcn->dxe_lock); + } + /* do not free, ownership transferred to mac80211 status cb */ + } else { ieee80211_free_txskb(wcn->hw, ctl->skb); } @@ -395,7 +453,7 @@ static void reap_tx_dxes(struct wcn36xx *wcn, struct wcn36xx_dxe_ch *ch) static irqreturn_t wcn36xx_irq_tx_complete(int irq, void *dev) { - struct wcn36xx *wcn = (struct wcn36xx *)dev; + struct wcn36xx *wcn = dev; int int_src, int_reason; wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_INT_SRC_RAW_REG, &int_src); @@ -434,8 +492,9 @@ static irqreturn_t wcn36xx_irq_tx_complete(int irq, void *dev) int_reason); if (int_reason & (WCN36XX_CH_STAT_INT_DONE_MASK | - WCN36XX_CH_STAT_INT_ED_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) { @@ -447,7 +506,6 @@ static irqreturn_t wcn36xx_irq_tx_complete(int irq, void *dev) 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, @@ -473,8 +531,9 @@ static irqreturn_t wcn36xx_irq_tx_complete(int irq, void *dev) int_reason); if (int_reason & (WCN36XX_CH_STAT_INT_DONE_MASK | - WCN36XX_CH_STAT_INT_ED_MASK)) + WCN36XX_CH_STAT_INT_ED_MASK)) { reap_tx_dxes(wcn, &wcn->dxe_tx_l_ch); + } } return IRQ_HANDLED; @@ -482,7 +541,7 @@ static irqreturn_t wcn36xx_irq_tx_complete(int irq, void *dev) static irqreturn_t wcn36xx_irq_rx_ready(int irq, void *dev) { - struct wcn36xx *wcn = (struct wcn36xx *)dev; + struct wcn36xx *wcn = dev; wcn36xx_dxe_rx_frame(wcn); @@ -563,6 +622,10 @@ static int wcn36xx_rx_handle_packets(struct wcn36xx *wcn, dxe = ctl->desc; while (!(READ_ONCE(dxe->ctrl) & WCN36xx_DXE_CTRL_VLD)) { + /* do not read until we own DMA descriptor */ + dma_rmb(); + + /* read/modify DMA descriptor */ skb = ctl->skb; dma_addr = dxe->dst_addr_l; ret = wcn36xx_dxe_fill_skb(wcn->dev, ctl, GFP_ATOMIC); @@ -573,9 +636,15 @@ static int wcn36xx_rx_handle_packets(struct wcn36xx *wcn, 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 */ + } + /* else keep old skb not submitted and reuse it for rx DMA + * (dropping the packet that it contained) + */ + /* flush descriptor changes before re-marking as valid */ + dma_wmb(); dxe->ctrl = ctrl; + ctl = ctl->next; dxe = ctl->desc; } @@ -750,7 +819,7 @@ int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn, (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; + ch->head_blk_ctl = ctl_skb->next; /* Commit all previous writes and set descriptors to VALID */ wmb(); @@ -781,6 +850,53 @@ unlock: return ret; } +static bool _wcn36xx_dxe_tx_channel_is_empty(struct wcn36xx_dxe_ch *ch) +{ + unsigned long flags; + struct wcn36xx_dxe_ctl *ctl_bd_start, *ctl_skb_start; + struct wcn36xx_dxe_ctl *ctl_bd, *ctl_skb; + bool ret = true; + + spin_lock_irqsave(&ch->lock, flags); + + /* Loop through ring buffer looking for nonempty entries. */ + ctl_bd_start = ch->head_blk_ctl; + ctl_bd = ctl_bd_start; + ctl_skb_start = ctl_bd_start->next; + ctl_skb = ctl_skb_start; + do { + if (ctl_skb->skb) { + ret = false; + goto unlock; + } + ctl_bd = ctl_skb->next; + ctl_skb = ctl_bd->next; + } while (ctl_skb != ctl_skb_start); + +unlock: + spin_unlock_irqrestore(&ch->lock, flags); + return ret; +} + +int wcn36xx_dxe_tx_flush(struct wcn36xx *wcn) +{ + int i = 0; + + /* Called with mac80211 queues stopped. Wait for empty HW queues. */ + do { + if (_wcn36xx_dxe_tx_channel_is_empty(&wcn->dxe_tx_l_ch) && + _wcn36xx_dxe_tx_channel_is_empty(&wcn->dxe_tx_h_ch)) { + return 0; + } + /* This ieee80211_ops callback is specifically allowed to + * sleep. + */ + usleep_range(1000, 1100); + } while (++i < 100); + + return -EBUSY; +} + int wcn36xx_dxe_init(struct wcn36xx *wcn) { int reg_data = 0, ret; @@ -799,7 +915,7 @@ int wcn36xx_dxe_init(struct wcn36xx *wcn) /***************************************/ /* Init descriptors for TX LOW channel */ /***************************************/ - ret = wcn36xx_dxe_init_descs(wcn->dev, &wcn->dxe_tx_l_ch); + ret = wcn36xx_dxe_init_descs(wcn, &wcn->dxe_tx_l_ch); if (ret) { dev_err(wcn->dev, "Error allocating descriptor\n"); return ret; @@ -813,15 +929,14 @@ int wcn36xx_dxe_init(struct wcn36xx *wcn) /* 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_WQ_TX_L(wcn)); 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); + ret = wcn36xx_dxe_init_descs(wcn, &wcn->dxe_tx_h_ch); if (ret) { dev_err(wcn->dev, "Error allocating descriptor\n"); goto out_err_txh_ch; @@ -836,23 +951,19 @@ int wcn36xx_dxe_init(struct wcn36xx *wcn) /* 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_WQ_TX_H(wcn)); 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); + ret = wcn36xx_dxe_init_descs(wcn, &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); @@ -875,13 +986,10 @@ int wcn36xx_dxe_init(struct wcn36xx *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); + ret = wcn36xx_dxe_init_descs(wcn, &wcn->dxe_rx_h_ch); if (ret) { dev_err(wcn->dev, "Error allocating descriptor\n"); goto out_err_rxh_ch; @@ -909,13 +1017,18 @@ int wcn36xx_dxe_init(struct wcn36xx *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; + timer_setup(&wcn->tx_ack_timer, wcn36xx_dxe_tx_timer, 0); + + /* Enable channel interrupts */ + wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_TX_L); + wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_TX_H); + wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_RX_L); + wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_RX_H); + return 0; out_err_irq: @@ -932,14 +1045,32 @@ out_err_txh_ch: void wcn36xx_dxe_deinit(struct wcn36xx *wcn) { + int reg_data = 0; + + /* Disable channel interrupts */ + wcn36xx_dxe_disable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_RX_H); + wcn36xx_dxe_disable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_RX_L); + wcn36xx_dxe_disable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_TX_H); + wcn36xx_dxe_disable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_TX_L); + free_irq(wcn->tx_irq, wcn); free_irq(wcn->rx_irq, wcn); + timer_delete(&wcn->tx_ack_timer); if (wcn->tx_ack_skb) { ieee80211_tx_status_irqsafe(wcn->hw, wcn->tx_ack_skb); wcn->tx_ack_skb = NULL; } + /* Put the DXE block into reset before freeing memory */ + reg_data = WCN36XX_DXE_REG_RESET; + wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_REG_CSR_RESET, reg_data); + wcn36xx_dxe_ch_free_skbs(wcn, &wcn->dxe_rx_l_ch); wcn36xx_dxe_ch_free_skbs(wcn, &wcn->dxe_rx_h_ch); + + wcn36xx_dxe_deinit_descs(wcn->dev, &wcn->dxe_tx_l_ch); + wcn36xx_dxe_deinit_descs(wcn->dev, &wcn->dxe_tx_h_ch); + wcn36xx_dxe_deinit_descs(wcn->dev, &wcn->dxe_rx_l_ch); + wcn36xx_dxe_deinit_descs(wcn->dev, &wcn->dxe_rx_h_ch); } |
