PCI: mediatek: Clear interrupt status before dispatching handler
authorqizhong cheng <qizhong.cheng@mediatek.com>
Mon, 11 Dec 2023 09:49:23 +0000 (17:49 +0800)
committerBjorn Helgaas <bhelgaas@google.com>
Tue, 9 Jan 2024 22:41:23 +0000 (16:41 -0600)
We found a failure when using the iperf tool during WiFi performance
testing, where some MSIs were received while clearing the interrupt
status, and these MSIs cannot be serviced.

The interrupt status can be cleared even if the MSI status remains pending.
As such, given the edge-triggered interrupt type, its status should be
cleared before being dispatched to the handler of the underling device.

[kwilczynski: commit log, code comment wording]
Link: https://lore.kernel.org/linux-pci/20231211094923.31967-1-jianjun.wang@mediatek.com
Fixes: 43e6409db64d ("PCI: mediatek: Add MSI support for MT2712 and MT7622")
Signed-off-by: qizhong cheng <qizhong.cheng@mediatek.com>
Signed-off-by: Jianjun Wang <jianjun.wang@mediatek.com>
Signed-off-by: Krzysztof WilczyƄski <kwilczynski@kernel.org>
[bhelgaas: rewrap comment]
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Cc: <stable@vger.kernel.org>
drivers/pci/controller/pcie-mediatek.c

index 66a8f73296fc8b7b91ae84d72824550671a45e4d..48372013f26d23b6a9e197c61c4ef4eeaece9cb7 100644 (file)
@@ -617,12 +617,18 @@ static void mtk_pcie_intr_handler(struct irq_desc *desc)
                if (status & MSI_STATUS){
                        unsigned long imsi_status;
 
+                       /*
+                        * The interrupt status can be cleared even if the
+                        * MSI status remains pending. As such, given the
+                        * edge-triggered interrupt type, its status should
+                        * be cleared before being dispatched to the
+                        * handler of the underlying device.
+                        */
+                       writel(MSI_STATUS, port->base + PCIE_INT_STATUS);
                        while ((imsi_status = readl(port->base + PCIE_IMSI_STATUS))) {
                                for_each_set_bit(bit, &imsi_status, MTK_MSI_IRQS_NUM)
                                        generic_handle_domain_irq(port->inner_domain, bit);
                        }
-                       /* Clear MSI interrupt status */
-                       writel(MSI_STATUS, port->base + PCIE_INT_STATUS);
                }
        }