Subject: kern/19919: auich(4) triggers "PCI master abort" erratum of the Intel 440MX
To: None <gnats-bugs@gnats.netbsd.org>
From: DHOYASHIKI Shinichi <clare@znet.or.jp>
List: netbsd-bugs
Date: 01/19/2003 11:59:22
>Number:         19919
>Category:       kern
>Synopsis:       auich(4) triggeres "PCI master abort" erratum of the Intel 440MX
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Sat Jan 18 19:00:01 PST 2003
>Closed-Date:
>Last-Modified:
>Originator:     DHOYASHIKI Shinichi
>Release:        NetBSD 1.6L as of 2003-01
>Organization:
	at home
>Environment:
IBM ThinkPad i series s30 (Intel 440MX chipset running 100MHz)
System: NetBSD minako 1.6L NetBSD 1.6L (MINAKO2) #15: Sat Jan 18 19:26:28 JST 2003 clare@minako:/cvs/tnf-work/sys/arch/i386/compile/MINAKO2 i386
Architecture: i386
Machine: i386

>Description:
	The Intel 82443MX running 100MHz system clock has serious hardware erratum,
	it causes PCI master abort of audio portion, and freezes (or hangups) until
	hardware cold boot, as of the Intel 82443MX Specification Update said.

	Detailed description of the erratum can be found at following URL:
	http://www.intel.com/design/chipsets/specupdt/245051.htm

>How-To-Repeat:
	Run mpg123 or similar audio player with auich(4) audio driver on
	IBM ThinkPad i series s30 or similar hardware.

>Fix:
	The patch following workarounds the hardware problem.

	Please note, the patch contains partial extentions to the bus_dma(9)
	implementation that introduces BUS_DMA_NOCACHE flag to disable CPU's memory
	caching of DMA buffer region by tweaking i386 MD page table entry.
	This PTE tweaking (to avoid memory cache) was requried by workaround solution
	of the hardware problem.

Index: dev/pci/auich.c
===================================================================
RCS file: /cvsroot/src/sys/dev/pci/auich.c,v
retrieving revision 1.32
diff -u -t -r1.32 auich.c
--- dev/pci/auich.c	16 Jan 2003 01:00:06 -0000	1.32
+++ dev/pci/auich.c	18 Jan 2003 12:08:32 -0000
@@ -107,6 +107,11 @@
  * TODO:
  *      - Add support for the dedicated microphone input.
  *      - 4ch/6ch support.
+ *
+ * NOTE:
+ *      - The 440MX B-stepping at running 100MHz has hardware erratum.
+ *        It causes PCI master abort and hangups until cold reboot.
+ *        http://www.intel.com/design/chipsets/specupdt/245051.htm
  */
 
 #include <sys/cdefs.h>
@@ -198,6 +203,13 @@
 
         struct auich_dma *sc_dmas;
 
+#ifdef DIAGNOSTIC
+        pci_chipset_tag_t sc_pc;
+        pcitag_t sc_pt;
+#endif
+        /* Intel 440MX hack */
+        int  sc_dmamem_flags;
+
         int sc_ignore_codecready;
         /* SiS 7012 hack */
         int  sc_sample_size;
@@ -377,6 +389,11 @@
         if (d == NULL)
                 panic("auich_attach: impossible");
 
+#ifdef DIAGNOSTIC
+        sc->sc_pc = pa->pa_pc;
+        sc->sc_pt = pa->pa_tag;
+#endif
+
         printf(": %s\n", d->name);
 
         if (pci_mapreg_map(pa, ICH_NAMBAR, PCI_MAPREG_TYPE_IO, 0,
@@ -434,6 +451,13 @@
                 sc->sc_ignore_codecready = TRUE;
         }
 
+        /* Intel 440MX B-stepping @ 100MHz quirk */
+        sc->sc_dmamem_flags = BUS_DMA_COHERENT;
+        if (d->vendor == PCI_VENDOR_INTEL
+            && d->product == PCI_PRODUCT_INTEL_82440MX_ACA) {
+                sc->sc_dmamem_flags |= BUS_DMA_NOCACHE;
+                printf("%s: DMA bug workaround enabled\n", sc->sc_dev.dv_xname);
+        }
 
         /* Set up DMA lists. */
         sc->ptr_pcmo = sc->ptr_pcmi = sc->ptr_mici = 0;
@@ -945,6 +969,16 @@
 {
         struct auich_softc *sc = v;
         int ret = 0, sts, gsts, i, qptr;
+#ifdef DIAGNOSTIC
+        int csts;
+#endif
+
+#ifdef DIAGNOSTIC
+        csts = pci_conf_read(sc->sc_pc, sc->sc_pt, PCI_COMMAND_STATUS_REG);
+        if (csts & PCI_STATUS_MASTER_ABORT) {
+                printf("auich_intr: PCI master abort\n");
+        }
+#endif
 
         gsts = bus_space_read_2(sc->iot, sc->aud_ioh, ICH_GSTS);
         DPRINTF(ICH_DEBUG_DMA, ("auich_intr: gsts=0x%x\n", gsts));
@@ -1072,6 +1106,9 @@
         struct auich_dmalist *q;
         struct auich_dma *p;
         size_t size;
+#ifdef DIAGNOSTIC
+        int csts;
+#endif
 
         DPRINTF(ICH_DEBUG_DMA,
             ("auich_trigger_output(%p, %p, %d, %p, %p, %p)\n",
@@ -1080,6 +1117,13 @@
         sc->sc_pintr = intr;
         sc->sc_parg = arg;
 
+#ifdef DIAGNOSTIC
+        csts = pci_conf_read(sc->sc_pc, sc->sc_pt, PCI_COMMAND_STATUS_REG);
+        if (csts & PCI_STATUS_MASTER_ABORT) {
+                printf("auich_trigger_output: PCI master abort\n");
+        }
+#endif
+
         for (p = sc->sc_dmas; p && KERNADDR(p) != start; p = p->next)
                 ;
         if (!p) {
@@ -1129,6 +1173,9 @@
         struct auich_dmalist *q;
         struct auich_dma *p;
         size_t size;
+#ifdef DIAGNOSTIC
+        int csts;
+#endif
 
         DPRINTF(ICH_DEBUG_DMA,
             ("auich_trigger_input(%p, %p, %d, %p, %p, %p)\n",
@@ -1137,6 +1184,13 @@
         sc->sc_rintr = intr;
         sc->sc_rarg = arg;
 
+#ifdef DIAGNOSTIC
+        csts = pci_conf_read(sc->sc_pc, sc->sc_pt, PCI_COMMAND_STATUS_REG);
+        if (csts & PCI_STATUS_MASTER_ABORT) {
+                printf("auich_trigger_input: PCI master abort\n");
+        }
+#endif
+
         for (p = sc->sc_dmas; p && KERNADDR(p) != start; p = p->next)
                 ;
         if (!p) {
@@ -1187,7 +1241,7 @@
                 return (error);
 
         error = bus_dmamem_map(sc->dmat, p->segs, p->nsegs, p->size,
-                               &p->addr, BUS_DMA_NOWAIT|BUS_DMA_COHERENT);
+                               &p->addr, BUS_DMA_NOWAIT|sc->sc_dmamem_flags);
         if (error)
                 goto free;
 
@@ -1243,7 +1297,7 @@
         if ((error = bus_dmamem_map(sc->dmat, &seg, rseg,
                                     sizeof(struct auich_cdata),
                                     (caddr_t *) &sc->sc_cdata,
-                                    BUS_DMA_COHERENT)) != 0) {
+                                    sc->sc_dmamem_flags)) != 0) {
                 printf("%s: unable to map control data, error = %d\n",
                     sc->sc_dev.dv_xname, error);
                 goto fail_1;
Index: arch/i386/include/bus.h
===================================================================
RCS file: /cvsroot/src/sys/arch/i386/include/bus.h,v
retrieving revision 1.39
diff -u -t -r1.39 bus.h
--- arch/i386/include/bus.h	1 Oct 2002 12:57:03 -0000	1.39
+++ arch/i386/include/bus.h	15 Jan 2003 12:22:20 -0000
@@ -1027,6 +1027,7 @@
 #define BUS_DMA_BUS4            0x080
 #define BUS_DMA_READ            0x100   /* mapping is device -> memory only */
 #define BUS_DMA_WRITE           0x200   /* mapping is memory -> device only */
+#define BUS_DMA_NOCACHE         0x400   /* not safe to memory cache writeback */
 
 /* Forwards needed by prototypes below. */
 struct mbuf;
Index: arch/i386/i386/bus_machdep.c
===================================================================
RCS file: /cvsroot/src/sys/arch/i386/i386/bus_machdep.c,v
retrieving revision 1.17
diff -u -t -r1.17 bus_machdep.c
--- arch/i386/i386/bus_machdep.c	1 Oct 2002 12:56:48 -0000	1.17
+++ arch/i386/i386/bus_machdep.c	18 Jan 2003 12:04:37 -0000
@@ -809,6 +809,9 @@
         vaddr_t va;
         bus_addr_t addr;
         int curseg;
+        int32_t cpumask = 0;
+        int nocache = (flags & BUS_DMA_NOCACHE) != 0 && cpu_class != CPUCLASS_386;
+        int marked = 0;
 
         size = round_page(size);
 
@@ -828,8 +831,23 @@
                         pmap_enter(pmap_kernel(), va, addr,
                             VM_PROT_READ | VM_PROT_WRITE,
                             PMAP_WIRED | VM_PROT_READ | VM_PROT_WRITE);
+                        /*
+                         * XXX mark page as non-cacheable
+                         */
+                        if (nocache) {
+                                pt_entry_t *pte = kvtopte(va);
+
+                                if ((*pte & PG_N) == 0) {
+                                        *pte |= PG_N;
+                                        pmap_tlb_shootdown(pmap_kernel(), va, *pte,
+                                            &cpumask);
+                                        marked = 1;
+                                }
+                        }
                 }
         }
+        if (marked)
+                pmap_tlb_shootnow(cpumask);
         pmap_update(pmap_kernel());
 
         return (0);
>Release-Note:
>Audit-Trail:
>Unformatted: