Intel SATA ICH5/5R 6300ESB support patches

From: Doug Ambrisko <ambrisko_at_ambrisko.com>
Date: Fri, 2 Apr 2004 22:12:27 -0800 (PST)
Here are some patches to deal with SATA devices causing errors and 
resulting in wedged systems.  Deals with drives removed/powered down,
drives powered up or plugged in and media errors.  We need to
read the SATA registers and twiddle the port enable on device 
departures.  If we don't then the ata code execution will result in
a lock-up.

I put in a patch for geom for bio_taskqueue_remove.  Since ata code
schedules bio_task it need to be cancelled when we abort and call
biodone.  If we don't cancel this task then when the task is 
run later we get a double free in UMA since we have cleaned up
twice and called biodone twice for the same request.  It seems
like in biodone we should clean up tasks there.  Suggestions
appreciated.  This band-aids it for:
     1)	dd if=/dev/ad2 of=/dev/null
     2)	then power down the drive.

There are other issues on media recovery that I've hit but
I'm going to do that separate to this HW support.  Promise
patches to deal with SATA issues should be coming soon.

These are based on my -stable patches but are enhanced a little to
deal with some other cases.

This should make -current systems more stable with SATA drives when
something goes wrong with them.

I'd like to commit them to -current after a review.

Doug A.

Index: geom/geom_io.c
===================================================================
RCS file: /usr/local/cvsroot/freebsd/src/sys/geom/geom_io.c,v
retrieving revision 1.53
diff -u -p -r1.53 geom_io.c
--- geom/geom_io.c	11 Feb 2004 18:21:32 -0000	1.53
+++ geom/geom_io.c	3 Apr 2004 05:55:53 -0000
_at__at_ -376,6 +376,14 _at__at_ bio_taskqueue(struct bio *bp, bio_task_t
 	g_bioq_unlock(&g_bio_run_up);
 }
 
+void
+bio_taskqueue_remove(struct bio *bp)
+{
+	g_bioq_lock(&g_bio_run_up);
+	TAILQ_REMOVE(&g_bio_run_task.bio_queue, bp, bio_queue);
+	wakeup(&g_wait_up);
+	g_bioq_unlock(&g_bio_run_up);
+}
 
 void
 g_io_schedule_up(struct thread *tp __unused)
Index: sys/bio.h
===================================================================
RCS file: /usr/local/cvsroot/freebsd/src/sys/sys/bio.h,v
retrieving revision 1.136
diff -u -p -r1.136 bio.h
--- sys/bio.h	28 Jan 2004 08:39:18 -0000	1.136
+++ sys/bio.h	3 Apr 2004 05:55:55 -0000
_at__at_ -123,6 +123,7 _at__at_ void bioq_insert_tail(struct bio_queue_h
 void bioq_remove(struct bio_queue_head *head, struct bio *bp);
 
 void bio_taskqueue(struct bio *bp, bio_task_t *fund, void *arg);
+void bio_taskqueue_remove(struct bio *bp);
 
 int	physio(dev_t dev, struct uio *uio, int ioflag);
 #define physread physio
Index: dev/ata/ata-all.h
===================================================================
RCS file: /usr/local/cvsroot/freebsd/src/sys/dev/ata/ata-all.h,v
retrieving revision 1.77
diff -u -p -r1.77 ata-all.h
--- dev/ata/ata-all.h	15 Mar 2004 12:03:47 -0000	1.77
+++ dev/ata/ata-all.h	3 Apr 2004 05:55:57 -0000
_at__at_ -357,6 +357,8 _at__at_ struct ata_channel {
     struct mtx			queue_mtx;	/* queue lock */
     TAILQ_HEAD(, ata_request)	ata_queue;	/* head of ATA queue */
     void			*running;	/* currently running request */
+    int				sata_master_idx;
+    int				sata_slave_idx;
 };
 
 /* disk bay/enclosure related */
Index: dev/ata/ata-chipset.c
===================================================================
RCS file: /usr/local/cvsroot/freebsd/src/sys/dev/ata/ata-chipset.c,v
retrieving revision 1.67
diff -u -p -r1.67 ata-chipset.c
--- dev/ata/ata-chipset.c	17 Mar 2004 17:50:27 -0000	1.67
+++ dev/ata/ata-chipset.c	3 Apr 2004 05:55:57 -0000
_at__at_ -79,6 +79,11 _at__at_ static int ata_highpoint_check_80pin(str
 static int ata_intel_chipinit(device_t);
 static void ata_intel_old_setmode(struct ata_device *, int);
 static void ata_intel_new_setmode(struct ata_device *, int);
+static int ata_intel_sata_allocate(device_t, struct ata_channel *);
+static void ata_intel_map_sata_ports(device_t, struct ata_channel *);
+static void ata_intel_sata_intr(void *);
+static void ata_intel_sata_status(struct ata_channel *);
+static void ata_intel_sata_reset(struct ata_channel *);
 static int ata_national_chipinit(device_t);
 static void ata_national_setmode(struct ata_device *, int);
 static int ata_nvidia_chipinit(device_t);
_at__at_ -813,7 +818,11 _at__at_ ata_intel_ident(device_t dev)
      { ATA_I82801DB,   0, 0, 0x00, ATA_UDMA5, "Intel ICH4" },
      { ATA_I82801DB_1, 0, 0, 0x00, ATA_UDMA5, "Intel ICH4" },
      { ATA_I82801EB,   0, 0, 0x00, ATA_UDMA5, "Intel ICH5" },
-     { ATA_I82801EB_1, 0, 0, 0x00, ATA_SA150, "Intel ICH5" },
+     { ATA_I82801EB_SATA, 0, 0, ISATA, ATA_SA150, "Intel ICH5" },
+     { ATA_I82801EB_SATA_RAID, 0, 0, ISATA, ATA_SA150, "Intel ICH5 RAID" },
+     { ATA_I6300ESB,   0, 0, ISATA, ATA_UDMA5, "Intel 6300ESB ICH" },
+     { ATA_I6300ESB_SATA, 0, 0, ISATA, ATA_SA150, "Intel 6300ESB ICH" },
+     { ATA_I6300ESB_SATA_RAID, 0, 0, ISATA, ATA_SA150, "Intel 6300ESB ICH RAID" },
      { 0, 0, 0, 0, 0, 0}};
     char buffer[64]; 
 
_at__at_ -830,10 +839,33 _at__at_ ata_intel_ident(device_t dev)
 static int
 ata_intel_chipinit(device_t dev)
 {
+    int rid = ATA_IRQ_RID;
     struct ata_pci_controller *ctlr = device_get_softc(dev);
+    void* intr = ata_generic_intr;
 
-    if (ata_setup_interrupt(dev))
-	return ENXIO;
+    if (ctlr->chip->cfg2 & ISATA) {
+	ctlr->allocate = ata_intel_sata_allocate;
+	intr = ata_intel_sata_intr;
+
+	/* Clear SATA error registers */
+	pci_write_config(dev, 0xa0, 0x54, 4);
+	pci_write_config(dev, 0xa4, pci_read_config(dev, 0xa4, 4), 4);
+	pci_write_config(dev, 0xa0, 0x64, 4);
+	pci_write_config(dev, 0xa4, pci_read_config(dev, 0xa4, 4), 4);
+    }
+
+    if (!ATA_MASTERDEV(dev)) {
+	if (!(ctlr->r_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+						   RF_SHAREABLE | RF_ACTIVE))) {
+	    device_printf(dev, "unable to map interrupt\n");
+	    return ENXIO;
+	}
+	if ((bus_setup_intr(dev, ctlr->r_irq, ATA_INTR_FLAGS,
+			    intr, ctlr, &ctlr->handle))) {
+	    device_printf(dev, "unable to setup interrupt\n");
+	    return ENXIO;
+	}
+    }
 
     if (ctlr->chip->chipid == ATA_I82371FB)
 	ctlr->setmode = ata_intel_old_setmode;
_at__at_ -841,6 +873,7 _at__at_ ata_intel_chipinit(device_t dev)
 	ctlr->setmode = ata_intel_new_setmode;
     else
 	ctlr->setmode = ata_sata_setmode;
+
     return 0;
 }
 
_at__at_ -924,6 +957,212 _at__at_ ata_intel_new_setmode(struct ata_device 
     pci_write_config(parent, 0x44, (reg44 & ~mask44) | new44, 1);
 
     atadev->mode = mode;
+}
+
+static int
+ata_intel_sata_allocate( device_t dev, struct ata_channel *ch)
+{
+    struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+    struct resource *io = NULL, *altio = NULL;
+    int i, rid;
+
+    rid = ATA_IOADDR_RID;
+    io = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
+			    0, ~0, ATA_IOSIZE, RF_ACTIVE);
+    if (!io)
+	return ENXIO;
+
+    rid = ATA_ALTADDR_RID;
+    altio = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
+			       0, ~0, ATA_ALTIOSIZE, RF_ACTIVE);
+    if (!altio) {
+	bus_release_resource(dev, SYS_RES_IOPORT, ATA_IOADDR_RID, io);
+	return ENXIO;
+    }
+
+    for (i = ATA_DATA; i <= ATA_STATUS; i ++) {
+	ch->r_io[i].res = io;
+	ch->r_io[i].offset = i;
+    }
+    ch->r_io[ATA_ALTSTAT].res = altio;
+    ch->r_io[ATA_ALTSTAT].offset = 0;
+    ch->r_io[ATA_IDX_ADDR].res = io;
+
+    if (ctlr->r_res1) {
+	for (i = ATA_BMCMD_PORT; i <= ATA_BMDTP_PORT; i++) {
+	    ch->r_io[i].res = ctlr->r_res1;
+	    ch->r_io[i].offset = (i - ATA_BMCMD_PORT)+(ch->unit * ATA_BMIOSIZE);
+	}
+
+	ctlr->dmainit(ch);
+    }
+
+    ch->reset = ata_intel_sata_reset;
+    ata_intel_map_sata_ports(dev, ch);
+    return 0;
+}
+
+void
+ata_intel_map_sata_ports(device_t dev, struct ata_channel *ch)
+{
+    device_t parent = device_get_parent(dev);
+    int unit = ch->unit;
+    int sata_config;
+
+    sata_config = pci_read_config(parent, 0x90, 1);
+
+    switch (sata_config & 7) {
+    case 0: /* SATA P0 on pri master SATA P1 on sec master */
+	if (unit == 0) {
+	    ch->sata_master_idx = 1;
+	    device_printf(dev, "SATA P0 primary master\n");
+        }
+	if (unit == 1) {
+	    ch->sata_master_idx = 2;
+	    device_printf(dev, "SATA P1 secondary master\n");
+	}
+	break;
+    case 1: /* SATA P0 on sec master SATA P1 on pri master */
+	if (unit == 0) {
+	    ch->sata_master_idx = 2;
+	    device_printf(dev, "SATA P0 secondary master\n");
+	}
+	if (unit == 1) {
+	    ch->sata_master_idx = 0;
+	    device_printf(dev, "SATA P1 primary master\n");
+	}
+	break;
+    case 4: /* SATA P0 on pri master SATA P1 on pri slave  */
+	if (unit == 0) {
+	    ch->sata_master_idx = 1;
+	    ch->sata_slave_idx = 2;
+	    device_printf(dev, "SATA P0 primary master\n");
+	    device_printf(dev, "SATA P1 primary slave\n");
+        }
+	break;
+    case 5: /* SATA P0 on pri slave  SATA P1 on pri master */
+	if (unit == 0) {
+	    ch->sata_master_idx = 2;
+	    ch->sata_slave_idx = 1;
+	    device_printf(dev, "SATA P0 primary slave\n");
+	    device_printf(dev, "SATA P1 primary master\n");
+	}
+	break;
+    case 6: /* SATA P0 on sec master SATA P1 on sec slave  */
+	if (unit == 1) {
+	    ch->sata_master_idx = 1;
+	    ch->sata_slave_idx = 2;
+	    device_printf(dev, "SATA P0 secondary master\n");
+	    device_printf(dev, "SATA P1 secondary slave\n");
+	}
+	break;
+    case 7: /* SATA P0 on sec slave  SATA P1 on sec master */
+	if (unit == 1) {
+	    ch->sata_master_idx = 2;
+	    ch->sata_slave_idx = 1;
+	    device_printf(dev, "SATA P0 secondary slave\n");
+	    device_printf(dev, "SATA P1 secondary master\n");
+	}
+	break;
+    }
+}
+
+
+static void
+ata_intel_sata_status(struct ata_channel *ch)
+{
+    device_t parent;
+    struct ata_pci_controller *ctlr;
+    u_int32_t sstatus, serror, scontrol;
+    int index = 0, device, base;  
+    
+    if (!ch->dev)	/* Channel not ready */
+	return;
+    parent = device_get_parent(ch->dev);
+    ctlr = device_get_softc(parent);
+
+    index = ch->sata_master_idx | ch->sata_slave_idx;;
+
+    for (device = 1; device <= 2; device++) {
+	if (index & device) {
+	    if (device == 1)
+		base = 0x50;
+	    else
+		base = 0x60;
+	
+	    pci_write_config(parent, 0xa0, base, 4);
+	    sstatus = pci_read_config(parent, 0xa4, 4);
+
+	    pci_write_config(parent, 0xa0, base + 4, 4);
+	    serror = pci_read_config(parent, 0xa4, 4);
+
+	    pci_write_config(parent, 0xa0, base + 8, 4);
+	    scontrol = pci_read_config(parent, 0xa4, 4);
+
+	    if (serror) {
+		if (sstatus == 0x05) { /* Drive left reset port */
+		    pci_write_config(parent, 0x92, 
+			pci_read_config(parent, 0x92, 2) & ~device, 2);
+		    pci_write_config(parent, 0x92, 
+			pci_read_config(parent, 0x92, 2) | device, 2);
+		}
+
+		/* Loop until SATA port is okay otherwise we can hang */
+		for (;;) {   
+		    pci_write_config(parent, 0xa0, base, 4);
+		    sstatus = pci_read_config(parent, 0xa4, 4);
+
+		    pci_write_config(parent, 0xa0, base + 4, 4);
+		    serror = pci_read_config(parent, 0xa4, 4);
+		    /* Acknowledge serror */
+		    pci_write_config(parent, 0xa4, serror, 4);
+
+		    pci_write_config(parent, 0xa0, base + 8, 4);
+		    scontrol = pci_read_config(parent, 0xa4, 4);
+		    device_printf(ch->dev, 
+			"Intel SATA P%d status %x error %x scontrol %x %d %x\n",
+			device - 1, sstatus, serror, scontrol, 
+			ch->unit, ch->devices);
+		    if (!serror && sstatus != 5)
+			break;
+
+		    DELAY(1000);
+		}
+	    }
+	}
+    }
+}
+
+static void 
+ata_intel_sata_intr(void *data)
+{
+    struct ata_pci_controller *ctlr = data;
+    struct ata_channel *ch;
+    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;
+	/* DJA deal with ATA_HWGONE was ATA_DEAD in my code */
+	ata_intel_sata_status(ch);
+	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);
+    }
+}
+
+static void
+ata_intel_sata_reset(struct ata_channel *ch)
+{
+    ata_intel_sata_status(ch);
 }
 
 /*
Index: dev/ata/ata-disk.c
===================================================================
RCS file: /usr/local/cvsroot/freebsd/src/sys/dev/ata/ata-disk.c,v
retrieving revision 1.171
diff -u -p -r1.171 ata-disk.c
--- dev/ata/ata-disk.c	1 Mar 2004 13:17:07 -0000	1.171
+++ dev/ata/ata-disk.c	3 Apr 2004 05:55:57 -0000
_at__at_ -321,6 +321,7 _at__at_ ad_done(struct ata_request *request)
     if ((bp->bio_error = request->result))
 	bp->bio_flags |= BIO_ERROR;
     bp->bio_resid = bp->bio_bcount - request->donecount;
+    bio_taskqueue_remove(bp);
     biodone(bp);
     ata_free_request(request);
 }
Index: dev/ata/ata-lowlevel.c
===================================================================
RCS file: /usr/local/cvsroot/freebsd/src/sys/dev/ata/ata-lowlevel.c,v
retrieving revision 1.31
diff -u -p -r1.31 ata-lowlevel.c
--- dev/ata/ata-lowlevel.c	15 Mar 2004 12:03:47 -0000	1.31
+++ dev/ata/ata-lowlevel.c	3 Apr 2004 05:55:57 -0000
_at__at_ -534,7 +534,11 _at__at_ ata_reset(struct ata_channel *ch)
     u_int8_t stat0 = 0, stat1 = 0;
     int mask = 0, timeout;
 
-    /* do we have any signs of ATA/ATAPI HW being present ? */
+    /* reset host end of channel (if supported) */
+    if (ch->reset)
+	ch->reset(ch);
+
+    /* DO we have any signs of ATA/ATAPI HW being present ? */
     ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_MASTER);
     DELAY(10);
     ostat0 = ATA_IDX_INB(ch, ATA_STATUS);
Index: dev/ata/ata-pci.c
===================================================================
RCS file: /usr/local/cvsroot/freebsd/src/sys/dev/ata/ata-pci.c,v
retrieving revision 1.77
diff -u -p -r1.77 ata-pci.c
--- dev/ata/ata-pci.c	17 Mar 2004 17:50:27 -0000	1.77
+++ dev/ata/ata-pci.c	3 Apr 2004 05:55:57 -0000
_at__at_ -188,7 +188,8 _at__at_ ata_pci_attach(device_t dev)
 	device_add_child(dev, "ata", ATA_MASTERDEV(dev) ?
 			 unit : devclass_find_free_unit(ata_devclass, 2));
 
-    return bus_generic_attach(dev); }
+    return bus_generic_attach(dev);
+}
 
 static int
 ata_pci_detach(device_t dev)
Index: dev/ata/ata-pci.h
===================================================================
RCS file: /usr/local/cvsroot/freebsd/src/sys/dev/ata/ata-pci.h,v
retrieving revision 1.28
diff -u -p -r1.28 ata-pci.h
--- dev/ata/ata-pci.h	16 Mar 2004 16:23:28 -0000	1.28
+++ dev/ata/ata-pci.h	3 Apr 2004 05:55:57 -0000
_at__at_ -118,7 +118,11 _at__at_ struct ata_pci_controller {
 #define ATA_I82801DB		0x24cb8086
 #define ATA_I82801DB_1		0x24ca8086
 #define ATA_I82801EB		0x24db8086
-#define ATA_I82801EB_1		0x24d18086
+#define ATA_I82801EB_SATA	0x24d18086
+#define ATA_I82801EB_SATA_RAID	0x24df8086
+#define ATA_I6300ESB		0x25a28086
+#define ATA_I6300ESB_SATA	0x25a38086
+#define ATA_I6300ESB_SATA_RAID	0x25b08086
 
 #define ATA_NATIONAL_ID		0x100b
 #define ATA_SC1100		0x0502100b
_at__at_ -247,6 +251,8 _at__at_ struct ata_pci_controller {
 #define HPT372		2
 #define HPT374		3
 #define HPTOLD		0x01
+
+#define ISATA		1
 
 #define PROLD		0
 #define PRNEW		1
Index: dev/ata/ata-queue.c
===================================================================
RCS file: /usr/local/cvsroot/freebsd/src/sys/dev/ata/ata-queue.c,v
retrieving revision 1.25
diff -u -p -r1.25 ata-queue.c
--- dev/ata/ata-queue.c	15 Mar 2004 12:03:48 -0000	1.25
+++ dev/ata/ata-queue.c	3 Apr 2004 05:55:57 -0000
_at__at_ -479,7 +479,8 _at__at_ ata_fail_requests(struct ata_channel *ch
     mtx_unlock(&ch->queue_mtx);
 
     /* if we have a request "in flight" fail it as well */
-    if ((!device || request->device == device) && (request = ch->running)) {
+    request = ch->running;
+    if (request && (!device || request->device == device)) {
 	untimeout((timeout_t *)ata_timeout, request, request->timeout_handle);
 	ATA_UNLOCK_CH(request->device->channel);
 	request->device->channel->locking(request->device->channel,
Received on Fri Apr 02 2004 - 20:12:52 UTC

This archive was generated by hypermail 2.4.0 : Wed May 19 2021 - 11:37:49 UTC