Subject: kern/9536: AMD 756 DMA/UDMA IDE not supported
To: None <gnats-bugs@gnats.netbsd.org>
From: None <dave@dtsp.co.nz>
List: netbsd-bugs
Date: 03/04/2000 06:51:49
>Number:         9536
>Category:       kern
>Synopsis:       AMD 756 DMA/UDMA IDE not supported
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    kern-bug-people (Kernel Bug People)
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Sat Mar  4 06:50:59 2000
>Last-Modified:
>Originator:     Dave Sainty
>Organization:
Dynamic Technology Services and Products Ltd (NZ)
>Release:        NetBSD-current 20000304
>Environment:
System: NetBSD tequila.dave.dtsp.co.nz 1.4R NetBSD 1.4R (TEQUILA) #3: Wed Feb 16 20:01:31 NZDT 2000 dave@tequila.dave.dtsp.co.nz:/vol/tequila/userB/u2/NetBSD-current/src/sys/arch/i386/compile/TEQUILA i386


>Description:
	The AMD 756 IDE controller found on some Athlon motherboards is fully
	functional, but NetBSD doesn't support DMA on it.
>How-To-Repeat:
	Try 'dd if=/dev/rwd0a of=/dev/null bs=65536' whilst running top.
	Notice that the CPU is running at 90% capacity in system.
	
>Fix:
	Apply the following patch!  Note it relies on my previous send-pr,
	defining the PCI product constants.

	I only have one drive in this machine and it is not Ultra-DMA.
	However, this fix works perfectly with it, and with my atapi DVD
	player (both use PIO mode 4, DMA mode 2).  The 'dd' test leaves the
	CPU completely idle, huge improvement... and one step closer to being
	able to play DVD's at full frame rate :)

--- sys/dev/pci/pciide.c.orig	Sun Jan 23 14:47:07 2000
+++ sys/dev/pci/pciide.c	Sun Mar  5 03:31:52 2000
@@ -109,6 +109,7 @@
 #include <dev/pci/pciidereg.h>
 #include <dev/pci/pciidevar.h>
 #include <dev/pci/pciide_piix_reg.h>
+#include <dev/pci/pciide_amd_reg.h>
 #include <dev/pci/pciide_apollo_reg.h>
 #include <dev/pci/pciide_cmd_reg.h>
 #include <dev/pci/pciide_cy693_reg.h>
@@ -157,6 +158,9 @@
 static u_int32_t piix_setup_idetim_drvs __P((struct ata_drive_datas*));
 static u_int32_t piix_setup_sidetim_timings __P((u_int8_t, u_int8_t, u_int8_t));
 
+void amd756_chip_map __P((struct pciide_softc*, struct pci_attach_args*));
+void amd756_setup_channel __P((struct channel_softc*));
+
 void apollo_chip_map __P((struct pciide_softc*, struct pci_attach_args*));
 void apollo_setup_channel __P((struct channel_softc*));
 
@@ -244,6 +248,18 @@
 	}
 };
 
+const struct pciide_product_desc pciide_amd_products[] =  {
+	{ PCI_PRODUCT_AMD_PBC756_IDE,
+	  0,
+	  "Advanced Micro Devices AMD756 IDE Controller",
+	  amd756_chip_map
+	},
+	{ 0,
+	  0,
+	  NULL,
+	}
+};
+
 const struct pciide_product_desc pciide_cmd_products[] =  {
 	{ PCI_PRODUCT_CMDTECH_640,
 	  0,
@@ -349,6 +365,7 @@
 	{ PCI_VENDOR_SIS, pciide_sis_products },
 	{ PCI_VENDOR_ALI, pciide_acer_products },
 	{ PCI_VENDOR_PROMISE, pciide_promise_products },
+	{ PCI_VENDOR_AMD, pciide_amd_products },
 	{ 0, NULL }
 };
 
@@ -1601,6 +1618,142 @@
 	else 
 		return PIIX_SIDETIM_ISP_SET(piix_isp_pio[mode], channel) |
 		    PIIX_SIDETIM_RTC_SET(piix_rtc_pio[mode], channel);
+}
+
+void
+amd756_chip_map(sc, pa)
+	struct pciide_softc *sc;
+	struct pci_attach_args *pa;
+{
+	struct pciide_channel *cp;
+	pcireg_t interface = PCI_INTERFACE(pci_conf_read(sc->sc_pc,
+				    sc->sc_tag, PCI_CLASS_REG));
+	int channel;
+	pcireg_t chanenable;
+	bus_size_t cmdsize, ctlsize;
+
+	if (pciide_chipen(sc, pa) == 0)
+		return;
+	printf("%s: bus-master DMA support present",
+	    sc->sc_wdcdev.sc_dev.dv_xname);
+	pciide_mapreg_dma(sc, pa);
+	printf("\n");
+	if (sc->sc_dma_ok)
+		sc->sc_wdcdev.cap |= WDC_CAPABILITY_DMA | WDC_CAPABILITY_UDMA;
+	sc->sc_wdcdev.cap |= WDC_CAPABILITY_DATA16 | WDC_CAPABILITY_DATA32 |
+	    WDC_CAPABILITY_MODE;
+	sc->sc_wdcdev.PIO_cap = 4;
+	sc->sc_wdcdev.DMA_cap = 2;
+	sc->sc_wdcdev.UDMA_cap = 4;
+	sc->sc_wdcdev.set_modes = amd756_setup_channel;
+	sc->sc_wdcdev.channels = sc->wdc_chanarray;
+	sc->sc_wdcdev.nchannels = PCIIDE_NUM_CHANNELS;
+	chanenable = pci_conf_read(sc->sc_pc, sc->sc_tag, AMD756_CHANSTATUS_EN);
+
+	WDCDEBUG_PRINT(("amd756_chip_map: Channel enable=0x%x\n", chanenable),
+	    DEBUG_PROBE);
+	for (channel = 0; channel < sc->sc_wdcdev.nchannels; channel++) {
+		cp = &sc->pciide_channels[channel];
+		if (pciide_chansetup(sc, channel, interface) == 0)
+			continue;
+
+		if ((chanenable & AMD756_CHAN_EN(channel)) == 0) {
+			printf("%s: %s channel ignored (disabled)\n",
+			    sc->sc_wdcdev.sc_dev.dv_xname, cp->name);
+			continue;
+		}
+		pciide_mapchan(pa, cp, interface, &cmdsize, &ctlsize,
+		    pciide_pci_intr);
+
+		if (pciiide_chan_candisable(cp))
+			chanenable &= ~AMD756_CHAN_EN(channel);
+		pciide_map_compat_intr(pa, cp, channel, interface);
+		if (cp->hw_ok == 0)
+			continue;
+
+		amd756_setup_channel(&cp->wdc_channel);
+	}
+	pci_conf_write(sc->sc_pc, sc->sc_tag, AMD756_CHANSTATUS_EN,
+	    chanenable);
+	return;
+}
+
+void
+amd756_setup_channel(chp)
+	struct channel_softc *chp;
+{
+	u_int32_t udmatim_reg, datatim_reg;
+	u_int8_t idedma_ctl;
+	int mode, drive;
+	struct ata_drive_datas *drvp;
+	struct pciide_channel *cp = (struct pciide_channel*)chp;
+	struct pciide_softc *sc = (struct pciide_softc *)cp->wdc_channel.wdc;
+
+	idedma_ctl = 0;
+	datatim_reg = pci_conf_read(sc->sc_pc, sc->sc_tag, AMD756_DATATIM);
+	udmatim_reg = pci_conf_read(sc->sc_pc, sc->sc_tag, AMD756_UDMA);
+	datatim_reg &= ~AMD756_DATATIM_MASK(chp->channel);
+	udmatim_reg &= ~AMD756_UDMA_MASK(chp->channel);
+
+	/* setup DMA if needed */
+	pciide_channel_dma_setup(cp);
+
+	for (drive = 0; drive < 2; drive++) {
+		drvp = &chp->ch_drive[drive];
+		/* If no drive, skip */
+		if ((drvp->drive_flags & DRIVE) == 0)
+			continue;
+		/* add timing values, setup DMA if needed */
+		if (((drvp->drive_flags & DRIVE_DMA) == 0 &&
+		    (drvp->drive_flags & DRIVE_UDMA) == 0)) {
+			mode = drvp->PIO_mode;
+			goto pio;
+		}
+		if ((chp->wdc->cap & WDC_CAPABILITY_UDMA) &&
+		    (drvp->drive_flags & DRIVE_UDMA)) {
+			/* use Ultra/DMA */
+			drvp->drive_flags &= ~DRIVE_DMA;
+			udmatim_reg |= AMD756_UDMA_EN(chp->channel, drive) |
+			    AMD756_UDMA_EN_MTH(chp->channel, drive) |
+			    AMD756_UDMA_TIME(chp->channel, drive,
+				amd756_udma_tim[drvp->UDMA_mode]);
+			/* can use PIO timings, MW DMA unused */
+			mode = drvp->PIO_mode;
+		} else {
+			/* use Multiword DMA */
+			drvp->drive_flags &= ~DRIVE_UDMA;
+			/* mode = min(pio, dma+2) */
+			if (drvp->PIO_mode <= (drvp->DMA_mode +2))
+				mode = drvp->PIO_mode;
+			else
+				mode = drvp->DMA_mode + 2;
+		}
+		idedma_ctl |= IDEDMA_CTL_DRV_DMA(drive);
+
+pio:		/* setup PIO mode */
+		if (mode <= 2) {
+			drvp->DMA_mode = 0;
+			drvp->PIO_mode = 0;
+			mode = 0;
+		} else {
+			drvp->PIO_mode = mode;
+			drvp->DMA_mode = mode - 2;
+		}
+		datatim_reg |=
+		    AMD756_DATATIM_PULSE(chp->channel, drive,
+			amd756_pio_set[mode]) |
+		    AMD756_DATATIM_RECOV(chp->channel, drive,
+			amd756_pio_rec[mode]);
+	}
+	if (idedma_ctl != 0) {
+		/* Add software bits in status register */
+		bus_space_write_1(sc->sc_dma_iot, sc->sc_dma_ioh,
+		    IDEDMA_CTL + (IDEDMA_SCH_OFFSET * chp->channel),
+		    idedma_ctl);
+	}
+	pciide_print_modes(cp);
+	pci_conf_write(sc->sc_pc, sc->sc_tag, AMD756_DATATIM, datatim_reg);
+	pci_conf_write(sc->sc_pc, sc->sc_tag, AMD756_UDMA, udmatim_reg);
 }
 
 void
--- /dev/null	Sun Mar  5 03:32:00 2000
+++ sys/dev/pci/pciide_amd_reg.h	Sun Mar  5 03:11:52 2000
@@ -0,0 +1,66 @@
+/*	$NetBSD: $	*/
+
+/*
+ * Copyright (c) 2000 David Sainty.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/*
+ * Registers definitions for AMD 756 PCI IDE controller.  Documentation
+ * available at: http://www.amd.com/products/cpg/athlon/techdocs/pdf/22548.pdf
+ */
+
+/* Channel enable */
+#define AMD756_CHANSTATUS_EN		0x40
+#define AMD756_CHAN_EN(chan)		(0x01 << (1 - (chan)))
+
+/* Data port timing controls */
+#define AMD756_DATATIM 0x48
+#define AMD756_DATATIM_MASK(channel) (0xffff << ((1 - (channel)) << 4))
+#define AMD756_DATATIM_RECOV(channel, drive, x) (((x) & 0xf) << \
+	(((1 - (channel)) << 4) + ((1 - (drive)) << 3)))
+#define AMD756_DATATIM_PULSE(channel, drive, x) (((x) & 0xf) << \
+	(((1 - (channel)) << 4) + ((1 - (drive)) << 3) + 4))
+
+static const int8_t amd756_pio_set[] = {0x0a, 0x0a, 0x0a, 0x02, 0x02};
+static const int8_t amd756_pio_rec[] = {0x08, 0x08, 0x08, 0x02, 0x00};
+
+/* Ultra-DMA/33 control */
+#define AMD756_UDMA 0x50
+#define AMD756_UDMA_MASK(channel) (0xffff << ((1 - (channel)) << 4))
+#define AMD756_UDMA_TIME(channel, drive, x) (((x) & 0x7) << \
+	(((1 - (channel)) << 4) + ((1 - (drive)) << 3)))
+#define AMD756_UDMA_EN(channel, drive) (0x40 << \
+	(((1 - (channel)) << 4) + ((1 - (drive)) << 3)))
+#define AMD756_UDMA_EN_MTH(channel, drive) (0x80 << \
+	(((1 - (channel)) << 4) + ((1 - (drive)) << 3)))
+
+static const int8_t amd756_udma_tim[] = {0x02, 0x01, 0x00, 0x04, 0x05};
>Audit-Trail:
>Unformatted: