amd74xx: workaround unreliable AltStatus register for nVidia controllers
Bartlomiej Zolnierkiewicz [Tue, 2 Dec 2008 19:40:03 +0000 (20:40 +0100)]
It seems that on some nVidia controllers using AltStatus register
can be unreliable so default to Status register if the PCI device
is in Compatibility Mode.  In order to achieve this:

* Add ide_pci_is_in_compatibility_mode() inline helper to <linux/ide.h>.

* Add IDE_HFLAG_BROKEN_ALTSTATUS host flag and set it in amd74xx host
  driver for nVidia controllers in Compatibility Mode.

* Teach actual_try_to_identify() and drive_is_ready() about the new flag.

This fixes the regression caused by removal of CONFIG_IDEPCI_SHARE_IRQ
config option in 2.6.25 and using AltStatus register unconditionally when
available (kernel.org bugs #11659 and #10216).

[ Moreover for CONFIG_IDEPCI_SHARE_IRQ=y (which is what most people
  and distributions use) it never worked correctly. ]

Thanks to Remy LABENE and Lars Winterfeld for help with debugging the problem.

More info at:
http://bugzilla.kernel.org/show_bug.cgi?id=11659
http://bugzilla.kernel.org/show_bug.cgi?id=10216

Reported-by: Remy LABENE <remy.labene@free.fr>
Tested-by: Remy LABENE <remy.labene@free.fr>
Tested-by: Lars Winterfeld <lars.winterfeld@tu-ilmenau.de>
Acked-by: Borislav Petkov <petkovbb@gmail.com>
Signed-off-by: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>

drivers/ide/amd74xx.c
drivers/ide/ide-iops.c
drivers/ide/ide-probe.c
include/linux/ide.h

index 81ec731..c6bcd30 100644 (file)
@@ -3,7 +3,7 @@
  * IDE driver for Linux.
  *
  * Copyright (c) 2000-2002 Vojtech Pavlik
- * Copyright (c) 2007 Bartlomiej Zolnierkiewicz
+ * Copyright (c) 2007-2008 Bartlomiej Zolnierkiewicz
  *
  * Based on the work of:
  *      Andre Hedrick
@@ -263,6 +263,15 @@ static int __devinit amd74xx_probe(struct pci_dev *dev, const struct pci_device_
                        d.udma_mask = ATA_UDMA5;
        }
 
+       /*
+        * It seems that on some nVidia controllers using AltStatus
+        * register can be unreliable so default to Status register
+        * if the device is in Compatibility Mode.
+        */
+       if (dev->vendor == PCI_VENDOR_ID_NVIDIA &&
+           ide_pci_is_in_compatibility_mode(dev))
+               d.host_flags |= IDE_HFLAG_BROKEN_ALTSTATUS;
+
        printk(KERN_INFO "%s %s: UDMA%s controller\n",
                d.name, pci_name(dev), amd_dma[fls(d.udma_mask) - 1]);
 
index 5d6ba14..142b957 100644 (file)
@@ -468,7 +468,8 @@ int drive_is_ready (ide_drive_t *drive)
         * an interrupt with another pci card/device.  We make no assumptions
         * about possible isa-pnp and pci-pnp issues yet.
         */
-       if (hwif->io_ports.ctl_addr)
+       if (hwif->io_ports.ctl_addr &&
+           (hwif->host_flags & IDE_HFLAG_BROKEN_ALTSTATUS) == 0)
                stat = hwif->tp_ops->read_altstatus(hwif);
        else
                /* Note: this may clear a pending IRQ!! */
index 1649ea5..c55bdbd 100644 (file)
@@ -266,7 +266,8 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd)
        /* take a deep breath */
        msleep(50);
 
-       if (io_ports->ctl_addr) {
+       if (io_ports->ctl_addr &&
+           (hwif->host_flags & IDE_HFLAG_BROKEN_ALTSTATUS) == 0) {
                a = tp_ops->read_altstatus(hwif);
                s = tp_ops->read_status(hwif);
                if ((a ^ s) & ~ATA_IDX)
index 54525be..010fb26 100644 (file)
@@ -1296,6 +1296,13 @@ extern int __ide_pci_register_driver(struct pci_driver *driver, struct module *o
 #define ide_pci_register_driver(d) pci_register_driver(d)
 #endif
 
+static inline int ide_pci_is_in_compatibility_mode(struct pci_dev *dev)
+{
+       if ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE && (dev->class & 5) != 5)
+               return 1;
+       return 0;
+}
+
 void ide_pci_setup_ports(struct pci_dev *, const struct ide_port_info *, int,
                         hw_regs_t *, hw_regs_t **);
 void ide_setup_pci_noise(struct pci_dev *, const struct ide_port_info *);
@@ -1375,6 +1382,7 @@ enum {
        IDE_HFLAG_IO_32BIT              = (1 << 24),
        /* unmask IRQs */
        IDE_HFLAG_UNMASK_IRQS           = (1 << 25),
+       IDE_HFLAG_BROKEN_ALTSTATUS      = (1 << 26),
        /* serialize ports if DMA is possible (for sl82c105) */
        IDE_HFLAG_SERIALIZE_DMA         = (1 << 27),
        /* force host out of "simplex" mode */