From 1a660164c291f41b2aa853a7269b310933574ef9 Mon Sep 17 00:00:00 2001 From: Mark Lord Date: Wed, 25 Feb 2009 15:18:32 -0500 Subject: [libata] Export ata_pio_queue_task() so that it can be used from sata_mv. Signed-off-by: Mark Lord Signed-off-by: Jeff Garzik --- include/linux/libata.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/linux/libata.h b/include/linux/libata.h index dc18b87ed72..19af7d22a7f 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -1008,6 +1008,9 @@ extern int ata_cable_sata(struct ata_port *ap); extern int ata_cable_ignore(struct ata_port *ap); extern int ata_cable_unknown(struct ata_port *ap); +extern void ata_pio_queue_task(struct ata_port *ap, void *data, + unsigned long delay); + /* Timing helpers */ extern unsigned int ata_pio_need_iordy(const struct ata_device *); extern const struct ata_timing *ata_timing_find_mode(u8 xfer_mode); -- cgit v1.2.3-70-g09d2 From 22ddbd1e036ce035c1cccb2496aefafac79aba2c Mon Sep 17 00:00:00 2001 From: Erik Inge Bolsø Date: Sat, 14 Mar 2009 21:37:48 +0100 Subject: include/linux/ata.h: add some more transfer masks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Erik Inge Bolsø Signed-off-by: Jeff Garzik --- include/linux/ata.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include') diff --git a/include/linux/ata.h b/include/linux/ata.h index 9a061accd8b..3901b0022cd 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -108,6 +108,8 @@ enum { ATA_PIO5 = ATA_PIO4 | (1 << 5), ATA_PIO6 = ATA_PIO5 | (1 << 6), + ATA_PIO4_ONLY = (1 << 4), + ATA_SWDMA0 = (1 << 0), ATA_SWDMA1 = ATA_SWDMA0 | (1 << 1), ATA_SWDMA2 = ATA_SWDMA1 | (1 << 2), @@ -117,6 +119,8 @@ enum { ATA_MWDMA0 = (1 << 0), ATA_MWDMA1 = ATA_MWDMA0 | (1 << 1), ATA_MWDMA2 = ATA_MWDMA1 | (1 << 2), + ATA_MWDMA3 = ATA_MWDMA2 | (1 << 3), + ATA_MWDMA4 = ATA_MWDMA3 | (1 << 4), ATA_MWDMA12_ONLY = (1 << 1) | (1 << 2), ATA_MWDMA2_ONLY = (1 << 2), @@ -131,6 +135,8 @@ enum { ATA_UDMA7 = ATA_UDMA6 | (1 << 7), /* ATA_UDMA7 is just for completeness... doesn't exist (yet?). */ + ATA_UDMA24_ONLY = (1 << 2) | (1 << 4), + ATA_UDMA_MASK_40C = ATA_UDMA2, /* udma0-2 */ /* DMA-related */ -- cgit v1.2.3-70-g09d2 From 3d47aa8e7e7b2aa09256590388aa8dddc79280f9 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Tue, 24 Mar 2009 10:23:19 +0000 Subject: [libata] Drain data on errors If the device is signalling that there is data to drain after an error we should read the bytes out and throw them away. Without this some devices and controllers get wedged and don't recover. Based on earlier work by Mark Lord Signed-off-by: Alan Cox Signed-off-by: Jeff Garzik --- drivers/ata/libata-sff.c | 45 +++++++++++++++++++++++++++++++++++++++++++-- drivers/ata/pata_pcmcia.c | 34 +++++++++++++++++++++++++++++++++- include/linux/libata.h | 3 +++ 3 files changed, 79 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index f93dc029dfd..9a10cb055ac 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -52,6 +52,7 @@ const struct ata_port_operations ata_sff_port_ops = { .softreset = ata_sff_softreset, .hardreset = sata_sff_hardreset, .postreset = ata_sff_postreset, + .drain_fifo = ata_sff_drain_fifo, .error_handler = ata_sff_error_handler, .post_internal_cmd = ata_sff_post_internal_cmd, @@ -2198,6 +2199,39 @@ void ata_sff_postreset(struct ata_link *link, unsigned int *classes) } EXPORT_SYMBOL_GPL(ata_sff_postreset); +/** + * ata_sff_drain_fifo - Stock FIFO drain logic for SFF controllers + * @qc: command + * + * Drain the FIFO and device of any stuck data following a command + * failing to complete. In some cases this is neccessary before a + * reset will recover the device. + * + */ + +void ata_sff_drain_fifo(struct ata_queued_cmd *qc) +{ + int count; + struct ata_port *ap; + + /* We only need to flush incoming data when a command was running */ + if (qc == NULL || qc->dma_dir == DMA_TO_DEVICE) + return; + + ap = qc->ap; + /* Drain up to 64K of data before we give up this recovery method */ + for (count = 0; (ap->ops->sff_check_status(ap) & ATA_DRQ) + && count < 32768; count++) + ioread16(ap->ioaddr.data_addr); + + /* Can become DEBUG later */ + if (count) + ata_port_printk(ap, KERN_DEBUG, + "drained %d bytes to clear DRQ.\n", count); + +} +EXPORT_SYMBOL_GPL(ata_sff_drain_fifo); + /** * ata_sff_error_handler - Stock error handler for BMDMA controller * @ap: port to handle error for @@ -2239,7 +2273,8 @@ void ata_sff_error_handler(struct ata_port *ap) * really a timeout event, adjust error mask and * cancel frozen state. */ - if (qc->err_mask == AC_ERR_TIMEOUT && (host_stat & ATA_DMA_ERR)) { + if (qc->err_mask == AC_ERR_TIMEOUT + && (host_stat & ATA_DMA_ERR)) { qc->err_mask = AC_ERR_HOST_BUS; thaw = 1; } @@ -2250,6 +2285,13 @@ void ata_sff_error_handler(struct ata_port *ap) ata_sff_sync(ap); /* FIXME: We don't need this */ ap->ops->sff_check_status(ap); ap->ops->sff_irq_clear(ap); + /* We *MUST* do FIFO draining before we issue a reset as several + * devices helpfully clear their internal state and will lock solid + * if we touch the data port post reset. Pass qc in case anyone wants + * to do different PIO/DMA recovery or has per command fixups + */ + if (ap->ops->drain_fifo) + ap->ops->drain_fifo(qc); spin_unlock_irqrestore(ap->lock, flags); @@ -2959,4 +3001,3 @@ out: EXPORT_SYMBOL_GPL(ata_pci_sff_init_one); #endif /* CONFIG_PCI */ - diff --git a/drivers/ata/pata_pcmcia.c b/drivers/ata/pata_pcmcia.c index a5cbcc280b2..f4d009ed50a 100644 --- a/drivers/ata/pata_pcmcia.c +++ b/drivers/ata/pata_pcmcia.c @@ -42,7 +42,7 @@ #define DRV_NAME "pata_pcmcia" -#define DRV_VERSION "0.3.3" +#define DRV_VERSION "0.3.5" /* * Private data structure to glue stuff together @@ -126,6 +126,37 @@ static unsigned int ata_data_xfer_8bit(struct ata_device *dev, return buflen; } +/** + * pcmcia_8bit_drain_fifo - Stock FIFO drain logic for SFF controllers + * @qc: command + * + * Drain the FIFO and device of any stuck data following a command + * failing to complete. In some cases this is neccessary before a + * reset will recover the device. + * + */ + +void pcmcia_8bit_drain_fifo(struct ata_queued_cmd *qc) +{ + int count; + struct ata_port *ap; + + /* We only need to flush incoming data when a command was running */ + if (qc == NULL || qc->dma_dir == DMA_TO_DEVICE) + return; + + ap = qc->ap; + + /* Drain up to 64K of data before we give up this recovery method */ + for (count = 0; (ap->ops->sff_check_status(ap) & ATA_DRQ) + && count++ < 65536;) + ioread8(ap->ioaddr.data_addr); + + if (count) + ata_port_printk(ap, KERN_WARNING, "drained %d bytes to clear DRQ.\n", + count); + +} static struct scsi_host_template pcmcia_sht = { ATA_PIO_SHT(DRV_NAME), @@ -143,6 +174,7 @@ static struct ata_port_operations pcmcia_8bit_port_ops = { .sff_data_xfer = ata_data_xfer_8bit, .cable_detect = ata_cable_40wire, .set_mode = pcmcia_set_mode_8bit, + .drain_fifo = pcmcia_8bit_drain_fifo, }; #define CS_CHECK(fn, ret) \ diff --git a/include/linux/libata.h b/include/linux/libata.h index 19af7d22a7f..3a07a32dfc2 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -836,6 +836,8 @@ struct ata_port_operations { void (*bmdma_start)(struct ata_queued_cmd *qc); void (*bmdma_stop)(struct ata_queued_cmd *qc); u8 (*bmdma_status)(struct ata_port *ap); + + void (*drain_fifo)(struct ata_queued_cmd *qc); #endif /* CONFIG_ATA_SFF */ ssize_t (*em_show)(struct ata_port *ap, char *buf); @@ -1587,6 +1589,7 @@ extern int ata_sff_softreset(struct ata_link *link, unsigned int *classes, extern int sata_sff_hardreset(struct ata_link *link, unsigned int *class, unsigned long deadline); extern void ata_sff_postreset(struct ata_link *link, unsigned int *classes); +extern void ata_sff_drain_fifo(struct ata_queued_cmd *qc); extern void ata_sff_error_handler(struct ata_port *ap); extern void ata_sff_post_internal_cmd(struct ata_queued_cmd *qc); extern int ata_sff_port_start(struct ata_port *ap); -- cgit v1.2.3-70-g09d2 From c96f1732e25362d10ee7bcac1df8412a2e6b7d23 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Tue, 24 Mar 2009 10:23:46 +0000 Subject: [libata] Improve timeout handling On a timeout call a device specific handler early in the recovery so that we can complete and process successful commands which timed out due to IRQ loss or the like rather more elegantly. [Revised to exclude the timeout handling on a few devices that inherit from SFF but are not SFF enough to use the default timeout handler] Signed-off-by: Alan Cox Signed-off-by: Jeff Garzik --- drivers/ata/libata-eh.c | 19 +++++++++++++++++-- drivers/ata/libata-sff.c | 46 +++++++++++++++++++++++++++++++++++++++++++++- drivers/ata/pata_isapnp.c | 12 ++++++++++-- drivers/ata/pdc_adma.c | 2 ++ drivers/ata/sata_mv.c | 2 ++ drivers/ata/sata_nv.c | 1 + drivers/ata/sata_promise.c | 2 ++ drivers/ata/sata_qstor.c | 1 + drivers/ata/sata_vsc.c | 3 +++ include/linux/libata.h | 2 ++ 10 files changed, 85 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index ea890911d4f..01831312c36 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -547,7 +547,7 @@ void ata_scsi_error(struct Scsi_Host *host) /* For new EH, all qcs are finished in one of three ways - * normal completion, error completion, and SCSI timeout. - * Both cmpletions can race against SCSI timeout. When normal + * Both completions can race against SCSI timeout. When normal * completion wins, the qc never reaches EH. When error * completion wins, the qc has ATA_QCFLAG_FAILED set. * @@ -562,7 +562,19 @@ void ata_scsi_error(struct Scsi_Host *host) int nr_timedout = 0; spin_lock_irqsave(ap->lock, flags); - + + /* This must occur under the ap->lock as we don't want + a polled recovery to race the real interrupt handler + + The lost_interrupt handler checks for any completed but + non-notified command and completes much like an IRQ handler. + + We then fall into the error recovery code which will treat + this as if normal completion won the race */ + + if (ap->ops->lost_interrupt) + ap->ops->lost_interrupt(ap); + list_for_each_entry_safe(scmd, tmp, &host->eh_cmd_q, eh_entry) { struct ata_queued_cmd *qc; @@ -606,6 +618,9 @@ void ata_scsi_error(struct Scsi_Host *host) ap->eh_tries = ATA_EH_MAX_TRIES; } else spin_unlock_wait(ap->lock); + + /* If we timed raced normal completion and there is nothing to + recover nr_timedout == 0 why exactly are we doing error recovery ? */ repeat: /* invoke error handler */ diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index 9a10cb055ac..8332e97a9de 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -65,6 +65,8 @@ const struct ata_port_operations ata_sff_port_ops = { .sff_irq_on = ata_sff_irq_on, .sff_irq_clear = ata_sff_irq_clear, + .lost_interrupt = ata_sff_lost_interrupt, + .port_start = ata_sff_port_start, }; EXPORT_SYMBOL_GPL(ata_sff_port_ops); @@ -1647,7 +1649,7 @@ EXPORT_SYMBOL_GPL(ata_sff_qc_fill_rtf); * RETURNS: * One if interrupt was handled, zero if not (shared irq). */ -inline unsigned int ata_sff_host_intr(struct ata_port *ap, +unsigned int ata_sff_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc) { struct ata_eh_info *ehi = &ap->link.eh_info; @@ -1775,6 +1777,48 @@ irqreturn_t ata_sff_interrupt(int irq, void *dev_instance) } EXPORT_SYMBOL_GPL(ata_sff_interrupt); +/** + * ata_sff_lost_interrupt - Check for an apparent lost interrupt + * @ap: port that appears to have timed out + * + * Called from the libata error handlers when the core code suspects + * an interrupt has been lost. If it has complete anything we can and + * then return. Interface must support altstatus for this faster + * recovery to occur. + * + * Locking: + * Caller holds host lock + */ + +void ata_sff_lost_interrupt(struct ata_port *ap) +{ + u8 status; + struct ata_queued_cmd *qc; + + /* Only one outstanding command per SFF channel */ + qc = ata_qc_from_tag(ap, ap->link.active_tag); + /* Check we have a live one.. */ + if (qc == NULL || !(qc->flags & ATA_QCFLAG_ACTIVE)) + return; + /* We cannot lose an interrupt on a polled command */ + if (qc->tf.flags & ATA_TFLAG_POLLING) + return; + /* See if the controller thinks it is still busy - if so the command + isn't a lost IRQ but is still in progress */ + status = ata_sff_altstatus(ap); + if (status & ATA_BUSY) + return; + + /* There was a command running, we are no longer busy and we have + no interrupt. */ + ata_port_printk(ap, KERN_WARNING, "lost interrupt (Status 0x%x)\n", + status); + /* Run the host interrupt logic as if the interrupt had not been + lost */ + ata_sff_host_intr(ap, qc); +} +EXPORT_SYMBOL_GPL(ata_sff_lost_interrupt); + /** * ata_sff_freeze - Freeze SFF controller port * @ap: port to freeze diff --git a/drivers/ata/pata_isapnp.c b/drivers/ata/pata_isapnp.c index afa8f704271..4bceb8803a1 100644 --- a/drivers/ata/pata_isapnp.c +++ b/drivers/ata/pata_isapnp.c @@ -17,7 +17,7 @@ #include #define DRV_NAME "pata_isapnp" -#define DRV_VERSION "0.2.2" +#define DRV_VERSION "0.2.5" static struct scsi_host_template isapnp_sht = { ATA_PIO_SHT(DRV_NAME), @@ -28,6 +28,13 @@ static struct ata_port_operations isapnp_port_ops = { .cable_detect = ata_cable_40wire, }; +static struct ata_port_operations isapnp_noalt_port_ops = { + .inherits = &ata_sff_port_ops, + .cable_detect = ata_cable_40wire, + /* No altstatus so we don't want to use the lost interrupt poll */ + .lost_interrupt = ATA_OP_NULL, +}; + /** * isapnp_init_one - attach an isapnp interface * @idev: PnP device @@ -65,7 +72,7 @@ static int isapnp_init_one(struct pnp_dev *idev, const struct pnp_device_id *dev ap = host->ports[0]; - ap->ops = &isapnp_port_ops; + ap->ops = &isapnp_noalt_port_ops; ap->pio_mask = ATA_PIO0; ap->flags |= ATA_FLAG_SLAVE_POSS; @@ -76,6 +83,7 @@ static int isapnp_init_one(struct pnp_dev *idev, const struct pnp_device_id *dev pnp_port_start(idev, 1), 1); ap->ioaddr.altstatus_addr = ctl_addr; ap->ioaddr.ctl_addr = ctl_addr; + ap->ops = &isapnp_port_ops; } ata_sff_std_ports(&ap->ioaddr); diff --git a/drivers/ata/pdc_adma.c b/drivers/ata/pdc_adma.c index c509c206a45..39588178d02 100644 --- a/drivers/ata/pdc_adma.c +++ b/drivers/ata/pdc_adma.c @@ -148,6 +148,8 @@ static struct scsi_host_template adma_ata_sht = { static struct ata_port_operations adma_ata_ops = { .inherits = &ata_sff_port_ops, + .lost_interrupt = ATA_OP_NULL, + .check_atapi_dma = adma_check_atapi_dma, .qc_prep = adma_qc_prep, .qc_issue = adma_qc_issue, diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index 8a751054c8a..a377226b81c 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -646,6 +646,8 @@ static struct scsi_host_template mv6_sht = { static struct ata_port_operations mv5_ops = { .inherits = &ata_sff_port_ops, + .lost_interrupt = ATA_OP_NULL, + .qc_defer = mv_qc_defer, .qc_prep = mv_qc_prep, .qc_issue = mv_qc_issue, diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c index 2f523f8c27f..6cda12ba812 100644 --- a/drivers/ata/sata_nv.c +++ b/drivers/ata/sata_nv.c @@ -408,6 +408,7 @@ static struct scsi_host_template nv_swncq_sht = { static struct ata_port_operations nv_common_ops = { .inherits = &ata_bmdma_port_ops, + .lost_interrupt = ATA_OP_NULL, .scr_read = nv_scr_read, .scr_write = nv_scr_write, }; diff --git a/drivers/ata/sata_promise.c b/drivers/ata/sata_promise.c index 3ad2b886363..b1fd7d62071 100644 --- a/drivers/ata/sata_promise.c +++ b/drivers/ata/sata_promise.c @@ -176,7 +176,9 @@ static const struct ata_port_operations pdc_common_ops = { .check_atapi_dma = pdc_check_atapi_dma, .qc_prep = pdc_qc_prep, .qc_issue = pdc_qc_issue, + .sff_irq_clear = pdc_irq_clear, + .lost_interrupt = ATA_OP_NULL, .post_internal_cmd = pdc_post_internal_cmd, .error_handler = pdc_error_handler, diff --git a/drivers/ata/sata_qstor.c b/drivers/ata/sata_qstor.c index 7112d89fd9f..c3936d35cda 100644 --- a/drivers/ata/sata_qstor.c +++ b/drivers/ata/sata_qstor.c @@ -147,6 +147,7 @@ static struct ata_port_operations qs_ata_ops = { .softreset = ATA_OP_NULL, .error_handler = qs_error_handler, .post_internal_cmd = ATA_OP_NULL, + .lost_interrupt = ATA_OP_NULL, .scr_read = qs_scr_read, .scr_write = qs_scr_write, diff --git a/drivers/ata/sata_vsc.c b/drivers/ata/sata_vsc.c index ef211f333d7..ed70bd28fa2 100644 --- a/drivers/ata/sata_vsc.c +++ b/drivers/ata/sata_vsc.c @@ -308,6 +308,9 @@ static struct scsi_host_template vsc_sata_sht = { static struct ata_port_operations vsc_sata_ops = { .inherits = &ata_bmdma_port_ops, + /* The IRQ handling is not quite standard SFF behaviour so we + cannot use the default lost interrupt handler */ + .lost_interrupt = ATA_OP_NULL, .sff_tf_load = vsc_sata_tf_load, .sff_tf_read = vsc_sata_tf_read, .freeze = vsc_freeze, diff --git a/include/linux/libata.h b/include/linux/libata.h index 3a07a32dfc2..76262d83656 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -795,6 +795,7 @@ struct ata_port_operations { ata_reset_fn_t pmp_hardreset; ata_postreset_fn_t pmp_postreset; void (*error_handler)(struct ata_port *ap); + void (*lost_interrupt)(struct ata_port *ap); void (*post_internal_cmd)(struct ata_queued_cmd *qc); /* @@ -1577,6 +1578,7 @@ extern bool ata_sff_qc_fill_rtf(struct ata_queued_cmd *qc); extern unsigned int ata_sff_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc); extern irqreturn_t ata_sff_interrupt(int irq, void *dev_instance); +extern void ata_sff_lost_interrupt(struct ata_port *ap); extern void ata_sff_freeze(struct ata_port *ap); extern void ata_sff_thaw(struct ata_port *ap); extern int ata_sff_prereset(struct ata_link *link, unsigned long deadline); -- cgit v1.2.3-70-g09d2