Module Name:    src
Committed By:   jdolecek
Date:           Wed Apr 19 20:49:17 UTC 2017

Modified Files:
        src/sys/dev/ata [jdolecek-ncq]: ata.c ata_wdc.c atavar.h satafis_subr.c
            wd.c wdvar.h
        src/sys/dev/ic [jdolecek-ncq]: ahcisata_core.c mvsata.c siisata.c wdc.c
        src/sys/dev/scsipi [jdolecek-ncq]: atapi_wdc.c

Log Message:
adjust ata code to support more than one active command, including the
timeout handling, add support for NCQ commands

move probe for NCQ and number of tags to middle layer, negotiate mutual
support between drive and controller

implement NCQ support in ahci(4)


To generate a diff of this commit:
cvs rdiff -u -r1.132.8.4 -r1.132.8.5 src/sys/dev/ata/ata.c
cvs rdiff -u -r1.105.6.2 -r1.105.6.3 src/sys/dev/ata/ata_wdc.c
cvs rdiff -u -r1.92.8.4 -r1.92.8.5 src/sys/dev/ata/atavar.h
cvs rdiff -u -r1.7.28.1 -r1.7.28.2 src/sys/dev/ata/satafis_subr.c
cvs rdiff -u -r1.428.2.6 -r1.428.2.7 src/sys/dev/ata/wd.c
cvs rdiff -u -r1.43.4.3 -r1.43.4.4 src/sys/dev/ata/wdvar.h
cvs rdiff -u -r1.57.6.6 -r1.57.6.7 src/sys/dev/ic/ahcisata_core.c
cvs rdiff -u -r1.35.6.5 -r1.35.6.6 src/sys/dev/ic/mvsata.c
cvs rdiff -u -r1.30.4.4 -r1.30.4.5 src/sys/dev/ic/siisata.c
cvs rdiff -u -r1.283.2.3 -r1.283.2.4 src/sys/dev/ic/wdc.c
cvs rdiff -u -r1.123.4.3 -r1.123.4.4 src/sys/dev/scsipi/atapi_wdc.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/ata/ata.c
diff -u src/sys/dev/ata/ata.c:1.132.8.4 src/sys/dev/ata/ata.c:1.132.8.5
--- src/sys/dev/ata/ata.c:1.132.8.4	Sat Apr 15 17:14:11 2017
+++ src/sys/dev/ata/ata.c	Wed Apr 19 20:49:17 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: ata.c,v 1.132.8.4 2017/04/15 17:14:11 jdolecek Exp $	*/
+/*	$NetBSD: ata.c,v 1.132.8.5 2017/04/19 20:49:17 jdolecek Exp $	*/
 
 /*
  * Copyright (c) 1998, 2001 Manuel Bouyer.  All rights reserved.
@@ -25,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ata.c,v 1.132.8.4 2017/04/15 17:14:11 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ata.c,v 1.132.8.5 2017/04/19 20:49:17 jdolecek Exp $");
 
 #include "opt_ata.h"
 
@@ -195,7 +195,7 @@ ata_queue_hwslot_to_xfer(struct ata_queu
 	struct ata_xfer *xfer;
 
 	KASSERT(hwslot < chq->queue_openings);
-	KASSERT(hwslot == 0 || (chq->queue_xfers_avail & __BIT(hwslot)) == 0);
+	KASSERT((chq->active_xfers_used & __BIT(hwslot)) != 0);
 
 	/* Usually the first entry will be the one */
 	TAILQ_FOREACH(xfer, &chq->active_xfers, c_activechain) {
@@ -203,27 +203,71 @@ ata_queue_hwslot_to_xfer(struct ata_queu
 			return xfer;
 	}
 
-	panic("%s: xfer with slot %d not found", __func__, hwslot);
+	panic("%s: xfer with slot %d not found (active %x)", __func__, hwslot,
+	    chq->active_xfers_used);
+}
+
+void
+ata_xfer_init(struct ata_xfer *xfer, bool zero)
+{
+	if (zero)
+		memset(xfer, 0, sizeof(*xfer));
+
+	callout_init(&xfer->c_timo_callout, 0); 	/* XXX MPSAFE */
+}
+
+void
+ata_xfer_destroy(struct ata_xfer *xfer)
+{
+	callout_halt(&xfer->c_timo_callout, NULL);	/* XXX MPSAFE */
+	callout_destroy(&xfer->c_timo_callout);
 }
 
 struct ata_queue *
-ata_queue_alloc(int openings)
+ata_queue_alloc(uint8_t openings)
 {
 	if (openings == 0)
 		openings = 1;
+
+	/*
+	 * While hw supports up to 32 tags, in practice we must never
+	 * allow 32 active commands, since that would signal same as
+	 * channel error. So just limit this to 31.
+	 */
+	if (openings > 31)
+		openings = 31;
+
 	struct ata_queue *chq = malloc(offsetof(struct ata_queue, queue_xfers[openings]),
 	    M_DEVBUF, M_WAITOK | M_ZERO);
-	if (chq != NULL) {
-		chq->queue_openings = openings;
-		chq->queue_xfers_avail = (1 << openings) - 1;
-		ata_queue_reset(chq);
-	}
+
+	chq->queue_openings = openings;
+	chq->queue_xfers_avail = (1 << openings) - 1;
+	ata_queue_reset(chq);
+
+	for (uint8_t i = 0; i < openings; i++)
+		ata_xfer_init(&chq->queue_xfers[i], false);
+
 	return chq;
 }
 
+static void
+ata_queue_downsize(struct ata_queue *chq, uint8_t openings)
+{
+	KASSERT(chq->queue_active == 0);
+	KASSERT(TAILQ_FIRST(&chq->queue_xfer) == NULL);
+	KASSERT(openings < chq->queue_openings);
+
+	chq->queue_openings = openings;
+	chq->queue_xfers_avail = (1 << openings) - 1;
+	ata_queue_reset(chq);
+}
+
 void
 ata_queue_free(struct ata_queue *chq)
 {
+	for (uint8_t i = 0; i < chq->queue_openings; i++)
+		ata_xfer_destroy(&chq->queue_xfers[i]);
+
 	free(chq, M_DEVBUF);
 }
 
@@ -240,11 +284,6 @@ ata_channel_attach(struct ata_channel *c
 	if (chp->ch_flags & ATACH_DISABLED)
 		return;
 
-	/* XXX callout_destroy */
-	callout_init(&chp->ch_callout, 0);
-
-	chq->queue_openings = 1;	/* XXX */
-
 	ata_queue_reset(chq);
 
 	chp->atabus = config_found_ia(chp->ch_atac->atac_dev, "ata", chp,
@@ -397,7 +436,6 @@ atabusconfig_thread(void *arg)
 		memset(&adev, 0, sizeof(struct ata_device));
 		adev.adev_bustype = atac->atac_bustype_ata;
 		adev.adev_channel = chp->ch_channel;
-		adev.adev_openings = chp->ch_queue->queue_openings;
 		adev.adev_drv_data = &chp->ch_drive[i];
 		chp->ch_drive[i].drv_softc = config_found_ia(atabus_sc->sc_dev,
 		    "ata_hl", &adev, ataprint);
@@ -787,7 +825,7 @@ ata_get_params(struct ata_drive_datas *d
 
 	tb = kmem_zalloc(DEV_BSIZE, KM_SLEEP);
 	memset(prms, 0, sizeof(struct ataparams));
-	memset(&xfer, 0, sizeof(xfer));
+	ata_xfer_init(&xfer, true);
 
 	if (drvp->drive_type == ATA_DRIVET_ATA) {
 		xfer.c_ata_c.r_command = WDCC_IDENTIFY;
@@ -870,6 +908,7 @@ ata_get_params(struct ata_drive_datas *d
 	rv = CMD_OK;
  out:
 	kmem_free(tb, DEV_BSIZE);
+	ata_xfer_destroy(&xfer);
 	return rv;
 }
 
@@ -877,11 +916,13 @@ int
 ata_set_mode(struct ata_drive_datas *drvp, uint8_t mode, uint8_t flags)
 {
 	struct ata_xfer xfer;
+	int rv;
 	struct ata_channel *chp = drvp->chnl_softc;
 	struct atac_softc *atac = chp->ch_atac;
 
 	ATADEBUG_PRINT(("ata_set_mode=0x%x\n", mode), DEBUG_FUNCS);
-	memset(&xfer, 0, sizeof(xfer));
+
+	ata_xfer_init(&xfer, true);
 
 	xfer.c_ata_c.r_command = SET_FEATURES;
 	xfer.c_ata_c.r_st_bmask = 0;
@@ -891,12 +932,20 @@ ata_set_mode(struct ata_drive_datas *drv
 	xfer.c_ata_c.flags = flags;
 	xfer.c_ata_c.timeout = 1000; /* 1s */
 	if ((*atac->atac_bustype_ata->ata_exec_command)(drvp,
-						&xfer) != ATACMD_COMPLETE)
-		return CMD_AGAIN;
+						&xfer) != ATACMD_COMPLETE) {
+		rv = CMD_AGAIN;
+		goto out;
+	}
 	if (xfer.c_ata_c.flags & (AT_ERROR | AT_TIMEOU | AT_DF)) {
-		return CMD_ERR;
+		rv = CMD_ERR;
+		goto out;
 	}
-	return CMD_OK;
+
+	rv = CMD_OK;
+
+out:
+	ata_xfer_destroy(&xfer);
+	return rv;
 }
 
 #if NATA_DMA
@@ -990,7 +1039,7 @@ atastart(struct ata_channel *chp)
 {
 	struct atac_softc *atac = chp->ch_atac;
 	struct ata_queue *chq = chp->ch_queue;
-	struct ata_xfer *xfer;
+	struct ata_xfer *xfer, *axfer;
 
 #ifdef ATA_DEBUG
 	int spl1, spl2;
@@ -1024,6 +1073,15 @@ atastart(struct ata_channel *chp)
 
 	KASSERT(chp->ch_ndrives == 1 || chq->queue_openings == 1);
 
+	/*
+	 * Can only take NCQ command if there are no current active
+	 * commands, or if the active commands are NCQ. Need only check
+	 * first xfer.
+	 */
+	axfer = TAILQ_FIRST(&chp->ch_queue->active_xfers);
+	if (axfer && (axfer->c_flags & C_NCQ) == 0)
+		return;
+
 	/* adjust chp, in case we have a shared queue */
 	chp = xfer->c_chp;
 	KASSERT(chp->ch_queue == chq);
@@ -1042,7 +1100,8 @@ atastart(struct ata_channel *chp)
 		return;
 	}
 #ifdef DIAGNOSTIC
-	if ((chp->ch_flags & ATACH_IRQ_WAIT) != 0)
+	if ((chp->ch_flags & ATACH_IRQ_WAIT) != 0
+	    && chp->ch_queue->queue_openings == 1)
 		panic("atastart: channel waiting for irq");
 #endif
 	ATADEBUG_PRINT(("atastart: xfer %p channel %d drive %d\n", xfer,
@@ -1077,7 +1136,9 @@ ata_get_xfer(struct ata_channel *chp)
 	xfer = &chq->queue_xfers[slot];
 	chq->queue_xfers_avail &= ~__BIT(slot);
 
-	memset(xfer, 0, sizeof(struct ata_xfer));
+	/* zero everything after the callout member */
+	memset(&xfer->c_startzero, 0,
+	    sizeof(struct ata_xfer) - offsetof(struct ata_xfer, c_startzero));
 	xfer->c_slot = slot;
 
 out:
@@ -1137,8 +1198,7 @@ ata_deactivate_xfer(struct ata_channel *
 	KASSERT(chq->queue_active > 0);
 	KASSERT((chq->active_xfers_used & __BIT(xfer->c_slot)) != 0);
 
-	//if ((xfer->c_flags & C_TIMEOU) == 0)
-	callout_stop(&chp->ch_callout);
+	callout_stop(&xfer->c_timo_callout);
 
 	TAILQ_REMOVE(&chq->active_xfers, xfer, c_activechain);
 	chq->active_xfers_used &= ~__BIT(xfer->c_slot);
@@ -1360,6 +1420,13 @@ ata_print_modes(struct ata_channel *chp)
 #endif
 		    )
 			aprint_verbose(" (using DMA)");
+
+		if (drvp->drive_flags & ATA_DRIVE_NCQ)
+			aprint_verbose(", NCQ (%d tags)",
+			    chp->ch_queue->queue_openings);
+		else if (drvp->drive_flags & ATA_DRIVE_WFUA)
+			aprint_verbose(", WRITE DMA FUA EXT");
+			
 #endif	/* NATA_DMA || NATA_PIOBM */
 		aprint_verbose("\n");
 	}
@@ -1433,7 +1500,7 @@ ata_probe_caps(struct ata_drive_datas *d
 	struct ata_channel *chp = drvp->chnl_softc;
 	struct atac_softc *atac = chp->ch_atac;
 	device_t drv_dev = drvp->drv_softc;
-	int i, printed, s;
+	int i, printed = 0, s;
 	const char *sep = "";
 	int cf_flags;
 
@@ -1485,7 +1552,6 @@ ata_probe_caps(struct ata_drive_datas *d
 	 */
 	if (params.atap_extensions != 0xffff &&
 	    (params.atap_extensions & WDC_EXT_MODES)) {
-		printed = 0;
 		/*
 		 * XXX some drives report something wrong here (they claim to
 		 * support PIO mode 8 !). As mode is coded on 3 bits in
@@ -1610,7 +1676,6 @@ ata_probe_caps(struct ata_drive_datas *d
 				break;
 			}
 		}
-		aprint_verbose("\n");
 	}
 
 	s = splbio();
@@ -1659,6 +1724,37 @@ ata_probe_caps(struct ata_drive_datas *d
 		}
 		splx(s);
 	}
+
+	/*
+	 * Probe WRITE DMA FUA EXT. Support is mandatory for devices
+	 * supporting LBA48, but nevertheless confirm with the feature flag.
+	 */
+	if (drvp->drive_flags & ATA_DRIVE_DMA) {
+		if ((params.atap_cmd2_en & ATA_CMD2_LBA48) != 0
+		    && (params.atap_cmd_def & ATA_CMDE_WFE)) {
+			drvp->drive_flags |= ATA_DRIVE_WFUA;
+			aprint_verbose("%s WRITE DMA FUA", sep);
+			sep = ",";
+		}
+	}
+
+	/* Probe NCQ support - READ/WRITE FPDMA QUEUED command support */
+	s = splbio();
+	drvp->drv_openings = 1;
+	if (params.atap_sata_caps & SATA_NATIVE_CMDQ) {
+		if (atac->atac_cap & ATAC_CAP_NCQ)
+			drvp->drive_flags |= ATA_DRIVE_NCQ;
+		drvp->drv_openings = (params.atap_queuedepth & 0x0f) + 1;
+		aprint_verbose("%s NCQ (%d tags)", sep, drvp->drv_openings);
+		sep = ",";
+	}
+	if (drvp->drv_openings < chp->ch_queue->queue_openings)
+		ata_queue_downsize(chp->ch_queue, drvp->drv_openings);
+	splx(s);
+
+	if (printed)
+		aprint_verbose("\n");
+
 #if NATA_UDMA
 	if ((atac->atac_cap & ATAC_CAP_UDMA) == 0) {
 		/* don't care about UDMA modes */
@@ -1879,3 +1975,23 @@ ata_delay(int ms, const char *msg, int f
 		kpause(msg, false, mstohz(ms), NULL);
 	}
 }
+
+void
+atacmd_toncq(struct ata_xfer *xfer, uint8_t *cmd, uint16_t *count,
+    uint16_t *features, uint8_t *device)
+{
+	if ((xfer->c_flags & C_NCQ) == 0)
+		return;
+
+	*cmd = (xfer->c_bio.flags & ATA_READ) ?
+	    WDCC_READ_FPDMA_QUEUED : WDCC_WRITE_FPDMA_QUEUED;
+
+	/* for FPDMA the block count is in features */
+	*features = *count;
+
+	/* NCQ tag */
+	*count = (xfer->c_slot << 3);
+
+	/* other device flags */
+	/* XXX FUA handling */
+}

Index: src/sys/dev/ata/ata_wdc.c
diff -u src/sys/dev/ata/ata_wdc.c:1.105.6.2 src/sys/dev/ata/ata_wdc.c:1.105.6.3
--- src/sys/dev/ata/ata_wdc.c:1.105.6.2	Sat Apr 15 12:01:23 2017
+++ src/sys/dev/ata/ata_wdc.c	Wed Apr 19 20:49:17 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: ata_wdc.c,v 1.105.6.2 2017/04/15 12:01:23 jdolecek Exp $	*/
+/*	$NetBSD: ata_wdc.c,v 1.105.6.3 2017/04/19 20:49:17 jdolecek Exp $	*/
 
 /*
  * Copyright (c) 1998, 2001, 2003 Manuel Bouyer.
@@ -54,7 +54,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ata_wdc.c,v 1.105.6.2 2017/04/15 12:01:23 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ata_wdc.c,v 1.105.6.3 2017/04/19 20:49:17 jdolecek Exp $");
 
 #include "opt_ata.h"
 #include "opt_wdc.h"
@@ -409,6 +409,8 @@ again:
 		}
 #if NATA_DMA
 		if (xfer->c_flags & C_DMA) {
+			uint16_t count = nblks, features = 0;
+
 			ata_bio->nblks = nblks;
 			ata_bio->nbytes = xfer->c_bcount;
 			cmd = (ata_bio->flags & ATA_READ) ?
@@ -448,11 +450,17 @@ again:
 				return;
 			}
 			if (ata_bio->flags & ATA_LBA48) {
-			    wdccommandext(chp, xfer->c_drive, atacmd_to48(cmd),
-				ata_bio->blkno, nblks, 0, WDSD_LBA);
+			    uint8_t device = WDSD_LBA;
+			    cmd = atacmd_to48(cmd);
+
+			    atacmd_toncq(xfer, &cmd, &count, &features,
+			        &device);
+
+			    wdccommandext(chp, xfer->c_drive, cmd,
+				ata_bio->blkno, count, features, device);
 			} else {
 			    wdccommand(chp, xfer->c_drive, cmd, cyl,
-				head, sect, nblks, 0);
+				head, sect, count, features);
 			}
 			/* start the DMA channel */
 			(*wdc->dma_start)(wdc->dma_arg,
@@ -460,8 +468,8 @@ again:
 			chp->ch_flags |= ATACH_DMA_WAIT;
 			/* start timeout machinery */
 			if ((xfer->c_flags & C_POLL) == 0)
-				callout_reset(&chp->ch_callout,
-				    ATA_DELAY / 1000 * hz, wdctimeout, chp);
+				callout_reset(&xfer->c_timo_callout,
+				    ATA_DELAY / 1000 * hz, wdctimeout, xfer);
 			/* wait for irq */
 			goto intr;
 		} /* else not DMA */
@@ -532,8 +540,8 @@ again:
 		}
 		/* start timeout machinery */
 		if ((xfer->c_flags & C_POLL) == 0)
-			callout_reset(&chp->ch_callout,
-			    ATA_DELAY / 1000 * hz, wdctimeout, chp);
+			callout_reset(&xfer->c_timo_callout,
+			    ATA_DELAY / 1000 * hz, wdctimeout, xfer);
 	} else if (ata_bio->nblks > 1) {
 		/* The number of blocks in the last stretch may be smaller. */
 		nblks = xfer->c_bcount / drvp->lp->d_secsize;
@@ -796,7 +804,7 @@ wdc_ata_bio_done(struct ata_channel *chp
 	    xfer->c_drive, (u_int)xfer->c_flags),
 	    DEBUG_XFERS);
 
-	callout_stop(&chp->ch_callout);
+	callout_stop(&xfer->c_timo_callout);
 
 	/* feed back residual bcount to our caller */
 	ata_bio->bcount = xfer->c_bcount;

Index: src/sys/dev/ata/atavar.h
diff -u src/sys/dev/ata/atavar.h:1.92.8.4 src/sys/dev/ata/atavar.h:1.92.8.5
--- src/sys/dev/ata/atavar.h:1.92.8.4	Sat Apr 15 17:14:11 2017
+++ src/sys/dev/ata/atavar.h	Wed Apr 19 20:49:17 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: atavar.h,v 1.92.8.4 2017/04/15 17:14:11 jdolecek Exp $	*/
+/*	$NetBSD: atavar.h,v 1.92.8.5 2017/04/19 20:49:17 jdolecek Exp $	*/
 
 /*
  * Copyright (c) 1998, 2001 Manuel Bouyer.
@@ -131,13 +131,15 @@ struct scsipi_xfer;
  * commands are queued in a list.
  */
 struct ata_xfer {
-	volatile u_int c_flags;	/* command state flags */
+	struct callout c_timo_callout;	/* timeout callout handle */
 
+#define c_startzero	c_chp
 	/* Channel and drive that are to process the request. */
 	struct ata_channel *c_chp;
 	uint16_t c_drive;
 	int8_t c_slot;			/* queue slot # */
 
+	volatile u_int c_flags;		/* command state flags */
 	void	*c_databuf;		/* pointer to data buffer */
 	int	c_bcount;		/* byte count left */
 	int	c_skip;			/* bytes already transferred */
@@ -173,6 +175,7 @@ struct ata_xfer {
 #define C_WAITACT	0x0020		/* wakeup when active */
 #define C_FREE		0x0040		/* call ata_free_xfer() asap */
 #define C_PIOBM		0x0080		/* command uses busmastering PIO */
+#define	C_NCQ		0x0100		/* command is queued  */
 
 /* reasons for c_kill_xfer() */
 #define KILL_GONE 1 /* device is gone */
@@ -219,7 +222,7 @@ struct ata_drive_datas {
 	uint8_t drive;		/* drive number */
 	int8_t ata_vers;	/* ATA version supported */
 	uint16_t drive_flags;	/* bitmask for drives present/absent and cap */
-#define	ATA_DRIVE_CAP32		0x0001
+#define	ATA_DRIVE_CAP32		0x0001	/* 32-bit transfer capable */
 #define	ATA_DRIVE_DMA		0x0002
 #define	ATA_DRIVE_UDMA		0x0004
 #define	ATA_DRIVE_MODE		0x0008	/* the drive reported its mode */
@@ -227,6 +230,8 @@ struct ata_drive_datas {
 #define	ATA_DRIVE_WAITDRAIN	0x0020	/* device is waiting for the queue to drain */
 #define	ATA_DRIVE_NOSTREAM	0x0040	/* no stream methods on this drive */
 #define ATA_DRIVE_ATAPIDSCW	0x0080	/* needs to wait for DSC in phase_complete */
+#define ATA_DRIVE_WFUA		0x0100	/* drive supports WRITE DMA FUA EXT */
+#define ATA_DRIVE_NCQ		0x0200	/* drive supports NCQ */
 
 	uint8_t drive_type;
 #define	ATA_DRIVET_NONE		0
@@ -267,6 +272,8 @@ struct ata_drive_datas {
 #define READY          1
 
 #if NATA_DMA
+	uint8_t drv_openings;		/* # of command tags */
+
 	/* numbers of xfers and DMA errs. Used by ata_dmaerr() */
 	uint8_t n_dmaerrs;
 	uint32_t n_xfers;
@@ -340,7 +347,6 @@ struct ata_bustype {
 struct ata_device {
 	const struct ata_bustype *adev_bustype;
 	int adev_channel;
-	int adev_openings;
 	struct ata_drive_datas *adev_drv_data;
 };
 
@@ -348,7 +354,6 @@ struct ata_device {
  * Per-channel data
  */
 struct ata_channel {
-	struct callout ch_callout;	/* callout handle */
 	int ch_channel;			/* location */
 	struct atac_softc *ch_atac;	/* ATA controller softc */
 
@@ -413,6 +418,7 @@ struct atac_softc {
 #define	ATAC_CAP_ATAPI_NOSTREAM 0x0080	/* don't use stream funcs on ATAPI */
 #define	ATAC_CAP_NOIRQ	0x1000		/* controller never interrupts */
 #define	ATAC_CAP_RAID	0x4000		/* controller "supports" RAID */
+#define ATAC_CAP_NCQ	0x8000		/* controller supports NCQ */
 
 	uint8_t	atac_pio_cap;		/* highest PIO mode supported */
 #if NATA_DMA
@@ -489,8 +495,10 @@ void	ata_probe_caps(struct ata_drive_dat
 void	ata_dmaerr(struct ata_drive_datas *, int);
 #endif
 void	ata_queue_idle(struct ata_queue *);
+void	ata_xfer_init(struct ata_xfer *, bool);
+void	ata_xfer_destroy(struct ata_xfer *);
 struct ata_queue *
-	ata_queue_alloc(int openings);
+	ata_queue_alloc(uint8_t openings);
 void	ata_queue_free(struct ata_queue *);
 struct ata_xfer *
 	ata_queue_hwslot_to_xfer(struct ata_queue *, int);
@@ -499,6 +507,10 @@ void	ata_delay(int, const char *, int);
 
 bool	ata_waitdrain_check(struct ata_channel *, int);
 bool	ata_waitdrain_xfer_check(struct ata_channel *, struct ata_xfer *);
+
+void	atacmd_toncq(struct ata_xfer *, uint8_t *, uint16_t *, uint16_t *,
+	    uint8_t *);
+
 #endif /* _KERNEL */
 
 #endif /* _DEV_ATA_ATAVAR_H_ */

Index: src/sys/dev/ata/satafis_subr.c
diff -u src/sys/dev/ata/satafis_subr.c:1.7.28.1 src/sys/dev/ata/satafis_subr.c:1.7.28.2
--- src/sys/dev/ata/satafis_subr.c:1.7.28.1	Sat Apr 15 12:01:23 2017
+++ src/sys/dev/ata/satafis_subr.c	Wed Apr 19 20:49:17 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: satafis_subr.c,v 1.7.28.1 2017/04/15 12:01:23 jdolecek Exp $ */
+/* $NetBSD: satafis_subr.c,v 1.7.28.2 2017/04/19 20:49:17 jdolecek Exp $ */
 
 /*-
  * Copyright (c) 2009 Jonathan A. Kollasch.
@@ -51,7 +51,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: satafis_subr.c,v 1.7.28.1 2017/04/15 12:01:23 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: satafis_subr.c,v 1.7.28.2 2017/04/19 20:49:17 jdolecek Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -102,9 +102,12 @@ satafis_rhd_construct_bio(struct ata_xfe
 {
 	struct ata_bio *ata_bio = &xfer->c_bio;
 	struct ata_drive_datas *drvp = &xfer->c_chp->ch_drive[xfer->c_drive];
-	int nblks;
+	uint16_t count, features;
+	uint8_t device;
 
-	nblks = xfer->c_bcount / drvp->lp->d_secsize;
+	count = xfer->c_bcount / drvp->lp->d_secsize;
+	features = 0;
+	device = WDSD_LBA;
 
 	memset(fis, 0, RHD_FISLEN);
 
@@ -113,27 +116,32 @@ satafis_rhd_construct_bio(struct ata_xfe
 	if (ata_bio->flags & ATA_LBA48) {
 		fis[rhd_command] = (ata_bio->flags & ATA_READ) ?
 		    WDCC_READDMA_EXT : WDCC_WRITEDMA_EXT;
+
+		atacmd_toncq(xfer, &fis[rhd_command], &count, &features,
+		    &device);
 	} else {
 		fis[rhd_command] =
 		    (ata_bio->flags & ATA_READ) ? WDCC_READDMA : WDCC_WRITEDMA;
 	}
+	fis[rhd_features0] = (features >> 0) & 0xff;
 
 	fis[rhd_lba0] = (ata_bio->blkno >> 0) & 0xff;
 	fis[rhd_lba1] = (ata_bio->blkno >> 8) & 0xff;
 	fis[rhd_lba2] = (ata_bio->blkno >> 16) & 0xff;
 	if ((ata_bio->flags & ATA_LBA48) != 0) {
-		fis[rhd_dh] = WDSD_LBA;
+		fis[rhd_dh] = device;
 		fis[rhd_lba3] = (ata_bio->blkno >> 24) & 0xff;
 		fis[rhd_lba4] = (ata_bio->blkno >> 32) & 0xff;
 		fis[rhd_lba5] = (ata_bio->blkno >> 40) & 0xff;
+		fis[rhd_features1] = (features >> 8) & 0xff;
 	} else {
 		fis[rhd_dh] = ((ata_bio->blkno >> 24) & 0x0f) |
 		    (((ata_bio->flags & ATA_LBA) != 0) ? WDSD_LBA : 0);
 	}
 
-	fis[rhd_count0] = nblks & 0xff;
+	fis[rhd_count0] = count & 0xff;
 	if ((ata_bio->flags & ATA_LBA48) != 0) {
-		fis[rhd_count1] = (nblks >> 8) & 0xff;
+		fis[rhd_count1] = (count >> 8) & 0xff;
 	}
 }
 

Index: src/sys/dev/ata/wd.c
diff -u src/sys/dev/ata/wd.c:1.428.2.6 src/sys/dev/ata/wd.c:1.428.2.7
--- src/sys/dev/ata/wd.c:1.428.2.6	Sat Apr 15 20:22:41 2017
+++ src/sys/dev/ata/wd.c	Wed Apr 19 20:49:17 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: wd.c,v 1.428.2.6 2017/04/15 20:22:41 jdolecek Exp $ */
+/*	$NetBSD: wd.c,v 1.428.2.7 2017/04/19 20:49:17 jdolecek Exp $ */
 
 /*
  * Copyright (c) 1998, 2001 Manuel Bouyer.  All rights reserved.
@@ -54,7 +54,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: wd.c,v 1.428.2.6 2017/04/15 20:22:41 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: wd.c,v 1.428.2.7 2017/04/19 20:49:17 jdolecek Exp $");
 
 #include "opt_ata.h"
 
@@ -426,32 +426,6 @@ wdattach(device_t parent, device_t self,
 		    "might not actually work\n", wd->sc_blksize);
 	}
 
-	/*
-	 * Probe WRITE DMA FUA EXT. Support is mandatory for devices
-	 * supporting LBA48, but nevertheless confirm with the feature flag.
-	 */
-	if (wd->sc_flags & WDF_LBA48
-	    && (wd->sc_params.atap_cmd_def & ATA_CMDE_WFE)) {
-		wd->sc_flags |= WDF_WFUA;
-	}
-
-	/* Probe NCQ support - READ/WRITE FPDMA QUEUED command support */
-	uint8_t max_depth = 1;
-	if (wd->sc_params.atap_sata_caps & SATA_NATIVE_CMDQ) {
-		wd->sc_flags |= WDF_NCQ;
-
-		max_depth = (wd->sc_params.atap_queuedepth & 0x0f) + 1;
-
-		/* XXX adjust adev_openings */
-	}
-
-	aprint_verbose_dev(self,
-	    "drive supports %sFUA, %sNCQ (queue depth max %d, using %u)\n",
-	    (wd->sc_flags & (WDF_WFUA|WDF_NCQ)) ? "" : "no ",
-	    (wd->sc_flags & WDF_NCQ) ? "" : "no ",
-	    max_depth, adev->adev_openings);
-
-
 out:
 	/*
 	 * Initialize and attach the disk structure.
@@ -823,6 +797,13 @@ fail:
 	     xfer->c_bio.bcount / wd->sc_dk.dk_label->d_secsize) >
 	    wd->sc_capacity28)
 		xfer->c_bio.flags |= ATA_LBA48;
+
+	/* If NCQ was negotiated, always use it */
+	if (wd->drvp->drive_flags & ATA_DRIVE_NCQ) {
+		xfer->c_bio.flags |= ATA_LBA48;
+		xfer->c_flags |= C_NCQ;
+	}
+
 	if (wd->sc_flags & WDF_LBA)
 		xfer->c_bio.flags |= ATA_LBA;
 	if (bp->b_flags & B_READ)
@@ -1917,6 +1898,7 @@ wd_setcache(struct wd_softc *wd, int bit
 {
 	struct ataparams params;
 	struct ata_xfer xfer;
+	int error;
 
 	if (wd_get_params(wd, AT_WAIT, &params) != 0)
 		return EIO;
@@ -1930,7 +1912,7 @@ wd_setcache(struct wd_softc *wd, int bit
 	    (bits & DKCACHE_SAVE) != 0)
 		return EOPNOTSUPP;
 
-	memset(&xfer, 0, sizeof(xfer));
+	ata_xfer_init(&xfer, true);
 	xfer.c_ata_c.r_command = SET_FEATURES;
 	xfer.c_ata_c.r_st_bmask = 0;
 	xfer.c_ata_c.r_st_pmask = 0;
@@ -1943,23 +1925,32 @@ wd_setcache(struct wd_softc *wd, int bit
 	if (wd->atabus->ata_exec_command(wd->drvp, &xfer) != ATACMD_COMPLETE) {
 		aprint_error_dev(wd->sc_dev,
 		    "wd_setcache command not complete\n");
-		return EIO;
+		error = EIO;
+		goto out;
 	}
+
 	if (xfer.c_ata_c.flags & (AT_ERROR | AT_TIMEOU | AT_DF)) {
 		char sbuf[sizeof(at_errbits) + 64];
 		snprintb(sbuf, sizeof(sbuf), at_errbits, xfer.c_ata_c.flags);
 		aprint_error_dev(wd->sc_dev, "wd_setcache: status=%s\n", sbuf);
-		return EIO;
+		error = EIO;
+		goto out;
 	}
-	return 0;
+
+	error = 0;
+
+out:
+	ata_xfer_destroy(&xfer);
+	return error;
 }
 
 static int
 wd_standby(struct wd_softc *wd, int flags)
 {
 	struct ata_xfer xfer;
+	int error;
 
-	memset(&xfer, 0, sizeof(xfer));
+	ata_xfer_init(&xfer, true);
 	xfer.c_ata_c.r_command = WDCC_STANDBY_IMMED;
 	xfer.c_ata_c.r_st_bmask = WDCS_DRDY;
 	xfer.c_ata_c.r_st_pmask = WDCS_DRDY;
@@ -1968,27 +1959,35 @@ wd_standby(struct wd_softc *wd, int flag
 	if (wd->atabus->ata_exec_command(wd->drvp, &xfer) != ATACMD_COMPLETE) {
 		aprint_error_dev(wd->sc_dev,
 		    "standby immediate command didn't complete\n");
-		return EIO;
+		error = EIO;
+		goto out;
 	}
 	if (xfer.c_ata_c.flags & AT_ERROR) {
 		if (xfer.c_ata_c.r_error == WDCE_ABRT) {
 			/* command not supported */
-			return ENODEV;
+			error = ENODEV;
+			goto out;
 		}
 	}
 	if (xfer.c_ata_c.flags & (AT_ERROR | AT_TIMEOU | AT_DF)) {
 		char sbuf[sizeof(at_errbits) + 64];
 		snprintb(sbuf, sizeof(sbuf), at_errbits, xfer.c_ata_c.flags);
 		aprint_error_dev(wd->sc_dev, "wd_standby: status=%s\n", sbuf);
-		return EIO;
+		error = EIO;
+		goto out;
 	}
-	return 0;
+	error = 0;
+
+out:
+	ata_xfer_destroy(&xfer);
+	return error;
 }
 
 int
 wd_flushcache(struct wd_softc *wd, int flags)
 {
 	struct ata_xfer xfer;
+	int error;
 
 	/*
 	 * WDCC_FLUSHCACHE is here since ATA-4, but some drives report
@@ -1998,7 +1997,8 @@ wd_flushcache(struct wd_softc *wd, int f
 	    ((wd->sc_params.atap_cmd_set2 & WDC_CMD2_FC) == 0 ||
 	    wd->sc_params.atap_cmd_set2 == 0xffff))
 		return ENODEV;
-	memset(&xfer, 0, sizeof(xfer));
+
+	ata_xfer_init(&xfer, true);
 	if ((wd->sc_params.atap_cmd2_en & ATA_CMD2_LBA48) != 0 &&
 	    (wd->sc_params.atap_cmd2_en & ATA_CMD2_FCE) != 0) {
 		xfer.c_ata_c.r_command = WDCC_FLUSHCACHE_EXT;
@@ -2012,12 +2012,14 @@ wd_flushcache(struct wd_softc *wd, int f
 	if (wd->atabus->ata_exec_command(wd->drvp, &xfer) != ATACMD_COMPLETE) {
 		aprint_error_dev(wd->sc_dev,
 		    "flush cache command didn't complete\n");
-		return EIO;
+		error = EIO;
+		goto out;
 	}
 	if (xfer.c_ata_c.flags & AT_ERROR) {
 		if (xfer.c_ata_c.r_error == WDCE_ABRT) {
 			/* command not supported */
-			return ENODEV;
+			error = ENODEV;
+			goto out;
 		}
 	}
 	if (xfer.c_ata_c.flags & (AT_ERROR | AT_TIMEOU | AT_DF)) {
@@ -2025,15 +2027,21 @@ wd_flushcache(struct wd_softc *wd, int f
 		snprintb(sbuf, sizeof(sbuf), at_errbits, xfer.c_ata_c.flags);
 		aprint_error_dev(wd->sc_dev, "wd_flushcache: status=%s\n",
 		    sbuf);
-		return EIO;
+		error = EIO;
+		goto out;
 	}
-	return 0;
+	error = 0;
+
+out:
+	ata_xfer_destroy(&xfer);
+	return error;
 }
 
 int
 wd_trim(struct wd_softc *wd, int part, daddr_t bno, long size)
 {
 	struct ata_xfer xfer;
+	int error;
 	unsigned char *req;
 
 	if (part != RAW_PART)
@@ -2049,7 +2057,7 @@ wd_trim(struct wd_softc *wd, int part, d
 	req[6] = size & 0xff;
 	req[7] = (size >> 8) & 0xff;
 
-	memset(&xfer, 0, sizeof(xfer));
+	ata_xfer_init(&xfer, true);
 	xfer.c_ata_c.r_command = ATA_DATA_SET_MANAGEMENT;
 	xfer.c_ata_c.r_count = 1;
 	xfer.c_ata_c.r_features = ATA_SUPPORT_DSM_TRIM;
@@ -2063,13 +2071,15 @@ wd_trim(struct wd_softc *wd, int part, d
 		aprint_error_dev(wd->sc_dev,
 		    "trim command didn't complete\n");
 		kmem_free(req, 512);
-		return EIO;
+		error = EIO;
+		goto out;
 	}
 	kmem_free(req, 512);
 	if (xfer.c_ata_c.flags & AT_ERROR) {
 		if (xfer.c_ata_c.r_error == WDCE_ABRT) {
 			/* command not supported */
-			return ENODEV;
+			error = ENODEV;
+			goto out;
 		}
 	}
 	if (xfer.c_ata_c.flags & (AT_ERROR | AT_TIMEOU | AT_DF)) {
@@ -2077,9 +2087,14 @@ wd_trim(struct wd_softc *wd, int part, d
 		snprintb(sbuf, sizeof(sbuf), at_errbits, xfer.c_ata_c.flags);
 		aprint_error_dev(wd->sc_dev, "wd_trim: status=%s\n",
 		    sbuf);
-		return EIO;
+		error = EIO;
+		goto out;
 	}
-	return 0;
+	error = 0;
+
+out:
+	ata_xfer_destroy(&xfer);
+	return error;
 }
 
 bool
@@ -2211,10 +2226,10 @@ wdioctlstrategy(struct buf *bp)
 		printf("wdioctlstrategy: "
 		    "No matching ioctl request found in queue\n");
 		error = EINVAL;
-		goto bad;
+		goto out;
 	}
 
-	memset(&xfer, 0, sizeof(xfer));
+	ata_xfer_init(&xfer, true);
 
 	/*
 	 * Abort if physio broke up the transfer
@@ -2223,7 +2238,7 @@ wdioctlstrategy(struct buf *bp)
 	if (bp->b_bcount != wi->wi_atareq.datalen) {
 		printf("physio split wd ioctl request... cannot proceed\n");
 		error = EIO;
-		goto bad;
+		goto out;
 	}
 
 	/*
@@ -2235,7 +2250,7 @@ wdioctlstrategy(struct buf *bp)
 	    (bp->b_bcount / wi_sector_size(wi)) >=
 	     (1 << NBBY)) {
 		error = EINVAL;
-		goto bad;
+		goto out;
 	}
 
 	/*
@@ -2244,7 +2259,7 @@ wdioctlstrategy(struct buf *bp)
 
 	if (wi->wi_atareq.timeout == 0) {
 		error = EINVAL;
-		goto bad;
+		goto out;
 	}
 
 	if (wi->wi_atareq.flags & ATACMD_READ)
@@ -2275,7 +2290,8 @@ wdioctlstrategy(struct buf *bp)
 	if (wi->wi_softc->atabus->ata_exec_command(wi->wi_softc->drvp, &xfer)
 	    != ATACMD_COMPLETE) {
 		wi->wi_atareq.retsts = ATACMD_ERROR;
-		goto bad;
+		error = EIO;
+		goto out;
 	}
 
 	if (xfer.c_ata_c.flags & (AT_ERROR | AT_TIMEOU | AT_DF)) {
@@ -2301,11 +2317,10 @@ wdioctlstrategy(struct buf *bp)
 		}
 	}
 
-	bp->b_error = 0;
-	biodone(bp);
-	return;
-bad:
+out:
+	ata_xfer_destroy(&xfer);
 	bp->b_error = error;
-	bp->b_resid = bp->b_bcount;
+	if (error)
+		bp->b_resid = bp->b_bcount;
 	biodone(bp);
 }

Index: src/sys/dev/ata/wdvar.h
diff -u src/sys/dev/ata/wdvar.h:1.43.4.3 src/sys/dev/ata/wdvar.h:1.43.4.4
--- src/sys/dev/ata/wdvar.h:1.43.4.3	Sat Apr 15 18:02:09 2017
+++ src/sys/dev/ata/wdvar.h	Wed Apr 19 20:49:17 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: wdvar.h,v 1.43.4.3 2017/04/15 18:02:09 jdolecek Exp $	*/
+/*	$NetBSD: wdvar.h,v 1.43.4.4 2017/04/19 20:49:17 jdolecek Exp $	*/
 
 /*
  * Copyright (c) 1998, 2001 Manuel Bouyer.
@@ -58,8 +58,6 @@ struct wd_softc {
 #define WDF_LBA		0x040 /* using LBA mode */
 #define WDF_KLABEL	0x080 /* retain label after 'full' close */
 #define WDF_LBA48	0x100 /* using 48-bit LBA mode */
-#define WDF_WFUA	0x200 /* supports FUA via WRITE DMA FUA EXT */
-#define WDF_NCQ		0x400 /* supports NCQ feature set */
 	uint64_t sc_capacity; /* full capacity of the device */
 	uint64_t sc_capacity512; /* ... in DEV_BSIZE blocks */
 	uint32_t sc_capacity28; /* capacity accessible with LBA28 commands */

Index: src/sys/dev/ic/ahcisata_core.c
diff -u src/sys/dev/ic/ahcisata_core.c:1.57.6.6 src/sys/dev/ic/ahcisata_core.c:1.57.6.7
--- src/sys/dev/ic/ahcisata_core.c:1.57.6.6	Sat Apr 15 23:58:31 2017
+++ src/sys/dev/ic/ahcisata_core.c	Wed Apr 19 20:49:17 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: ahcisata_core.c,v 1.57.6.6 2017/04/15 23:58:31 jdolecek Exp $	*/
+/*	$NetBSD: ahcisata_core.c,v 1.57.6.7 2017/04/19 20:49:17 jdolecek Exp $	*/
 
 /*
  * Copyright (c) 2006 Manuel Bouyer.
@@ -26,7 +26,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ahcisata_core.c,v 1.57.6.6 2017/04/15 23:58:31 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ahcisata_core.c,v 1.57.6.7 2017/04/19 20:49:17 jdolecek Exp $");
 
 #include <sys/types.h>
 #include <sys/malloc.h>
@@ -299,7 +299,8 @@ ahci_attach(struct ahci_softc *sc)
 	    (sc->sc_atac.atac_nchannels == 1 ? "" : "s"), 
 	    sc->sc_ncmds, (sc->sc_ncmds == 1 ? "" : "s"), buf);
 
-	sc->sc_atac.atac_cap = ATAC_CAP_DATA16 | ATAC_CAP_DMA | ATAC_CAP_UDMA;
+	sc->sc_atac.atac_cap = ATAC_CAP_DATA16 | ATAC_CAP_DMA | ATAC_CAP_UDMA
+		| ((sc->sc_ahci_cap & AHCI_CAP_NCQ) ? ATAC_CAP_NCQ : 0);
 	sc->sc_atac.atac_cap |= sc->sc_atac_capflags;
 	sc->sc_atac.atac_pio_cap = 4;
 	sc->sc_atac.atac_dma_cap = 2;
@@ -365,7 +366,7 @@ ahci_attach(struct ahci_softc *sc)
 		sc->sc_chanarray[i] = chp;
 		chp->ch_channel = i;
 		chp->ch_atac = &sc->sc_atac;
-		chp->ch_queue = ata_queue_alloc(1); // XXX sc_ncmds
+		chp->ch_queue = ata_queue_alloc(sc->sc_ncmds);
 		if (chp->ch_queue == NULL) {
 			aprint_error("%s port %d: can't allocate memory for "
 			    "command queue", AHCINAME(sc), i);
@@ -566,19 +567,19 @@ ahci_intr_port(struct ahci_softc *sc, st
 	    chp->ch_channel, is, AHCI_READ(sc, AHCI_P_CI(chp->ch_channel))),
 	    DEBUG_INTR);
 
-	slot = (AHCI_READ(sc, AHCI_P_CMD(chp->ch_channel))
-		& AHCI_P_CMD_CCS_MASK) >> AHCI_P_CMD_CCS_SHIFT;
-	if ((achp->ahcic_cmds_active & (1 << slot)) == 0)
-		return;
-
 	if (is & (AHCI_P_IX_TFES | AHCI_P_IX_HBFS | AHCI_P_IX_IFS |
 	    AHCI_P_IX_OFS | AHCI_P_IX_UFS)) {
+#if 1
+	printf("ahci_intr_port %s port %d is 0x%x CI 0x%x\n", AHCINAME(sc),
+	    chp->ch_channel, is, AHCI_READ(sc, AHCI_P_CI(chp->ch_channel)));
+#endif
+		slot = (AHCI_READ(sc, AHCI_P_CMD(chp->ch_channel))
+			& AHCI_P_CMD_CCS_MASK) >> AHCI_P_CMD_CCS_SHIFT;
+		/* XXX iterate */
+		if ((achp->ahcic_cmds_active & (1 << slot)) == 0)
+			return;
 		/* stop channel */
 		ahci_channel_stop(sc, chp, 0);
-		if (slot != 0) {
-			printf("ahci_intr_port: slot %d\n", slot);
-			panic("ahci_intr_port");
-		}
 		xfer = ata_queue_hwslot_to_xfer(chp->ch_queue, slot);
 		if (is & AHCI_P_IX_TFES) {
 			tfd = AHCI_READ(sc, AHCI_P_TFD(chp->ch_channel));
@@ -601,10 +602,17 @@ ahci_intr_port(struct ahci_softc *sc, st
 		    == 0)
 			ahci_channel_start(sc, chp, 0, 0);
 	} else {
-		xfer = ata_queue_hwslot_to_xfer(chp->ch_queue, slot);
-		if ((AHCI_READ(sc, AHCI_P_CI(chp->ch_channel)) & (1 << slot))
-		    == 0) {
-			xfer->c_intr(chp, xfer, 0);
+		uint32_t clear = AHCI_READ(sc, AHCI_P_CI(chp->ch_channel))
+			| AHCI_READ(sc, AHCI_P_SACT(chp->ch_channel));
+
+		for (slot=0; slot < sc->sc_ncmds; slot++) {
+			if ((achp->ahcic_cmds_active & (1 << slot)) == 0)
+				return;
+			if ((clear & (1 << slot)) == 0) {
+				xfer = ata_queue_hwslot_to_xfer(chp->ch_queue,
+				    slot);
+				xfer->c_intr(chp, xfer, 0);
+			}
 		}
 	}
 }
@@ -896,7 +904,7 @@ ahci_probe_drive(struct ata_channel *chp
 		AHCI_WRITE(sc, AHCI_P_IE(chp->ch_channel),
 		    AHCI_P_IX_TFES | AHCI_P_IX_HBFS | AHCI_P_IX_IFS |
 		    AHCI_P_IX_OFS | AHCI_P_IX_DPS | AHCI_P_IX_UFS |
-		    AHCI_P_IX_PSS | AHCI_P_IX_DHRS);
+		    AHCI_P_IX_PSS | AHCI_P_IX_DHRS | AHCI_P_IX_SDBS);
 		/* wait 500ms before actually starting operations */
 		tsleep(&sc, PRIBIO, "ahciprb", mstohz(500));
 		break;
@@ -1015,8 +1023,8 @@ ahci_cmd_start(struct ata_channel *chp, 
 
 	if ((ata_c->flags & AT_POLL) == 0) {
 		chp->ch_flags |= ATACH_IRQ_WAIT; /* wait for interrupt */
-		callout_reset(&chp->ch_callout, mstohz(ata_c->timeout),
-		    ahci_timeout, chp);
+		callout_reset(&xfer->c_timo_callout, mstohz(ata_c->timeout),
+		    ahci_timeout, xfer);
 		return;
 	}
 	/*
@@ -1216,6 +1224,8 @@ ahci_bio_start(struct ata_channel *chp, 
 	}
 	chp->ch_flags |= ATACH_IRQ_WAIT;
 	chp->ch_status = 0;
+	if (xfer->c_flags & C_NCQ)
+		AHCI_WRITE(sc, AHCI_P_SACT(chp->ch_channel), 1 << xfer->c_slot);
 	/* start command */
 	AHCI_WRITE(sc, AHCI_P_CI(chp->ch_channel), 1 << xfer->c_slot);
 	/* and says we started this command */
@@ -1223,8 +1233,8 @@ ahci_bio_start(struct ata_channel *chp, 
 
 	if ((xfer->c_flags & C_POLL) == 0) {
 		chp->ch_flags |= ATACH_IRQ_WAIT; /* wait for interrupt */
-		callout_reset(&chp->ch_callout, mstohz(ATA_DELAY),
-		    ahci_timeout, chp);
+		callout_reset(&xfer->c_timo_callout, mstohz(ATA_DELAY),
+		    ahci_timeout, xfer);
 		return;
 	}
 	/*
@@ -1409,18 +1419,34 @@ ahci_channel_start(struct ahci_softc *sc
 static void
 ahci_timeout(void *v)
 {
-	struct ata_channel *chp = (struct ata_channel *)v;
-	struct ata_xfer *xfer = ata_queue_hwslot_to_xfer(chp->ch_queue, 0); /* XXX slot */
-#ifdef AHCI_DEBUG
+	struct ata_xfer *xfer = v;
+	struct ata_channel *chp = xfer->c_chp;
+	struct ahci_channel *achp = (struct ahci_channel *)chp;
 	struct ahci_softc *sc = (struct ahci_softc *)chp->ch_atac;
-#endif
-	int s = splbio();
-	AHCIDEBUG_PRINT(("ahci_timeout xfer %p intr %#x ghc %08x is %08x\n", xfer, AHCI_READ(sc, AHCI_P_IS(chp->ch_channel)), AHCI_READ(sc, AHCI_GHC), AHCI_READ(sc, AHCI_IS)), DEBUG_INTR);
-	
-	if ((chp->ch_flags & ATACH_IRQ_WAIT) != 0) {
+	int s;
+
+	s = splbio();
+	AHCIDEBUG_PRINT(("ahci_timeout intr %#x ghc %08x is %08x\n",
+	    AHCI_READ(sc, AHCI_P_IS(chp->ch_channel)),
+	    AHCI_READ(sc, AHCI_GHC),
+	    AHCI_READ(sc, AHCI_IS)), DEBUG_INTR);
+
+	uint32_t active = AHCI_READ(sc, AHCI_P_CI(chp->ch_channel))
+			| AHCI_READ(sc, AHCI_P_SACT(chp->ch_channel));
+	aprint_error_dev(sc->sc_atac.atac_dev,
+	    "timeout xfer slot %d active 0x%x\n", xfer->c_slot, active);
+
+	/*
+	 * The command must be active - otherwise the timeout should have been
+	 * stopped.
+	 */
+	KASSERT((achp->ahcic_cmds_active & (1 << xfer->c_slot)) != 0);
+
+	/* Mark as timed out unless the xfer is actually finished */
+	if ((active & (1 << xfer->c_slot)) != 0)
 		xfer->c_flags |= C_TIMEOU;
-		xfer->c_intr(chp, xfer, 0);
-	}
+	xfer->c_intr(chp, xfer, 0);
+
 	splx(s);
 }
 
@@ -1637,8 +1663,8 @@ ahci_atapi_start(struct ata_channel *chp
 
 	if ((xfer->c_flags & C_POLL) == 0) {
 		chp->ch_flags |= ATACH_IRQ_WAIT; /* wait for interrupt */
-		callout_reset(&chp->ch_callout, mstohz(sc_xfer->timeout),
-		    ahci_timeout, chp);
+		callout_reset(&xfer->c_timo_callout, mstohz(sc_xfer->timeout),
+		    ahci_timeout, xfer);
 		return;
 	}
 	/*

Index: src/sys/dev/ic/mvsata.c
diff -u src/sys/dev/ic/mvsata.c:1.35.6.5 src/sys/dev/ic/mvsata.c:1.35.6.6
--- src/sys/dev/ic/mvsata.c:1.35.6.5	Sat Apr 15 17:14:11 2017
+++ src/sys/dev/ic/mvsata.c	Wed Apr 19 20:49:17 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: mvsata.c,v 1.35.6.5 2017/04/15 17:14:11 jdolecek Exp $	*/
+/*	$NetBSD: mvsata.c,v 1.35.6.6 2017/04/19 20:49:17 jdolecek Exp $	*/
 /*
  * Copyright (c) 2008 KIYOHARA Takashi
  * All rights reserved.
@@ -26,7 +26,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: mvsata.c,v 1.35.6.5 2017/04/15 17:14:11 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: mvsata.c,v 1.35.6.6 2017/04/19 20:49:17 jdolecek Exp $");
 
 #include "opt_mvsata.h"
 
@@ -1139,7 +1139,7 @@ again:
 			chp->ch_flags |= ATACH_DMA_WAIT;
 			/* start timeout machinery */
 			if ((xfer->c_flags & C_POLL) == 0)
-				callout_reset(&chp->ch_callout,
+				callout_reset(&xfer->c_timo_callout,
 				    ATA_DELAY / 1000 * hz,
 				    mvsata_edma_timeout, xfer);
 			/* wait for irq */
@@ -1226,8 +1226,8 @@ do_pio:
 
 		/* start timeout machinery */
 		if ((xfer->c_flags & C_POLL) == 0)
-			callout_reset(&chp->ch_callout,
-			    ATA_DELAY / 1000 * hz, wdctimeout, chp);
+			callout_reset(&xfer->c_timo_callout,
+			    ATA_DELAY / 1000 * hz, wdctimeout, xfer);
 	} else if (ata_bio->nblks > 1) {
 		/* The number of blocks in the last stretch may be smaller. */
 		nblks = xfer->c_bcount / drvp->lp->d_secsize;
@@ -1429,7 +1429,7 @@ mvsata_bio_done(struct ata_channel *chp,
 	    device_xname(MVSATA_DEV2(mvport)), chp->ch_channel, xfer->c_drive,
 	    (u_int)xfer->c_flags));
 
-	callout_stop(&chp->ch_callout);
+	callout_stop(&xfer->c_timo_callout);
 
 	/* EDMA restart, if enabled */
 	if (!(xfer->c_flags & C_DMA) && mvport->port_edmamode != nodma) {
@@ -1605,8 +1605,8 @@ mvsata_wdc_cmd_start(struct ata_channel 
 
 	if ((ata_c->flags & AT_POLL) == 0) {
 		chp->ch_flags |= ATACH_IRQ_WAIT; /* wait for interrupt */
-		callout_reset(&chp->ch_callout, ata_c->timeout / 1000 * hz,
-		    wdctimeout, chp);
+		callout_reset(&xfer->c_timo_callout, ata_c->timeout / 1000 * hz,
+		    wdctimeout, xfer);
 		return;
 	}
 	/*
@@ -1707,8 +1707,8 @@ again:
 		ata_c->flags |= AT_XFDONE;
 		if ((ata_c->flags & AT_POLL) == 0) {
 			chp->ch_flags |= ATACH_IRQ_WAIT; /* wait for intr */
-			callout_reset(&chp->ch_callout,
-			    mstohz(ata_c->timeout), wdctimeout, chp);
+			callout_reset(&xfer->c_timo_callout,
+			    mstohz(ata_c->timeout), wdctimeout, xfer);
 			return 1;
 		} else
 			goto again;
@@ -1946,8 +1946,8 @@ ready:
 	}
 	/* start timeout machinery */
 	if ((sc_xfer->xs_control & XS_CTL_POLL) == 0)
-		callout_reset(&chp->ch_callout, mstohz(sc_xfer->timeout),
-		    wdctimeout, chp);
+		callout_reset(&xfer->c_timo_callout, mstohz(sc_xfer->timeout),
+		    wdctimeout, xfer);
 
 	MVSATA_WDC_WRITE_1(mvport, SRB_H, WDSD_IBM);
 	switch (wdc_wait_for_unbusy(chp, ATAPI_DELAY, wait_flags)  < 0) {
@@ -2315,7 +2315,7 @@ mvsata_atapi_phase_complete(struct ata_x
 				mvsata_atapi_reset(chp, xfer);
 				return;
 			} else
-				callout_reset(&chp->ch_callout, 1,
+				callout_reset(&xfer->c_timo_callout, 1,
 				    mvsata_atapi_polldsc, xfer);
 			return;
 		}

Index: src/sys/dev/ic/siisata.c
diff -u src/sys/dev/ic/siisata.c:1.30.4.4 src/sys/dev/ic/siisata.c:1.30.4.5
--- src/sys/dev/ic/siisata.c:1.30.4.4	Sat Apr 15 17:14:11 2017
+++ src/sys/dev/ic/siisata.c	Wed Apr 19 20:49:17 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: siisata.c,v 1.30.4.4 2017/04/15 17:14:11 jdolecek Exp $ */
+/* $NetBSD: siisata.c,v 1.30.4.5 2017/04/19 20:49:17 jdolecek Exp $ */
 
 /* from ahcisata_core.c */
 
@@ -79,7 +79,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: siisata.c,v 1.30.4.4 2017/04/15 17:14:11 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: siisata.c,v 1.30.4.5 2017/04/19 20:49:17 jdolecek Exp $");
 
 #include <sys/types.h>
 #include <sys/param.h>
@@ -865,8 +865,8 @@ siisata_cmd_start(struct ata_channel *ch
 
 	if ((ata_c->flags & AT_POLL) == 0) {
 		chp->ch_flags |= ATACH_IRQ_WAIT; /* wait for interrupt */
-		callout_reset(&chp->ch_callout, mstohz(ata_c->timeout),
-		    siisata_timeout, chp);
+		callout_reset(&xfer->c_timo_callout, mstohz(ata_c->timeout),
+		    siisata_timeout, xfer);
 		goto out;
 	}
 
@@ -881,7 +881,7 @@ siisata_cmd_start(struct ata_channel *ch
 	}
 
 	if ((ata_c->flags & AT_DONE) == 0) {
-		siisata_timeout(chp);
+		siisata_timeout(xfer);
 	}
 
 	/* reenable interrupts */
@@ -928,7 +928,7 @@ siisata_cmd_complete(struct ata_channel 
 	if (xfer->c_flags & C_TIMEOU)
 		ata_c->flags |= AT_TIMEOU;
 	else
-		callout_stop(&chp->ch_callout);
+		callout_stop(&xfer->c_timo_callout);
 
 	if (chp->ch_status & WDCS_BSY) {
 		ata_c->flags |= AT_TIMEOU;
@@ -1067,8 +1067,8 @@ siisata_bio_start(struct ata_channel *ch
 
 	if ((ata_bio->flags & ATA_POLL) == 0) {
 		chp->ch_flags |= ATACH_IRQ_WAIT; /* wait for interrupt */
-		callout_reset(&chp->ch_callout, mstohz(ATA_DELAY),
-		    siisata_timeout, chp);
+		callout_reset(&xfer->c_timo_callout, mstohz(ATA_DELAY),
+		    siisata_timeout, xfer);
 		goto out;
 	}
 
@@ -1133,7 +1133,7 @@ siisata_bio_complete(struct ata_channel 
 	if (xfer->c_flags & C_TIMEOU) {
 		ata_bio->error = TIMEOUT;
 	} else {
-		callout_stop(&chp->ch_callout);
+		callout_stop(&xfer->c_timo_callout);
 		ata_bio->error = NOERROR;
 	}
 
@@ -1178,8 +1178,8 @@ siisata_bio_complete(struct ata_channel 
 void
 siisata_timeout(void *v)
 {
-	struct ata_channel *chp = (struct ata_channel *)v;
-	struct ata_xfer *xfer = ata_queue_hwslot_to_xfer(chp->ch_queue, 0); /* XXX slot */
+	struct ata_xfer *xfer = v;
+	struct ata_channel *chp = xfer->c_chp;
 	int slot = SIISATA_NON_NCQ_SLOT;
 	int s = splbio();
 	SIISATA_DEBUG_PRINT(("%s: %p\n", __func__, xfer), DEBUG_INTR);
@@ -1612,8 +1612,8 @@ siisata_atapi_start(struct ata_channel *
 
 	if ((xfer->c_flags & C_POLL) == 0) {
 		chp->ch_flags |= ATACH_IRQ_WAIT; /* wait for interrupt */
-		callout_reset(&chp->ch_callout, mstohz(sc_xfer->timeout),
-		    siisata_timeout, chp);
+		callout_reset(&xfer->c_timo_callout, mstohz(sc_xfer->timeout),
+		    siisata_timeout, xfer);
 		goto out;
 	}
 
@@ -1627,7 +1627,7 @@ siisata_atapi_start(struct ata_channel *
 		DELAY(1000);
 	}
 	if ((sc_xfer->xs_status & XS_STS_DONE) == 0) {
-		siisata_timeout(chp);
+		siisata_timeout(xfer);
 	}
 	/* reenable interrupts */
 	siisata_enable_port_interrupt(chp);
@@ -1654,7 +1654,7 @@ siisata_atapi_complete(struct ata_channe
 	if (xfer->c_flags & C_TIMEOU) {
 		sc_xfer->error = XS_TIMEOUT;
 	} else {
-		callout_stop(&chp->ch_callout);
+		callout_stop(&xfer->c_timo_callout);
 		sc_xfer->error = XS_NOERROR;
 	}
 

Index: src/sys/dev/ic/wdc.c
diff -u src/sys/dev/ic/wdc.c:1.283.2.3 src/sys/dev/ic/wdc.c:1.283.2.4
--- src/sys/dev/ic/wdc.c:1.283.2.3	Sat Apr 15 17:14:11 2017
+++ src/sys/dev/ic/wdc.c	Wed Apr 19 20:49:17 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: wdc.c,v 1.283.2.3 2017/04/15 17:14:11 jdolecek Exp $ */
+/*	$NetBSD: wdc.c,v 1.283.2.4 2017/04/19 20:49:17 jdolecek Exp $ */
 
 /*
  * Copyright (c) 1998, 2001, 2003 Manuel Bouyer.  All rights reserved.
@@ -58,7 +58,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: wdc.c,v 1.283.2.3 2017/04/15 17:14:11 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: wdc.c,v 1.283.2.4 2017/04/19 20:49:17 jdolecek Exp $");
 
 #include "opt_ata.h"
 #include "opt_wdc.h"
@@ -1323,7 +1323,8 @@ wdc_dmawait(struct ata_channel *chp, str
 void
 wdctimeout(void *arg)
 {
-	struct ata_channel *chp = (struct ata_channel *)arg;
+	struct ata_xfer *xfer = arg;
+	struct ata_channel *chp = xfer->c_chp;
 #if NATA_DMA || NATA_PIOBM
 	struct wdc_softc *wdc = CHAN_TO_WDC(chp);
 #endif
@@ -1332,7 +1333,6 @@ wdctimeout(void *arg)
 	ATADEBUG_PRINT(("wdctimeout\n"), DEBUG_FUNCS);
 
 	s = splbio();
-	struct ata_xfer *xfer = ata_queue_hwslot_to_xfer(chp->ch_queue, 0);
 	KASSERT(xfer != NULL);
 	if ((chp->ch_flags & ATACH_IRQ_WAIT) != 0) {
 		__wdcerror(chp, "lost interrupt");
@@ -1355,7 +1355,7 @@ wdctimeout(void *arg)
 		 * in case it will miss another irq while in this transfer
 		 * We arbitray chose it to be 1s
 		 */
-		callout_reset(&chp->ch_callout, hz, wdctimeout, chp);
+		callout_reset(&xfer->c_timo_callout, hz, wdctimeout, xfer);
 		xfer->c_flags |= C_TIMEOU;
 		chp->ch_flags &= ~ATACH_IRQ_WAIT;
 		KASSERT(xfer->c_intr != NULL);
@@ -1464,8 +1464,8 @@ __wdccommand_start(struct ata_channel *c
 
 	if ((ata_c->flags & AT_POLL) == 0) {
 		chp->ch_flags |= ATACH_IRQ_WAIT; /* wait for interrupt */
-		callout_reset(&chp->ch_callout, ata_c->timeout / 1000 * hz,
-		    wdctimeout, chp);
+		callout_reset(&xfer->c_timo_callout, ata_c->timeout / 1000 * hz,
+		    wdctimeout, xfer);
 		return;
 	}
 	/*
@@ -1574,8 +1574,8 @@ __wdccommand_intr(struct ata_channel *ch
 		ata_c->flags |= AT_XFDONE;
 		if ((ata_c->flags & AT_POLL) == 0) {
 			chp->ch_flags |= ATACH_IRQ_WAIT; /* wait for interrupt */
-			callout_reset(&chp->ch_callout,
-			    mstohz(ata_c->timeout), wdctimeout, chp);
+			callout_reset(&xfer->c_timo_callout,
+			    mstohz(ata_c->timeout), wdctimeout, xfer);
 			return 1;
 		} else {
 			goto again;

Index: src/sys/dev/scsipi/atapi_wdc.c
diff -u src/sys/dev/scsipi/atapi_wdc.c:1.123.4.3 src/sys/dev/scsipi/atapi_wdc.c:1.123.4.4
--- src/sys/dev/scsipi/atapi_wdc.c:1.123.4.3	Sat Apr 15 17:14:11 2017
+++ src/sys/dev/scsipi/atapi_wdc.c	Wed Apr 19 20:49:17 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: atapi_wdc.c,v 1.123.4.3 2017/04/15 17:14:11 jdolecek Exp $	*/
+/*	$NetBSD: atapi_wdc.c,v 1.123.4.4 2017/04/19 20:49:17 jdolecek Exp $	*/
 
 /*
  * Copyright (c) 1998, 2001 Manuel Bouyer.
@@ -25,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: atapi_wdc.c,v 1.123.4.3 2017/04/15 17:14:11 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: atapi_wdc.c,v 1.123.4.4 2017/04/19 20:49:17 jdolecek Exp $");
 
 #ifndef ATADEBUG
 #define ATADEBUG
@@ -197,8 +197,10 @@ wdc_atapi_get_params(struct scsipi_chann
 	struct wdc_regs *wdr = &wdc->regs[chan->chan_channel];
 	struct ata_channel *chp = atac->atac_channels[chan->chan_channel];
 	struct ata_xfer xfer;
+	int rv;
+
+	ata_xfer_init(&xfer, true);
 
-	memset(&xfer, 0, sizeof(xfer));
 	xfer.c_ata_c.r_command = ATAPI_SOFT_RESET;
 	xfer.c_ata_c.r_st_bmask = 0;
 	xfer.c_ata_c.r_st_pmask = 0;
@@ -215,7 +217,8 @@ wdc_atapi_get_params(struct scsipi_chann
 		    "failed for drive %s:%d:%d: error 0x%x\n",
 		    device_xname(atac->atac_dev), chp->ch_channel, drive,
 		    xfer.c_ata_c.r_error), DEBUG_PROBE);
-		return -1;
+		rv = -1;
+		goto out;
 	}
 	chp->ch_drive[drive].state = 0;
 
@@ -228,9 +231,14 @@ wdc_atapi_get_params(struct scsipi_chann
 		    "failed for drive %s:%d:%d: error 0x%x\n",
 		    device_xname(atac->atac_dev), chp->ch_channel, drive,
 		    xfer.c_ata_c.r_error), DEBUG_PROBE);
-		return -1;
+		rv = -1;
+		goto out;
 	}
-	return 0;
+	rv = 0;
+
+out:
+	ata_xfer_destroy(&xfer);
+	return rv;
 }
 
 static void
@@ -564,8 +572,8 @@ ready:
 	}
 	/* start timeout machinery */
 	if ((sc_xfer->xs_control & XS_CTL_POLL) == 0)
-		callout_reset(&chp->ch_callout, mstohz(sc_xfer->timeout),
-		    wdctimeout, chp);
+		callout_reset(&xfer->c_timo_callout, mstohz(sc_xfer->timeout),
+		    wdctimeout, xfer);
 
 	if (wdc->select)
 		wdc->select(chp, xfer->c_drive);
@@ -1020,7 +1028,7 @@ wdc_atapi_phase_complete(struct ata_xfer
 				wdc_atapi_reset(chp, xfer);
 				return;
 			} else
-				callout_reset(&chp->ch_callout, 1,
+				callout_reset(&xfer->c_timo_callout, 1,
 				    wdc_atapi_polldsc, xfer);
 			return;
 		}

Reply via email to