]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/scsi/libata-core.c
[SCSI] qla2xxx: Drop legacy 'bypass lun scan for tape device' code.
[linux-2.6-omap-h63xx.git] / drivers / scsi / libata-core.c
index 9ea1025879147ca524999e9595c53413ea808b1f..46c4cdbaee86dc26114299c2a1ccfa45150b9c39 100644 (file)
@@ -562,16 +562,28 @@ static const u8 ata_rw_cmds[] = {
        ATA_CMD_WRITE_MULTI,
        ATA_CMD_READ_MULTI_EXT,
        ATA_CMD_WRITE_MULTI_EXT,
+       0,
+       0,
+       0,
+       ATA_CMD_WRITE_MULTI_FUA_EXT,
        /* pio */
        ATA_CMD_PIO_READ,
        ATA_CMD_PIO_WRITE,
        ATA_CMD_PIO_READ_EXT,
        ATA_CMD_PIO_WRITE_EXT,
+       0,
+       0,
+       0,
+       0,
        /* dma */
        ATA_CMD_READ,
        ATA_CMD_WRITE,
        ATA_CMD_READ_EXT,
-       ATA_CMD_WRITE_EXT
+       ATA_CMD_WRITE_EXT,
+       0,
+       0,
+       0,
+       ATA_CMD_WRITE_FUA_EXT
 };
 
 /**
@@ -584,25 +596,36 @@ static const u8 ata_rw_cmds[] = {
  *     LOCKING:
  *     caller.
  */
-void ata_rwcmd_protocol(struct ata_queued_cmd *qc)
+int ata_rwcmd_protocol(struct ata_queued_cmd *qc)
 {
        struct ata_taskfile *tf = &qc->tf;
        struct ata_device *dev = qc->dev;
+       u8 cmd;
 
-       int index, lba48, write;
+       int index, fua, lba48, write;
  
+       fua = (tf->flags & ATA_TFLAG_FUA) ? 4 : 0;
        lba48 = (tf->flags & ATA_TFLAG_LBA48) ? 2 : 0;
        write = (tf->flags & ATA_TFLAG_WRITE) ? 1 : 0;
 
        if (dev->flags & ATA_DFLAG_PIO) {
+               tf->protocol = ATA_PROT_PIO;
+               index = dev->multi_count ? 0 : 8;
+       } else if (lba48 && (qc->ap->flags & ATA_FLAG_PIO_LBA48)) {
+               /* Unable to use DMA due to host limitation */
                tf->protocol = ATA_PROT_PIO;
                index = dev->multi_count ? 0 : 4;
        } else {
                tf->protocol = ATA_PROT_DMA;
-               index = 8;
+               index = 16;
        }
 
-       tf->command = ata_rw_cmds[index + lba48 + write];
+       cmd = ata_rw_cmds[index + fua + lba48 + write];
+       if (cmd) {
+               tf->command = cmd;
+               return 0;
+       }
+       return -1;
 }
 
 static const char * const xfer_mode_str[] = {
@@ -1032,18 +1055,22 @@ static unsigned int ata_pio_modes(const struct ata_device *adev)
 {
        u16 modes;
 
-       /* Usual case. Word 53 indicates word 88 is valid */
-       if (adev->id[ATA_ID_FIELD_VALID] & (1 << 2)) {
+       /* Usual case. Word 53 indicates word 64 is valid */
+       if (adev->id[ATA_ID_FIELD_VALID] & (1 << 1)) {
                modes = adev->id[ATA_ID_PIO_MODES] & 0x03;
                modes <<= 3;
                modes |= 0x7;
                return modes;
        }
 
-       /* If word 88 isn't valid then Word 51 holds the PIO timing number
-          for the maximum. Turn it into a mask and return it */
-       modes = (2 << (adev->id[ATA_ID_OLD_PIO_MODES] & 0xFF)) - 1 ;
+       /* If word 64 isn't valid then Word 51 high byte holds the PIO timing
+          number for the maximum. Turn it into a mask and return it */
+       modes = (2 << ((adev->id[ATA_ID_OLD_PIO_MODES] >> 8) & 0xFF)) - 1 ;
        return modes;
+       /* But wait.. there's more. Design your standards by committee and
+          you too can get a free iordy field to process. However its the 
+          speeds not the modes that are supported... Note drivers using the
+          timing API will get this right anyway */
 }
 
 struct ata_exec_internal_arg {
@@ -1145,6 +1172,39 @@ ata_exec_internal(struct ata_port *ap, struct ata_device *dev,
        return AC_ERR_OTHER;
 }
 
+/**
+ *     ata_pio_need_iordy      -       check if iordy needed
+ *     @adev: ATA device
+ *
+ *     Check if the current speed of the device requires IORDY. Used
+ *     by various controllers for chip configuration.
+ */
+
+unsigned int ata_pio_need_iordy(const struct ata_device *adev)
+{
+       int pio;
+       int speed = adev->pio_mode - XFER_PIO_0;
+
+       if (speed < 2)
+               return 0;
+       if (speed > 2)
+               return 1;
+               
+       /* If we have no drive specific rule, then PIO 2 is non IORDY */
+
+       if (adev->id[ATA_ID_FIELD_VALID] & 2) { /* EIDE */
+               pio = adev->id[ATA_ID_EIDE_PIO];
+               /* Is the speed faster than the drive allows non IORDY ? */
+               if (pio) {
+                       /* This is cycle times not frequency - watch the logic! */
+                       if (pio > 240)  /* PIO2 is 240nS per cycle */
+                               return 1;
+                       return 0;
+               }
+       }
+       return 0;
+}
+
 /**
  *     ata_dev_identify - obtain IDENTIFY x DEVICE page
  *     @ap: port on which device we wish to probe resides
@@ -1396,7 +1456,7 @@ void ata_dev_config(struct ata_port *ap, unsigned int i)
                ap->udma_mask &= ATA_UDMA5;
                ap->host->max_sectors = ATA_MAX_SECTORS;
                ap->host->hostt->max_sectors = ATA_MAX_SECTORS;
-               ap->device->flags |= ATA_DFLAG_LOCK_SECTORS;
+               ap->device[i].flags |= ATA_DFLAG_LOCK_SECTORS;
        }
 
        if (ap->ops->dev_config)
@@ -1728,7 +1788,7 @@ static const struct {
        { ATA_SHIFT_PIO,        XFER_PIO_0 },
 };
 
-static inline u8 base_from_shift(unsigned int shift)
+static u8 base_from_shift(unsigned int shift)
 {
        int i;
 
@@ -3037,10 +3097,21 @@ static void ata_pio_data_xfer(struct ata_port *ap, unsigned char *buf,
 static void ata_data_xfer(struct ata_port *ap, unsigned char *buf,
                          unsigned int buflen, int do_write)
 {
-       if (ap->flags & ATA_FLAG_MMIO)
-               ata_mmio_data_xfer(ap, buf, buflen, do_write);
-       else
-               ata_pio_data_xfer(ap, buf, buflen, do_write);
+       /* Make the crap hardware pay the costs not the good stuff */
+       if (unlikely(ap->flags & ATA_FLAG_IRQ_MASK)) {
+               unsigned long flags;
+               local_irq_save(flags);
+               if (ap->flags & ATA_FLAG_MMIO)
+                       ata_mmio_data_xfer(ap, buf, buflen, do_write);
+               else
+                       ata_pio_data_xfer(ap, buf, buflen, do_write);
+               local_irq_restore(flags);
+       } else {
+               if (ap->flags & ATA_FLAG_MMIO)
+                       ata_mmio_data_xfer(ap, buf, buflen, do_write);
+               else
+                       ata_pio_data_xfer(ap, buf, buflen, do_write);
+       }
 }
 
 /**
@@ -4154,6 +4225,96 @@ err_out:
  *     Inherited from caller.
  */
 
+/*
+ * Execute a 'simple' command, that only consists of the opcode 'cmd' itself,
+ * without filling any other registers
+ */
+static int ata_do_simple_cmd(struct ata_port *ap, struct ata_device *dev,
+                            u8 cmd)
+{
+       struct ata_taskfile tf;
+       int err;
+
+       ata_tf_init(ap, &tf, dev->devno);
+
+       tf.command = cmd;
+       tf.flags |= ATA_TFLAG_DEVICE;
+       tf.protocol = ATA_PROT_NODATA;
+
+       err = ata_exec_internal(ap, dev, &tf, DMA_NONE, NULL, 0);
+       if (err)
+               printk(KERN_ERR "%s: ata command failed: %d\n",
+                               __FUNCTION__, err);
+
+       return err;
+}
+
+static int ata_flush_cache(struct ata_port *ap, struct ata_device *dev)
+{
+       u8 cmd;
+
+       if (!ata_try_flush_cache(dev))
+               return 0;
+
+       if (ata_id_has_flush_ext(dev->id))
+               cmd = ATA_CMD_FLUSH_EXT;
+       else
+               cmd = ATA_CMD_FLUSH;
+
+       return ata_do_simple_cmd(ap, dev, cmd);
+}
+
+static int ata_standby_drive(struct ata_port *ap, struct ata_device *dev)
+{
+       return ata_do_simple_cmd(ap, dev, ATA_CMD_STANDBYNOW1);
+}
+
+static int ata_start_drive(struct ata_port *ap, struct ata_device *dev)
+{
+       return ata_do_simple_cmd(ap, dev, ATA_CMD_IDLEIMMEDIATE);
+}
+
+/**
+ *     ata_device_resume - wakeup a previously suspended devices
+ *
+ *     Kick the drive back into action, by sending it an idle immediate
+ *     command and making sure its transfer mode matches between drive
+ *     and host.
+ *
+ */
+int ata_device_resume(struct ata_port *ap, struct ata_device *dev)
+{
+       if (ap->flags & ATA_FLAG_SUSPENDED) {
+               ap->flags &= ~ATA_FLAG_SUSPENDED;
+               ata_set_mode(ap);
+       }
+       if (!ata_dev_present(dev))
+               return 0;
+       if (dev->class == ATA_DEV_ATA)
+               ata_start_drive(ap, dev);
+
+       return 0;
+}
+
+/**
+ *     ata_device_suspend - prepare a device for suspend
+ *
+ *     Flush the cache on the drive, if appropriate, then issue a
+ *     standbynow command.
+ *
+ */
+int ata_device_suspend(struct ata_port *ap, struct ata_device *dev)
+{
+       if (!ata_dev_present(dev))
+               return 0;
+       if (dev->class == ATA_DEV_ATA)
+               ata_flush_cache(ap, dev);
+
+       ata_standby_drive(ap, dev);
+       ap->flags |= ATA_FLAG_SUSPENDED;
+       return 0;
+}
+
 int ata_port_start (struct ata_port *ap)
 {
        struct device *dev = ap->host_set->dev;
@@ -4902,6 +5063,23 @@ int pci_test_config_bits(struct pci_dev *pdev, const struct pci_bits *bits)
 
        return (tmp == bits->val) ? 1 : 0;
 }
+
+int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+       pci_save_state(pdev);
+       pci_disable_device(pdev);
+       pci_set_power_state(pdev, PCI_D3hot);
+       return 0;
+}
+
+int ata_pci_device_resume(struct pci_dev *pdev)
+{
+       pci_set_power_state(pdev, PCI_D0);
+       pci_restore_state(pdev);
+       pci_enable_device(pdev);
+       pci_set_master(pdev);
+       return 0;
+}
 #endif /* CONFIG_PCI */
 
 
@@ -4996,6 +5174,7 @@ EXPORT_SYMBOL_GPL(ata_dev_id_string);
 EXPORT_SYMBOL_GPL(ata_dev_config);
 EXPORT_SYMBOL_GPL(ata_scsi_simulate);
 
+EXPORT_SYMBOL_GPL(ata_pio_need_iordy);
 EXPORT_SYMBOL_GPL(ata_timing_compute);
 EXPORT_SYMBOL_GPL(ata_timing_merge);
 
@@ -5005,4 +5184,11 @@ EXPORT_SYMBOL_GPL(ata_pci_host_stop);
 EXPORT_SYMBOL_GPL(ata_pci_init_native_mode);
 EXPORT_SYMBOL_GPL(ata_pci_init_one);
 EXPORT_SYMBOL_GPL(ata_pci_remove_one);
+EXPORT_SYMBOL_GPL(ata_pci_device_suspend);
+EXPORT_SYMBOL_GPL(ata_pci_device_resume);
 #endif /* CONFIG_PCI */
+
+EXPORT_SYMBOL_GPL(ata_device_suspend);
+EXPORT_SYMBOL_GPL(ata_device_resume);
+EXPORT_SYMBOL_GPL(ata_scsi_device_suspend);
+EXPORT_SYMBOL_GPL(ata_scsi_device_resume);