[libata] sata_vsc: support PCI MSI
authorDan Wolstenholme <daniel@wolstenholme.net>
Tue, 9 Jan 2007 10:59:21 +0000 (05:59 -0500)
committerJeff Garzik <jeff@garzik.org>
Fri, 9 Feb 2007 22:39:31 +0000 (17:39 -0500)
Signed-off-by: Jeff Garzik <jeff@garzik.org>
drivers/ata/sata_vsc.c

index 0fa1b89..1a5f357 100644 (file)
@@ -94,8 +94,14 @@ enum {
                              VSC_SATA_INT_ERROR_P    | VSC_SATA_INT_ERROR_R | \
                              VSC_SATA_INT_ERROR_E    | VSC_SATA_INT_ERROR_M | \
                              VSC_SATA_INT_PHY_CHANGE),
+
+       /* Host private flags (hp_flags) */
+       VSC_SATA_HP_FLAG_MSI    = (1 << 0),
 };
 
+struct vsc_sata_host_priv {
+       u32     hp_flags;
+};
 
 #define is_vsc_sata_int_err(port_idx, int_status) \
         (int_status & (VSC_SATA_INT_ERROR << (8 * port_idx)))
@@ -118,6 +124,20 @@ static void vsc_sata_scr_write (struct ata_port *ap, unsigned int sc_reg,
 }
 
 
+static void vsc_sata_host_stop(struct ata_host_set *host_set)
+{
+       struct vsc_sata_host_priv *hpriv = host_set->private_data;
+       struct pci_dev *pdev = to_pci_dev(host_set->dev);
+
+       if (hpriv->hp_flags & VSC_SATA_HP_FLAG_MSI)
+               pci_disable_msi(pdev);
+       else
+               pci_intx(pdev, 0);
+       kfree (hpriv);
+       ata_pci_host_stop(host_set);
+}
+
+
 static void vsc_intr_mask_update(struct ata_port *ap, u8 ctl)
 {
        void __iomem *mask_addr;
@@ -312,7 +332,7 @@ static const struct ata_port_operations vsc_sata_ops = {
        .scr_write              = vsc_sata_scr_write,
        .port_start             = ata_port_start,
        .port_stop              = ata_port_stop,
-       .host_stop              = ata_pci_host_stop,
+       .host_stop              = vsc_sata_host_stop,
 };
 
 static void __devinit vsc_sata_setup_port(struct ata_ioports *port, unsigned long base)
@@ -341,6 +361,7 @@ static int __devinit vsc_sata_init_one (struct pci_dev *pdev, const struct pci_d
 {
        static int printed_version;
        struct ata_probe_ent *probe_ent = NULL;
+       struct vsc_sata_host_priv *hpriv;
        unsigned long base;
        int pci_dev_busy = 0;
        void __iomem *mmio_base;
@@ -382,6 +403,7 @@ static int __devinit vsc_sata_init_one (struct pci_dev *pdev, const struct pci_d
                rc = -ENOMEM;
                goto err_out_regions;
        }
+
        memset(probe_ent, 0, sizeof(*probe_ent));
        probe_ent->dev = pci_dev_to_dev(pdev);
        INIT_LIST_HEAD(&probe_ent->node);
@@ -393,19 +415,33 @@ static int __devinit vsc_sata_init_one (struct pci_dev *pdev, const struct pci_d
        }
        base = (unsigned long) mmio_base;
 
+       hpriv = kmalloc(sizeof(*hpriv), GFP_KERNEL);
+       if (!hpriv) {
+               rc = -ENOMEM;
+               goto err_out_iounmap;
+       }
+       memset(hpriv, 0, sizeof(*hpriv));
+
        /*
         * Due to a bug in the chip, the default cache line size can't be used
         */
        pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 0x80);
 
+       if (pci_enable_msi(pdev) == 0) {
+               hpriv->hp_flags |= VSC_SATA_HP_FLAG_MSI;
+               pci_intx(pdev, 0);
+       }
+       else
+               probe_ent->irq_flags = IRQF_SHARED;
+
        probe_ent->sht = &vsc_sata_sht;
        probe_ent->port_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
                                ATA_FLAG_MMIO;
        probe_ent->port_ops = &vsc_sata_ops;
        probe_ent->n_ports = 4;
        probe_ent->irq = pdev->irq;
-       probe_ent->irq_flags = IRQF_SHARED;
        probe_ent->mmio_base = mmio_base;
+       probe_ent->private_data = hpriv;
 
        /* We don't care much about the PIO/UDMA masks, but the core won't like us
         * if we don't fill these
@@ -432,10 +468,12 @@ static int __devinit vsc_sata_init_one (struct pci_dev *pdev, const struct pci_d
 
        /* FIXME: check ata_device_add return value */
        ata_device_add(probe_ent);
-       kfree(probe_ent);
 
+       kfree(probe_ent);
        return 0;
 
+err_out_iounmap:
+       pci_iounmap(pdev, mmio_base);
 err_out_free_ent:
        kfree(probe_ent);
 err_out_regions: