From 4ae41ff887224b39c3c3c673a918193e90be558f Mon Sep 17 00:00:00 2001 From: Kou Ishizaki Date: Fri, 20 Jul 2007 01:11:53 +0200 Subject: scc_pata.c: Workaround for errata A308 (take 2) Workaround for errata A308: turn down the UDMA mode and retry the DMA command when the data lost condition is detected. take2: udma_filter() hook is used to limit ATAPI UDMA mode. Signed-off-by: Kou Ishizaki Signed-off-by: Akira Iguchi Cc: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/pci/scc_pata.c | 67 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index a96333c74bf1..fd4b1a24ecab 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -401,6 +401,33 @@ static int scc_ide_dma_end(ide_drive_t * drive) ide_hwif_t *hwif = HWIF(drive); unsigned long intsts_port = hwif->dma_base + 0x014; u32 reg; + int dma_stat, data_loss = 0; + static int retry = 0; + + /* errata A308 workaround: Step5 (check data loss) */ + /* We don't check non ide_disk because it is limited to UDMA4 */ + if (!(in_be32((void __iomem *)IDE_ALTSTATUS_REG) & ERR_STAT) && + drive->media == ide_disk && drive->current_speed > XFER_UDMA_4) { + reg = in_be32((void __iomem *)intsts_port); + if (!(reg & INTSTS_ACTEINT)) { + printk(KERN_WARNING "%s: operation failed (transfer data loss)\n", + drive->name); + data_loss = 1; + if (retry++) { + struct request *rq = HWGROUP(drive)->rq; + int unit; + /* ERROR_RESET and drive->crc_count are needed + * to reduce DMA transfer mode in retry process. + */ + if (rq) + rq->errors |= ERROR_RESET; + for (unit = 0; unit < MAX_DRIVES; unit++) { + ide_drive_t *drive = &hwif->drives[unit]; + drive->crc_count++; + } + } + } + } while (1) { reg = in_be32((void __iomem *)intsts_port); @@ -469,27 +496,25 @@ static int scc_ide_dma_end(ide_drive_t * drive) break; } - return __ide_dma_end(drive); + dma_stat = __ide_dma_end(drive); + if (data_loss) + dma_stat |= 2; /* emulate DMA error (to retry command) */ + return dma_stat; } /* returns 1 if dma irq issued, 0 otherwise */ static int scc_dma_test_irq(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); - u8 dma_stat = hwif->INB(hwif->dma_status); + ide_hwif_t *hwif = HWIF(drive); + u32 int_stat = in_be32((void __iomem *)hwif->dma_base + 0x014); - /* return 1 if INTR asserted */ - if ((dma_stat & 4) == 4) + /* SCC errata A252,A308 workaround: Step4 */ + if ((in_be32((void __iomem *)IDE_ALTSTATUS_REG) & ERR_STAT) && + (int_stat & INTSTS_INTRQ)) return 1; - /* Workaround for PTERADD: emulate DMA_INTR when - * - IDE_STATUS[ERR] = 1 - * - INT_STATUS[INTRQ] = 1 - * - DMA_STATUS[IORACTA] = 1 - */ - if (in_be32((void __iomem *)IDE_ALTSTATUS_REG) & ERR_STAT && - in_be32((void __iomem *)(hwif->dma_base + 0x014)) & INTSTS_INTRQ && - dma_stat & 1) + /* SCC errata A308 workaround: Step5 (polling IOIRQS) */ + if (int_stat & INTSTS_IOIRQS) return 1; if (!drive->waiting_for_dma) @@ -498,6 +523,21 @@ static int scc_dma_test_irq(ide_drive_t *drive) return 0; } +static u8 scc_udma_filter(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + u8 mask = hwif->ultra_mask; + + /* errata A308 workaround: limit non ide_disk drive to UDMA4 */ + if ((drive->media != ide_disk) && (mask & 0xE0)) { + printk(KERN_INFO "%s: limit %s to UDMA4\n", + SCC_PATA_NAME, drive->name); + mask = 0x1F; + } + + return mask; +} + /** * setup_mmio_scc - map CTRL/BMID region * @dev: PCI device we are configuring @@ -702,6 +742,7 @@ static void __devinit init_hwif_scc(ide_hwif_t *hwif) hwif->tuneproc = scc_tuneproc; hwif->ide_dma_check = scc_config_drive_for_dma; hwif->ide_dma_test_irq = scc_dma_test_irq; + hwif->udma_filter = scc_udma_filter; hwif->drives[0].autotune = IDE_TUNE_AUTO; hwif->drives[1].autotune = IDE_TUNE_AUTO; -- cgit v1.2.2