Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/netbsd-1-4]: src/sys/dev/pci Apply patch (requested by jhawk):



details:   https://anonhg.NetBSD.org/src/rev/6d56705680dc
branches:  netbsd-1-4
changeset: 470603:6d56705680dc
user:      he <he%NetBSD.org@localhost>
date:      Sat May 13 18:24:52 2000 +0000

description:
Apply patch (requested by jhawk):
  Fix the fxp driver so that it works after a warm-boot from windows
  and across an APM suspend/resume or hibernate/resume cycle.  Fixes
  PR#9370, PR#9548, PR#9571 and PR#9573.

diffstat:

 sys/dev/pci/if_fxp.c    |  127 +++++++++++++++++++++++++++++++++++++++++++++--
 sys/dev/pci/if_fxpvar.h |    5 +-
 2 files changed, 125 insertions(+), 7 deletions(-)

diffs (218 lines):

diff -r 0e9e6709c853 -r 6d56705680dc sys/dev/pci/if_fxp.c
--- a/sys/dev/pci/if_fxp.c      Sat May 13 17:22:20 2000 +0000
+++ b/sys/dev/pci/if_fxp.c      Sat May 13 18:24:52 2000 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: if_fxp.c,v 1.33 1999/03/23 23:18:50 thorpej Exp $      */
+/*     $NetBSD: if_fxp.c,v 1.33.2.1 2000/05/13 18:24:52 he Exp $       */
 
 /*-
  * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
@@ -113,12 +113,22 @@
 
 #include <dev/mii/miivar.h>
 
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcidevs.h>
+
 #include <dev/pci/if_fxpreg.h>
 #include <dev/pci/if_fxpvar.h>
 
-#include <dev/pci/pcivar.h>
-#include <dev/pci/pcireg.h>
-#include <dev/pci/pcidevs.h>
+/*
+ * Constants for PCI (ACPI) power management manipulation.
+ */
+
+#define PCI_PMCSR_STATE_MASK   0x03
+#define PCI_PMCSR_STATE_D0      0x00
+#define PCI_PMCSR_STATE_D1      0x01
+#define PCI_PMCSR_STATE_D2      0x02
+#define PCI_PMCSR_STATE_D3      0x03
 
 /*
  * NOTE!  On the Alpha, we have an alignment constraint.  The
@@ -172,6 +182,7 @@
 static void fxp_80c24_mediastatus __P((struct ifnet *, struct ifmediareq *));
 
 static inline void fxp_scb_wait        __P((struct fxp_softc *));
+static inline void fxp_pci_confreg_restore __P((struct fxp_softc *));
 static int fxp_intr            __P((void *));
 static void fxp_start          __P((struct ifnet *));
 static int fxp_ioctl           __P((struct ifnet *, u_long, caddr_t));
@@ -219,6 +230,53 @@
                printf("%s: WARNING: SCB timed out!\n", sc->sc_dev.dv_xname);
 }
 
+/*
+ * Restore PCI configuration registers that may have been clobbered.
+ * This is necessary due to bugs on the Sony VAIO Z505-series on-board
+ * ethernet, after an APM suspend/resume.  Ideally this function would
+ * be called from a power-hook after APM resume, but no such hook
+ * exists at this time, so instead we call it when the driver detects
+ * something awry.
+ */
+
+static inline void
+fxp_pci_confreg_restore(sc)
+        struct fxp_softc *sc;
+{
+       pcireg_t reg;
+       int flag;
+
+       /*
+        * Check to see if the command register is blank -- if so, then
+        * we'll assume that all the clobberable-registers have been
+        * clobbered.
+        */
+       flag = ((reg = pci_conf_read(sc->sc_pc, sc->sc_tag,
+           PCI_COMMAND_STATUS_REG)) & 0xffff) == 0;
+
+       /*
+        * In general, the above metric is accurate. Unfortunately,
+        * it is inaccurate across a hibernation. Ideally APM/ACPI
+        * code should take note of hibernation events and execute
+        * a hibernation wakeup hook, but that isn't here at this time.
+        */
+       flag |= 1;
+
+       if (flag) {
+               pci_conf_write(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG,
+                   (reg & 0xffff0000) |
+                   (sc->sc_regs[PCI_COMMAND_STATUS_REG>>2] & 0xffff));
+               pci_conf_write(sc->sc_pc, sc->sc_tag, PCI_BHLC_REG,
+                   sc->sc_regs[PCI_BHLC_REG>>2]);
+               pci_conf_write(sc->sc_pc, sc->sc_tag, PCI_MAPREG_START+0x0,
+                   sc->sc_regs[(PCI_MAPREG_START+0x0)>>2]);
+               pci_conf_write(sc->sc_pc, sc->sc_tag, PCI_MAPREG_START+0x4,
+                   sc->sc_regs[(PCI_MAPREG_START+0x4)>>2]);
+               pci_conf_write(sc->sc_pc, sc->sc_tag, PCI_MAPREG_START+0x8,
+                   sc->sc_regs[(PCI_MAPREG_START+0x8)>>2]);
+       }
+}
+
 static int fxp_match __P((struct device *, struct cfdata *, void *));
 static void fxp_attach __P((struct device *, struct device *, void *));
 
@@ -270,6 +328,7 @@
        bus_size_t size;
        int flags, rseg, i, error, attach_stage;
        struct fxp_phytype *fp;
+       int pci_pwrmgmt_cap_reg, pci_pwrmgmt_csr_reg;
 
        /*
         * Map control/status registers.
@@ -332,6 +391,49 @@
            PCI_COMMAND_MASTER_ENABLE);
 
        /*
+        * Under some circumstances (such as APM suspend/resume
+        * cycles, and across ACPI power state changes), the
+        * i82257-family can lose the contents of critical PCI
+        * configuration registers, causing the card to be
+        * non-responsive and useless.  This occurs on the Sony VAIO
+        * Z505-series, among others.  Preserve them here so they can
+        * be later restored (by fxp_pci_confreg_restore()).
+        */
+       sc->sc_pc = pc;
+       sc->sc_tag = pa->pa_tag;
+       sc->sc_regs[PCI_COMMAND_STATUS_REG>>2] =
+           pci_conf_read(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
+       sc->sc_regs[PCI_BHLC_REG>>2] =
+           pci_conf_read(pc, pa->pa_tag, PCI_BHLC_REG);
+       sc->sc_regs[(PCI_MAPREG_START+0x0)>>2] =
+           pci_conf_read(pc, pa->pa_tag, PCI_MAPREG_START+0x0);
+       sc->sc_regs[(PCI_MAPREG_START+0x4)>>2] =
+           pci_conf_read(pc, pa->pa_tag, PCI_MAPREG_START+0x4);
+       sc->sc_regs[(PCI_MAPREG_START+0x8)>>2] =
+           pci_conf_read(pc, pa->pa_tag, PCI_MAPREG_START+0x8);
+
+       /*
+        * Work around BIOS ACPI bugs where the chip is inadvertantly
+        * left in ACPI D3 (lowest power state).  First confirm the device
+        * supports ACPI power management, then move it to the D0 (fully
+        * functional) state if it is not already there.
+        */
+       if (pci_get_capability(pc, pa->pa_tag, PCI_CAP_PWRMGMT,
+           &pci_pwrmgmt_cap_reg, 0)) {
+               pcireg_t reg;
+
+               pci_pwrmgmt_csr_reg = pci_pwrmgmt_cap_reg + 4;
+               reg = pci_conf_read(pc, pa->pa_tag, pci_pwrmgmt_csr_reg);
+               if ((reg & PCI_PMCSR_STATE_MASK) != PCI_PMCSR_STATE_D0) {
+                   pci_conf_write(pc, pa->pa_tag, pci_pwrmgmt_csr_reg,
+                       (reg & ~PCI_PMCSR_STATE_MASK) |
+                       PCI_PMCSR_STATE_D0);
+               }
+       }
+       /* Restore PCI configuration registers. */
+       fxp_pci_confreg_restore(sc);
+
+       /*
         * Allocate our interrupt.
         */
        if (pci_intr_map(pc, pa->pa_intrtag, pa->pa_intrpin,
@@ -1216,6 +1318,9 @@
        struct fxp_cb_tx *txp;
        int i, s, prm, error;
 
+       /* Restore PCI configuration registers. */
+       fxp_pci_confreg_restore(sc);
+
        s = splnet();
        /*
         * Cancel any pending I/O
@@ -1315,10 +1420,15 @@
        bus_dmamap_sync(sc->sc_dmat, sc->sc_tx_dmamaps[0],
            0, sizeof(struct fxp_cb_config),
            BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
-       while (!(cbp->cb_status & FXP_CB_STATUS_C))
+       i = 10000;
+       while (!(cbp->cb_status & FXP_CB_STATUS_C) && --i)
                bus_dmamap_sync(sc->sc_dmat, sc->sc_tx_dmamaps[0],
                    0, sizeof(struct fxp_cb_config),
                    BUS_DMASYNC_POSTREAD);
+       if (i == 0) {
+               printf("%s: dmasync timeout\n", sc->sc_dev.dv_xname);
+               return;
+       }
 
        /* Unload the DMA map */
        bus_dmamap_unload(sc->sc_dmat, sc->sc_tx_dmamaps[0]);
@@ -1354,10 +1464,15 @@
        bus_dmamap_sync(sc->sc_dmat, sc->sc_tx_dmamaps[0],
            0, sizeof(struct fxp_cb_ias),
            BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
-       while (!(cb_ias->cb_status & FXP_CB_STATUS_C))
+       i = 10000;
+       while (!(cb_ias->cb_status & FXP_CB_STATUS_C) && --i)
                bus_dmamap_sync(sc->sc_dmat, sc->sc_tx_dmamaps[0],
                    0, sizeof(struct fxp_cb_ias),
                    BUS_DMASYNC_POSTREAD);
+       if (i == 0) {
+               printf("%s: dmasync timeout\n", sc->sc_dev.dv_xname);
+               return;
+       }
 
        /* Unload the DMA map */
        bus_dmamap_unload(sc->sc_dmat, sc->sc_tx_dmamaps[0]);
diff -r 0e9e6709c853 -r 6d56705680dc sys/dev/pci/if_fxpvar.h
--- a/sys/dev/pci/if_fxpvar.h   Sat May 13 17:22:20 2000 +0000
+++ b/sys/dev/pci/if_fxpvar.h   Sat May 13 18:24:52 2000 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: if_fxpvar.h,v 1.9 1999/02/18 01:23:41 thorpej Exp $    */
+/*     $NetBSD: if_fxpvar.h,v 1.9.2.1 2000/05/13 18:24:53 he Exp $     */
 
 /*-
  * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
@@ -135,6 +135,9 @@
 struct fxp_softc {
        struct device sc_dev;           /* generic device structures */
        void *sc_ih;                    /* interrupt handler cookie */
+       pci_chipset_tag_t sc_pc;
+       pcitag_t sc_tag;
+       pcireg_t sc_regs[0x20>>2];      /* saved PCI config regs (sparse) */
        bus_space_tag_t sc_st;          /* bus space tag */
        bus_space_handle_t sc_sh;       /* bus space handle */
        bus_dma_tag_t sc_dmat;          /* bus dma tag */



Home | Main Index | Thread Index | Old Index