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