ultra5/cmd646 hang

Thomas Moestl t.moestl at tu-bs.de
Thu Nov 20 17:47:38 PST 2003


On Thu, 2003/11/20 at 09:34:34 +0100, Soren Schmidt wrote:
> It seems Thomas Moestl wrote:
> > The issue is that the chip does not set ATA_BMSTAT_INTERRUPT, even though
> > it is DMA-capable. My hackaround is to add an interrupt handler for the
> > CMD646 that does only check this bit if a DMA transfer is in progress,
> > like it was done in pre-ATAng times. This probably isn't the best solution,
> > but it works for now. On the problematic boxen, you will also have to
> > set hw.ata.ata_dma=0 (they fell back to PIO before automatically anyway,
> > and DMA operations time out even if all interrupts are admitted).
> > I am not sure whether this is caused by some missing bit of setup that
> > is required, but that the firmware doesn't do for us, or whether it
> > applies only to some buggy/crippled chip revision in some machines.
> 
> Hmm, the only 646 chip I hace sits in an alpha board which -current
> hasn't been upgraded for quite some time as it broke it (might be
> fixed in the meantime, I'll try to check..
> However if that bit doesn't work the chip has *serious* problems
> and cannot be used in DMA mode at all. I dont recall seeing this
> problem on the one in my alpha though, so it might be specific to
> certain silicon (rev number cannot be trusted in old CMD chips
> either, they are so crappy its unbeliveable)...

I've played around some more with panther2, and managed to get it to work
seemingly stable in WDMA2 mode by tweaking the initialization code a bit.
I've attached the patch which I have used; the following changes in it
seem to all be required:

- Programming the timings before setting the transfer mode with
  ata_controlcmd(atadev, ATA_SETFEATURES, ATA_SF_SETXFER, ...);
- The added interrupt acking code in the chipset interrupt handler
  (cribbed from NetBSD)
- #if 0-ing out the code that sets the PIO timings. I have not
  yet investigated whether this is because of the PIO initialization
  of the disk before DMA is tried, or causes troubles when used
  for the secondary master, which is a PIO3 CD-ROM.

I'll do some more testing tomorrow, and try to shine some light into
the PIO timing issue.

	- Thomas

-- 
Thomas Moestl <t.moestl at tu-bs.de>	http://www.tu-bs.de/~y0015675/
              <tmm at FreeBSD.org>		http://people.FreeBSD.org/~tmm/
PGP fingerprint: 1C97 A604 2BD0 E492 51D0  9C0F 1FE6 4F1D 419C 776C
-------------- next part --------------
Index: ata-chipset.c
===================================================================
RCS file: /vol/ncvs/src/sys/dev/ata/ata-chipset.c,v
retrieving revision 1.46
diff -u -r1.46 ata-chipset.c
--- ata-chipset.c	18 Nov 2003 15:27:28 -0000	1.46
+++ ata-chipset.c	21 Nov 2003 01:22:20 -0000
@@ -101,6 +101,7 @@
 static int ata_sii_mio_allocate(device_t, struct ata_channel *);
 static void ata_sii_intr(void *);
 static void ata_cmd_intr(void *);
+static void ata_nobms_intr(void *);
 static void ata_sii_setmode(struct ata_device *, int);
 static void ata_cmd_setmode(struct ata_device *, int);
 static int ata_sis_chipinit(device_t);
@@ -1581,7 +1582,7 @@
      { ATA_CMD649,    0x00, 0,	      SIIINTR,   ATA_UDMA5, "CMD 649" },
      { ATA_CMD648,    0x00, 0,	      SIIINTR,   ATA_UDMA4, "CMD 648" },
      { ATA_CMD646,    0x07, 0,	      0,	 ATA_UDMA2, "CMD 646U2" },
-     { ATA_CMD646,    0x00, 0,	      0,	 ATA_WDMA2, "CMD 646" },
+     { ATA_CMD646,    0x00, 0,	      SIINOBMS,	 ATA_WDMA2, "CMD 646" },
      { 0, 0, 0, 0, 0, 0}};
     char buffer[64];
 
@@ -1599,6 +1600,7 @@
 ata_sii_chipinit(device_t dev)
 {
     struct ata_pci_controller *ctlr = device_get_softc(dev);
+    void (*ih)(void *);
     int rid = ATA_IRQ_RID;
 
     if (!(ctlr->r_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1,
@@ -1637,10 +1639,14 @@
 	    ctlr->setmode = ata_sii_setmode;
     }
     else {
+	if (ctlr->chip->cfg2 & SIIINTR)
+	    ih = ata_cmd_intr;
+	else if (ctlr->chip->cfg2 & SIINOBMS)
+	    ih = ata_nobms_intr;
+	else
+	    ih = ata_generic_intr;
 	if ((bus_setup_intr(dev, ctlr->r_irq, ATA_INTR_FLAGS,
-			    ctlr->chip->cfg2 & SIIINTR ? 
-			    ata_cmd_intr : ata_generic_intr,
-			    ctlr, &ctlr->handle))) {
+			    ih, ctlr, &ctlr->handle))) {
 	    device_printf(dev, "unable to setup interrupt\n");
 	    return ENXIO;
 	}
@@ -1743,6 +1749,40 @@
 }
 
 static void
+ata_nobms_intr(void *data)
+{
+    struct ata_pci_controller *ctlr = data;
+    struct ata_channel *ch;
+    int cr, conf;
+    int unit;
+
+    /* implement this as a toggle instead to balance load XXX */
+    for (unit = 0; unit < 2; unit++) {
+	if (!(ch = ctlr->interrupt[unit].argument))
+	    continue;
+	/*
+	 * The CMD646 does not always seem to set ATA_BMSTAT_INTERRUPT.
+	 * Only check it when a transfer is active.
+	 */
+	if (ch->dma && (ch->dma->flags & ATA_DMA_ACTIVE)) {
+	    int bmstat = ATA_IDX_INB(ch, ATA_BMSTAT_PORT) & ATA_BMSTAT_MASK;
+
+	    if ((bmstat & (ATA_BMSTAT_ACTIVE | ATA_BMSTAT_INTERRUPT)) !=
+		ATA_BMSTAT_INTERRUPT)
+		continue;
+	    ATA_IDX_OUTB(ch, ATA_BMSTAT_PORT, bmstat & ~ATA_BMSTAT_ERROR);
+	    DELAY(1);
+	}
+	ctlr->interrupt[unit].function(ch);
+	/* Ack the interrupt. */
+	cr = unit == 0 ? 0x50 : 0x57;
+	conf = pci_read_config(device_get_parent(ch->dev), cr, 1);
+	pci_write_config(device_get_parent(ch->dev), cr,
+	    conf | (unit == 0 ? 0x04 : 0x10), 1);
+    }
+}
+
+static void
 ata_sii_setmode(struct ata_device *atadev, int mode)
 {
     device_t parent = device_get_parent(atadev->channel->dev);
@@ -1805,56 +1845,64 @@
 }
 
 static void
+ata_cmd_settimings(struct ata_device *atadev, device_t parent, int mode)
+{
+    int devno = (atadev->channel->unit << 1) + ATA_DEV(atadev->unit);
+    int treg = 0x54 + (devno < 3) ? (devno << 1) : 7;
+    int ureg = atadev->channel->unit ? 0x7b : 0x73;
+
+    if (mode >= ATA_UDMA0) {	
+	int udmatimings[][2] = { { 0x31,  0xc2 }, { 0x21,  0x82 },
+				 { 0x11,  0x42 }, { 0x25,  0x8a },
+				 { 0x15,  0x4a }, { 0x05,  0x0a } };
+
+	u_int8_t umode = pci_read_config(parent, ureg, 1);
+
+	umode &= ~(atadev->unit == ATA_MASTER ? 0x35 : 0xca);
+	umode |= udmatimings[mode & ATA_MODE_MASK][ATA_DEV(atadev->unit)];
+	pci_write_config(parent, ureg, umode, 1);
+    }
+    else if (mode >= ATA_WDMA0) { 
+	int dmatimings[] = { 0x87, 0x32, 0x3f };
+
+	pci_write_config(parent, treg, dmatimings[mode & ATA_MODE_MASK], 1);
+	pci_write_config(parent, ureg, 
+			 pci_read_config(parent, ureg, 1) &
+			 ~(atadev->unit == ATA_MASTER ? 0x35 : 0xca), 1);
+    }
+    else {
+#if 0
+	int piotimings[] = { 0xa9, 0x57, 0x44, 0x32, 0x3f };
+	pci_write_config(parent, treg,
+			 piotimings[(mode & ATA_MODE_MASK) - ATA_PIO0], 1);
+	pci_write_config(parent, ureg, 
+			 pci_read_config(parent, ureg, 1) &
+			 ~(atadev->unit == ATA_MASTER ? 0x35 : 0xca), 1);
+#endif
+    }
+
+}
+
+static void
 ata_cmd_setmode(struct ata_device *atadev, int mode)
 {
     device_t parent = device_get_parent(atadev->channel->dev);
     struct ata_pci_controller *ctlr = device_get_softc(parent);
-    int devno = (atadev->channel->unit << 1) + ATA_DEV(atadev->unit);
     int error;
 
     mode = ata_limit_mode(atadev, mode, ctlr->chip->max_dma);
-
     mode = ata_check_80pin(atadev, mode);
 
+    ata_cmd_settimings(atadev, parent, mode);
     error = ata_controlcmd(atadev, ATA_SETFEATURES, ATA_SF_SETXFER, 0, mode);
-
     if (bootverbose)
 	ata_prtdev(atadev, "%ssetting %s on %s chip\n",
 		   (error) ? "FAILURE " : "",
 		   ata_mode2str(mode), ctlr->chip->text);
-    if (!error) {
-	int treg = 0x54 + (devno < 3) ? (devno << 1) : 7;
-	int ureg = atadev->channel->unit ? 0x7b : 0x73;
-
-	if (mode >= ATA_UDMA0) {	
-	    int udmatimings[][2] = { { 0x31,  0xc2 }, { 0x21,  0x82 },
-				     { 0x11,  0x42 }, { 0x25,  0x8a },
-				     { 0x15,  0x4a }, { 0x05,  0x0a } };
-
-	    u_int8_t umode = pci_read_config(parent, ureg, 1);
-
-	    umode &= ~(atadev->unit == ATA_MASTER ? 0x35 : 0xca);
-	    umode |= udmatimings[mode & ATA_MODE_MASK][ATA_DEV(atadev->unit)];
-	    pci_write_config(parent, ureg, umode, 1);
-	}
-	else if (mode >= ATA_WDMA0) { 
-	    int dmatimings[] = { 0x87, 0x32, 0x3f };
-
-	    pci_write_config(parent, treg, dmatimings[mode & ATA_MODE_MASK], 1);
-	    pci_write_config(parent, ureg, 
-			     pci_read_config(parent, ureg, 1) &
-			     ~(atadev->unit == ATA_MASTER ? 0x35 : 0xca), 1);
-	}
-	else {
-	   int piotimings[] = { 0xa9, 0x57, 0x44, 0x32, 0x3f };
-	    pci_write_config(parent, treg,
-			     piotimings[(mode & ATA_MODE_MASK) - ATA_PIO0], 1);
-	    pci_write_config(parent, ureg, 
-			     pci_read_config(parent, ureg, 1) &
-			     ~(atadev->unit == ATA_MASTER ? 0x35 : 0xca), 1);
-	}
+    if (error)
+	ata_cmd_settimings(atadev, parent, atadev->mode);
+    else
 	atadev->mode = mode;
-    }
 }
 
 /*
Index: ata-pci.h
===================================================================
RCS file: /vol/ncvs/src/sys/dev/ata/ata-pci.h,v
retrieving revision 1.18
diff -u -r1.18 ata-pci.h
--- ata-pci.h	18 Nov 2003 15:27:28 -0000	1.18
+++ ata-pci.h	21 Nov 2003 00:21:00 -0000
@@ -255,6 +255,7 @@
 #define SIIMEMIO	1
 #define SIIINTR		0x01
 #define SIISETCLK	0x02
+#define SIINOBMS	0x04
 
 #define SIS_SOUTH	1
 #define SISSATA		2
Index: ata-queue.c
===================================================================
RCS file: /vol/ncvs/src/sys/dev/ata/ata-queue.c,v
retrieving revision 1.11
diff -u -r1.11 ata-queue.c
--- ata-queue.c	20 Oct 2003 14:28:37 -0000	1.11
+++ ata-queue.c	21 Nov 2003 00:21:00 -0000
@@ -316,6 +316,8 @@
 ata_timeout(struct ata_request *request)
 {
     struct ata_channel *ch = request->device->channel;
+    struct ata_device *reqdev = request->device;
+    char *reqstr = ata_cmd2str(request);
     int quiet = request->flags & ATA_R_QUIET;
 
     /* clear timeout etc */
@@ -324,10 +326,11 @@
     /* call hw.interrupt to try finish up the command */
     ch->hw.interrupt(request->device->channel);
     if (ch->running != request) {
+	/* request might already be freed - use copies. */
 	if (!quiet)
-	    ata_prtdev(request->device,
+	    ata_prtdev(reqdev,
 		       "WARNING - %s recovered from missing interrupt\n",
-		       ata_cmd2str(request));
+		       reqstr);
 	return;
     }
 


More information about the freebsd-sparc64 mailing list