diff --git a/drivers/ata/pdc_adma.c b/drivers/ata/pdc_adma.c index 5c79271401afc29ecb216ab46b6c46b7b82ff1a5..c95c1bf7df37787b709088bdb45ee87d571bef65 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; } }