Browse Source

x86: Disable DAC on VIA bridges

Several reports that VIA bridges don't support DAC and corrupt
data.  I don't know if it's fixed, but let's just blacklist
them all for now.

It can be overwritten with iommu=usedac

Signed-off-by: Andi Kleen <ak@suse.de>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Andi Kleen 18 years ago
parent
commit
388c19e176
3 changed files with 45 additions and 0 deletions
  1. 27 0
      arch/i386/kernel/pci-dma.c
  2. 12 0
      arch/x86_64/kernel/pci-dma.c
  3. 6 0
      include/asm-i386/dma-mapping.h

+ 27 - 0
arch/i386/kernel/pci-dma.c

@@ -12,6 +12,7 @@
 #include <linux/string.h>
 #include <linux/string.h>
 #include <linux/pci.h>
 #include <linux/pci.h>
 #include <linux/module.h>
 #include <linux/module.h>
+#include <linux/pci.h>
 #include <asm/io.h>
 #include <asm/io.h>
 
 
 struct dma_coherent_mem {
 struct dma_coherent_mem {
@@ -148,3 +149,29 @@ void *dma_mark_declared_memory_occupied(struct device *dev,
 	return mem->virt_base + (pos << PAGE_SHIFT);
 	return mem->virt_base + (pos << PAGE_SHIFT);
 }
 }
 EXPORT_SYMBOL(dma_mark_declared_memory_occupied);
 EXPORT_SYMBOL(dma_mark_declared_memory_occupied);
+
+#ifdef CONFIG_PCI
+/* Many VIA bridges seem to corrupt data for DAC. Disable it here */
+
+int forbid_dac;
+EXPORT_SYMBOL(forbid_dac);
+
+static __devinit void via_no_dac(struct pci_dev *dev)
+{
+	if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI && forbid_dac == 0) {
+		printk(KERN_INFO "PCI: VIA PCI bridge detected. Disabling DAC.\n");
+		forbid_dac = 1;
+	}
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_ANY_ID, via_no_dac);
+
+static int check_iommu(char *s)
+{
+	if (!strcmp(s, "usedac")) {
+		forbid_dac = -1;
+		return 1;
+	}
+	return 0;
+}
+__setup("iommu=", check_iommu);
+#endif

+ 12 - 0
arch/x86_64/kernel/pci-dma.c

@@ -322,5 +322,17 @@ static int __init pci_iommu_init(void)
 	return 0;
 	return 0;
 }
 }
 
 
+#ifdef CONFIG_PCI
+/* Many VIA bridges seem to corrupt data for DAC. Disable it here */
+
+static __devinit void via_no_dac(struct pci_dev *dev)
+{
+	if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI && forbid_dac == 0) {
+		printk(KERN_INFO "PCI: VIA PCI bridge detected. Disabling DAC.\n");
+		forbid_dac = 1;
+	}
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_ANY_ID, via_no_dac);
+#endif
 /* Must execute after PCI subsystem */
 /* Must execute after PCI subsystem */
 fs_initcall(pci_iommu_init);
 fs_initcall(pci_iommu_init);

+ 6 - 0
include/asm-i386/dma-mapping.h

@@ -123,6 +123,8 @@ dma_mapping_error(dma_addr_t dma_addr)
 	return 0;
 	return 0;
 }
 }
 
 
+extern int forbid_dac;
+
 static inline int
 static inline int
 dma_supported(struct device *dev, u64 mask)
 dma_supported(struct device *dev, u64 mask)
 {
 {
@@ -134,6 +136,10 @@ dma_supported(struct device *dev, u64 mask)
         if(mask < 0x00ffffff)
         if(mask < 0x00ffffff)
                 return 0;
                 return 0;
 
 
+	/* Work around chipset bugs */
+	if (forbid_dac > 0 && mask > 0xffffffffULL)
+		return 0;
+
 	return 1;
 	return 1;
 }
 }