The following diff for the ATI SB600 / SB700 chipsets needs testing
with the EHCI controller. There is a temporary printf to see when the
workaround is being applied.

Attached patch is intended to avoid USB subsystem hang symptoms on
all ATI SB600 revisions and ATI SB700 south bridge revisions A12 and A13.

The USB subsystem hang symptom is observed when the system has multiple
USB devices connected to it. In some cases a USB hub may be required to
observe this symptom.

This patch works around the problem by correcting the internal register
setting that will help by changing the behavior of the internal logic to
avoid the USB subsystem hang issue. The change in the behavior of the
logic does not impact the normal operation of the USB subsystem.

>From NetBSD (also in Linux)


Index: ehci_pci.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/ehci_pci.c,v
retrieving revision 1.15
diff -u -p -r1.15 ehci_pci.c
--- ehci_pci.c  29 Mar 2009 21:53:52 -0000      1.15
+++ ehci_pci.c  23 Jun 2009 08:48:36 -0000
@@ -58,6 +58,18 @@ extern int ehcidebug;
 #define DPRINTF(x)
 #endif
 
+enum ehci_pci_quirk_flags {
+       EHCI_PCI_QUIRK_ATI_SB600 = 0x1, /* always need a quirk */
+       EHCI_PCI_QUIRK_ATI_SB700 = 0x2, /* depends on the SMB revision */
+};
+
+static const struct pci_quirkdata ehci_pci_quirks[] = {
+       { PCI_VENDOR_ATI, PCI_PRODUCT_ATI_SB600_EHCI,
+           EHCI_PCI_QUIRK_ATI_SB600 },
+       { PCI_VENDOR_ATI, PCI_PRODUCT_ATI_SB700_EHCI,
+           EHCI_PCI_QUIRK_ATI_SB700 },
+};
+
 struct ehci_pci_softc {
        ehci_softc_t            sc;
        pci_chipset_tag_t       sc_pc;
@@ -65,6 +77,14 @@ struct ehci_pci_softc {
        void                    *sc_ih;         /* interrupt vectoring */
 };
 
+int ehci_sb700_match(struct pci_attach_args *pa);
+int ehci_apply_ati_quirks(struct ehci_pci_softc *sc);
+enum ehci_pci_quirk_flags ehci_pci_lookup_quirkdata(pci_vendor_id_t,
+       pci_product_id_t);
+
+#define EHCI_SBx00_WORKAROUND_REG      0x50
+#define EHCI_SBx00_WORKAROUND_ENABLE   (1 << 3)
+
 int    ehci_pci_match(struct device *, void *, void *);
 void   ehci_pci_attach(struct device *, struct device *, void *);
 int    ehci_pci_detach(struct device *, int);
@@ -77,7 +97,6 @@ struct cfattach ehci_pci_ca = {
        ehci_pci_detach, ehci_activate
 };
 
-
 int
 ehci_pci_match(struct device *parent, void *match, void *aux)
 {
@@ -103,7 +122,11 @@ ehci_pci_attach(struct device *parent, s
        const char *vendor;
        char *devname = sc->sc.sc_bus.bdev.dv_xname;
        usbd_status r;
-       int s;
+       int quirk, s;
+
+       /* Check for quirks */
+       quirk = ehci_pci_lookup_quirkdata(PCI_VENDOR(pa->pa_id),
+                                          PCI_PRODUCT(pa->pa_id));
 
        /* Map I/O registers */
        if (pci_mapreg_map(pa, PCI_CBMEM, PCI_MAPREG_TYPE_MEM, 0,
@@ -122,6 +145,17 @@ ehci_pci_attach(struct device *parent, s
        DPRINTF(("%s: offs=%d\n", devname, sc->sc.sc_offs));
        EOWRITE2(&sc->sc, EHCI_USBINTR, 0);
 
+       /* Handle quirks */
+       switch (quirk) {
+       case EHCI_PCI_QUIRK_ATI_SB600:
+               ehci_apply_ati_quirks(sc);
+               break;
+       case EHCI_PCI_QUIRK_ATI_SB700:
+               if (pci_find_device(NULL, ehci_sb700_match))
+                       ehci_apply_ati_quirks(sc);
+               break;
+       }
+
        /* Map and establish the interrupt. */
        if (pci_intr_map(pa, &ih)) {
                printf(": couldn't map interrupt\n");
@@ -270,4 +304,48 @@ ehci_pci_shutdown(void *v)
        /* best not to do this anymore; BIOS SMM spins? */
        ehci_pci_givecontroller(sc);
 #endif
+}
+
+int
+ehci_sb700_match(struct pci_attach_args *pa)
+{
+       if (!(PCI_VENDOR(pa->pa_id) == PCI_VENDOR_ATI &&
+           PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_ATI_SBX00_SMB))
+               return (0);
+
+       switch (PCI_REVISION(pa->pa_class)) {
+       case 0x3a:
+       case 0x3b:
+               return (1);
+       }
+
+       return (0);
+}
+
+int
+ehci_apply_ati_quirks(struct ehci_pci_softc *sc)
+{
+       pcireg_t value;
+
+       printf(", applying ATI SB600/SB700 workaround");
+       value = pci_conf_read(sc->sc_pc, sc->sc_tag,
+           EHCI_SBx00_WORKAROUND_REG);
+       pci_conf_write(sc->sc_pc, sc->sc_tag, EHCI_SBx00_WORKAROUND_REG,
+           value | EHCI_SBx00_WORKAROUND_ENABLE);
+
+       return (0);
+}
+
+enum ehci_pci_quirk_flags
+ehci_pci_lookup_quirkdata(pci_vendor_id_t vendor, pci_product_id_t product)
+{
+       int i;
+
+       for (i = 0; i < nitems(ehci_pci_quirks); i++) {
+               if (vendor == ehci_pci_quirks[i].vendor &&
+                   product == ehci_pci_quirks[i].product)
+                       return (ehci_pci_quirks[i].quirks);
+       }
+
+       return (0);
 }

-- 
This message has been scanned for viruses and
dangerous content by MailScanner, and is
believed to be clean.

Reply via email to