ata_piix: save, use saved and restore IOCFG
[safe/jmp/linux-2.6] / drivers / ata / ata_piix.c
index 5fdf167..7865954 100644 (file)
@@ -154,11 +154,13 @@ struct piix_map_db {
 
 struct piix_host_priv {
        const int *map;
+       u32 saved_iocfg;
        void __iomem *sidpr;
 };
 
 static int piix_init_one(struct pci_dev *pdev,
                         const struct pci_device_id *ent);
+static void piix_remove_one(struct pci_dev *pdev);
 static int piix_pata_prereset(struct ata_link *link, unsigned long deadline);
 static void piix_set_piomode(struct ata_port *ap, struct ata_device *adev);
 static void piix_set_dmamode(struct ata_port *ap, struct ata_device *adev);
@@ -296,7 +298,7 @@ static struct pci_driver piix_pci_driver = {
        .name                   = DRV_NAME,
        .id_table               = piix_pci_tbl,
        .probe                  = piix_init_one,
-       .remove                 = ata_pci_remove_one,
+       .remove                 = piix_remove_one,
 #ifdef CONFIG_PM
        .suspend                = piix_pci_device_suspend,
        .resume                 = piix_pci_device_resume,
@@ -610,8 +612,9 @@ static const struct ich_laptop ich_laptop[] = {
 static int ich_pata_cable_detect(struct ata_port *ap)
 {
        struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+       struct piix_host_priv *hpriv = ap->host->private_data;
        const struct ich_laptop *lap = &ich_laptop[0];
-       u8 tmp, mask;
+       u8 mask;
 
        /* Check for specials - Acer Aspire 5602WLMi */
        while (lap->device) {
@@ -625,8 +628,7 @@ static int ich_pata_cable_detect(struct ata_port *ap)
 
        /* check BIOS cable detect results */
        mask = ap->port_no == 0 ? PIIX_80C_PRI : PIIX_80C_SEC;
-       pci_read_config_byte(pdev, PIIX_IOCFG, &tmp);
-       if ((tmp & mask) == 0)
+       if ((hpriv->saved_iocfg & mask) == 0)
                return ATA_CBL_PATA40;
        return ATA_CBL_PATA80;
 }
@@ -1350,7 +1352,7 @@ static int __devinit piix_init_sidpr(struct ata_host *host)
        return 0;
 }
 
-static void piix_iocfg_bit18_quirk(struct pci_dev *pdev)
+static void piix_iocfg_bit18_quirk(struct ata_host *host)
 {
        static const struct dmi_system_id sysids[] = {
                {
@@ -1367,7 +1369,8 @@ static void piix_iocfg_bit18_quirk(struct pci_dev *pdev)
 
                { }     /* terminate list */
        };
-       u32 iocfg;
+       struct pci_dev *pdev = to_pci_dev(host->dev);
+       struct piix_host_priv *hpriv = host->private_data;
 
        if (!dmi_check_system(sysids))
                return;
@@ -1376,12 +1379,11 @@ static void piix_iocfg_bit18_quirk(struct pci_dev *pdev)
         * seem to use it to disable a channel.  Clear the bit on the
         * affected systems.
         */
-       pci_read_config_dword(pdev, PIIX_IOCFG, &iocfg);
-       if (iocfg & (1 << 18)) {
+       if (hpriv->saved_iocfg & (1 << 18)) {
                dev_printk(KERN_INFO, &pdev->dev,
                           "applying IOCFG bit18 quirk\n");
-               iocfg &= ~(1 << 18);
-               pci_write_config_dword(pdev, PIIX_IOCFG, iocfg);
+               pci_write_config_dword(pdev, PIIX_IOCFG,
+                                      hpriv->saved_iocfg & ~(1 << 18));
        }
 }
 
@@ -1430,6 +1432,17 @@ static int __devinit piix_init_one(struct pci_dev *pdev,
        if (rc)
                return rc;
 
+       hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL);
+       if (!hpriv)
+               return -ENOMEM;
+
+       /* Save IOCFG, this will be used for cable detection, quirk
+        * detection and restoration on detach.  This is necessary
+        * because some ACPI implementations mess up cable related
+        * bits on _STM.  Reported on kernel bz#11879.
+        */
+       pci_read_config_dword(pdev, PIIX_IOCFG, &hpriv->saved_iocfg);
+
        /* ICH6R may be driven by either ata_piix or ahci driver
         * regardless of BIOS configuration.  Make sure AHCI mode is
         * off.
@@ -1441,10 +1454,6 @@ static int __devinit piix_init_one(struct pci_dev *pdev,
        }
 
        /* SATA map init can change port_info, do it before prepping host */
-       hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL);
-       if (!hpriv)
-               return -ENOMEM;
-
        if (port_flags & ATA_FLAG_SATA)
                hpriv->map = piix_init_sata_map(pdev, port_info,
                                        piix_map_db_table[ent->driver_data]);
@@ -1463,7 +1472,7 @@ static int __devinit piix_init_one(struct pci_dev *pdev,
        }
 
        /* apply IOCFG bit18 quirk */
-       piix_iocfg_bit18_quirk(pdev);
+       piix_iocfg_bit18_quirk(host);
 
        /* On ICH5, some BIOSen disable the interrupt using the
         * PCI_COMMAND_INTX_DISABLE bit added in PCI 2.3.
@@ -1488,6 +1497,16 @@ static int __devinit piix_init_one(struct pci_dev *pdev,
        return ata_pci_sff_activate_host(host, ata_sff_interrupt, &piix_sht);
 }
 
+static void piix_remove_one(struct pci_dev *pdev)
+{
+       struct ata_host *host = dev_get_drvdata(&pdev->dev);
+       struct piix_host_priv *hpriv = host->private_data;
+
+       pci_write_config_dword(pdev, PIIX_IOCFG, hpriv->saved_iocfg);
+
+       ata_pci_remove_one(pdev);
+}
+
 static int __init piix_init(void)
 {
        int rc;