libata: separate out ata_host_start()
authorTejun Heo <htejun@gmail.com>
Tue, 17 Apr 2007 14:44:06 +0000 (23:44 +0900)
committerJeff Garzik <jeff@garzik.org>
Sat, 28 Apr 2007 18:16:02 +0000 (14:16 -0400)
Separate out ata_host_start() from ata_device_add().  ata_host_start()
calls ->port_start on each port if available and freezes the port.

Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
drivers/ata/libata-core.c
include/linux/libata.h

index c831c9e..aea766a 100644 (file)
@@ -5819,11 +5819,14 @@ static void ata_host_release(struct device *gendev, void *res)
        for (i = 0; i < host->n_ports; i++) {
                struct ata_port *ap = host->ports[i];
 
-               if (ap && ap->ops->port_stop)
+               if (!ap)
+                       continue;
+
+               if ((host->flags & ATA_HOST_STARTED) && ap->ops->port_stop)
                        ap->ops->port_stop(ap);
        }
 
-       if (host->ops->host_stop)
+       if ((host->flags & ATA_HOST_STARTED) && host->ops->host_stop)
                host->ops->host_stop(host);
 
        for (i = 0; i < host->n_ports; i++) {
@@ -5842,6 +5845,56 @@ static void ata_host_release(struct device *gendev, void *res)
        dev_set_drvdata(gendev, NULL);
 }
 
+/**
+ *     ata_host_start - start and freeze ports of an ATA host
+ *     @host: ATA host to start ports for
+ *
+ *     Start and then freeze ports of @host.  Started status is
+ *     recorded in host->flags, so this function can be called
+ *     multiple times.  Ports are guaranteed to get started only
+ *     once.
+ *
+ *     LOCKING:
+ *     Inherited from calling layer (may sleep).
+ *
+ *     RETURNS:
+ *     0 if all ports are started successfully, -errno otherwise.
+ */
+int ata_host_start(struct ata_host *host)
+{
+       int i, rc;
+
+       if (host->flags & ATA_HOST_STARTED)
+               return 0;
+
+       for (i = 0; i < host->n_ports; i++) {
+               struct ata_port *ap = host->ports[i];
+
+               if (ap->ops->port_start) {
+                       rc = ap->ops->port_start(ap);
+                       if (rc) {
+                               ata_port_printk(ap, KERN_ERR, "failed to "
+                                               "start port (errno=%d)\n", rc);
+                               goto err_out;
+                       }
+               }
+
+               ata_eh_freeze_port(ap);
+       }
+
+       host->flags |= ATA_HOST_STARTED;
+       return 0;
+
+ err_out:
+       while (--i >= 0) {
+               struct ata_port *ap = host->ports[i];
+
+               if (ap->ops->port_stop)
+                       ap->ops->port_stop(ap);
+       }
+       return rc;
+}
+
 /**
  *     ata_sas_host_init - Initialize a host struct
  *     @host:  host to initialize
@@ -5931,14 +5984,6 @@ int ata_device_add(const struct ata_probe_ent *ent)
                        continue;
                }
 
-               /* start port */
-               rc = ap->ops->port_start(ap);
-               if (rc) {
-                       host->ports[i] = NULL;
-                       scsi_host_put(ap->scsi_host);
-                       goto err_out;
-               }
-
                /* Report the secondary IRQ for second channel legacy */
                if (i == 1 && ent->irq2)
                        irq_line = ent->irq2;
@@ -5956,11 +6001,13 @@ int ata_device_add(const struct ata_probe_ent *ent)
                                ap->ioaddr.ctl_addr,
                                ap->ioaddr.bmdma_addr,
                                irq_line);
-
-               /* freeze port before requesting IRQ */
-               ata_eh_freeze_port(ap);
        }
 
+       /* start ports */
+       rc = ata_host_start(host);
+       if (rc)
+               goto err_out;
+
        /* obtain irq, that may be shared between channels */
        rc = devm_request_irq(dev, ent->irq, ent->port_ops->irq_handler,
                              ent->irq_flags, DRV_NAME, host);
@@ -6446,6 +6493,7 @@ EXPORT_SYMBOL_GPL(ata_dummy_port_ops);
 EXPORT_SYMBOL_GPL(ata_std_bios_param);
 EXPORT_SYMBOL_GPL(ata_std_ports);
 EXPORT_SYMBOL_GPL(ata_host_init);
+EXPORT_SYMBOL_GPL(ata_host_start);
 EXPORT_SYMBOL_GPL(ata_device_add);
 EXPORT_SYMBOL_GPL(ata_host_detach);
 EXPORT_SYMBOL_GPL(ata_sg_init);
index ced9dd5..bda26e8 100644 (file)
@@ -210,6 +210,7 @@ enum {
 
        /* host set flags */
        ATA_HOST_SIMPLEX        = (1 << 0),     /* Host is simplex, one DMA channel per host only */
+       ATA_HOST_STARTED        = (1 << 1),     /* Host started */
 
        /* various lengths of time */
        ATA_TMOUT_BOOT          = 30 * HZ,      /* heuristic */
@@ -732,6 +733,7 @@ extern int ata_pci_device_resume(struct pci_dev *pdev);
 #endif
 extern int ata_pci_clear_simplex(struct pci_dev *pdev);
 #endif /* CONFIG_PCI */
+extern int ata_host_start(struct ata_host *host);
 extern int ata_device_add(const struct ata_probe_ent *ent);
 extern void ata_host_detach(struct ata_host *host);
 extern void ata_host_init(struct ata_host *, struct device *,