]> err.no Git - linux-2.6/blobdiff - drivers/scsi/libata-core.c
V4L/DVB (4322): Fix dvb-pll autoprobing
[linux-2.6] / drivers / scsi / libata-core.c
index ad5cac79627c60887db46f52ebed6c68d33d0b53..386e5f21e191894da37dbaa44d58d5ad6769fd81 100644 (file)
@@ -1417,7 +1417,7 @@ int ata_dev_configure(struct ata_device *dev, int print_info)
                        ata_dev_config_ncq(dev, ncq_desc, sizeof(ncq_desc));
 
                        /* print device info to dmesg */
-                       if (ata_msg_info(ap))
+                       if (ata_msg_drv(ap) && print_info)
                                ata_dev_printk(dev, KERN_INFO, "ATA-%d, "
                                        "max %s, %Lu sectors: %s %s\n",
                                        ata_id_major_version(id),
@@ -1440,7 +1440,7 @@ int ata_dev_configure(struct ata_device *dev, int print_info)
                        }
 
                        /* print device info to dmesg */
-                       if (ata_msg_info(ap))
+                       if (ata_msg_drv(ap) && print_info)
                                ata_dev_printk(dev, KERN_INFO, "ATA-%d, "
                                        "max %s, %Lu sectors: CHS %u/%u/%u\n",
                                        ata_id_major_version(id),
@@ -1452,7 +1452,7 @@ int ata_dev_configure(struct ata_device *dev, int print_info)
 
                if (dev->id[59] & 0x100) {
                        dev->multi_count = dev->id[59] & 0xff;
-                       if (ata_msg_info(ap))
+                       if (ata_msg_drv(ap) && print_info)
                                ata_dev_printk(dev, KERN_INFO,
                                        "ata%u: dev %u multi count %u\n",
                                        ap->id, dev->devno, dev->multi_count);
@@ -1481,7 +1481,7 @@ int ata_dev_configure(struct ata_device *dev, int print_info)
                }
 
                /* print device info to dmesg */
-               if (ata_msg_info(ap))
+               if (ata_msg_drv(ap) && print_info)
                        ata_dev_printk(dev, KERN_INFO, "ATAPI, max %s%s\n",
                                       ata_mode_string(xfer_mask),
                                       cdb_intr_string);
@@ -1491,7 +1491,7 @@ int ata_dev_configure(struct ata_device *dev, int print_info)
 
        /* limit bridge transfers to udma5, 200 sectors */
        if (ata_dev_knobble(dev)) {
-               if (ata_msg_info(ap))
+               if (ata_msg_drv(ap) && print_info)
                        ata_dev_printk(dev, KERN_INFO,
                                       "applying bridge limits\n");
                dev->udma_mask &= ATA_UDMA5;
@@ -5009,86 +5009,120 @@ int ata_flush_cache(struct ata_device *dev)
        return 0;
 }
 
-static int ata_standby_drive(struct ata_device *dev)
+static int ata_host_set_request_pm(struct ata_host_set *host_set,
+                                  pm_message_t mesg, unsigned int action,
+                                  unsigned int ehi_flags, int wait)
 {
-       unsigned int err_mask;
+       unsigned long flags;
+       int i, rc;
 
-       err_mask = ata_do_simple_cmd(dev, ATA_CMD_STANDBYNOW1);
-       if (err_mask) {
-               ata_dev_printk(dev, KERN_ERR, "failed to standby drive "
-                              "(err_mask=0x%x)\n", err_mask);
-               return -EIO;
-       }
+       for (i = 0; i < host_set->n_ports; i++) {
+               struct ata_port *ap = host_set->ports[i];
 
-       return 0;
-}
+               /* Previous resume operation might still be in
+                * progress.  Wait for PM_PENDING to clear.
+                */
+               if (ap->pflags & ATA_PFLAG_PM_PENDING) {
+                       ata_port_wait_eh(ap);
+                       WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING);
+               }
 
-static int ata_start_drive(struct ata_device *dev)
-{
-       unsigned int err_mask;
+               /* request PM ops to EH */
+               spin_lock_irqsave(ap->lock, flags);
 
-       err_mask = ata_do_simple_cmd(dev, ATA_CMD_IDLEIMMEDIATE);
-       if (err_mask) {
-               ata_dev_printk(dev, KERN_ERR, "failed to start drive "
-                              "(err_mask=0x%x)\n", err_mask);
-               return -EIO;
+               ap->pm_mesg = mesg;
+               if (wait) {
+                       rc = 0;
+                       ap->pm_result = &rc;
+               }
+
+               ap->pflags |= ATA_PFLAG_PM_PENDING;
+               ap->eh_info.action |= action;
+               ap->eh_info.flags |= ehi_flags;
+
+               ata_port_schedule_eh(ap);
+
+               spin_unlock_irqrestore(ap->lock, flags);
+
+               /* wait and check result */
+               if (wait) {
+                       ata_port_wait_eh(ap);
+                       WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING);
+                       if (rc)
+                               return rc;
+               }
        }
 
        return 0;
 }
 
 /**
- *     ata_device_resume - wakeup a previously suspended devices
- *     @dev: the device to resume
+ *     ata_host_set_suspend - suspend host_set
+ *     @host_set: host_set to suspend
+ *     @mesg: PM message
  *
- *     Kick the drive back into action, by sending it an idle immediate
- *     command and making sure its transfer mode matches between drive
- *     and host.
+ *     Suspend @host_set.  Actual operation is performed by EH.  This
+ *     function requests EH to perform PM operations and waits for EH
+ *     to finish.
  *
+ *     LOCKING:
+ *     Kernel thread context (may sleep).
+ *
+ *     RETURNS:
+ *     0 on success, -errno on failure.
  */
-int ata_device_resume(struct ata_device *dev)
+int ata_host_set_suspend(struct ata_host_set *host_set, pm_message_t mesg)
 {
-       struct ata_port *ap = dev->ap;
+       int i, j, rc;
 
-       if (ap->pflags & ATA_PFLAG_SUSPENDED) {
-               struct ata_device *failed_dev;
+       rc = ata_host_set_request_pm(host_set, mesg, 0, ATA_EHI_QUIET, 1);
+       if (rc)
+               goto fail;
 
-               ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT);
-               ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 200000);
+       /* EH is quiescent now.  Fail if we have any ready device.
+        * This happens if hotplug occurs between completion of device
+        * suspension and here.
+        */
+       for (i = 0; i < host_set->n_ports; i++) {
+               struct ata_port *ap = host_set->ports[i];
 
-               ap->pflags &= ~ATA_PFLAG_SUSPENDED;
-               while (ata_set_mode(ap, &failed_dev))
-                       ata_dev_disable(failed_dev);
+               for (j = 0; j < ATA_MAX_DEVICES; j++) {
+                       struct ata_device *dev = &ap->device[j];
+
+                       if (ata_dev_ready(dev)) {
+                               ata_port_printk(ap, KERN_WARNING,
+                                               "suspend failed, device %d "
+                                               "still active\n", dev->devno);
+                               rc = -EBUSY;
+                               goto fail;
+                       }
+               }
        }
-       if (!ata_dev_enabled(dev))
-               return 0;
-       if (dev->class == ATA_DEV_ATA)
-               ata_start_drive(dev);
 
+       host_set->dev->power.power_state = mesg;
        return 0;
+
+ fail:
+       ata_host_set_resume(host_set);
+       return rc;
 }
 
 /**
- *     ata_device_suspend - prepare a device for suspend
- *     @dev: the device to suspend
- *     @state: target power management state
+ *     ata_host_set_resume - resume host_set
+ *     @host_set: host_set to resume
  *
- *     Flush the cache on the drive, if appropriate, then issue a
- *     standbynow command.
+ *     Resume @host_set.  Actual operation is performed by EH.  This
+ *     function requests EH to perform PM operations and returns.
+ *     Note that all resume operations are performed parallely.
+ *
+ *     LOCKING:
+ *     Kernel thread context (may sleep).
  */
-int ata_device_suspend(struct ata_device *dev, pm_message_t state)
+void ata_host_set_resume(struct ata_host_set *host_set)
 {
-       struct ata_port *ap = dev->ap;
-
-       if (!ata_dev_enabled(dev))
-               return 0;
-       if (dev->class == ATA_DEV_ATA)
-               ata_flush_cache(dev);
-
-       if (state.event != PM_EVENT_FREEZE)
-               ata_standby_drive(dev);
-       ap->pflags |= ATA_PFLAG_SUSPENDED;
-       return 0;
+       ata_host_set_request_pm(host_set, PMSG_ON, ATA_EH_SOFTRESET,
+                               ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET, 0);
+       host_set->dev->power.power_state = PMSG_ON;
 }
 
 /**
@@ -5733,20 +5767,55 @@ 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)
+void ata_pci_device_do_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;
+
+       if (state.event == PM_EVENT_SUSPEND) {
+               pci_disable_device(pdev);
+               pci_set_power_state(pdev, PCI_D3hot);
+       }
 }
 
-int ata_pci_device_resume(struct pci_dev *pdev)
+void ata_pci_device_do_resume(struct pci_dev *pdev)
 {
        pci_set_power_state(pdev, PCI_D0);
        pci_restore_state(pdev);
        pci_enable_device(pdev);
        pci_set_master(pdev);
+}
+
+int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+       struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev);
+       int rc = 0;
+
+       rc = ata_host_set_suspend(host_set, state);
+       if (rc)
+               return rc;
+
+       if (host_set->next) {
+               rc = ata_host_set_suspend(host_set->next, state);
+               if (rc) {
+                       ata_host_set_resume(host_set);
+                       return rc;
+               }
+       }
+
+       ata_pci_device_do_suspend(pdev, state);
+
+       return 0;
+}
+
+int ata_pci_device_resume(struct pci_dev *pdev)
+{
+       struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev);
+
+       ata_pci_device_do_resume(pdev);
+       ata_host_set_resume(host_set);
+       if (host_set->next)
+               ata_host_set_resume(host_set->next);
+
        return 0;
 }
 #endif /* CONFIG_PCI */
@@ -5926,6 +5995,8 @@ EXPORT_SYMBOL_GPL(sata_scr_write);
 EXPORT_SYMBOL_GPL(sata_scr_write_flush);
 EXPORT_SYMBOL_GPL(ata_port_online);
 EXPORT_SYMBOL_GPL(ata_port_offline);
+EXPORT_SYMBOL_GPL(ata_host_set_suspend);
+EXPORT_SYMBOL_GPL(ata_host_set_resume);
 EXPORT_SYMBOL_GPL(ata_id_string);
 EXPORT_SYMBOL_GPL(ata_id_c_string);
 EXPORT_SYMBOL_GPL(ata_scsi_simulate);
@@ -5940,14 +6011,14 @@ 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_do_suspend);
+EXPORT_SYMBOL_GPL(ata_pci_device_do_resume);
 EXPORT_SYMBOL_GPL(ata_pci_device_suspend);
 EXPORT_SYMBOL_GPL(ata_pci_device_resume);
 EXPORT_SYMBOL_GPL(ata_pci_default_filter);
 EXPORT_SYMBOL_GPL(ata_pci_clear_simplex);
 #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);