bk://gkernel.bkbits.net/libata-2.6
jgarzik@pobox.com|ChangeSet|20050218205104|53749 jgarzik

# This is a BitKeeper generated diff -Nru style patch.
#
# ChangeSet
#   2005/02/22 14:29:10-08:00 akpm@bix.(none) 
#   Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-libata
# 
# drivers/scsi/libata-core.c
#   2005/02/22 14:29:05-08:00 akpm@bix.(none) +0 -0
#   Auto merged
# 
# ChangeSet
#   2005/02/18 15:49:44-05:00 linville@tuxdriver.com 
#   [PATCH] libata: fix command queue leak when xlat_func fails
#   
#   ata_scsi_translate allocates from the libata command queue by calling
#   ata_scsi_qc_new.  If xlat_func returns non-zero, control jumps to
#   err_out which fails to free the allocated command.  Fix is to add a
#   new API to free unused commands.
#   
#   Signed-off-by: John W. Linville <linville@tuxdriver.com>
#   Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
# 
# drivers/scsi/libata.h
#   2005/02/18 15:49:33-05:00 linville@tuxdriver.com +1 -0
#   [PATCH] libata: fix command queue leak when xlat_func fails
#   
#   ata_scsi_translate allocates from the libata command queue by calling
#   ata_scsi_qc_new.  If xlat_func returns non-zero, control jumps to
#   err_out which fails to free the allocated command.  Fix is to add a
#   new API to free unused commands.
#   
#   Signed-off-by: John W. Linville <linville@tuxdriver.com>
#   Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
# 
# drivers/scsi/libata-scsi.c
#   2005/02/18 15:49:33-05:00 linville@tuxdriver.com +1 -0
#   [PATCH] libata: fix command queue leak when xlat_func fails
#   
#   ata_scsi_translate allocates from the libata command queue by calling
#   ata_scsi_qc_new.  If xlat_func returns non-zero, control jumps to
#   err_out which fails to free the allocated command.  Fix is to add a
#   new API to free unused commands.
#   
#   Signed-off-by: John W. Linville <linville@tuxdriver.com>
#   Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
# 
# drivers/scsi/libata-core.c
#   2005/02/18 15:49:33-05:00 linville@tuxdriver.com +18 -0
#   [PATCH] libata: fix command queue leak when xlat_func fails
#   
#   ata_scsi_translate allocates from the libata command queue by calling
#   ata_scsi_qc_new.  If xlat_func returns non-zero, control jumps to
#   err_out which fails to free the allocated command.  Fix is to add a
#   new API to free unused commands.
#   
#   Signed-off-by: John W. Linville <linville@tuxdriver.com>
#   Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
# 
# ChangeSet
#   2005/02/17 19:43:54-05:00 jgarzik@pobox.com 
#   Merge pobox.com:/garz/repo/libata-dev/bmdma-cb
#   into pobox.com:/garz/repo/libata-2.6
# 
# drivers/scsi/sata_vsc.c
#   2005/02/17 19:43:51-05:00 jgarzik@pobox.com +0 -0
#   Auto merged
# 
# drivers/scsi/sata_via.c
#   2005/02/17 19:43:51-05:00 jgarzik@pobox.com +0 -0
#   Auto merged
# 
# drivers/scsi/sata_uli.c
#   2005/02/17 19:43:51-05:00 jgarzik@pobox.com +0 -0
#   Auto merged
# 
# drivers/scsi/sata_svw.c
#   2005/02/17 19:43:51-05:00 jgarzik@pobox.com +0 -0
#   Auto merged
# 
# drivers/scsi/sata_sis.c
#   2005/02/17 19:43:51-05:00 jgarzik@pobox.com +0 -0
#   Auto merged
# 
# drivers/scsi/sata_sil.c
#   2005/02/17 19:43:51-05:00 jgarzik@pobox.com +0 -0
#   Auto merged
# 
# drivers/scsi/sata_nv.c
#   2005/02/17 19:43:51-05:00 jgarzik@pobox.com +0 -0
#   Auto merged
# 
# drivers/scsi/libata-core.c
#   2005/02/17 19:43:51-05:00 jgarzik@pobox.com +0 -0
#   Auto merged
# 
# ChangeSet
#   2005/02/17 19:29:23-05:00 Mat.Loikkanen@synopsys.com 
#   [libata] add ->bmdma_{stop,status} hooks
#   
#   The timeout/error handling path was assuming that the hardware in
#   question was PCI IDE BMDMA-like, which is incorrect in a few cases.
#   
#   Turn direct function calls into two new hooks.
# 
# include/linux/libata.h
#   2005/02/17 19:29:16-05:00 Mat.Loikkanen@synopsys.com +5 -40
#   [libata] add ->bmdma_{stop,status} hooks
#   
#   The timeout/error handling path was assuming that the hardware in
#   question was PCI IDE BMDMA-like, which is incorrect in a few cases.
#   
#   Turn direct function calls into two new hooks.
# 
# drivers/scsi/sata_vsc.c
#   2005/02/17 19:29:16-05:00 Mat.Loikkanen@synopsys.com +2 -0
#   [libata] add ->bmdma_{stop,status} hooks
#   
#   The timeout/error handling path was assuming that the hardware in
#   question was PCI IDE BMDMA-like, which is incorrect in a few cases.
#   
#   Turn direct function calls into two new hooks.
# 
# drivers/scsi/sata_via.c
#   2005/02/17 19:29:16-05:00 Mat.Loikkanen@synopsys.com +3 -0
#   [libata] add ->bmdma_{stop,status} hooks
#   
#   The timeout/error handling path was assuming that the hardware in
#   question was PCI IDE BMDMA-like, which is incorrect in a few cases.
#   
#   Turn direct function calls into two new hooks.
# 
# drivers/scsi/sata_uli.c
#   2005/02/17 19:29:16-05:00 Mat.Loikkanen@synopsys.com +2 -0
#   [libata] add ->bmdma_{stop,status} hooks
#   
#   The timeout/error handling path was assuming that the hardware in
#   question was PCI IDE BMDMA-like, which is incorrect in a few cases.
#   
#   Turn direct function calls into two new hooks.
# 
# drivers/scsi/sata_svw.c
#   2005/02/17 19:29:16-05:00 Mat.Loikkanen@synopsys.com +2 -0
#   [libata] add ->bmdma_{stop,status} hooks
#   
#   The timeout/error handling path was assuming that the hardware in
#   question was PCI IDE BMDMA-like, which is incorrect in a few cases.
#   
#   Turn direct function calls into two new hooks.
# 
# drivers/scsi/sata_sis.c
#   2005/02/17 19:29:16-05:00 Mat.Loikkanen@synopsys.com +2 -0
#   [libata] add ->bmdma_{stop,status} hooks
#   
#   The timeout/error handling path was assuming that the hardware in
#   question was PCI IDE BMDMA-like, which is incorrect in a few cases.
#   
#   Turn direct function calls into two new hooks.
# 
# drivers/scsi/sata_sil.c
#   2005/02/17 19:29:16-05:00 Mat.Loikkanen@synopsys.com +2 -0
#   [libata] add ->bmdma_{stop,status} hooks
#   
#   The timeout/error handling path was assuming that the hardware in
#   question was PCI IDE BMDMA-like, which is incorrect in a few cases.
#   
#   Turn direct function calls into two new hooks.
# 
# drivers/scsi/sata_nv.c
#   2005/02/17 19:29:16-05:00 Mat.Loikkanen@synopsys.com +2 -0
#   [libata] add ->bmdma_{stop,status} hooks
#   
#   The timeout/error handling path was assuming that the hardware in
#   question was PCI IDE BMDMA-like, which is incorrect in a few cases.
#   
#   Turn direct function calls into two new hooks.
# 
# drivers/scsi/libata-core.c
#   2005/02/17 19:29:16-05:00 Mat.Loikkanen@synopsys.com +45 -7
#   [libata] add ->bmdma_{stop,status} hooks
#   
#   The timeout/error handling path was assuming that the hardware in
#   question was PCI IDE BMDMA-like, which is incorrect in a few cases.
#   
#   Turn direct function calls into two new hooks.
# 
# drivers/scsi/ata_piix.c
#   2005/02/17 19:29:16-05:00 Mat.Loikkanen@synopsys.com +4 -0
#   [libata] add ->bmdma_{stop,status} hooks
#   
#   The timeout/error handling path was assuming that the hardware in
#   question was PCI IDE BMDMA-like, which is incorrect in a few cases.
#   
#   Turn direct function calls into two new hooks.
# 
# ChangeSet
#   2005/02/17 01:15:16-05:00 lsml@rtr.ca 
#   [PATCH] sata_qstor: new basic driver for Pacific Digital
#   
#   This is a new basic libata SATA driver
#   for the Pacific Digital QStor SATA/RAID card.
#   
#   It features ordinary per-drive SATA with single-request DMA
#   for R/W commands, and PIO for everything else.
#   
#   It currently does not implement any of the hardware RAID support,
#   nor hot-plug, nor the tagged/native command-queuing features.
#   
#   On the other hand, it is small and simple as a result.
#   
#   Signed-off-by: Mark Lord <mlord@pobox.com>
#   Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
# 
# drivers/scsi/sata_qstor.c
#   2005/02/16 20:25:01-05:00 lsml@rtr.ca +700 -0
#   sata_qstor: new basic driver for Pacific Digital
# 
# drivers/scsi/sata_qstor.c
#   2005/02/16 20:25:01-05:00 lsml@rtr.ca +0 -0
#   BitKeeper file /garz/repo/libata-dev/qstor/drivers/scsi/sata_qstor.c
# 
# drivers/scsi/Makefile
#   2005/02/16 20:29:23-05:00 lsml@rtr.ca +1 -0
#   sata_qstor: new basic driver for Pacific Digital
# 
# drivers/scsi/Kconfig
#   2005/02/16 20:29:23-05:00 lsml@rtr.ca +8 -0
#   sata_qstor: new basic driver for Pacific Digital
# 
# ChangeSet
#   2005/02/14 01:28:57-08:00 akpm@bix.(none) 
#   Merge bk://gkernel.bkbits.net/libata-2.6
#   into bix.(none):/usr/src/bk-libata
# 
# drivers/scsi/ahci.c
#   2005/02/14 01:28:51-08:00 akpm@bix.(none) +0 -0
#   Auto merged
# 
# ChangeSet
#   2005/02/13 19:58:07-05:00 jgarzik@pobox.com 
#   [libata] do not call pci_disable_device() for certain errors
#   
#   If PCI request regions fails, then someone else is using the
#   hardware we wish to use.  For that one case, calling pci_disable_device()
#   is rather rude.
# 
# drivers/scsi/sata_vsc.c
#   2005/02/13 19:58:01-05:00 jgarzik@pobox.com +6 -2
#   [libata] do not call pci_disable_device() for certain errors
#   
#   If PCI request regions fails, then someone else is using the
#   hardware we wish to use.  For that one case, calling pci_disable_device()
#   is rather rude.
# 
# drivers/scsi/sata_via.c
#   2005/02/13 19:58:01-05:00 jgarzik@pobox.com +6 -2
#   [libata] do not call pci_disable_device() for certain errors
#   
#   If PCI request regions fails, then someone else is using the
#   hardware we wish to use.  For that one case, calling pci_disable_device()
#   is rather rude.
# 
# drivers/scsi/sata_uli.c
#   2005/02/13 19:58:01-05:00 jgarzik@pobox.com +6 -2
#   [libata] do not call pci_disable_device() for certain errors
#   
#   If PCI request regions fails, then someone else is using the
#   hardware we wish to use.  For that one case, calling pci_disable_device()
#   is rather rude.
# 
# drivers/scsi/sata_sx4.c
#   2005/02/13 19:58:01-05:00 jgarzik@pobox.com +6 -2
#   [libata] do not call pci_disable_device() for certain errors
#   
#   If PCI request regions fails, then someone else is using the
#   hardware we wish to use.  For that one case, calling pci_disable_device()
#   is rather rude.
# 
# drivers/scsi/sata_svw.c
#   2005/02/13 19:58:01-05:00 jgarzik@pobox.com +6 -2
#   [libata] do not call pci_disable_device() for certain errors
#   
#   If PCI request regions fails, then someone else is using the
#   hardware we wish to use.  For that one case, calling pci_disable_device()
#   is rather rude.
# 
# drivers/scsi/sata_sis.c
#   2005/02/13 19:58:01-05:00 jgarzik@pobox.com +6 -2
#   [libata] do not call pci_disable_device() for certain errors
#   
#   If PCI request regions fails, then someone else is using the
#   hardware we wish to use.  For that one case, calling pci_disable_device()
#   is rather rude.
# 
# drivers/scsi/sata_sil.c
#   2005/02/13 19:58:01-05:00 jgarzik@pobox.com +6 -2
#   [libata] do not call pci_disable_device() for certain errors
#   
#   If PCI request regions fails, then someone else is using the
#   hardware we wish to use.  For that one case, calling pci_disable_device()
#   is rather rude.
# 
# drivers/scsi/sata_promise.c
#   2005/02/13 19:58:01-05:00 jgarzik@pobox.com +6 -2
#   [libata] do not call pci_disable_device() for certain errors
#   
#   If PCI request regions fails, then someone else is using the
#   hardware we wish to use.  For that one case, calling pci_disable_device()
#   is rather rude.
# 
# drivers/scsi/sata_nv.c
#   2005/02/13 19:58:01-05:00 jgarzik@pobox.com +6 -2
#   [libata] do not call pci_disable_device() for certain errors
#   
#   If PCI request regions fails, then someone else is using the
#   hardware we wish to use.  For that one case, calling pci_disable_device()
#   is rather rude.
# 
# drivers/scsi/libata-core.c
#   2005/02/13 19:58:01-05:00 jgarzik@pobox.com +12 -4
#   [libata] do not call pci_disable_device() for certain errors
#   
#   If PCI request regions fails, then someone else is using the
#   hardware we wish to use.  For that one case, calling pci_disable_device()
#   is rather rude.
# 
# drivers/scsi/ahci.c
#   2005/02/13 19:58:01-05:00 jgarzik@pobox.com +6 -2
#   [libata] do not call pci_disable_device() for certain errors
#   
#   If PCI request regions fails, then someone else is using the
#   hardware we wish to use.  For that one case, calling pci_disable_device()
#   is rather rude.
# 
# ChangeSet
#   2005/02/07 14:47:54-08:00 akpm@bix.(none) 
#   Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-libata
# 
# drivers/scsi/ahci.c
#   2005/02/07 14:47:48-08:00 akpm@bix.(none) +0 -0
#   Auto merged
# 
# ChangeSet
#   2005/02/03 12:14:36-08:00 akpm@bix.(none) 
#   Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-libata
# 
# drivers/pci/quirks.c
#   2005/02/03 12:14:32-08:00 akpm@bix.(none) +0 -0
#   Auto merged
# 
# ChangeSet
#   2005/01/27 20:11:35-08:00 akpm@bix.(none) 
#   Merge bk://gkernel.bkbits.net/libata-2.6
#   into bix.(none):/usr/src/bk-libata
# 
# drivers/scsi/ahci.c
#   2005/01/27 20:11:29-08:00 akpm@bix.(none) +0 -0
#   Auto merged
# 
# ChangeSet
#   2005/01/10 14:23:34-08:00 akpm@bix.(none) 
#   Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-libata
# 
# drivers/pci/quirks.c
#   2005/01/10 14:23:30-08:00 akpm@bix.(none) +0 -0
#   Auto merged
# 
# ChangeSet
#   2005/01/08 20:52:09-08:00 akpm@bix.(none) 
#   Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-libata
# 
# drivers/scsi/ahci.c
#   2005/01/08 20:52:04-08:00 akpm@bix.(none) +0 -0
#   Auto merged
# 
# drivers/pci/quirks.c
#   2005/01/08 20:52:04-08:00 akpm@bix.(none) +0 -0
#   Auto merged
# 
# ChangeSet
#   2005/01/08 18:28:34-08:00 akpm@bix.(none) 
#   Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-libata
# 
# drivers/scsi/ahci.c
#   2005/01/08 18:28:24-08:00 akpm@bix.(none) +0 -0
#   Auto merged
# 
# ChangeSet
#   2005/01/07 14:18:26-08:00 akpm@bix.(none) 
#   Merge bk://gkernel.bkbits.net/libata-2.6
#   into bix.(none):/usr/src/bk-libata
# 
# drivers/pci/quirks.c
#   2005/01/07 14:18:21-08:00 akpm@bix.(none) +0 -0
#   Auto merged
# 
# ChangeSet
#   2004/11/16 01:35:17-05:00 jgarzik@pobox.com 
#   [libata sata_via] add support for VT6421 SATA
# 
# drivers/scsi/sata_via.c
#   2004/11/16 01:35:11-05:00 jgarzik@pobox.com +84 -10
#   [libata sata_via] add support for VT6421 SATA
# 
# ChangeSet
#   2004/11/16 01:31:58-05:00 jgarzik@pobox.com 
#   [libata sata_via] minor cleanups
#   
#   Preparation for addition of VT6421 support.  Mostly moving bits
#   of code into discrete functions.
# 
# drivers/scsi/sata_via.c
#   2004/11/16 01:31:52-05:00 jgarzik@pobox.com +67 -47
#   [libata sata_via] minor cleanups
#   
#   Preparation for addition of VT6421 support.  Mostly moving bits
#   of code into discrete functions.
# 
diff -Nru a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
--- a/drivers/scsi/Kconfig	2005-02-22 18:15:48 -08:00
+++ b/drivers/scsi/Kconfig	2005-02-22 18:15:48 -08:00
@@ -457,6 +457,14 @@
 
 	  If unsure, say N.
 
+config SCSI_SATA_QSTOR
+	tristate "Pacific Digital SATA QStor support"
+	depends on SCSI_SATA && PCI
+	help
+	  This option enables support for Pacific Digital Serial ATA QStor.
+
+	  If unsure, say N.
+
 config SCSI_SATA_SX4
 	tristate "Promise SATA SX4 support"
 	depends on SCSI_SATA && PCI && EXPERIMENTAL
diff -Nru a/drivers/scsi/Makefile b/drivers/scsi/Makefile
--- a/drivers/scsi/Makefile	2005-02-22 18:15:48 -08:00
+++ b/drivers/scsi/Makefile	2005-02-22 18:15:48 -08:00
@@ -125,6 +125,7 @@
 obj-$(CONFIG_SCSI_SATA_SVW)	+= libata.o sata_svw.o
 obj-$(CONFIG_SCSI_ATA_PIIX)	+= libata.o ata_piix.o
 obj-$(CONFIG_SCSI_SATA_PROMISE)	+= libata.o sata_promise.o
+obj-$(CONFIG_SCSI_SATA_QSTOR)	+= libata.o sata_qstor.o
 obj-$(CONFIG_SCSI_SATA_SIL)	+= libata.o sata_sil.o
 obj-$(CONFIG_SCSI_SATA_VIA)	+= libata.o sata_via.o
 obj-$(CONFIG_SCSI_SATA_VITESSE)	+= libata.o sata_vsc.o
diff -Nru a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c
--- a/drivers/scsi/ahci.c	2005-02-22 18:15:48 -08:00
+++ b/drivers/scsi/ahci.c	2005-02-22 18:15:48 -08:00
@@ -940,6 +940,7 @@
 	unsigned long base;
 	void *mmio_base;
 	unsigned int board_idx = (unsigned int) ent->driver_data;
+	int pci_dev_busy = 0;
 	int rc;
 
 	VPRINTK("ENTER\n");
@@ -952,8 +953,10 @@
 		return rc;
 
 	rc = pci_request_regions(pdev, DRV_NAME);
-	if (rc)
+	if (rc) {
+		pci_dev_busy = 1;
 		goto err_out;
+	}
 
 	pci_enable_intx(pdev);
 
@@ -1015,7 +1018,8 @@
 err_out_regions:
 	pci_release_regions(pdev);
 err_out:
-	pci_disable_device(pdev);
+	if (!pci_dev_busy)
+		pci_disable_device(pdev);
 	return rc;
 }
 
diff -Nru a/drivers/scsi/ata_piix.c b/drivers/scsi/ata_piix.c
--- a/drivers/scsi/ata_piix.c	2005-02-22 18:15:48 -08:00
+++ b/drivers/scsi/ata_piix.c	2005-02-22 18:15:48 -08:00
@@ -138,6 +138,8 @@
 
 	.bmdma_setup		= ata_bmdma_setup,
 	.bmdma_start		= ata_bmdma_start,
+	.bmdma_stop		= ata_bmdma_stop,
+	.bmdma_status		= ata_bmdma_status,
 	.qc_prep		= ata_qc_prep,
 	.qc_issue		= ata_qc_issue_prot,
 
@@ -163,6 +165,8 @@
 
 	.bmdma_setup		= ata_bmdma_setup,
 	.bmdma_start		= ata_bmdma_start,
+	.bmdma_stop		= ata_bmdma_stop,
+	.bmdma_status		= ata_bmdma_status,
 	.qc_prep		= ata_qc_prep,
 	.qc_issue		= ata_qc_issue_prot,
 
diff -Nru a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
--- a/drivers/scsi/libata-core.c	2005-02-22 18:15:48 -08:00
+++ b/drivers/scsi/libata-core.c	2005-02-22 18:15:48 -08:00
@@ -2601,10 +2601,10 @@
 
 	case ATA_PROT_DMA:
 	case ATA_PROT_ATAPI_DMA:
-		host_stat = ata_bmdma_status(ap);
+		host_stat = ap->ops->bmdma_status(ap);
 
 		/* before we do anything else, clear DMA-Start bit */
-		ata_bmdma_stop(ap);
+		ap->ops->bmdma_stop(ap);
 
 		/* fall through */
 
@@ -2613,7 +2613,7 @@
 		drv_stat = ata_chk_status(ap);
 
 		/* ack bmdma irq events */
-		ata_bmdma_ack_irq(ap);
+		ap->ops->irq_clear(ap);
 
 		printk(KERN_ERR "ata%u: command 0x%x timeout, stat 0x%x host_stat 0x%x\n",
 		       ap->id, qc->tf.command, drv_stat, host_stat);
@@ -2752,6 +2752,24 @@
 }
 
 /**
+ *	ata_qc_free - free unused ata_queued_cmd
+ *	@qc: Command to complete
+ *
+ *	Designed to free unused ata_queued_cmd object
+ *	in case something prevents using it.
+ *
+ *	LOCKING:
+ *
+ */
+void ata_qc_free(struct ata_queued_cmd *qc)
+{
+	assert(qc != NULL);	/* ata_qc_from_tag _might_ return NULL */
+	assert(qc->waiting == NULL);	/* nothing should be waiting */
+
+	__ata_qc_complete(qc);
+}
+
+/**
  *	ata_qc_complete - Complete an active ATA command
  *	@qc: Command to complete
  *	@drv_stat: ATA status register contents
@@ -3042,7 +3060,43 @@
 
 void ata_bmdma_irq_clear(struct ata_port *ap)
 {
-	ata_bmdma_ack_irq(ap);
+    if (ap->flags & ATA_FLAG_MMIO) {
+        void __iomem *mmio = ((void __iomem *) ap->ioaddr.bmdma_addr) + ATA_DMA_STATUS;
+        writeb(readb(mmio), mmio);
+    } else {
+        unsigned long addr = ap->ioaddr.bmdma_addr + ATA_DMA_STATUS;
+        outb(inb(addr), addr);
+    }
+
+}
+
+u8 ata_bmdma_status(struct ata_port *ap)
+{
+	u8 host_stat;
+	if (ap->flags & ATA_FLAG_MMIO) {
+		void __iomem *mmio = (void __iomem *) ap->ioaddr.bmdma_addr;
+		host_stat = readb(mmio + ATA_DMA_STATUS);
+	} else
+	host_stat = inb(ap->ioaddr.bmdma_addr + ATA_DMA_STATUS);
+	return host_stat;
+}
+
+void ata_bmdma_stop(struct ata_port *ap)
+{
+	if (ap->flags & ATA_FLAG_MMIO) {
+		void __iomem *mmio = (void __iomem *) ap->ioaddr.bmdma_addr;
+
+		/* clear start/stop bit */
+		writeb(readb(mmio + ATA_DMA_CMD) & ~ATA_DMA_START,
+			mmio + ATA_DMA_CMD);
+	} else {
+		/* clear start/stop bit */
+		outb(inb(ap->ioaddr.bmdma_addr + ATA_DMA_CMD) & ~ATA_DMA_START,
+			ap->ioaddr.bmdma_addr + ATA_DMA_CMD);
+	}
+
+	/* one-PIO-cycle guaranteed wait, per spec, for HDMA1:0 transition */
+	ata_altstatus(ap);        /* dummy read */
 }
 
 /**
@@ -3072,7 +3126,7 @@
 	case ATA_PROT_ATAPI_DMA:
 	case ATA_PROT_ATAPI:
 		/* check status of DMA engine */
-		host_stat = ata_bmdma_status(ap);
+		host_stat = ap->ops->bmdma_status(ap);
 		VPRINTK("ata%u: host_stat 0x%X\n", ap->id, host_stat);
 
 		/* if it's not our irq... */
@@ -3080,7 +3134,7 @@
 			goto idle_irq;
 
 		/* before we do anything else, clear DMA-Start bit */
-		ata_bmdma_stop(ap);
+		ap->ops->bmdma_stop(ap);
 
 		/* fall through */
 
@@ -3099,7 +3153,7 @@
 			ap->id, qc->tf.protocol, status);
 
 		/* ack bmdma irq events */
-		ata_bmdma_ack_irq(ap);
+		ap->ops->irq_clear(ap);
 
 		/* complete taskfile transaction */
 		ata_qc_complete(qc, status);
@@ -3656,6 +3710,7 @@
 	struct ata_port_info *port[2];
 	u8 tmp8, mask;
 	unsigned int legacy_mode = 0;
+	int disable_dev_on_err = 1;
 	int rc;
 
 	DPRINTK("ENTER\n");
@@ -3686,8 +3741,10 @@
 		return rc;
 
 	rc = pci_request_regions(pdev, DRV_NAME);
-	if (rc)
+	if (rc) {
+		disable_dev_on_err = 0;
 		goto err_out;
+	}
 
 	if (legacy_mode) {
 		if (!request_region(0x1f0, 8, "libata")) {
@@ -3697,8 +3754,10 @@
 			conflict = ____request_resource(&ioport_resource, &res);
 			if (!strcmp(conflict->name, "libata"))
 				legacy_mode |= (1 << 0);
-			else
+			else {
+				disable_dev_on_err = 0;
 				printk(KERN_WARNING "ata: 0x1f0 IDE port busy\n");
+			}
 		} else
 			legacy_mode |= (1 << 0);
 
@@ -3709,8 +3768,10 @@
 			conflict = ____request_resource(&ioport_resource, &res);
 			if (!strcmp(conflict->name, "libata"))
 				legacy_mode |= (1 << 1);
-			else
+			else {
+				disable_dev_on_err = 0;
 				printk(KERN_WARNING "ata: 0x170 IDE port busy\n");
+			}
 		} else
 			legacy_mode |= (1 << 1);
 	}
@@ -3760,7 +3821,8 @@
 		release_region(0x170, 8);
 	pci_release_regions(pdev);
 err_out:
-	pci_disable_device(pdev);
+	if (disable_dev_on_err)
+		pci_disable_device(pdev);
 	return rc;
 }
 
@@ -3918,6 +3980,8 @@
 EXPORT_SYMBOL_GPL(ata_bmdma_setup);
 EXPORT_SYMBOL_GPL(ata_bmdma_start);
 EXPORT_SYMBOL_GPL(ata_bmdma_irq_clear);
+EXPORT_SYMBOL_GPL(ata_bmdma_status);
+EXPORT_SYMBOL_GPL(ata_bmdma_stop);
 EXPORT_SYMBOL_GPL(ata_port_probe);
 EXPORT_SYMBOL_GPL(sata_phy_reset);
 EXPORT_SYMBOL_GPL(__sata_phy_reset);
diff -Nru a/drivers/scsi/libata-scsi.c b/drivers/scsi/libata-scsi.c
--- a/drivers/scsi/libata-scsi.c	2005-02-22 18:15:48 -08:00
+++ b/drivers/scsi/libata-scsi.c	2005-02-22 18:15:48 -08:00
@@ -701,6 +701,7 @@
 	return;
 
 err_out:
+	ata_qc_free(qc);
 	ata_bad_cdb(cmd, done);
 	DPRINTK("EXIT - badcmd\n");
 }
diff -Nru a/drivers/scsi/libata.h b/drivers/scsi/libata.h
--- a/drivers/scsi/libata.h	2005-02-22 18:15:48 -08:00
+++ b/drivers/scsi/libata.h	2005-02-22 18:15:48 -08:00
@@ -37,6 +37,7 @@
 /* libata-core.c */
 extern struct ata_queued_cmd *ata_qc_new_init(struct ata_port *ap,
 				      struct ata_device *dev);
+extern void ata_qc_free(struct ata_queued_cmd *qc);
 extern int ata_qc_issue(struct ata_queued_cmd *qc);
 extern int ata_check_atapi_dma(struct ata_queued_cmd *qc);
 extern void ata_dev_select(struct ata_port *ap, unsigned int device,
diff -Nru a/drivers/scsi/sata_nv.c b/drivers/scsi/sata_nv.c
--- a/drivers/scsi/sata_nv.c	2005-02-22 18:15:48 -08:00
+++ b/drivers/scsi/sata_nv.c	2005-02-22 18:15:48 -08:00
@@ -217,6 +217,8 @@
 	.phy_reset		= sata_phy_reset,
 	.bmdma_setup		= ata_bmdma_setup,
 	.bmdma_start		= ata_bmdma_start,
+	.bmdma_stop		= ata_bmdma_stop,
+	.bmdma_status		= ata_bmdma_status,
 	.qc_prep		= ata_qc_prep,
 	.qc_issue		= ata_qc_issue_prot,
 	.eng_timeout		= ata_eng_timeout,
@@ -332,6 +334,7 @@
 	struct nv_host *host;
 	struct ata_port_info *ppi;
 	struct ata_probe_ent *probe_ent;
+	int pci_dev_busy = 0;
 	int rc;
 	u32 bar;
 
@@ -350,8 +353,10 @@
 		goto err_out;
 
 	rc = pci_request_regions(pdev, DRV_NAME);
-	if (rc)
+	if (rc) {
+		pci_dev_busy = 1;
 		goto err_out_disable;
+	}
 
 	rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
 	if (rc)
@@ -427,7 +432,8 @@
 err_out_regions:
 	pci_release_regions(pdev);
 err_out_disable:
-	pci_disable_device(pdev);
+	if (!pci_dev_busy)
+		pci_disable_device(pdev);
 err_out:
 	return rc;
 }
diff -Nru a/drivers/scsi/sata_promise.c b/drivers/scsi/sata_promise.c
--- a/drivers/scsi/sata_promise.c	2005-02-22 18:15:48 -08:00
+++ b/drivers/scsi/sata_promise.c	2005-02-22 18:15:48 -08:00
@@ -556,6 +556,7 @@
 	unsigned long base;
 	void *mmio_base;
 	unsigned int board_idx = (unsigned int) ent->driver_data;
+	int pci_dev_busy = 0;
 	int rc;
 
 	if (!printed_version++)
@@ -570,8 +571,10 @@
 		return rc;
 
 	rc = pci_request_regions(pdev, DRV_NAME);
-	if (rc)
+	if (rc) {
+		pci_dev_busy = 1;
 		goto err_out;
+	}
 
 	rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
 	if (rc)
@@ -650,7 +653,8 @@
 err_out_regions:
 	pci_release_regions(pdev);
 err_out:
-	pci_disable_device(pdev);
+	if (!pci_dev_busy)
+		pci_disable_device(pdev);
 	return rc;
 }
 
diff -Nru a/drivers/scsi/sata_qstor.c b/drivers/scsi/sata_qstor.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/scsi/sata_qstor.c	2005-02-22 18:15:48 -08:00
@@ -0,0 +1,700 @@
+/*
+ *  sata_qstor.c - Pacific Digital Corporation QStor SATA
+ *
+ *  Maintained by:  Mark Lord <mlord@pobox.com>
+ *
+ *  Copyright 2005 Pacific Digital Corporation.
+ *  (OSL/GPL code release authorized by Jalil Fadavi).
+ *
+ *  The contents of this file are subject to the Open
+ *  Software License version 1.1 that can be found at
+ *  http://www.opensource.org/licenses/osl-1.1.txt and is included herein
+ *  by reference.
+ *
+ *  Alternatively, the contents of this file may be used under the terms
+ *  of the GNU General Public License version 2 (the "GPL") as distributed
+ *  in the kernel source COPYING file, in which case the provisions of
+ *  the GPL are applicable instead of the above.  If you wish to allow
+ *  the use of your version of this file only under the terms of the
+ *  GPL and not to allow others to use your version of this file under
+ *  the OSL, indicate your decision by deleting the provisions above and
+ *  replace them with the notice and other provisions required by the GPL.
+ *  If you do not delete the provisions above, a recipient may use your
+ *  version of this file under either the OSL or the GPL.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include <asm/io.h>
+#include <linux/libata.h>
+
+#define DRV_NAME	"sata_qstor"
+#define DRV_VERSION	"0.03"
+
+enum {
+	QS_PORTS		= 4,
+	QS_MAX_PRD		= LIBATA_MAX_PRD,
+	QS_CPB_ORDER		= 6,
+	QS_CPB_BYTES		= (1 << QS_CPB_ORDER),
+	QS_PRD_BYTES		= QS_MAX_PRD * 16,
+	QS_PKT_BYTES		= QS_CPB_BYTES + QS_PRD_BYTES,
+
+	QS_DMA_BOUNDARY		= ~0UL,
+
+	/* global register offsets */
+	QS_HCF_CNFG3		= 0x0003, /* host configuration offset */
+	QS_HID_HPHY		= 0x0004, /* host physical interface info */
+	QS_HCT_CTRL		= 0x00e4, /* global interrupt mask offset */
+	QS_HST_SFF		= 0x0100, /* host status fifo offset */
+	QS_HVS_SERD3		= 0x0393, /* PHY enable offset */
+
+	/* global control bits */
+	QS_HPHY_64BIT		= (1 << 1), /* 64-bit bus detected */
+	QS_CNFG3_GSRST		= 0x01,     /* global chip reset */
+	QS_SERD3_PHY_ENA	= 0xf0,     /* PHY detection ENAble*/
+
+	/* per-channel register offsets */
+	QS_CCF_CPBA		= 0x0710, /* chan CPB base address */
+	QS_CCF_CSEP		= 0x0718, /* chan CPB separation factor */
+	QS_CFC_HUFT		= 0x0800, /* host upstream fifo threshold */
+	QS_CFC_HDFT		= 0x0804, /* host downstream fifo threshold */
+	QS_CFC_DUFT		= 0x0808, /* dev upstream fifo threshold */
+	QS_CFC_DDFT		= 0x080c, /* dev downstream fifo threshold */
+	QS_CCT_CTR0		= 0x0900, /* chan control-0 offset */
+	QS_CCT_CTR1		= 0x0901, /* chan control-1 offset */
+	QS_CCT_CFF		= 0x0a00, /* chan command fifo offset */
+
+	/* channel control bits */
+	QS_CTR0_REG		= (1 << 1),   /* register mode (vs. pkt mode) */
+	QS_CTR0_CLER		= (1 << 2),   /* clear channel errors */
+	QS_CTR1_RDEV		= (1 << 1),   /* sata phy/comms reset */
+	QS_CTR1_RCHN		= (1 << 4),   /* reset channel logic */
+	QS_CCF_RUN_PKT		= 0x107,      /* RUN a new dma PKT */
+
+	/* pkt sub-field headers */
+	QS_HCB_HDR		= 0x01,   /* Host Control Block header */
+	QS_DCB_HDR		= 0x02,   /* Device Control Block header */
+
+	/* pkt HCB flag bits */
+	QS_HF_DIRO		= (1 << 0),   /* data DIRection Out */
+	QS_HF_DAT		= (1 << 3),   /* DATa pkt */
+	QS_HF_IEN		= (1 << 4),   /* Interrupt ENable */
+	QS_HF_VLD		= (1 << 5),   /* VaLiD pkt */
+
+	/* pkt DCB flag bits */
+	QS_DF_PORD		= (1 << 2),   /* Pio OR Dma */
+	QS_DF_ELBA		= (1 << 3),   /* Extended LBA (lba48) */
+
+	/* PCI device IDs */
+	board_2068_idx		= 0,	/* QStor 4-port SATA/RAID */
+};
+
+typedef enum { qs_state_idle, qs_state_pkt, qs_state_mmio } qs_state_t;
+
+struct qs_port_priv {
+	u8			*pkt;
+	dma_addr_t		pkt_dma;
+	qs_state_t		state;
+};
+
+static u32 qs_scr_read (struct ata_port *ap, unsigned int sc_reg);
+static void qs_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
+static int qs_ata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
+static irqreturn_t qs_intr (int irq, void *dev_instance, struct pt_regs *regs);
+static int qs_port_start(struct ata_port *ap);
+static void qs_host_stop(struct ata_host_set *host_set);
+static void qs_port_stop(struct ata_port *ap);
+static void qs_phy_reset(struct ata_port *ap);
+static void qs_qc_prep(struct ata_queued_cmd *qc);
+static int qs_qc_issue(struct ata_queued_cmd *qc);
+static int qs_check_atapi_dma(struct ata_queued_cmd *qc);
+static void qs_bmdma_stop(struct ata_port *ap);
+static u8 qs_bmdma_status(struct ata_port *ap);
+static void qs_irq_clear(struct ata_port *ap);
+
+static Scsi_Host_Template qs_ata_sht = {
+	.module			= THIS_MODULE,
+	.name			= DRV_NAME,
+	.ioctl			= ata_scsi_ioctl,
+	.queuecommand		= ata_scsi_queuecmd,
+	.eh_strategy_handler	= ata_scsi_error,
+	.can_queue		= ATA_DEF_QUEUE,
+	.this_id		= ATA_SHT_THIS_ID,
+	.sg_tablesize		= QS_MAX_PRD,
+	.max_sectors		= ATA_MAX_SECTORS,
+	.cmd_per_lun		= ATA_SHT_CMD_PER_LUN,
+	.emulated		= ATA_SHT_EMULATED,
+	//FIXME .use_clustering		= ATA_SHT_USE_CLUSTERING,
+	.use_clustering		= ENABLE_CLUSTERING,
+	.proc_name		= DRV_NAME,
+	.dma_boundary		= QS_DMA_BOUNDARY,
+	.slave_configure	= ata_scsi_slave_config,
+	.bios_param		= ata_std_bios_param,
+};
+
+static struct ata_port_operations qs_ata_ops = {
+	.port_disable		= ata_port_disable,
+	.tf_load		= ata_tf_load,
+	.tf_read		= ata_tf_read,
+	.check_status		= ata_check_status,
+	.check_atapi_dma	= qs_check_atapi_dma,
+	.exec_command		= ata_exec_command,
+	.dev_select		= ata_std_dev_select,
+	.phy_reset		= qs_phy_reset,
+	.qc_prep		= qs_qc_prep,
+	.qc_issue		= qs_qc_issue,
+	.eng_timeout		= ata_eng_timeout,
+	.irq_handler		= qs_intr,
+	.irq_clear		= qs_irq_clear,
+	.scr_read		= qs_scr_read,
+	.scr_write		= qs_scr_write,
+	.port_start		= qs_port_start,
+	.port_stop		= qs_port_stop,
+	.host_stop		= qs_host_stop,
+	.bmdma_stop		= qs_bmdma_stop,
+	.bmdma_status		= qs_bmdma_status,
+};
+
+static struct ata_port_info qs_port_info[] = {
+	/* board_2068_idx */
+	{
+		.sht		= &qs_ata_sht,
+		.host_flags	= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+				  ATA_FLAG_SATA_RESET |
+				  //FIXME ATA_FLAG_SRST |
+				  ATA_FLAG_MMIO,
+		.pio_mask	= 0x10, /* pio4 */
+		.udma_mask	= 0x7f, /* udma0-6 */
+		.port_ops	= &qs_ata_ops,
+	},
+};
+
+static struct pci_device_id qs_ata_pci_tbl[] = {
+	{ PCI_VENDOR_ID_PDC, 0x2068, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+	  board_2068_idx },
+
+	{ }	/* terminate list */
+};
+
+static struct pci_driver qs_ata_pci_driver = {
+	.name			= DRV_NAME,
+	.id_table		= qs_ata_pci_tbl,
+	.probe			= qs_ata_init_one,
+	.remove			= ata_pci_remove_one,
+};
+
+static int qs_check_atapi_dma(struct ata_queued_cmd *qc)
+{
+	return 1;	/* ATAPI DMA not supported */
+}
+
+static void qs_bmdma_stop(struct ata_port *ap)
+{
+	/* nothing */
+}
+
+static u8 qs_bmdma_status(struct ata_port *ap)
+{
+	return 0;
+}
+
+static void qs_irq_clear(struct ata_port *ap)
+{
+	/* nothing */
+}
+
+static void qs_enter_reg_mode(struct ata_port *ap)
+{
+	u8 __iomem *chan = ap->host_set->mmio_base + (ap->port_no * 0x4000);
+
+	writeb(QS_CTR0_REG, chan + QS_CCT_CTR0);
+	readb(chan + QS_CCT_CTR0);        /* flush */
+}
+
+static void qs_phy_reset(struct ata_port *ap)
+{
+	u8 __iomem *chan = ap->host_set->mmio_base + (ap->port_no * 0x4000);
+	struct qs_port_priv *pp = ap->private_data;
+
+	pp->state = qs_state_idle;
+	writeb(QS_CTR1_RCHN, chan + QS_CCT_CTR1);
+	qs_enter_reg_mode(ap);
+	sata_phy_reset(ap);
+}
+
+static u32 qs_scr_read (struct ata_port *ap, unsigned int sc_reg)
+{
+	if (sc_reg > SCR_CONTROL)
+		return ~0U;
+	return readl((void __iomem *)(ap->ioaddr.scr_addr + (sc_reg * 8)));
+}
+
+static void qs_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val)
+{
+	if (sc_reg > SCR_CONTROL)
+		return;
+	writel(val, (void __iomem *)(ap->ioaddr.scr_addr + (sc_reg * 8)));
+}
+
+static void qs_fill_sg(struct ata_queued_cmd *qc)
+{
+	struct scatterlist *sg = qc->sg;
+	struct ata_port *ap = qc->ap;
+	struct qs_port_priv *pp = ap->private_data;
+	unsigned int nelem;
+	u8 *prd = pp->pkt + QS_CPB_BYTES;
+
+	assert(sg != NULL);
+	assert(qc->n_elem > 0);
+
+	for (nelem = 0; nelem < qc->n_elem; nelem++,sg++) {
+		u64 addr;
+		u32 len;
+
+		addr = sg_dma_address(sg);
+		*(u64 *)prd = cpu_to_le64(addr);
+		prd += sizeof(u64);
+
+		len = sg_dma_len(sg);
+		*(u32 *)prd = cpu_to_le32(len);
+		prd += sizeof(u64);
+
+		VPRINTK("PRD[%u] = (0x%llX, 0x%X)\n", nelem,
+					(unsigned long long)addr, len);
+	}
+}
+
+static void qs_qc_prep(struct ata_queued_cmd *qc)
+{
+	struct qs_port_priv *pp = qc->ap->private_data;
+	u8 dflags = QS_DF_PORD, *buf = pp->pkt;
+	u8 hflags = QS_HF_DAT | QS_HF_IEN | QS_HF_VLD;
+	u64 addr;
+
+	VPRINTK("ENTER\n");
+
+	qs_enter_reg_mode(qc->ap);
+	if (qc->tf.protocol != ATA_PROT_DMA) {
+		ata_qc_prep(qc);
+		return;
+	}
+
+	qs_fill_sg(qc);
+
+	if ((qc->tf.flags & ATA_TFLAG_WRITE))
+		hflags |= QS_HF_DIRO;
+	if ((qc->tf.flags & ATA_TFLAG_LBA48))
+		dflags |= QS_DF_ELBA;
+
+	/* host control block (HCB) */
+	buf[ 0] = QS_HCB_HDR;
+	buf[ 1] = hflags;
+	*(u32 *)(&buf[ 4]) = cpu_to_le32(qc->nsect * ATA_SECT_SIZE);
+	*(u32 *)(&buf[ 8]) = cpu_to_le32(qc->n_elem);
+	addr = ((u64)pp->pkt_dma) + QS_CPB_BYTES;
+	*(u64 *)(&buf[16]) = cpu_to_le64(addr);
+
+	/* device control block (DCB) */
+	buf[24] = QS_DCB_HDR;
+	buf[28] = dflags;
+
+	/* frame information structure (FIS) */
+	ata_tf_to_fis(&qc->tf, &buf[32], 0);
+}
+
+static inline void qs_packet_start(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+	u8 __iomem *chan = ap->host_set->mmio_base + (ap->port_no * 0x4000);
+
+	VPRINTK("ENTER, ap %p\n", ap);
+
+	writeb(QS_CTR0_CLER, chan + QS_CCT_CTR0);
+	wmb();                             /* flush PRDs and pkt to memory */
+	writel(QS_CCF_RUN_PKT, chan + QS_CCT_CFF);
+	readl(chan + QS_CCT_CFF);          /* flush */
+}
+
+static int qs_qc_issue(struct ata_queued_cmd *qc)
+{
+	struct qs_port_priv *pp = qc->ap->private_data;
+
+	switch (qc->tf.protocol) {
+	case ATA_PROT_DMA:
+
+		pp->state = qs_state_pkt;
+		qs_packet_start(qc);
+		return 0;
+
+	case ATA_PROT_ATAPI_DMA:
+		BUG();
+		break;
+
+	default:
+		break;
+	}
+
+	pp->state = qs_state_mmio;
+	return ata_qc_issue_prot(qc);
+}
+
+static inline unsigned int qs_intr_pkt(struct ata_host_set *host_set)
+{
+	unsigned int handled = 0;
+	u8 sFFE;
+	u8 __iomem *mmio_base = host_set->mmio_base;
+
+	do {
+		u32 sff0 = readl(mmio_base + QS_HST_SFF);
+		u32 sff1 = readl(mmio_base + QS_HST_SFF + 4);
+		u8 sEVLD = (sff1 >> 30) & 0x01;	/* valid flag */
+		sFFE  = sff1 >> 31;		/* empty flag */
+
+		if (sEVLD) {
+			u8 sDST = sff0 >> 16;	/* dev status */
+			u8 sHST = sff1 & 0x3f;	/* host status */
+			unsigned int port_no = (sff1 >> 8) & 0x03;
+			struct ata_port *ap = host_set->ports[port_no];
+
+			DPRINTK("SFF=%08x%08x: sCHAN=%u sHST=%d sDST=%02x\n",
+					sff1, sff0, port_no, sHST, sDST);
+			handled = 1;
+			if (ap && (!(ap->flags & ATA_FLAG_PORT_DISABLED))) {
+				struct ata_queued_cmd *qc;
+				struct qs_port_priv *pp = ap->private_data;
+				if (!pp || pp->state != qs_state_pkt)
+					continue;
+				qc = ata_qc_from_tag(ap, ap->active_tag);
+				if (qc && (!(qc->tf.ctl & ATA_NIEN))) {
+					switch (sHST) {
+					case 0: /* sucessful CPB */
+					case 3: /* device error */
+						pp->state = qs_state_idle;
+						qs_enter_reg_mode(qc->ap);
+						ata_qc_complete(qc, sDST);
+						break;
+					default:
+						break;
+					}
+				}
+			}
+		}
+	} while (!sFFE);
+	return handled;
+}
+
+static inline unsigned int qs_intr_mmio(struct ata_host_set *host_set)
+{
+	unsigned int handled = 0, port_no;
+
+	for (port_no = 0; port_no < host_set->n_ports; ++port_no) {
+		struct ata_port *ap;
+		ap = host_set->ports[port_no];
+		if (ap && (!(ap->flags & ATA_FLAG_PORT_DISABLED))) {
+			struct ata_queued_cmd *qc;
+			struct qs_port_priv *pp = ap->private_data;
+			if (!pp || pp->state != qs_state_mmio)
+				continue;
+			qc = ata_qc_from_tag(ap, ap->active_tag);
+			if (qc && (!(qc->tf.ctl & ATA_NIEN))) {
+
+				/* check main status, clearing INTRQ */
+				u8 status = ata_chk_status(ap);
+				if ((status & ATA_BUSY))
+					continue;
+				DPRINTK("ata%u: protocol %d (dev_stat 0x%X)\n",
+					ap->id, qc->tf.protocol, status);
+		
+				/* complete taskfile transaction */
+				pp->state = qs_state_idle;
+				ata_qc_complete(qc, status);
+				handled = 1;
+			}
+		}
+	}
+	return handled;
+}
+
+static irqreturn_t qs_intr(int irq, void *dev_instance, struct pt_regs *regs)
+{
+	struct ata_host_set *host_set = dev_instance;
+	unsigned int handled = 0;
+
+	VPRINTK("ENTER\n");
+
+	spin_lock(&host_set->lock);
+	handled  = qs_intr_pkt(host_set) | qs_intr_mmio(host_set);
+	spin_unlock(&host_set->lock);
+
+	VPRINTK("EXIT\n");
+
+	return IRQ_RETVAL(handled);
+}
+
+static void qs_ata_setup_port(struct ata_ioports *port, unsigned long base)
+{
+	port->cmd_addr		=
+	port->data_addr		= base + 0x400;
+	port->error_addr	=
+	port->feature_addr	= base + 0x408; /* hob_feature = 0x409 */
+	port->nsect_addr	= base + 0x410; /* hob_nsect   = 0x411 */
+	port->lbal_addr		= base + 0x418; /* hob_lbal    = 0x419 */
+	port->lbam_addr		= base + 0x420; /* hob_lbam    = 0x421 */
+	port->lbah_addr		= base + 0x428; /* hob_lbah    = 0x429 */
+	port->device_addr	= base + 0x430;
+	port->status_addr	=
+	port->command_addr	= base + 0x438;
+	port->altstatus_addr	=
+	port->ctl_addr		= base + 0x440;
+	port->scr_addr		= base + 0xc00;
+}
+
+static int qs_port_start(struct ata_port *ap)
+{
+	struct device *dev = ap->host_set->dev;
+	struct qs_port_priv *pp;
+	void __iomem *mmio_base = ap->host_set->mmio_base;
+	void __iomem *chan = mmio_base + (ap->port_no * 0x4000);
+	u64 addr;
+	int rc;
+
+	rc = ata_port_start(ap);
+	if (rc)
+		return rc;
+	qs_enter_reg_mode(ap);
+	pp = kcalloc(1, sizeof(*pp), GFP_KERNEL);
+	if (!pp) {
+		rc = -ENOMEM;
+		goto err_out;
+	}
+	pp->pkt = dma_alloc_coherent(dev, QS_PKT_BYTES, &pp->pkt_dma,
+								GFP_KERNEL);
+	if (!pp->pkt) {
+		rc = -ENOMEM;
+		goto err_out_kfree;
+	}
+	memset(pp->pkt, 0, QS_PKT_BYTES);
+	ap->private_data = pp;
+
+	addr = (u64)pp->pkt_dma;
+	writel((u32) addr,        chan + QS_CCF_CPBA);
+	writel((u32)(addr >> 32), chan + QS_CCF_CPBA + 4);
+	return 0;
+
+err_out_kfree:
+	kfree(pp);
+err_out:
+	ata_port_stop(ap);
+	return rc;
+}
+
+static void qs_port_stop(struct ata_port *ap)
+{
+	struct device *dev = ap->host_set->dev;
+	struct qs_port_priv *pp = ap->private_data;
+
+	if (pp != NULL) {
+		ap->private_data = NULL;
+		if (pp->pkt != NULL)
+			dma_free_coherent(dev, QS_PKT_BYTES, pp->pkt,
+								pp->pkt_dma);
+		kfree(pp);
+	}
+	ata_port_stop(ap);
+}
+
+static void qs_host_stop(struct ata_host_set *host_set)
+{
+	void __iomem *mmio_base = host_set->mmio_base;
+
+	writeb(0, mmio_base + QS_HCT_CTRL); /* disable host interrupts */
+	writeb(QS_CNFG3_GSRST, mmio_base + QS_HCF_CNFG3); /* global reset */
+}
+
+static void qs_host_init(unsigned int chip_id, struct ata_probe_ent *pe)
+{
+	void __iomem *mmio_base = pe->mmio_base;
+	unsigned int port_no;
+
+	writeb(0, mmio_base + QS_HCT_CTRL); /* disable host interrupts */
+	writeb(QS_CNFG3_GSRST, mmio_base + QS_HCF_CNFG3); /* global reset */
+
+	/* reset each channel in turn */
+	for (port_no = 0; port_no < pe->n_ports; ++port_no) {
+		u8 __iomem *chan = mmio_base + (port_no * 0x4000);
+		writeb(QS_CTR1_RDEV|QS_CTR1_RCHN, chan + QS_CCT_CTR1);
+		writeb(QS_CTR0_REG, chan + QS_CCT_CTR0);
+		readb(chan + QS_CCT_CTR0);        /* flush */
+	}
+	writeb(QS_SERD3_PHY_ENA, mmio_base + QS_HVS_SERD3); /* enable phy */
+
+	for (port_no = 0; port_no < pe->n_ports; ++port_no) {
+		u8 __iomem *chan = mmio_base + (port_no * 0x4000);
+		/* set FIFO depths to same settings as Windows driver */
+		writew(32, chan + QS_CFC_HUFT);
+		writew(32, chan + QS_CFC_HDFT);
+		writew(10, chan + QS_CFC_DUFT);
+		writew( 8, chan + QS_CFC_DDFT);
+		/* set CPB size in bytes, as a power of two */
+		writeb(QS_CPB_ORDER,    chan + QS_CCF_CSEP);
+	}
+	writeb(1, mmio_base + QS_HCT_CTRL); /* enable host interrupts */
+}
+
+/*
+ * The QStor understands 64-bit buses, and uses 64-bit fields
+ * for DMA pointers regardless of bus width.  We just have to
+ * make sure our DMA masks are set appropriately for whatever
+ * bridge lies between us and the QStor, and then the DMA mapping
+ * code will ensure we only ever "see" appropriate buffer addresses.
+ * If we're 32-bit limited somewhere, then our 64-bit fields will
+ * just end up with zeros in the upper 32-bits, without any special
+ * logic required outside of this routine (below).
+ */
+static int qs_set_dma_masks(struct pci_dev *pdev, void __iomem *mmio_base)
+{
+	u32 bus_info = readl(mmio_base + QS_HID_HPHY);
+	int rc, have_64bit_bus = (bus_info & QS_HPHY_64BIT);
+
+	if (have_64bit_bus &&
+	    !pci_set_dma_mask(pdev, 0xffffffffffffffffULL)) {
+		rc = pci_set_consistent_dma_mask(pdev, 0xffffffffffffffffULL);
+		if (rc) {
+			rc = pci_set_consistent_dma_mask(pdev, 0xffffffffULL);
+			if (rc) {
+				printk(KERN_ERR DRV_NAME
+					"(%s): 64-bit DMA enable failed\n",
+					pci_name(pdev));
+				return rc;
+			}
+		}
+	} else {
+		rc = pci_set_dma_mask(pdev, 0xffffffffULL);
+		if (rc) {
+			printk(KERN_ERR DRV_NAME
+				"(%s): 32-bit DMA enable failed\n",
+				pci_name(pdev));
+			return rc;
+		}
+		rc = pci_set_consistent_dma_mask(pdev, 0xffffffffULL);
+		if (rc) {
+			printk(KERN_ERR DRV_NAME
+				"(%s): 32-bit consistent DMA enable failed\n",
+				pci_name(pdev));
+			return rc;
+		}
+	}
+	return 0;
+}
+
+static int qs_ata_init_one(struct pci_dev *pdev,
+				const struct pci_device_id *ent)
+{
+	static int printed_version;
+	struct ata_probe_ent *probe_ent = NULL;
+	void __iomem *mmio_base;
+	unsigned int board_idx = (unsigned int) ent->driver_data;
+	int rc, port_no;
+
+	if (!printed_version++)
+		printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n");
+
+	rc = pci_enable_device(pdev);
+	if (rc)
+		return rc;
+
+	rc = pci_request_regions(pdev, DRV_NAME);
+	if (rc)
+		goto err_out;
+
+	if ((pci_resource_flags(pdev, 4) & IORESOURCE_MEM) == 0) {
+		rc = -ENODEV;
+		goto err_out_regions;
+	}
+
+	mmio_base = ioremap(pci_resource_start(pdev, 4),
+		            pci_resource_len(pdev, 4));
+	if (mmio_base == NULL) {
+		rc = -ENOMEM;
+		goto err_out_regions;
+	}
+
+	rc = qs_set_dma_masks(pdev, mmio_base);
+	if (rc)
+		goto err_out_iounmap;
+
+	probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL);
+	if (probe_ent == NULL) {
+		rc = -ENOMEM;
+		goto err_out_iounmap;
+	}
+
+	memset(probe_ent, 0, sizeof(*probe_ent));
+	probe_ent->dev = pci_dev_to_dev(pdev);
+	INIT_LIST_HEAD(&probe_ent->node);
+
+	probe_ent->sht		= qs_port_info[board_idx].sht;
+	probe_ent->host_flags	= qs_port_info[board_idx].host_flags;
+	probe_ent->pio_mask	= qs_port_info[board_idx].pio_mask;
+	probe_ent->mwdma_mask	= qs_port_info[board_idx].mwdma_mask;
+	probe_ent->udma_mask	= qs_port_info[board_idx].udma_mask;
+	probe_ent->port_ops	= qs_port_info[board_idx].port_ops;
+
+	probe_ent->irq		= pdev->irq;
+	probe_ent->irq_flags	= SA_SHIRQ;
+	probe_ent->mmio_base	= mmio_base;
+	probe_ent->n_ports	= QS_PORTS;
+
+	for (port_no = 0; port_no < probe_ent->n_ports; ++port_no) {
+		unsigned long chan = (unsigned long)mmio_base +
+							(port_no * 0x4000);
+		qs_ata_setup_port(&probe_ent->port[port_no], chan);
+	}
+
+	pci_set_master(pdev);
+
+	/* initialize adapter */
+	qs_host_init(board_idx, probe_ent);
+
+	rc = ata_device_add(probe_ent);
+	kfree(probe_ent);
+	if (rc != QS_PORTS)
+		goto err_out_iounmap;
+	return 0;
+
+err_out_iounmap:
+	iounmap(mmio_base);
+err_out_regions:
+	pci_release_regions(pdev);
+err_out:
+	pci_disable_device(pdev);
+	return rc;
+}
+
+static int __init qs_ata_init(void)
+{
+	return pci_module_init(&qs_ata_pci_driver);
+}
+
+static void __exit qs_ata_exit(void)
+{
+	pci_unregister_driver(&qs_ata_pci_driver);
+}
+
+MODULE_AUTHOR("Mark Lord");
+MODULE_DESCRIPTION("Pacific Digital Corporation QStor SATA low-level driver");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, qs_ata_pci_tbl);
+MODULE_VERSION(DRV_VERSION);
+
+module_init(qs_ata_init);
+module_exit(qs_ata_exit);
diff -Nru a/drivers/scsi/sata_sil.c b/drivers/scsi/sata_sil.c
--- a/drivers/scsi/sata_sil.c	2005-02-22 18:15:48 -08:00
+++ b/drivers/scsi/sata_sil.c	2005-02-22 18:15:48 -08:00
@@ -139,6 +139,8 @@
 	.post_set_mode		= sil_post_set_mode,
 	.bmdma_setup            = ata_bmdma_setup,
 	.bmdma_start            = ata_bmdma_start,
+	.bmdma_stop		= ata_bmdma_stop,
+	.bmdma_status		= ata_bmdma_status,
 	.qc_prep		= ata_qc_prep,
 	.qc_issue		= ata_qc_issue_prot,
 	.eng_timeout		= ata_eng_timeout,
@@ -336,6 +338,7 @@
 	void *mmio_base;
 	int rc;
 	unsigned int i;
+	int pci_dev_busy = 0;
 	u32 tmp, irq_mask;
 
 	if (!printed_version++)
@@ -350,8 +353,10 @@
 		return rc;
 
 	rc = pci_request_regions(pdev, DRV_NAME);
-	if (rc)
+	if (rc) {
+		pci_dev_busy = 1;
 		goto err_out;
+	}
 
 	rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
 	if (rc)
@@ -438,7 +443,8 @@
 err_out_regions:
 	pci_release_regions(pdev);
 err_out:
-	pci_disable_device(pdev);
+	if (!pci_dev_busy)
+		pci_disable_device(pdev);
 	return rc;
 }
 
diff -Nru a/drivers/scsi/sata_sis.c b/drivers/scsi/sata_sis.c
--- a/drivers/scsi/sata_sis.c	2005-02-22 18:15:48 -08:00
+++ b/drivers/scsi/sata_sis.c	2005-02-22 18:15:48 -08:00
@@ -102,6 +102,8 @@
 	.phy_reset		= sata_phy_reset,
 	.bmdma_setup            = ata_bmdma_setup,
 	.bmdma_start            = ata_bmdma_start,
+	.bmdma_stop		= ata_bmdma_stop,
+	.bmdma_status		= ata_bmdma_status,
 	.qc_prep		= ata_qc_prep,
 	.qc_issue		= ata_qc_issue_prot,
 	.eng_timeout		= ata_eng_timeout,
@@ -200,14 +202,17 @@
 	int rc;
 	u32 genctl;
 	struct ata_port_info *ppi;
+	int pci_dev_busy = 0;
 
 	rc = pci_enable_device(pdev);
 	if (rc)
 		return rc;
 
 	rc = pci_request_regions(pdev, DRV_NAME);
-	if (rc)
+	if (rc) {
+		pci_dev_busy = 1;
 		goto err_out;
+	}
 
 	rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
 	if (rc)
@@ -259,7 +264,8 @@
 	pci_release_regions(pdev);
 
 err_out:
-	pci_disable_device(pdev);
+	if (!pci_dev_busy)
+		pci_disable_device(pdev);
 	return rc;
 
 }
diff -Nru a/drivers/scsi/sata_svw.c b/drivers/scsi/sata_svw.c
--- a/drivers/scsi/sata_svw.c	2005-02-22 18:15:48 -08:00
+++ b/drivers/scsi/sata_svw.c	2005-02-22 18:15:48 -08:00
@@ -301,6 +301,8 @@
 	.phy_reset		= sata_phy_reset,
 	.bmdma_setup		= k2_bmdma_setup_mmio,
 	.bmdma_start		= k2_bmdma_start_mmio,
+	.bmdma_stop		= ata_bmdma_stop,
+	.bmdma_status		= ata_bmdma_status,
 	.qc_prep		= ata_qc_prep,
 	.qc_issue		= ata_qc_issue_prot,
 	.eng_timeout		= ata_eng_timeout,
@@ -338,6 +340,7 @@
 	struct ata_probe_ent *probe_ent = NULL;
 	unsigned long base;
 	void *mmio_base;
+	int pci_dev_busy = 0;
 	int rc;
 
 	if (!printed_version++)
@@ -359,8 +362,10 @@
 
 	/* Request PCI regions */
 	rc = pci_request_regions(pdev, DRV_NAME);
-	if (rc)
+	if (rc) {
+		pci_dev_busy = 1;
 		goto err_out;
+	}
 
 	rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
 	if (rc)
@@ -433,7 +438,8 @@
 err_out_regions:
 	pci_release_regions(pdev);
 err_out:
-	pci_disable_device(pdev);
+	if (!pci_dev_busy)
+		pci_disable_device(pdev);
 	return rc;
 }
 
diff -Nru a/drivers/scsi/sata_sx4.c b/drivers/scsi/sata_sx4.c
--- a/drivers/scsi/sata_sx4.c	2005-02-22 18:15:48 -08:00
+++ b/drivers/scsi/sata_sx4.c	2005-02-22 18:15:48 -08:00
@@ -1366,6 +1366,7 @@
 	void *mmio_base, *dimm_mmio = NULL;
 	struct pdc_host_priv *hpriv = NULL;
 	unsigned int board_idx = (unsigned int) ent->driver_data;
+	int pci_dev_busy = 0;
 	int rc;
 
 	if (!printed_version++)
@@ -1380,8 +1381,10 @@
 		return rc;
 
 	rc = pci_request_regions(pdev, DRV_NAME);
-	if (rc)
+	if (rc) {
+		pci_dev_busy = 1;
 		goto err_out;
+	}
 
 	rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
 	if (rc)
@@ -1471,7 +1474,8 @@
 err_out_regions:
 	pci_release_regions(pdev);
 err_out:
-	pci_disable_device(pdev);
+	if (!pci_dev_busy)
+		pci_disable_device(pdev);
 	return rc;
 }
 
diff -Nru a/drivers/scsi/sata_uli.c b/drivers/scsi/sata_uli.c
--- a/drivers/scsi/sata_uli.c	2005-02-22 18:15:48 -08:00
+++ b/drivers/scsi/sata_uli.c	2005-02-22 18:15:48 -08:00
@@ -97,6 +97,8 @@
 
 	.bmdma_setup            = ata_bmdma_setup,
 	.bmdma_start            = ata_bmdma_start,
+	.bmdma_stop		= ata_bmdma_stop,
+	.bmdma_status		= ata_bmdma_status,
 	.qc_prep		= ata_qc_prep,
 	.qc_issue		= ata_qc_issue_prot,
 
@@ -185,14 +187,17 @@
 	struct ata_port_info *ppi;
 	int rc;
 	unsigned int board_idx = (unsigned int) ent->driver_data;
+	int pci_dev_busy = 0;
 
 	rc = pci_enable_device(pdev);
 	if (rc)
 		return rc;
 
 	rc = pci_request_regions(pdev, DRV_NAME);
-	if (rc)
+	if (rc) {
+		pci_dev_busy = 1;
 		goto err_out;
+	}
 
 	rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
 	if (rc)
@@ -260,7 +265,8 @@
 	pci_release_regions(pdev);
 
 err_out:
-	pci_disable_device(pdev);
+	if (!pci_dev_busy)
+		pci_disable_device(pdev);
 	return rc;
 
 }
diff -Nru a/drivers/scsi/sata_via.c b/drivers/scsi/sata_via.c
--- a/drivers/scsi/sata_via.c	2005-02-22 18:15:48 -08:00
+++ b/drivers/scsi/sata_via.c	2005-02-22 18:15:48 -08:00
@@ -24,6 +24,11 @@
    If you do not delete the provisions above, a recipient may use your
    version of this file under either the OSL or the GPL.
 
+   ----------------------------------------------------------------------
+
+   To-do list:
+   * VT6421 PATA support
+
  */
 
 #include <linux/kernel.h>
@@ -38,11 +43,14 @@
 #include <asm/io.h>
 
 #define DRV_NAME	"sata_via"
-#define DRV_VERSION	"1.0"
+#define DRV_VERSION	"1.1"
 
-enum {
-	via_sata		= 0,
+enum board_ids_enum {
+	vt6420,
+	vt6421,
+};
 
+enum {
 	SATA_CHAN_ENAB		= 0x40, /* SATA channel enable */
 	SATA_INT_GATE		= 0x41, /* SATA interrupt gating */
 	SATA_NATIVE_MODE	= 0x42, /* Native mode enable */
@@ -50,10 +58,8 @@
 
 	PORT0			= (1 << 1),
 	PORT1			= (1 << 0),
-
-	ENAB_ALL		= PORT0 | PORT1,
-
-	INT_GATE_ALL		= PORT0 | PORT1,
+	ALL_PORTS		= PORT0 | PORT1,
+	N_PORTS			= 2,
 
 	NATIVE_MODE_ALL		= (1 << 7) | (1 << 6) | (1 << 5) | (1 << 4),
 
@@ -66,7 +72,8 @@
 static void svia_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
 
 static struct pci_device_id svia_pci_tbl[] = {
-	{ 0x1106, 0x3149, PCI_ANY_ID, PCI_ANY_ID, 0, 0, via_sata },
+	{ 0x1106, 0x3149, PCI_ANY_ID, PCI_ANY_ID, 0, 0, vt6420 },
+	{ 0x1106, 0x3249, PCI_ANY_ID, PCI_ANY_ID, 0, 0, vt6421 },
 
 	{ }	/* terminate list */
 };
@@ -110,6 +117,9 @@
 
 	.bmdma_setup            = ata_bmdma_setup,
 	.bmdma_start            = ata_bmdma_start,
+	.bmdma_stop		= ata_bmdma_stop,
+	.bmdma_status		= ata_bmdma_status,
+
 	.qc_prep		= ata_qc_prep,
 	.qc_issue		= ata_qc_issue_prot,
 
@@ -158,18 +168,132 @@
 	8, 4, 8, 4, 16, 256
 };
 
+static const unsigned int vt6421_bar_sizes[] = {
+	16, 16, 16, 16, 32, 128
+};
+
 static unsigned long svia_scr_addr(unsigned long addr, unsigned int port)
 {
 	return addr + (port * 128);
 }
 
+static unsigned long vt6421_scr_addr(unsigned long addr, unsigned int port)
+{
+	return addr + (port * 64);
+}
+
+static void vt6421_init_addrs(struct ata_probe_ent *probe_ent,
+			      struct pci_dev *pdev,
+			      unsigned int port)
+{
+	unsigned long reg_addr = pci_resource_start(pdev, port);
+	unsigned long bmdma_addr = pci_resource_start(pdev, 4) + (port * 8);
+	unsigned long scr_addr;
+
+	probe_ent->port[port].cmd_addr = reg_addr;
+	probe_ent->port[port].altstatus_addr =
+	probe_ent->port[port].ctl_addr = (reg_addr + 8) | ATA_PCI_CTL_OFS;
+	probe_ent->port[port].bmdma_addr = bmdma_addr;
+
+	scr_addr = vt6421_scr_addr(pci_resource_start(pdev, 5), port);
+	probe_ent->port[port].scr_addr = scr_addr;
+
+	ata_std_ports(&probe_ent->port[port]);
+}
+
+static struct ata_probe_ent *vt6420_init_probe_ent(struct pci_dev *pdev)
+{
+	struct ata_probe_ent *probe_ent;
+	struct ata_port_info *ppi = &svia_port_info;
+
+	probe_ent = ata_pci_init_native_mode(pdev, &ppi);
+	if (!probe_ent)
+		return NULL;
+
+	probe_ent->port[0].scr_addr =
+		svia_scr_addr(pci_resource_start(pdev, 5), 0);
+	probe_ent->port[1].scr_addr =
+		svia_scr_addr(pci_resource_start(pdev, 5), 1);
+
+	return probe_ent;
+}
+
+static struct ata_probe_ent *vt6421_init_probe_ent(struct pci_dev *pdev)
+{
+	struct ata_probe_ent *probe_ent;
+	unsigned int i;
+
+	probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL);
+	if (!probe_ent)
+		return NULL;
+
+	memset(probe_ent, 0, sizeof(*probe_ent));
+	probe_ent->dev = pci_dev_to_dev(pdev);
+	INIT_LIST_HEAD(&probe_ent->node);
+
+	probe_ent->sht		= &svia_sht;
+	probe_ent->host_flags	= ATA_FLAG_SATA | ATA_FLAG_SATA_RESET |
+				  ATA_FLAG_NO_LEGACY;
+	probe_ent->port_ops	= &svia_sata_ops;
+	probe_ent->n_ports	= N_PORTS;
+	probe_ent->irq		= pdev->irq;
+	probe_ent->irq_flags	= SA_SHIRQ;
+	probe_ent->pio_mask	= 0x1f;
+	probe_ent->mwdma_mask	= 0x07;
+	probe_ent->udma_mask	= 0x7f;
+
+	for (i = 0; i < N_PORTS; i++)
+		vt6421_init_addrs(probe_ent, pdev, i);
+
+	return probe_ent;
+}
+
+static void svia_configure(struct pci_dev *pdev)
+{
+	u8 tmp8;
+
+	pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &tmp8);
+	printk(KERN_INFO DRV_NAME "(%s): routed to hard irq line %d\n",
+	       pci_name(pdev),
+	       (int) (tmp8 & 0xf0) == 0xf0 ? 0 : tmp8 & 0x0f);
+
+	/* make sure SATA channels are enabled */
+	pci_read_config_byte(pdev, SATA_CHAN_ENAB, &tmp8);
+	if ((tmp8 & ALL_PORTS) != ALL_PORTS) {
+		printk(KERN_DEBUG DRV_NAME "(%s): enabling SATA channels (0x%x)\n",
+		       pci_name(pdev), (int) tmp8);
+		tmp8 |= ALL_PORTS;
+		pci_write_config_byte(pdev, SATA_CHAN_ENAB, tmp8);
+	}
+
+	/* make sure interrupts for each channel sent to us */
+	pci_read_config_byte(pdev, SATA_INT_GATE, &tmp8);
+	if ((tmp8 & ALL_PORTS) != ALL_PORTS) {
+		printk(KERN_DEBUG DRV_NAME "(%s): enabling SATA channel interrupts (0x%x)\n",
+		       pci_name(pdev), (int) tmp8);
+		tmp8 |= ALL_PORTS;
+		pci_write_config_byte(pdev, SATA_INT_GATE, tmp8);
+	}
+
+	/* make sure native mode is enabled */
+	pci_read_config_byte(pdev, SATA_NATIVE_MODE, &tmp8);
+	if ((tmp8 & NATIVE_MODE_ALL) != NATIVE_MODE_ALL) {
+		printk(KERN_DEBUG DRV_NAME "(%s): enabling SATA channel native mode (0x%x)\n",
+		       pci_name(pdev), (int) tmp8);
+		tmp8 |= NATIVE_MODE_ALL;
+		pci_write_config_byte(pdev, SATA_NATIVE_MODE, tmp8);
+	}
+}
+
 static int svia_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
 {
 	static int printed_version;
 	unsigned int i;
 	int rc;
-	struct ata_port_info *ppi;
 	struct ata_probe_ent *probe_ent;
+	int board_id = (int) ent->driver_data;
+	const int *bar_sizes;
+	int pci_dev_busy = 0;
 	u8 tmp8;
 
 	if (!printed_version++)
@@ -180,20 +304,28 @@
 		return rc;
 
 	rc = pci_request_regions(pdev, DRV_NAME);
-	if (rc)
+	if (rc) {
+		pci_dev_busy = 1;
 		goto err_out;
+	}
 
-	pci_read_config_byte(pdev, SATA_PATA_SHARING, &tmp8);
-	if (tmp8 & SATA_2DEV) {
-		printk(KERN_ERR DRV_NAME "(%s): SATA master/slave not supported (0x%x)\n",
-		       pci_name(pdev), (int) tmp8);
-		rc = -EIO;
-		goto err_out_regions;
+	if (board_id == vt6420) {
+		pci_read_config_byte(pdev, SATA_PATA_SHARING, &tmp8);
+		if (tmp8 & SATA_2DEV) {
+			printk(KERN_ERR DRV_NAME "(%s): SATA master/slave not supported (0x%x)\n",
+		       	pci_name(pdev), (int) tmp8);
+			rc = -EIO;
+			goto err_out_regions;
+		}
+
+		bar_sizes = &svia_bar_sizes[0];
+	} else {
+		bar_sizes = &vt6421_bar_sizes[0];
 	}
 
 	for (i = 0; i < ARRAY_SIZE(svia_bar_sizes); i++)
 		if ((pci_resource_start(pdev, i) == 0) ||
-		    (pci_resource_len(pdev, i) < svia_bar_sizes[i])) {
+		    (pci_resource_len(pdev, i) < bar_sizes[i])) {
 			printk(KERN_ERR DRV_NAME "(%s): invalid PCI BAR %u (sz 0x%lx, val 0x%lx)\n",
 			       pci_name(pdev), i,
 			       pci_resource_start(pdev, i),
@@ -209,8 +341,11 @@
 	if (rc)
 		goto err_out_regions;
 
-	ppi = &svia_port_info;
-	probe_ent = ata_pci_init_native_mode(pdev, &ppi);
+	if (board_id == vt6420)
+		probe_ent = vt6420_init_probe_ent(pdev);
+	else
+		probe_ent = vt6421_init_probe_ent(pdev);
+	
 	if (!probe_ent) {
 		printk(KERN_ERR DRV_NAME "(%s): out of memory\n",
 		       pci_name(pdev));
@@ -218,42 +353,7 @@
 		goto err_out_regions;
 	}
 
-	probe_ent->port[0].scr_addr =
-		svia_scr_addr(pci_resource_start(pdev, 5), 0);
-	probe_ent->port[1].scr_addr =
-		svia_scr_addr(pci_resource_start(pdev, 5), 1);
-
-	pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &tmp8);
-	printk(KERN_INFO DRV_NAME "(%s): routed to hard irq line %d\n",
-	       pci_name(pdev),
-	       (int) (tmp8 & 0xf0) == 0xf0 ? 0 : tmp8 & 0x0f);
-
-	/* make sure SATA channels are enabled */
-	pci_read_config_byte(pdev, SATA_CHAN_ENAB, &tmp8);
-	if ((tmp8 & ENAB_ALL) != ENAB_ALL) {
-		printk(KERN_DEBUG DRV_NAME "(%s): enabling SATA channels (0x%x)\n",
-		       pci_name(pdev), (int) tmp8);
-		tmp8 |= ENAB_ALL;
-		pci_write_config_byte(pdev, SATA_CHAN_ENAB, tmp8);
-	}
-
-	/* make sure interrupts for each channel sent to us */
-	pci_read_config_byte(pdev, SATA_INT_GATE, &tmp8);
-	if ((tmp8 & INT_GATE_ALL) != INT_GATE_ALL) {
-		printk(KERN_DEBUG DRV_NAME "(%s): enabling SATA channel interrupts (0x%x)\n",
-		       pci_name(pdev), (int) tmp8);
-		tmp8 |= INT_GATE_ALL;
-		pci_write_config_byte(pdev, SATA_INT_GATE, tmp8);
-	}
-
-	/* make sure native mode is enabled */
-	pci_read_config_byte(pdev, SATA_NATIVE_MODE, &tmp8);
-	if ((tmp8 & NATIVE_MODE_ALL) != NATIVE_MODE_ALL) {
-		printk(KERN_DEBUG DRV_NAME "(%s): enabling SATA channel native mode (0x%x)\n",
-		       pci_name(pdev), (int) tmp8);
-		tmp8 |= NATIVE_MODE_ALL;
-		pci_write_config_byte(pdev, SATA_NATIVE_MODE, tmp8);
-	}
+	svia_configure(pdev);
 
 	pci_set_master(pdev);
 
@@ -266,7 +366,8 @@
 err_out_regions:
 	pci_release_regions(pdev);
 err_out:
-	pci_disable_device(pdev);
+	if (!pci_dev_busy)
+		pci_disable_device(pdev);
 	return rc;
 }
 
diff -Nru a/drivers/scsi/sata_vsc.c b/drivers/scsi/sata_vsc.c
--- a/drivers/scsi/sata_vsc.c	2005-02-22 18:15:48 -08:00
+++ b/drivers/scsi/sata_vsc.c	2005-02-22 18:15:48 -08:00
@@ -217,6 +217,8 @@
 	.phy_reset		= sata_phy_reset,
 	.bmdma_setup            = ata_bmdma_setup,
 	.bmdma_start            = ata_bmdma_start,
+	.bmdma_stop		= ata_bmdma_stop,
+	.bmdma_status		= ata_bmdma_status,
 	.qc_prep		= ata_qc_prep,
 	.qc_issue		= ata_qc_issue_prot,
 	.eng_timeout		= ata_eng_timeout,
@@ -255,6 +257,7 @@
 	static int printed_version;
 	struct ata_probe_ent *probe_ent = NULL;
 	unsigned long base;
+	int pci_dev_busy = 0;
 	void *mmio_base;
 	int rc;
 
@@ -274,8 +277,10 @@
 	}
 
 	rc = pci_request_regions(pdev, DRV_NAME);
-	if (rc)
+	if (rc) {
+		pci_dev_busy = 1;
 		goto err_out;
+	}
 
 	/*
 	 * Use 32 bit DMA mask, because 64 bit address support is poor.
@@ -352,7 +357,8 @@
 err_out_regions:
 	pci_release_regions(pdev);
 err_out:
-	pci_disable_device(pdev);
+	if (!pci_dev_busy)
+		pci_disable_device(pdev);
 	return rc;
 }
 
diff -Nru a/include/linux/libata.h b/include/linux/libata.h
--- a/include/linux/libata.h	2005-02-22 18:15:48 -08:00
+++ b/include/linux/libata.h	2005-02-22 18:15:48 -08:00
@@ -360,6 +360,9 @@
 	void (*port_stop) (struct ata_port *ap);
 
 	void (*host_stop) (struct ata_host_set *host_set);
+
+	void (*bmdma_stop) (struct ata_port *ap);
+	u8   (*bmdma_status) (struct ata_port *ap);
 };
 
 struct ata_port_info {
@@ -415,6 +418,8 @@
 			      unsigned int ofs, unsigned int len);
 extern void ata_bmdma_setup (struct ata_queued_cmd *qc);
 extern void ata_bmdma_start (struct ata_queued_cmd *qc);
+extern void ata_bmdma_stop(struct ata_port *ap);
+extern u8   ata_bmdma_status(struct ata_port *ap);
 extern void ata_bmdma_irq_clear(struct ata_port *ap);
 extern void ata_qc_complete(struct ata_queued_cmd *qc, u8 drv_stat);
 extern void ata_eng_timeout(struct ata_port *ap);
@@ -593,46 +598,6 @@
 static inline unsigned int sata_dev_present(struct ata_port *ap)
 {
 	return ((scr_read(ap, SCR_STATUS) & 0xf) == 0x3) ? 1 : 0;
-}
-
-static inline void ata_bmdma_stop(struct ata_port *ap)
-{
-	if (ap->flags & ATA_FLAG_MMIO) {
-		void __iomem *mmio = (void __iomem *) ap->ioaddr.bmdma_addr;
-
-		/* clear start/stop bit */
-		writeb(readb(mmio + ATA_DMA_CMD) & ~ATA_DMA_START,
-		      mmio + ATA_DMA_CMD);
-	} else {
-		/* clear start/stop bit */
-		outb(inb(ap->ioaddr.bmdma_addr + ATA_DMA_CMD) & ~ATA_DMA_START,
-		    ap->ioaddr.bmdma_addr + ATA_DMA_CMD);
-	}
-
-	/* one-PIO-cycle guaranteed wait, per spec, for HDMA1:0 transition */
-	ata_altstatus(ap);	      /* dummy read */
-}
-
-static inline void ata_bmdma_ack_irq(struct ata_port *ap)
-{
-	if (ap->flags & ATA_FLAG_MMIO) {
-		void __iomem *mmio = ((void __iomem *) ap->ioaddr.bmdma_addr) + ATA_DMA_STATUS;
-		writeb(readb(mmio), mmio);
-	} else {
-		unsigned long addr = ap->ioaddr.bmdma_addr + ATA_DMA_STATUS;
-		outb(inb(addr), addr);
-	}
-}
-
-static inline u8 ata_bmdma_status(struct ata_port *ap)
-{
-	u8 host_stat;
-	if (ap->flags & ATA_FLAG_MMIO) {
-		void __iomem *mmio = (void __iomem *) ap->ioaddr.bmdma_addr;
-		host_stat = readb(mmio + ATA_DMA_STATUS);
-	} else
-		host_stat = inb(ap->ioaddr.bmdma_addr + ATA_DMA_STATUS);
-	return host_stat;
 }
 
 static inline int ata_try_flush_cache(struct ata_device *dev)