summary refs log tree commit diff
path: root/drivers/ata
diff options
context:
space:
mode:
authorJeff Garzik <jeff@garzik.org>2007-08-03 11:10:07 -0400
committerJeff Garzik <jeff@garzik.org>2007-10-12 14:55:30 -0400
commit640fdb504941fa2b9f6f274716fc9f97f2bf6bff (patch)
tree3e712ef4edacd3c1701aaad77ca2e2819b395053 /drivers/ata
parent782e3b3b3804c38d5130c7f21d7ec7bf6709023f (diff)
downloadlinux-640fdb504941fa2b9f6f274716fc9f97f2bf6bff.tar.gz
[libata] pdc_adma: convert to new exception handling (EH) framework
Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/ata')
-rw-r--r--drivers/ata/pdc_adma.c82
1 files changed, 67 insertions, 15 deletions
diff --git a/drivers/ata/pdc_adma.c b/drivers/ata/pdc_adma.c
index 5c79271401af..c95c1bf7df37 100644
--- a/drivers/ata/pdc_adma.c
+++ b/drivers/ata/pdc_adma.c
@@ -92,6 +92,8 @@ enum {
 
 	/* CPB bits */
 	cDONE			= (1 << 0),
+	cATERR			= (1 << 3),
+
 	cVLD			= (1 << 0),
 	cDAT			= (1 << 2),
 	cIEN			= (1 << 3),
@@ -131,14 +133,15 @@ static int adma_ata_init_one (struct pci_dev *pdev,
 static int adma_port_start(struct ata_port *ap);
 static void adma_host_stop(struct ata_host *host);
 static void adma_port_stop(struct ata_port *ap);
-static void adma_phy_reset(struct ata_port *ap);
 static void adma_qc_prep(struct ata_queued_cmd *qc);
 static unsigned int adma_qc_issue(struct ata_queued_cmd *qc);
 static int adma_check_atapi_dma(struct ata_queued_cmd *qc);
 static void adma_bmdma_stop(struct ata_queued_cmd *qc);
 static u8 adma_bmdma_status(struct ata_port *ap);
 static void adma_irq_clear(struct ata_port *ap);
-static void adma_eng_timeout(struct ata_port *ap);
+static void adma_freeze(struct ata_port *ap);
+static void adma_thaw(struct ata_port *ap);
+static void adma_error_handler(struct ata_port *ap);
 
 static struct scsi_host_template adma_ata_sht = {
 	.module			= THIS_MODULE,
@@ -165,12 +168,13 @@ static const struct ata_port_operations adma_ata_ops = {
 	.exec_command		= ata_exec_command,
 	.check_status		= ata_check_status,
 	.dev_select		= ata_std_dev_select,
-	.phy_reset		= adma_phy_reset,
 	.check_atapi_dma	= adma_check_atapi_dma,
 	.data_xfer		= ata_data_xfer,
 	.qc_prep		= adma_qc_prep,
 	.qc_issue		= adma_qc_issue,
-	.eng_timeout		= adma_eng_timeout,
+	.freeze			= adma_freeze,
+	.thaw			= adma_thaw,
+	.error_handler		= adma_error_handler,
 	.irq_clear		= adma_irq_clear,
 	.irq_on			= ata_irq_on,
 	.irq_ack		= ata_irq_ack,
@@ -184,7 +188,7 @@ static const struct ata_port_operations adma_ata_ops = {
 static struct ata_port_info adma_port_info[] = {
 	/* board_1841_idx */
 	{
-		.flags		= ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST |
+		.flags		= ATA_FLAG_SLAVE_POSS |
 				  ATA_FLAG_NO_LEGACY | ATA_FLAG_MMIO |
 				  ATA_FLAG_PIO_POLLING,
 		.pio_mask	= 0x10, /* pio4 */
@@ -273,24 +277,41 @@ static inline void adma_enter_reg_mode(struct ata_port *ap)
 	readb(chan + ADMA_STATUS);	/* flush */
 }
 
-static void adma_phy_reset(struct ata_port *ap)
+static void adma_freeze(struct ata_port *ap)
 {
-	struct adma_port_priv *pp = ap->private_data;
+	void __iomem *chan = ADMA_PORT_REGS(ap);
+
+	/* mask/clear ATA interrupts */
+	writeb(ATA_NIEN, ap->ioaddr.ctl_addr);
+	ata_check_status(ap);
+
+	/* reset ADMA to idle state */
+	writew(aPIOMD4 | aNIEN | aRSTADM, chan + ADMA_CONTROL);
+	udelay(2);
+	writew(aPIOMD4 | aNIEN, chan + ADMA_CONTROL);
+	udelay(2);
+}
 
-	pp->state = adma_state_idle;
+static void adma_thaw(struct ata_port *ap)
+{
 	adma_reinit_engine(ap);
-	ata_port_probe(ap);
-	ata_bus_reset(ap);
 }
 
-static void adma_eng_timeout(struct ata_port *ap)
+static int adma_prereset(struct ata_port *ap, unsigned long deadline)
 {
 	struct adma_port_priv *pp = ap->private_data;
 
 	if (pp->state != adma_state_idle) /* healthy paranoia */
 		pp->state = adma_state_mmio;
 	adma_reinit_engine(ap);
-	ata_eng_timeout(ap);
+
+	return ata_std_prereset(ap, deadline);
+}
+
+static void adma_error_handler(struct ata_port *ap)
+{
+	ata_do_eh(ap, adma_prereset, ata_std_softreset, NULL,
+		  ata_std_postreset);
 }
 
 static int adma_fill_sg(struct ata_queued_cmd *qc)
@@ -466,12 +487,31 @@ static inline unsigned int adma_intr_pkt(struct ata_host *host)
 			continue;
 		qc = ata_qc_from_tag(ap, ap->active_tag);
 		if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING))) {
-			if ((status & (aPERR | aPSD | aUIRQ)))
+			if (status & aPERR)
+				qc->err_mask |= AC_ERR_HOST_BUS;
+			else if ((status & (aPSD | aUIRQ)))
 				qc->err_mask |= AC_ERR_OTHER;
+
+			if (pp->pkt[0] & cATERR)
+				qc->err_mask |= AC_ERR_DEV;
 			else if (pp->pkt[0] != cDONE)
 				qc->err_mask |= AC_ERR_OTHER;
 
-			ata_qc_complete(qc);
+			if (!qc->err_mask)
+				ata_qc_complete(qc);
+			else {
+				struct ata_eh_info *ehi = &ap->eh_info;
+				ata_ehi_clear_desc(ehi);
+				ata_ehi_push_desc(ehi,
+					"ADMA-status 0x%02X", status);
+				ata_ehi_push_desc(ehi,
+					"pkt[0] 0x%02X", pp->pkt[0]);
+
+				if (qc->err_mask == AC_ERR_DEV)
+					ata_port_abort(ap);
+				else
+					ata_port_freeze(ap);
+			}
 		}
 	}
 	return handled;
@@ -502,7 +542,19 @@ static inline unsigned int adma_intr_mmio(struct ata_host *host)
 				/* complete taskfile transaction */
 				pp->state = adma_state_idle;
 				qc->err_mask |= ac_err_mask(status);
-				ata_qc_complete(qc);
+				if (!qc->err_mask)
+					ata_qc_complete(qc);
+				else {
+					struct ata_eh_info *ehi = &ap->eh_info;
+					ata_ehi_clear_desc(ehi);
+					ata_ehi_push_desc(ehi,
+						"status 0x%02X", status);
+
+					if (qc->err_mask == AC_ERR_DEV)
+						ata_port_abort(ap);
+					else
+						ata_port_freeze(ap);
+				}
 				handled = 1;
 			}
 		}