wcn36xx: Fix DMA channel enable/disable cycle
authorBryan O'Donoghue <bryan.odonoghue@linaro.org>
Fri, 5 Nov 2021 12:21:50 +0000 (12:21 +0000)
committerKalle Valo <kvalo@codeaurora.org>
Mon, 8 Nov 2021 13:21:15 +0000 (15:21 +0200)
Right now we have a broken sequence where we enable DMA channel interrupts
which can be left enabled and never disabled if we hit an error path.

Worse still when we unload the driver, the DMA channel interrupt bits are
left intact. About the only saving grace here is that we do remember to
disable the wcnss interrupt when unload the driver.

Fixes: 8e84c2582169 ("wcn36xx: mac80211 driver for Qualcomm WCN3660/WCN3680 hardware")
Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/20211105122152.1580542-2-bryan.odonoghue@linaro.org
drivers/net/wireless/ath/wcn36xx/dxe.c

index fd627c9..d6c6215 100644 (file)
@@ -272,6 +272,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,
+                                 &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)
@@ -916,7 +931,6 @@ int wcn36xx_dxe_init(struct wcn36xx *wcn)
                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 */
@@ -940,9 +954,6 @@ int wcn36xx_dxe_init(struct wcn36xx *wcn)
 
        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 */
        /***************************************/
@@ -952,7 +963,6 @@ int wcn36xx_dxe_init(struct wcn36xx *wcn)
                goto out_err_rxl_ch;
        }
 
-
        /* For RX we need to preallocated buffers */
        wcn36xx_dxe_ch_alloc_skb(wcn, &wcn->dxe_rx_l_ch);
 
@@ -975,9 +985,6 @@ 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 */
        /***************************************/
@@ -1009,15 +1016,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:
@@ -1034,6 +1044,12 @@ out_err_txh_ch:
 
 void wcn36xx_dxe_deinit(struct wcn36xx *wcn)
 {
+       /* 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);
        del_timer(&wcn->tx_ack_timer);