Module Name: src Committed By: bouyer Date: Wed Jun 17 20:33:39 UTC 2009
Modified Files: src/sys/dev/pci [netbsd-5]: ehci_pci.c Log Message: Pull up following revision(s) (requested by cegger in ticket #814): sys/dev/pci/ehci_pci.c: revision 1.45 Apply hw workaround required for all SB600 revisions and SB700 revisions A12 and A13 to avoid USB subsystem hang symptom. The USB subsystem hang symptom is observed when the system has multiple USB devices connected to it or one USB device is often re-connected. 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. This fix has been discussed, developped, reviewed, polished up and tested on current-users by several people. Thread starts at: http://mail-index.netbsd.org/current-users/2009/05/17/msg009460.html To generate a diff of this commit: cvs rdiff -u -r1.38 -r1.38.10.1 src/sys/dev/pci/ehci_pci.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/pci/ehci_pci.c diff -u src/sys/dev/pci/ehci_pci.c:1.38 src/sys/dev/pci/ehci_pci.c:1.38.10.1 --- src/sys/dev/pci/ehci_pci.c:1.38 Mon Apr 28 20:23:54 2008 +++ src/sys/dev/pci/ehci_pci.c Wed Jun 17 20:33:39 2009 @@ -1,4 +1,4 @@ -/* $NetBSD: ehci_pci.c,v 1.38 2008/04/28 20:23:54 martin Exp $ */ +/* $NetBSD: ehci_pci.c,v 1.38.10.1 2009/06/17 20:33:39 bouyer Exp $ */ /* * Copyright (c) 2001, 2002 The NetBSD Foundation, Inc. @@ -30,7 +30,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ehci_pci.c,v 1.38 2008/04/28 20:23:54 martin Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ehci_pci.c,v 1.38.10.1 2009/06/17 20:33:39 bouyer Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -60,6 +60,18 @@ #define DPRINTF(x) #endif +enum ehci_pci_quirk_flags { + EHCI_PCI_QUIRK_AMD_SB600 = 0x1, /* always need a quirk */ + EHCI_PCI_QUIRK_AMD_SB700 = 0x2, /* depends on the SMB revision */ +}; + +static const struct pci_quirkdata ehci_pci_quirks[] = { + { PCI_VENDOR_ATI, PCI_PRODUCT_ATI_SB600_USB_EHCI, + EHCI_PCI_QUIRK_AMD_SB600 }, + { PCI_VENDOR_ATI, PCI_PRODUCT_ATI_SB700_USB_EHCI, + EHCI_PCI_QUIRK_AMD_SB700 }, +}; + static void ehci_release_ownership(ehci_softc_t *sc, pci_chipset_tag_t pc, pcitag_t tag); static void ehci_get_ownership(ehci_softc_t *sc, pci_chipset_tag_t pc, @@ -74,7 +86,15 @@ void *sc_ih; /* interrupt vectoring */ }; +static int ehci_sb700_match(struct pci_attach_args *pa); +static int ehci_apply_amd_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_MAX_BIOS_WAIT 1000 /* ms */ +#define EHCI_SBx00_WORKAROUND_REG 0x50 +#define EHCI_SBx00_WORKAROUND_ENABLE __BIT(27) + static int ehci_pci_match(struct device *parent, struct cfdata *match, @@ -106,6 +126,7 @@ usbd_status r; int ncomp; struct usb_pci *up; + int quirk; sc->sc.sc_dev = self; sc->sc.sc_bus.hci_private = sc; @@ -116,6 +137,10 @@ aprint_normal(": %s (rev. 0x%02x)\n", devinfo, PCI_REVISION(pa->pa_class)); + /* 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, &sc->sc.iot, &sc->sc.ioh, NULL, &sc->sc.sc_size)) { @@ -127,6 +152,17 @@ sc->sc_tag = tag; sc->sc.sc_bus.dmatag = pa->pa_dmat; + /* Handle quirks */ + switch (quirk) { + case EHCI_PCI_QUIRK_AMD_SB600: + ehci_apply_amd_quirks(sc); + break; + case EHCI_PCI_QUIRK_AMD_SB700: + if (pci_find_device(NULL, ehci_sb700_match)) + ehci_apply_amd_quirks(sc); + break; + } + /* Enable the device. */ csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG); pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, @@ -381,3 +417,47 @@ ehci_get_ownership(&sc->sc, sc->sc_pc, sc->sc_tag); return ehci_resume(dv PMF_FN_CALL); } + +static 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_SB600_SMB)) + return 0; + + switch (PCI_REVISION(pa->pa_class)) { + case 0x3a: + case 0x3b: + return 1; + } + + return 0; +} + +static int +ehci_apply_amd_quirks(struct ehci_pci_softc *sc) +{ + pcireg_t value; + + aprint_normal_dev(sc->sc.sc_dev, + "applying AMD SB600/SB700 USB freeze workaround\n"); + 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 < __arraycount(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; +} +