scc_pata.c: Workaround for errata A308 (take 2)
[pandora-kernel.git] / drivers / ide / pci / scc_pata.c
index f84bf79..fd4b1a2 100644 (file)
@@ -165,9 +165,9 @@ scc_ide_outbsync(ide_drive_t * drive, u8 addr, unsigned long port)
        ide_hwif_t *hwif = HWIF(drive);
 
        out_be32((void*)port, addr);
-       __asm__ __volatile__("eieio":::"memory");
+       eieio();
        in_be32((void*)(hwif->dma_base + 0x01c));
-       __asm__ __volatile__("eieio":::"memory");
+       eieio();
 }
 
 static void
@@ -189,23 +189,6 @@ scc_ide_outsl(unsigned long port, void *addr, u32 count)
        }
 }
 
-/**
- *     scc_ratemask    -       Compute available modes
- *     @drive: IDE drive
- *
- *     Compute the available speeds for the devices on the interface.
- *     Enforce UDMA33 as a limit if there is no 80pin cable present.
- */
-
-static u8 scc_ratemask(ide_drive_t *drive)
-{
-       u8 mode = 4;
-
-       if (!eighty_ninty_three(drive))
-               mode = min(mode, (u8)1);
-       return mode;
-}
-
 /**
  *     scc_tuneproc    -       tune a drive PIO mode
  *     @drive: drive to tune
@@ -273,7 +256,7 @@ static void scc_tuneproc(ide_drive_t *drive, byte mode_wanted)
 static int scc_tune_chipset(ide_drive_t *drive, byte xferspeed)
 {
        ide_hwif_t *hwif = HWIF(drive);
-       u8 speed = ide_rate_filter(scc_ratemask(drive), xferspeed);
+       u8 speed = ide_rate_filter(drive, xferspeed);
        struct scc_ports *ports = ide_get_hwifdata(hwif);
        unsigned long ctl_base = ports->ctl;
        unsigned long cckctrl_port = ctl_base + 0xff0;
@@ -338,26 +321,6 @@ static int scc_tune_chipset(ide_drive_t *drive, byte xferspeed)
        return ide_config_drive_speed(drive, speed);
 }
 
-/**
- *     scc_config_chipset_for_dma      -       configure for DMA
- *     @drive: drive to configure
- *
- *     Called by scc_config_drive_for_dma().
- */
-
-static int scc_config_chipset_for_dma(ide_drive_t *drive)
-{
-       u8 speed = ide_dma_speed(drive, scc_ratemask(drive));
-
-       if (!speed)
-               return 0;
-
-       if (scc_tune_chipset(drive, speed))
-               return 0;
-
-       return ide_dma_enable(drive);
-}
-
 /**
  *     scc_configure_drive_for_dma     -       set up for DMA transfers
  *     @drive: drive we are going to set up
@@ -371,7 +334,7 @@ static int scc_config_chipset_for_dma(ide_drive_t *drive)
 
 static int scc_config_drive_for_dma(ide_drive_t *drive)
 {
-       if (ide_use_dma(drive) && scc_config_chipset_for_dma(drive))
+       if (ide_tune_dma(drive))
                return 0;
 
        if (ide_use_fast_pio(drive))
@@ -438,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);
@@ -506,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)
@@ -535,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
@@ -739,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;
@@ -753,7 +757,7 @@ static void __devinit init_hwif_scc(ide_hwif_t *hwif)
        hwif->atapi_dma = 1;
 
        /* we support 80c cable only. */
-       hwif->udma_four = 1;
+       hwif->cbl = ATA_CBL_PATA80;
 
        hwif->autodma = 0;
        if (!noautodma)