fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / ide / ide-lib.c
index b2fa5ec..32e2b16 100644 (file)
@@ -1,4 +1,3 @@
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/string.h>
 #include <linux/delay.h>
 #include <linux/hdreg.h>
 #include <linux/ide.h>
+#include <linux/bitops.h>
 
 #include <asm/byteorder.h>
 #include <asm/irq.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
-#include <asm/bitops.h>
 
 /*
  *     IDE library routines. These are plug in code that most 
@@ -72,75 +71,96 @@ EXPORT_SYMBOL(ide_xfer_verbose);
 /**
  *     ide_dma_speed   -       compute DMA speed
  *     @drive: drive
- *     @mode; intended mode
+ *     @mode:  modes available
  *
  *     Checks the drive capabilities and returns the speed to use
- *     for the transfer. Returns -1 if the requested mode is unknown
- *     (eg PIO)
+ *     for the DMA transfer.  Returns 0 if the drive is incapable
+ *     of DMA transfers.
  */
  
 u8 ide_dma_speed(ide_drive_t *drive, u8 mode)
 {
        struct hd_driveid *id   = drive->id;
        ide_hwif_t *hwif        = HWIF(drive);
+       u8 ultra_mask, mwdma_mask, swdma_mask;
        u8 speed = 0;
 
        if (drive->media != ide_disk && hwif->atapi_dma == 0)
                return 0;
 
-       switch(mode) {
-               case 0x04:
-                       if ((id->dma_ultra & 0x0040) &&
-                           (id->dma_ultra & hwif->ultra_mask))
-                               { speed = XFER_UDMA_6; break; }
-               case 0x03:
-                       if ((id->dma_ultra & 0x0020) &&
-                           (id->dma_ultra & hwif->ultra_mask))
-                               { speed = XFER_UDMA_5; break; }
-               case 0x02:
-                       if ((id->dma_ultra & 0x0010) &&
-                           (id->dma_ultra & hwif->ultra_mask))
-                               { speed = XFER_UDMA_4; break; }
-                       if ((id->dma_ultra & 0x0008) &&
-                           (id->dma_ultra & hwif->ultra_mask))
-                               { speed = XFER_UDMA_3; break; }
-               case 0x01:
-                       if ((id->dma_ultra & 0x0004) &&
-                           (id->dma_ultra & hwif->ultra_mask))
-                               { speed = XFER_UDMA_2; break; }
-                       if ((id->dma_ultra & 0x0002) &&
-                           (id->dma_ultra & hwif->ultra_mask))
-                               { speed = XFER_UDMA_1; break; }
-                       if ((id->dma_ultra & 0x0001) &&
-                           (id->dma_ultra & hwif->ultra_mask))
-                               { speed = XFER_UDMA_0; break; }
-               case 0x00:
-                       if ((id->dma_mword & 0x0004) &&
-                           (id->dma_mword & hwif->mwdma_mask))
-                               { speed = XFER_MW_DMA_2; break; }
-                       if ((id->dma_mword & 0x0002) &&
-                           (id->dma_mword & hwif->mwdma_mask))
-                               { speed = XFER_MW_DMA_1; break; }
-                       if ((id->dma_mword & 0x0001) &&
-                           (id->dma_mword & hwif->mwdma_mask))
-                               { speed = XFER_MW_DMA_0; break; }
-                       if ((id->dma_1word & 0x0004) &&
-                           (id->dma_1word & hwif->swdma_mask))
-                               { speed = XFER_SW_DMA_2; break; }
-                       if ((id->dma_1word & 0x0002) &&
-                           (id->dma_1word & hwif->swdma_mask))
-                               { speed = XFER_SW_DMA_1; break; }
-                       if ((id->dma_1word & 0x0001) &&
-                           (id->dma_1word & hwif->swdma_mask))
-                               { speed = XFER_SW_DMA_0; break; }
-       }
+       /* Capable of UltraDMA modes? */
+       ultra_mask = id->dma_ultra & hwif->ultra_mask;
+
+       if (!(id->field_valid & 4))
+               mode = 0;       /* fallback to MW/SW DMA if no UltraDMA */
+
+       switch (mode) {
+       case 4:
+               if (ultra_mask & 0x40) {
+                       speed = XFER_UDMA_6;
+                       break;
+               }
+       case 3:
+               if (ultra_mask & 0x20) {
+                       speed = XFER_UDMA_5;
+                       break;
+               }
+       case 2:
+               if (ultra_mask & 0x10) {
+                       speed = XFER_UDMA_4;
+                       break;
+               }
+               if (ultra_mask & 0x08) {
+                       speed = XFER_UDMA_3;
+                       break;
+               }
+       case 1:
+               if (ultra_mask & 0x04) {
+                       speed = XFER_UDMA_2;
+                       break;
+               }
+               if (ultra_mask & 0x02) {
+                       speed = XFER_UDMA_1;
+                       break;
+               }
+               if (ultra_mask & 0x01) {
+                       speed = XFER_UDMA_0;
+                       break;
+               }
+       case 0:
+               mwdma_mask = id->dma_mword & hwif->mwdma_mask;
+
+               if (mwdma_mask & 0x04) {
+                       speed = XFER_MW_DMA_2;
+                       break;
+               }
+               if (mwdma_mask & 0x02) {
+                       speed = XFER_MW_DMA_1;
+                       break;
+               }
+               if (mwdma_mask & 0x01) {
+                       speed = XFER_MW_DMA_0;
+                       break;
+               }
 
-//     printk("%s: %s: mode 0x%02x, speed 0x%02x\n",
-//             __FUNCTION__, drive->name, mode, speed);
+               swdma_mask = id->dma_1word & hwif->swdma_mask;
+
+               if (swdma_mask & 0x04) {
+                       speed = XFER_SW_DMA_2;
+                       break;
+               }
+               if (swdma_mask & 0x02) {
+                       speed = XFER_SW_DMA_1;
+                       break;
+               }
+               if (swdma_mask & 0x01) {
+                       speed = XFER_SW_DMA_0;
+                       break;
+               }
+       }
 
        return speed;
 }
-
 EXPORT_SYMBOL(ide_dma_speed);
 
 
@@ -164,8 +184,7 @@ u8 ide_rate_filter (u8 mode, u8 speed)
 //     printk("%s: mode 0x%02x, speed 0x%02x\n", __FUNCTION__, mode, speed);
 
        /* So that we remember to update this if new modes appear */
-       if (mode > 4)
-               BUG();
+       BUG_ON(mode > 4);
        return min(speed, speed_max[mode]);
 #else /* !CONFIG_BLK_DEV_IDEDMA */
        return min(speed, (u8)XFER_PIO_4);
@@ -421,8 +440,6 @@ void ide_toggle_bounce(ide_drive_t *drive, int on)
                blk_queue_bounce_limit(drive->queue, addr);
 }
 
-EXPORT_SYMBOL(ide_toggle_bounce);
-
 /**
  *     ide_set_xfer_rate       -       set transfer rate
  *     @drive: drive to set
@@ -447,6 +464,112 @@ int ide_set_xfer_rate(ide_drive_t *drive, u8 rate)
 
 EXPORT_SYMBOL_GPL(ide_set_xfer_rate);
 
+static void ide_dump_opcode(ide_drive_t *drive)
+{
+       struct request *rq;
+       u8 opcode = 0;
+       int found = 0;
+
+       spin_lock(&ide_lock);
+       rq = NULL;
+       if (HWGROUP(drive))
+               rq = HWGROUP(drive)->rq;
+       spin_unlock(&ide_lock);
+       if (!rq)
+               return;
+       if (rq->cmd_type == REQ_TYPE_ATA_CMD ||
+           rq->cmd_type == REQ_TYPE_ATA_TASK) {
+               char *args = rq->buffer;
+               if (args) {
+                       opcode = args[0];
+                       found = 1;
+               }
+       } else if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) {
+               ide_task_t *args = rq->special;
+               if (args) {
+                       task_struct_t *tf = (task_struct_t *) args->tfRegister;
+                       opcode = tf->command;
+                       found = 1;
+               }
+       }
+
+       printk("ide: failed opcode was: ");
+       if (!found)
+               printk("unknown\n");
+       else
+               printk("0x%02x\n", opcode);
+}
+
+static u8 ide_dump_ata_status(ide_drive_t *drive, const char *msg, u8 stat)
+{
+       ide_hwif_t *hwif = HWIF(drive);
+       unsigned long flags;
+       u8 err = 0;
+
+       local_irq_save(flags);
+       printk("%s: %s: status=0x%02x { ", drive->name, msg, stat);
+       if (stat & BUSY_STAT)
+               printk("Busy ");
+       else {
+               if (stat & READY_STAT)  printk("DriveReady ");
+               if (stat & WRERR_STAT)  printk("DeviceFault ");
+               if (stat & SEEK_STAT)   printk("SeekComplete ");
+               if (stat & DRQ_STAT)    printk("DataRequest ");
+               if (stat & ECC_STAT)    printk("CorrectedError ");
+               if (stat & INDEX_STAT)  printk("Index ");
+               if (stat & ERR_STAT)    printk("Error ");
+       }
+       printk("}\n");
+       if ((stat & (BUSY_STAT|ERR_STAT)) == ERR_STAT) {
+               err = hwif->INB(IDE_ERROR_REG);
+               printk("%s: %s: error=0x%02x { ", drive->name, msg, err);
+               if (err & ABRT_ERR)     printk("DriveStatusError ");
+               if (err & ICRC_ERR)
+                       printk((err & ABRT_ERR) ? "BadCRC " : "BadSector ");
+               if (err & ECC_ERR)      printk("UncorrectableError ");
+               if (err & ID_ERR)       printk("SectorIdNotFound ");
+               if (err & TRK0_ERR)     printk("TrackZeroNotFound ");
+               if (err & MARK_ERR)     printk("AddrMarkNotFound ");
+               printk("}");
+               if ((err & (BBD_ERR | ABRT_ERR)) == BBD_ERR ||
+                   (err & (ECC_ERR|ID_ERR|MARK_ERR))) {
+                       if (drive->addressing == 1) {
+                               __u64 sectors = 0;
+                               u32 low = 0, high = 0;
+                               low = ide_read_24(drive);
+                               hwif->OUTB(drive->ctl|0x80, IDE_CONTROL_REG);
+                               high = ide_read_24(drive);
+                               sectors = ((__u64)high << 24) | low;
+                               printk(", LBAsect=%llu, high=%d, low=%d",
+                                      (unsigned long long) sectors,
+                                      high, low);
+                       } else {
+                               u8 cur = hwif->INB(IDE_SELECT_REG);
+                               if (cur & 0x40) {       /* using LBA? */
+                                       printk(", LBAsect=%ld", (unsigned long)
+                                        ((cur&0xf)<<24)
+                                        |(hwif->INB(IDE_HCYL_REG)<<16)
+                                        |(hwif->INB(IDE_LCYL_REG)<<8)
+                                        | hwif->INB(IDE_SECTOR_REG));
+                               } else {
+                                       printk(", CHS=%d/%d/%d",
+                                        (hwif->INB(IDE_HCYL_REG)<<8) +
+                                         hwif->INB(IDE_LCYL_REG),
+                                         cur & 0xf,
+                                         hwif->INB(IDE_SECTOR_REG));
+                               }
+                       }
+                       if (HWGROUP(drive) && HWGROUP(drive)->rq)
+                               printk(", sector=%llu",
+                                       (unsigned long long)HWGROUP(drive)->rq->sector);
+               }
+               printk("\n");
+       }
+       ide_dump_opcode(drive);
+       local_irq_restore(flags);
+       return err;
+}
+
 /**
  *     ide_dump_atapi_status       -       print human readable atapi status
  *     @drive: drive that status applies to
@@ -455,7 +578,8 @@ EXPORT_SYMBOL_GPL(ide_set_xfer_rate);
  *
  *     Error reporting, in human readable form (luxurious, but a memory hog).
  */
-byte ide_dump_atapi_status (ide_drive_t *drive, const char *msg, byte stat)
+
+static u8 ide_dump_atapi_status(ide_drive_t *drive, const char *msg, u8 stat)
 {
        unsigned long flags;
 
@@ -463,9 +587,9 @@ byte ide_dump_atapi_status (ide_drive_t *drive, const char *msg, byte stat)
        atapi_error_t error;
 
        status.all = stat;
-       local_irq_set(flags);
-       printk("%s: %s: status=0x%02x", drive->name, msg, stat);
-       printk(" { ");
+       error.all = 0;
+       local_irq_save(flags);
+       printk("%s: %s: status=0x%02x { ", drive->name, msg, stat);
        if (status.b.bsy)
                printk("Busy ");
        else {
@@ -477,21 +601,39 @@ byte ide_dump_atapi_status (ide_drive_t *drive, const char *msg, byte stat)
                if (status.b.idx)       printk("Index ");
                if (status.b.check)     printk("Error ");
        }
-       printk("}");
-       printk("\n");
-       if ((status.all & (status.b.bsy|status.b.check)) == status.b.check) {
+       printk("}\n");
+       if (status.b.check && !status.b.bsy) {
                error.all = HWIF(drive)->INB(IDE_ERROR_REG);
-               printk("%s: %s: error=0x%02x", drive->name, msg, error.all);
+               printk("%s: %s: error=0x%02x { ", drive->name, msg, error.all);
                if (error.b.ili)        printk("IllegalLengthIndication ");
                if (error.b.eom)        printk("EndOfMedia ");
-               if (error.b.abrt)       printk("Aborted Command ");
+               if (error.b.abrt)       printk("AbortedCommand ");
                if (error.b.mcr)        printk("MediaChangeRequested ");
-               if (error.b.sense_key)  printk("LastFailedSense 0x%02x ",
+               if (error.b.sense_key)  printk("LastFailedSense=0x%02x ",
                                                error.b.sense_key);
-               printk("\n");
+               printk("}\n");
        }
+       ide_dump_opcode(drive);
        local_irq_restore(flags);
        return error.all;
 }
 
-EXPORT_SYMBOL(ide_dump_atapi_status);
+/**
+ *     ide_dump_status         -       translate ATA/ATAPI error
+ *     @drive: drive the error occured on
+ *     @msg: information string
+ *     @stat: status byte
+ *
+ *     Error reporting, in human readable form (luxurious, but a memory hog).
+ *     Combines the drive name, message and status byte to provide a
+ *     user understandable explanation of the device error.
+ */
+
+u8 ide_dump_status(ide_drive_t *drive, const char *msg, u8 stat)
+{
+       if (drive->media == ide_disk)
+               return ide_dump_ata_status(drive, msg, stat);
+       return ide_dump_atapi_status(drive, msg, stat);
+}
+
+EXPORT_SYMBOL(ide_dump_status);