mirror of
https://github.com/torvalds/linux.git
synced 2026-05-30 10:04:04 +02:00
The switch case in loongson_gpu_fixup_dma_hang() may not DC2 or DC3, and
readl(crtc_reg) will access with random address, because the "device" is
from "base+PCI_DEVICE_ID", "base" is from "pdev->devfn+1". This is wrong
when my platform inserts a discrete GPU:
lspci -tv
-[0000:00]-+-00.0 Loongson Technology LLC Hyper Transport Bridge Controller
...
+-06.0 Loongson Technology LLC LG100 GPU
+-06.2 Loongson Technology LLC Device 7a37
...
Add a default switch case to fix the panic as below:
Kernel ade access[#1]:
CPU: 0 PID: 1 Comm: swapper/0 Not tainted 6.6.136-loong64-desktop-hwe+ #4
pc 90000000017e5534 ra 90000000017e54c0 tp 90000001002f8000 sp 90000001002fb6c0
a0 80000efe00003100 a1 0000000000003100 a2 0000000000000000 a3 0000000000000002
a4 90000001002fb6b4 a5 900000087cdb58fd a6 90000000027af000 a7 0000000000000001
t0 00000000000085b9 t1 000000000000ffff t2 0000000000000000 t3 0000000000000000
t4 fffffffffffffffd t5 00000000fffb6d9c t6 0000000000083b00 t7 00000000000070c0
t8 900000087cdb4d94 u0 900000087cdb58fd s9 90000001002fb826 s0 90000000031c12c8
s1 7fffffffffffff00 s2 90000000031c12d0 s3 0000000000002710 s4 0000000000000000
s5 0000000000000000 s6 9000000100053000 s7 7fffffffffffff00 s8 90000000030d4000
ra: 90000000017e54c0 loongson_gpu_fixup_dma_hang+0x40/0x210
ERA: 90000000017e5534 loongson_gpu_fixup_dma_hang+0xb4/0x210
CRMD: 000000b0 (PLV0 -IE -DA +PG DACF=CC DACM=CC -WE)
PRMD: 00000004 (PPLV0 +PIE -PWE)
EUEN: 00000000 (-FPE -SXE -ASXE -BTE)
ECFG: 00071c1d (LIE=0,2-4,10-12 VS=7)
ESTAT: 00480000 [ADEM] (IS= ECode=8 EsubCode=1)
BADV: 7fffffffffffff00
PRID: 0014d000 (Loongson-64bit, Loongson-3A6000-HV)
Modules linked in:
Process swapper/0 (pid: 1, threadinfo=(____ptrval____), task=(____ptrval____))
Stack : 0000000000000006 90000001002fb778 90000001002fb704 0000000000000007
0000000016a65700 90000000017e5690 000000000000ffff ffffffffffffffff
900000000209f7c0 9000000100053000 900000000209f7a8 9000000000eebc08
0000000000000000 0000000000000000 0000000000000006 90000001002fb778
90000001000530b8 90000000027af000 0000000000000000 9000000100054000
9000000100053000 9000000000ebb70c 9000000100004c00 9000000004000001
90000001002fb7e4 bae765461f31cb12 0000000000000000 0000000000000000
0000000000000006 90000000027af000 0000000000000030 90000000027af000
900000087cd6f800 9000000100053000 0000000000000000 9000000000ebc560
7a2500147cdaf720 bae765461f31cb12 0000000000000001 0000000000000030
...
Call Trace:
[<90000000017e5534>] loongson_gpu_fixup_dma_hang+0xb4/0x210
[<9000000000eebc08>] pci_fixup_device+0x108/0x280
[<9000000000ebb70c>] pci_setup_device+0x24c/0x690
[<9000000000ebc560>] pci_scan_single_device+0xe0/0x140
[<9000000000ebc684>] pci_scan_slot+0xc4/0x280
[<9000000000ebdd00>] pci_scan_child_bus_extend+0x60/0x3f0
[<9000000000f5bc94>] acpi_pci_root_create+0x2b4/0x420
[<90000000017e5e74>] pci_acpi_scan_root+0x2d4/0x440
[<9000000000f5b02c>] acpi_pci_root_add+0x21c/0x3a0
[<9000000000f4ee54>] acpi_bus_attach+0x1a4/0x3c0
[<90000000010e200c>] device_for_each_child+0x6c/0xe0
[<9000000000f4bbf4>] acpi_dev_for_each_child+0x44/0x70
[<9000000000f4ef40>] acpi_bus_attach+0x290/0x3c0
[<90000000010e200c>] device_for_each_child+0x6c/0xe0
[<9000000000f4bbf4>] acpi_dev_for_each_child+0x44/0x70
[<9000000000f4ef40>] acpi_bus_attach+0x290/0x3c0
[<9000000000f5211c>] acpi_bus_scan+0x6c/0x280
[<900000000189c028>] acpi_scan_init+0x194/0x310
[<900000000189bc6c>] acpi_init+0xcc/0x140
[<9000000000220cdc>] do_one_initcall+0x4c/0x310
[<90000000018618fc>] kernel_init_freeable+0x258/0x2d4
[<900000000184326c>] kernel_init+0x28/0x13c
[<9000000000222008>] ret_from_kernel_thread+0xc/0xa4
Cc: stable@vger.kernel.org
Fixes: 95db0c9f52 ("LoongArch: Workaround LS2K/LS7A GPU DMA hang bug")
Link: https://gist.github.com/opsiff/ebf2dac51b4013d22462f2124c55f807
Link: https://gist.github.com/opsiff/a62f2a73db0492b3c49bf223a339b133
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
185 lines
4.7 KiB
C
185 lines
4.7 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
|
|
*/
|
|
#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>
|
|
|
|
#define PCI_DEVICE_ID_LOONGSON_HOST 0x7a00
|
|
#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)
|
|
{
|
|
struct pci_bus *bus_tmp = pci_find_bus(domain, bus);
|
|
|
|
if (bus_tmp)
|
|
return bus_tmp->ops->read(bus_tmp, devfn, reg, len, val);
|
|
return -EINVAL;
|
|
}
|
|
|
|
int raw_pci_write(unsigned int domain, unsigned int bus, unsigned int devfn,
|
|
int reg, int len, u32 val)
|
|
{
|
|
struct pci_bus *bus_tmp = pci_find_bus(domain, bus);
|
|
|
|
if (bus_tmp)
|
|
return bus_tmp->ops->write(bus_tmp, devfn, reg, len, val);
|
|
return -EINVAL;
|
|
}
|
|
|
|
phys_addr_t mcfg_addr_init(int node)
|
|
{
|
|
return (((u64)node << 44) | MCFG_EXT_PCICFG_BASE);
|
|
}
|
|
|
|
static int __init pcibios_init(void)
|
|
{
|
|
unsigned int lsize;
|
|
|
|
/*
|
|
* Set PCI cacheline size to that of the last level in the
|
|
* cache hierarchy.
|
|
*/
|
|
lsize = cpu_last_level_cache_line_size();
|
|
|
|
if (lsize) {
|
|
pci_dfl_cache_line_size = lsize >> 2;
|
|
|
|
pr_debug("PCI: pci_cache_line_size set to %d bytes\n", lsize);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
subsys_initcall(pcibios_init);
|
|
|
|
int pcibios_device_add(struct pci_dev *dev)
|
|
{
|
|
int id;
|
|
struct irq_domain *dom;
|
|
|
|
id = pci_domain_nr(dev->bus);
|
|
dom = irq_find_matching_fwnode(get_pch_msi_handle(id), DOMAIN_BUS_PCI_MSI);
|
|
dev_set_msi_domain(&dev->dev, dom);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int pcibios_alloc_irq(struct pci_dev *dev)
|
|
{
|
|
if (acpi_disabled)
|
|
return 0;
|
|
if (pci_dev_msi_enabled(dev))
|
|
return 0;
|
|
return acpi_pci_irq_enable(dev);
|
|
}
|
|
|
|
static void pci_fixup_vgadev(struct pci_dev *pdev)
|
|
{
|
|
struct pci_dev *devp = NULL;
|
|
|
|
while ((devp = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, devp))) {
|
|
if (devp->vendor != PCI_VENDOR_ID_LOONGSON) {
|
|
vga_set_default_device(devp);
|
|
dev_info(&pdev->dev,
|
|
"Overriding boot device as %X:%X\n",
|
|
devp->vendor, devp->device);
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
default:
|
|
iounmap(regbase);
|
|
return;
|
|
}
|
|
|
|
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);
|