From owner-freebsd-current Mon Oct 28 17:26:43 2002 Delivered-To: freebsd-current@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 113D737B401 for ; Mon, 28 Oct 2002 17:26:39 -0800 (PST) Received: from salmon.maths.tcd.ie (salmon.maths.tcd.ie [134.226.81.11]) by mx1.FreeBSD.org (Postfix) with SMTP id D5F1243E42 for ; Mon, 28 Oct 2002 17:26:37 -0800 (PST) (envelope-from iedowse@maths.tcd.ie) Received: from walton.maths.tcd.ie by salmon.maths.tcd.ie with SMTP id ; 29 Oct 2002 01:26:36 +0000 (GMT) To: current@freebsd.org Subject: ELCR in PCI-ISA bridge not getting set on resume Date: Tue, 29 Oct 2002 01:26:36 +0000 From: Ian Dowse Message-ID: <200210290126.aa73484@salmon.maths.tcd.ie> Sender: owner-freebsd-current@FreeBSD.ORG Precedence: bulk List-ID: List-Archive: (Web Archive) List-Help: (List Instructions) List-Subscribe: List-Unsubscribe: X-Loop: FreeBSD.ORG Since starting to use -current with ACPI on a Sony C1 laptop, I noticed that after resume, occasionally IRQ 9 would get stuck and not deliver any interrupts. IRQ 9 is shared by sound, USB and the pccard slot. It turned out that something was not saving the ELCR edge/level control registers in the PCI-ISA bridge, so on resume IRQ 9 was configured in edge-triggered mode, making interrupt loss inevitable. The patch below makes the "isab" driver save and restore the ELCR around suspends on the Intel 82371AB. Any comments on whether this is the right way or the right place to solve the problem? Ian Index: isa_pci.c =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/pci/isa_pci.c,v retrieving revision 1.6 diff -u -r1.6 isa_pci.c --- isa_pci.c 21 Dec 2001 01:28:46 -0000 1.6 +++ isa_pci.c 29 Oct 2002 01:01:33 -0000 @@ -37,21 +37,36 @@ #include #include #include +#include +#include #include +#include #include #include +#define ELCR_IOADDR 0x4d0 /* Interrupt Edge/Level Control Registers */ +#define ELCR_IOLEN 2 + +struct isab_softc { + struct resource *elcr_res; + u_char saved_elcr[ELCR_IOLEN]; +}; + static int isab_probe(device_t dev); static int isab_attach(device_t dev); +static int isab_detach(device_t dev); +static int isab_resume(device_t dev); +static int isab_suspend(device_t dev); static device_method_t isab_methods[] = { /* Device interface */ DEVMETHOD(device_probe, isab_probe), DEVMETHOD(device_attach, isab_attach), + DEVMETHOD(device_detach, isab_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), - DEVMETHOD(device_suspend, bus_generic_suspend), - DEVMETHOD(device_resume, bus_generic_resume), + DEVMETHOD(device_suspend, isab_suspend), + DEVMETHOD(device_resume, isab_resume), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), @@ -68,7 +83,7 @@ static driver_t isab_driver = { "isab", isab_methods, - 0, + sizeof(struct isab_softc), }; static devclass_t isab_devclass; @@ -143,14 +158,82 @@ isab_attach(device_t dev) { device_t child; + struct isab_softc *sc = device_get_softc(dev); + int error, rid; /* * Attach an ISA bus. Note that we can only have one ISA bus. */ child = device_add_child(dev, "isa", 0); - if (child != NULL) - return(bus_generic_attach(dev)); + if (child != NULL) { + error = bus_generic_attach(dev); + if (error) + return (error); + } + + switch (pci_get_devid(dev)) { + case 0x71108086: /* Intel 82371AB */ + /* + * Sometimes the ELCR (Edge/Level Control Register) is not restored + * correctly on resume by the BIOS, so we handle it ourselves. + */ + rid = 0; + bus_set_resource(dev, SYS_RES_IOPORT, rid, ELCR_IOADDR, ELCR_IOLEN); + sc->elcr_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, + RF_ACTIVE); + if (sc->elcr_res == NULL) + device_printf(dev, "failed to allocate ELCR resource\n"); + break; + } return(0); } +static int +isab_detach(device_t dev) +{ + struct isab_softc *sc = device_get_softc(dev); + + if (sc->elcr_res != NULL) + bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->elcr_res); + + return (bus_generic_detach(dev)); +} + +static int +isab_suspend(device_t dev) +{ + struct isab_softc *sc = device_get_softc(dev); + bus_space_tag_t bst; + bus_space_handle_t bsh; + int i; + + /* Save the ELCR if required. */ + if (sc->elcr_res != NULL) { + bst = rman_get_bustag(sc->elcr_res); + bsh = rman_get_bushandle(sc->elcr_res); + for (i = 0; i < ELCR_IOLEN; i++) + sc->saved_elcr[i] = bus_space_read_1(bst, bsh, i); + } + + return (bus_generic_suspend(dev)); +} + +static int +isab_resume(device_t dev) +{ + struct isab_softc *sc = device_get_softc(dev); + bus_space_tag_t bst; + bus_space_handle_t bsh; + int i; + + /* Restore the ELCR if required. */ + if (sc->elcr_res != NULL) { + bst = rman_get_bustag(sc->elcr_res); + bsh = rman_get_bushandle(sc->elcr_res); + for (i = 0; i < ELCR_IOLEN; i++) + bus_space_write_1(bst, bsh, i, sc->saved_elcr[i]); + } + + return (bus_generic_resume(dev)); +} To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-current" in the body of the message