From owner-freebsd-bugs Mon May 22 13:30:10 2000 Delivered-To: freebsd-bugs@freebsd.org Received: from freefall.freebsd.org (freefall.FreeBSD.ORG [204.216.27.21]) by hub.freebsd.org (Postfix) with ESMTP id 424CC37B718 for ; Mon, 22 May 2000 13:30:02 -0700 (PDT) (envelope-from gnats@FreeBSD.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.9.3/8.9.2) id NAA93853; Mon, 22 May 2000 13:30:01 -0700 (PDT) (envelope-from gnats@FreeBSD.org) Received: from host251-12.infonet.tufts.edu (host251-12.infonet.tufts.edu [130.64.251.12]) by hub.freebsd.org (Postfix) with ESMTP id ED33B37B715 for ; Mon, 22 May 2000 13:25:56 -0700 (PDT) (envelope-from mryan01@host251-12.infonet.tufts.edu) Received: (from mryan01@localhost) by vapre (8.9.3/8.9.3) id QAA00520; Mon, 22 May 2000 16:22:56 -0400 (EDT) (envelope-from mryan01) Message-Id: <200005222022.QAA00520@vapre> Date: Mon, 22 May 2000 16:22:56 -0400 (EDT) From: mike ryan To: FreeBSD-gnats-submit@freebsd.org X-Send-Pr-Version: 3.2 Subject: kern/18756: [PATCH] fxp device causes lockups after suspend/resume Sender: owner-freebsd-bugs@FreeBSD.ORG Precedence: bulk X-Loop: FreeBSD.org >Number: 18756 >Category: kern >Synopsis: [PATCH] fxp device causes lockups after suspend/resume >Confidential: no >Severity: serious >Priority: medium >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: sw-bug >Submitter-Id: current-users >Arrival-Date: Mon May 22 13:30:01 PDT 2000 >Closed-Date: >Last-Modified: >Originator: mike ryan >Release: FreeBSD 4.0-STABLE i386 >Organization: >Environment: sony vaio z505hs, recent 4.0-STABLE GENERIC kernel. >Description: with the internal fxp device up and running, the machine will lock up after suspend/resume. a DDB trace indicates an infinite loop while waiting for a DMA to complete within the fxp_init function in sys/pci/if_fxp.c at line 1518: 1511 /* 1512 * Start the config command/DMA. 1513 */ 1514 fxp_scb_wait(sc); 1515 CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, vtophys(&cbp->cb_status)); 1516 CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START); 1517 /* ...and wait for it to complete. */ 1518 while (!(cbp->cb_status & FXP_CB_STATUS_C)); >How-To-Repeat: ifconfig fxp0 up, suspend, and resume. >Fix: the patch below includes code translated from netbsd to avoid and log a few infinite loops, including the one above, and to avoid trying to handle interrupts before the device is resumed, and from linux to save and restore some necessary PCI data. it makes suspend/resume work for me, but i haven't tested it anywhere but my laptop. Index: sys/pci/if_fxp.c =================================================================== RCS file: /usr/local/src/FREEBSD/src/sys/pci/if_fxp.c,v retrieving revision 1.77.2.1 diff -c -r1.77.2.1 if_fxp.c *** sys/pci/if_fxp.c 2000/03/29 02:02:38 1.77.2.1 --- sys/pci/if_fxp.c 2000/05/22 20:01:22 *************** *** 222,227 **** --- 222,228 ---- static void fxp_mediastatus __P((struct ifnet *, struct ifmediareq *)); static void fxp_set_media __P((struct fxp_softc *, int)); static __inline void fxp_scb_wait __P((struct fxp_softc *)); + static __inline void fxp_dma_wait __P((volatile u_int16_t *, struct fxp_softc *sc)); static FXP_INTR_TYPE fxp_intr __P((void *)); static void fxp_start __P((struct ifnet *)); static int fxp_ioctl __P((struct ifnet *, *************** *** 291,299 **** { int i = 10000; ! while (CSR_READ_1(sc, FXP_CSR_SCB_COMMAND) && --i); } /************************************************************* * Operating system-specific autoconfiguration glue *************************************************************/ --- 292,316 ---- { int i = 10000; ! while (CSR_READ_1(sc, FXP_CSR_SCB_COMMAND) && --i) ! DELAY(2); ! if (i == 0) ! printf(FXP_FORMAT ": SCB timeout\n", FXP_ARGS(sc)); } + static __inline void + fxp_dma_wait(status, sc) + volatile u_int16_t *status; + struct fxp_softc *sc; + { + int i = 10000; + + while (!(*status & FXP_CB_STATUS_C) && --i) + DELAY(2); + if (i == 0) + printf(FXP_FORMAT ": DMA timeout\n", FXP_ARGS(sc)); + } + /************************************************************* * Operating system-specific autoconfiguration glue *************************************************************/ *************** *** 680,691 **** --- 697,773 ---- return 0; } + /* + * Device suspend routine. Stop the interface and save some PCI + * settings in case the BIOS doesn't restore them properly on + * resume. + */ + static int + fxp_suspend(device_t dev) + { + struct fxp_softc *sc = device_get_softc(dev); + int i, s = splimp(); + + fxp_stop(sc); + + for (i=0; i<5; i++) + sc->saved_maps[i] = pci_read_config(dev, PCIR_MAPS + i*4, 4); + sc->saved_biosaddr = pci_read_config(dev, PCIR_BIOS, 4); + sc->saved_intline = pci_read_config(dev, PCIR_INTLINE, 1); + sc->saved_cachelnsz = pci_read_config(dev, PCIR_CACHELNSZ, 1); + sc->saved_lattimer = pci_read_config(dev, PCIR_LATTIMER, 1); + + splx(s); + + return 0; + } + + /* + * Device resume routine. Restore some PCI settings in case the BIOS + * doesn't, re-enable busmastering, and restart the interface if + * appropriate. + */ + static int + fxp_resume(device_t dev) + { + struct fxp_softc *sc = device_get_softc(dev); + struct ifnet *ifp = &sc->sc_if; + u_int16_t pci_command; + int i, s = splimp(); + + /* better way to do this? */ + for (i=0; i<5; i++) + pci_write_config(dev, PCIR_MAPS + i*4, sc->saved_maps[i], 4); + pci_write_config(dev, PCIR_BIOS, sc->saved_biosaddr, 4); + pci_write_config(dev, PCIR_INTLINE, sc->saved_intline, 1); + pci_write_config(dev, PCIR_CACHELNSZ, sc->saved_cachelnsz, 1); + pci_write_config(dev, PCIR_LATTIMER, sc->saved_lattimer, 1); + + /* reenable busmastering */ + pci_command = pci_read_config(dev, PCIR_COMMAND, 2); + pci_command |= (PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); + pci_write_config(dev, PCIR_COMMAND, pci_command, 2); + + CSR_WRITE_4(sc, FXP_CSR_PORT, FXP_PORT_SELECTIVE_RESET); + DELAY(10); + + /* reinitialize interface if necessary */ + if (ifp->if_flags & IFF_UP) + fxp_init(sc); + + splx(s); + + return 0; + } + static device_method_t fxp_methods[] = { /* Device interface */ DEVMETHOD(device_probe, fxp_probe), DEVMETHOD(device_attach, fxp_attach), DEVMETHOD(device_detach, fxp_detach), DEVMETHOD(device_shutdown, fxp_shutdown), + DEVMETHOD(device_suspend, fxp_suspend), + DEVMETHOD(device_resume, fxp_resume), { 0, 0 } }; *************** *** 1096,1101 **** --- 1178,1188 ---- int claimed = 0; #endif + /* don't try to service interrupts when the interface isn't running */ + if ((ifp->if_flags & IFF_RUNNING) == 0) { + return; + } + while ((statack = CSR_READ_1(sc, FXP_CSR_SCB_STATACK)) != 0) { #if defined(__NetBSD__) claimed = 1; *************** *** 1358,1363 **** --- 1445,1453 ---- struct fxp_cb_tx *txp; int i; + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + ifp->if_timer = 0; + /* * Cancel stats updater. */ *************** *** 1400,1408 **** panic("fxp_stop: no buffers!"); } } - - ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); - ifp->if_timer = 0; } /* --- 1490,1495 ---- *************** *** 1515,1521 **** CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, vtophys(&cbp->cb_status)); CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START); /* ...and wait for it to complete. */ ! while (!(cbp->cb_status & FXP_CB_STATUS_C)); /* * Now initialize the station address. Temporarily use the TxCB --- 1602,1608 ---- CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, vtophys(&cbp->cb_status)); CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START); /* ...and wait for it to complete. */ ! fxp_dma_wait(&cbp->cb_status, sc); /* * Now initialize the station address. Temporarily use the TxCB *************** *** 1538,1544 **** fxp_scb_wait(sc); CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START); /* ...and wait for it to complete. */ ! while (!(cb_ias->cb_status & FXP_CB_STATUS_C)); /* * Initialize transmit control block (TxCB) list. --- 1625,1631 ---- fxp_scb_wait(sc); CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START); /* ...and wait for it to complete. */ ! fxp_dma_wait(&cb_ias->cb_status, sc); /* * Initialize transmit control block (TxCB) list. *************** *** 1977,1982 **** --- 2064,2070 ---- struct ifnet *ifp = &sc->sc_if; struct ifmultiaddr *ifma; int nmcasts; + int count; /* * If there are queued commands, we must wait until they are all *************** *** 2058,2065 **** * Wait until command unit is not active. This should never * be the case when nothing is queued, but make sure anyway. */ while ((CSR_READ_1(sc, FXP_CSR_SCB_RUSCUS) >> 6) == ! FXP_SCB_CUS_ACTIVE) ; /* * Start the multicast setup command. --- 2146,2159 ---- * Wait until command unit is not active. This should never * be the case when nothing is queued, but make sure anyway. */ + count = 100; while ((CSR_READ_1(sc, FXP_CSR_SCB_RUSCUS) >> 6) == ! FXP_SCB_CUS_ACTIVE && --count) ! DELAY(10); ! if (count == 0) { ! printf(FXP_FORMAT ": command queue timeout\n", FXP_ARGS(sc)); ! return; ! } /* * Start the multicast setup command. Index: sys/pci/if_fxpvar.h =================================================================== RCS file: /usr/local/src/FREEBSD/src/sys/pci/if_fxpvar.h,v retrieving revision 1.9.2.1 diff -c -r1.9.2.1 if_fxpvar.h *** sys/pci/if_fxpvar.h 2000/03/29 02:02:39 1.9.2.1 --- sys/pci/if_fxpvar.h 2000/05/22 19:58:05 *************** *** 68,73 **** --- 68,78 ---- int phy_primary_device; /* device type of primary PHY */ int phy_10Mbps_only; /* PHY is 10Mbps-only device */ int eeprom_size; /* size of serial EEPROM */ + u_int32_t saved_maps[5]; /* pci data */ + u_int32_t saved_biosaddr; + u_int8_t saved_intline; + u_int8_t saved_cachelnsz; + u_int8_t saved_lattimer; }; /* Macros to ease CSR access. */ >Release-Note: >Audit-Trail: >Unformatted: To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-bugs" in the body of the message