Automatic merge of rsync://rsync.kernel.org/pub/scm/linux/kernel/git/torvalds/linux...
[pandora-kernel.git] / drivers / scsi / ahci.c
index 8263b3a..ee53b22 100644 (file)
@@ -153,6 +153,7 @@ struct ahci_sg {
 
 struct ahci_host_priv {
        unsigned long           flags;
+       unsigned int            have_msi; /* is PCI MSI enabled? */
        u32                     cap;    /* cache of HOST_CAP register */
        u32                     port_map; /* cache of HOST_PORTS_IMPL reg */
 };
@@ -183,6 +184,7 @@ static void ahci_qc_prep(struct ata_queued_cmd *qc);
 static u8 ahci_check_status(struct ata_port *ap);
 static u8 ahci_check_err(struct ata_port *ap);
 static inline int ahci_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc);
+static void ahci_remove_one (struct pci_dev *pdev);
 
 static Scsi_Host_Template ahci_sht = {
        .module                 = THIS_MODULE,
@@ -272,7 +274,7 @@ static struct pci_driver ahci_pci_driver = {
        .name                   = DRV_NAME,
        .id_table               = ahci_pci_tbl,
        .probe                  = ahci_init_one,
-       .remove                 = ata_pci_remove_one,
+       .remove                 = ahci_remove_one,
 };
 
 
@@ -290,6 +292,8 @@ static void ahci_host_stop(struct ata_host_set *host_set)
 {
        struct ahci_host_priv *hpriv = host_set->private_data;
        kfree(hpriv);
+
+       ata_host_stop(host_set);
 }
 
 static int ahci_port_start(struct ata_port *ap)
@@ -877,15 +881,19 @@ static int ahci_host_init(struct ata_probe_ent *probe_ent)
 }
 
 /* move to PCI layer, integrate w/ MSI stuff */
-static void pci_enable_intx(struct pci_dev *pdev)
+static void pci_intx(struct pci_dev *pdev, int enable)
 {
-       u16 pci_command;
+       u16 pci_command, new;
 
        pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
-       if (pci_command & PCI_COMMAND_INTX_DISABLE) {
-               pci_command &= ~PCI_COMMAND_INTX_DISABLE;
+
+       if (enable)
+               new = pci_command & ~PCI_COMMAND_INTX_DISABLE;
+       else
+               new = pci_command | PCI_COMMAND_INTX_DISABLE;
+
+       if (new != pci_command)
                pci_write_config_word(pdev, PCI_COMMAND, pci_command);
-       }
 }
 
 static void ahci_print_info(struct ata_probe_ent *probe_ent)
@@ -967,7 +975,7 @@ static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
        unsigned long base;
        void *mmio_base;
        unsigned int board_idx = (unsigned int) ent->driver_data;
-       int pci_dev_busy = 0;
+       int have_msi, pci_dev_busy = 0;
        int rc;
 
        VPRINTK("ENTER\n");
@@ -985,12 +993,17 @@ static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
                goto err_out;
        }
 
-       pci_enable_intx(pdev);
+       if (pci_enable_msi(pdev) == 0)
+               have_msi = 1;
+       else {
+               pci_intx(pdev, 1);
+               have_msi = 0;
+       }
 
        probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL);
        if (probe_ent == NULL) {
                rc = -ENOMEM;
-               goto err_out_regions;
+               goto err_out_msi;
        }
 
        memset(probe_ent, 0, sizeof(*probe_ent));
@@ -1023,6 +1036,8 @@ static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
        probe_ent->mmio_base = mmio_base;
        probe_ent->private_data = hpriv;
 
+       hpriv->have_msi = have_msi;
+
        /* initialize adapter */
        rc = ahci_host_init(probe_ent);
        if (rc)
@@ -1042,7 +1057,11 @@ err_out_iounmap:
        iounmap(mmio_base);
 err_out_free_ent:
        kfree(probe_ent);
-err_out_regions:
+err_out_msi:
+       if (have_msi)
+               pci_disable_msi(pdev);
+       else
+               pci_intx(pdev, 0);
        pci_release_regions(pdev);
 err_out:
        if (!pci_dev_busy)
@@ -1050,6 +1069,42 @@ err_out:
        return rc;
 }
 
+static void ahci_remove_one (struct pci_dev *pdev)
+{
+       struct device *dev = pci_dev_to_dev(pdev);
+       struct ata_host_set *host_set = dev_get_drvdata(dev);
+       struct ahci_host_priv *hpriv = host_set->private_data;
+       struct ata_port *ap;
+       unsigned int i;
+       int have_msi;
+
+       for (i = 0; i < host_set->n_ports; i++) {
+               ap = host_set->ports[i];
+
+               scsi_remove_host(ap->host);
+       }
+
+       have_msi = hpriv->have_msi;
+       free_irq(host_set->irq, host_set);
+       host_set->ops->host_stop(host_set);
+       iounmap(host_set->mmio_base);
+
+       for (i = 0; i < host_set->n_ports; i++) {
+               ap = host_set->ports[i];
+
+               ata_scsi_release(ap->host);
+               scsi_host_put(ap->host);
+       }
+
+       if (have_msi)
+               pci_disable_msi(pdev);
+       else
+               pci_intx(pdev, 0);
+       pci_release_regions(pdev);
+       kfree(host_set);
+       pci_disable_device(pdev);
+       dev_set_drvdata(dev, NULL);
+}
 
 static int __init ahci_init(void)
 {