summaryrefslogtreecommitdiff
path: root/arch/loongarch
diff options
context:
space:
mode:
authorHuacai Chen <chenhuacai@loongson.cn>2026-03-26 14:29:09 +0800
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-04-02 13:25:48 +0200
commit3446dd359ba9a6ce5a1ca389c0d48c434d3cc915 (patch)
tree98e2afa0579db5f5a06b2ed48c3545fc2286caf9 /arch/loongarch
parentdb4942eab22e8821843b3240dc1a31d7337f0581 (diff)
LoongArch: Workaround LS2K/LS7A GPU DMA hang bug
commit 95db0c9f526d583634cddb2e5914718570fbac87 upstream. 1. Hardware limitation: GPU, DC and VPU are typically PCI device 06.0, 06.1 and 06.2. They share some hardware resources, so when configure the PCI 06.0 device BAR1, DMA memory access cannot be performed through this BAR, otherwise it will cause hardware abnormalities. 2. In typical scenarios of reboot or S3/S4, DC access to memory through BAR is not prohibited, resulting in GPU DMA hangs. 3. Workaround method: When configuring the 06.0 device BAR1, turn off the memory access of DC, GPU and VPU (via DC's CRTC registers). Cc: stable@vger.kernel.org Signed-off-by: Qianhai Wu <wuqianhai@loongson.cn> Signed-off-by: Huacai Chen <chenhuacai@loongson.cn> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'arch/loongarch')
-rw-r--r--arch/loongarch/pci/pci.c80
1 files changed, 80 insertions, 0 deletions
diff --git a/arch/loongarch/pci/pci.c b/arch/loongarch/pci/pci.c
index d923295ab8c6..d233ea2218fe 100644
--- a/arch/loongarch/pci/pci.c
+++ b/arch/loongarch/pci/pci.c
@@ -5,9 +5,11 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/acpi.h>
+#include <linux/delay.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/vgaarb.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
#include <asm/cacheflush.h>
#include <asm/loongson.h>
@@ -15,6 +17,9 @@
#define PCI_DEVICE_ID_LOONGSON_DC1 0x7a06
#define PCI_DEVICE_ID_LOONGSON_DC2 0x7a36
#define PCI_DEVICE_ID_LOONGSON_DC3 0x7a46
+#define PCI_DEVICE_ID_LOONGSON_GPU1 0x7a15
+#define PCI_DEVICE_ID_LOONGSON_GPU2 0x7a25
+#define PCI_DEVICE_ID_LOONGSON_GPU3 0x7a35
int raw_pci_read(unsigned int domain, unsigned int bus, unsigned int devfn,
int reg, int len, u32 *val)
@@ -99,3 +104,78 @@ static void pci_fixup_vgadev(struct pci_dev *pdev)
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_DC1, pci_fixup_vgadev);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_DC2, pci_fixup_vgadev);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_DC3, pci_fixup_vgadev);
+
+#define CRTC_NUM_MAX 2
+#define CRTC_OUTPUT_ENABLE 0x100
+
+static void loongson_gpu_fixup_dma_hang(struct pci_dev *pdev, bool on)
+{
+ u32 i, val, count, crtc_offset, device;
+ void __iomem *crtc_reg, *base, *regbase;
+ static u32 crtc_status[CRTC_NUM_MAX] = { 0 };
+
+ base = pdev->bus->ops->map_bus(pdev->bus, pdev->devfn + 1, 0);
+ device = readw(base + PCI_DEVICE_ID);
+
+ regbase = ioremap(readq(base + PCI_BASE_ADDRESS_0) & ~0xffull, SZ_64K);
+ if (!regbase) {
+ pci_err(pdev, "Failed to ioremap()\n");
+ return;
+ }
+
+ switch (device) {
+ case PCI_DEVICE_ID_LOONGSON_DC2:
+ crtc_reg = regbase + 0x1240;
+ crtc_offset = 0x10;
+ break;
+ case PCI_DEVICE_ID_LOONGSON_DC3:
+ crtc_reg = regbase;
+ crtc_offset = 0x400;
+ break;
+ }
+
+ for (i = 0; i < CRTC_NUM_MAX; i++, crtc_reg += crtc_offset) {
+ val = readl(crtc_reg);
+
+ if (!on)
+ crtc_status[i] = val;
+
+ /* No need to fixup if the status is off at startup. */
+ if (!(crtc_status[i] & CRTC_OUTPUT_ENABLE))
+ continue;
+
+ if (on)
+ val |= CRTC_OUTPUT_ENABLE;
+ else
+ val &= ~CRTC_OUTPUT_ENABLE;
+
+ mb();
+ writel(val, crtc_reg);
+
+ for (count = 0; count < 40; count++) {
+ val = readl(crtc_reg) & CRTC_OUTPUT_ENABLE;
+ if ((on && val) || (!on && !val))
+ break;
+ udelay(1000);
+ }
+
+ pci_info(pdev, "DMA hang fixup at reg[0x%lx]: 0x%x\n",
+ (unsigned long)crtc_reg & 0xffff, readl(crtc_reg));
+ }
+
+ iounmap(regbase);
+}
+
+static void pci_fixup_dma_hang_early(struct pci_dev *pdev)
+{
+ loongson_gpu_fixup_dma_hang(pdev, false);
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_GPU2, pci_fixup_dma_hang_early);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_GPU3, pci_fixup_dma_hang_early);
+
+static void pci_fixup_dma_hang_final(struct pci_dev *pdev)
+{
+ loongson_gpu_fixup_dma_hang(pdev, true);
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_GPU2, pci_fixup_dma_hang_final);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_GPU3, pci_fixup_dma_hang_final);