summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authordg <dg@FreeBSD.org>2000-09-17 22:12:12 +0000
committerdg <dg@FreeBSD.org>2000-09-17 22:12:12 +0000
commit6fd9f8fc68d398e4231dd400462cb261b6701c5d (patch)
tree3cb221b7278ec4b39733839f4c419fdaf1c6179b /sys
parenta1dbfee15805bbe173b7923cd72d9c76d381af20 (diff)
downloadFreeBSD-src-6fd9f8fc68d398e4231dd400462cb261b6701c5d.zip
FreeBSD-src-6fd9f8fc68d398e4231dd400462cb261b6701c5d.tar.gz
Added support for APM suspend/resume.
PR: 18756 Submitted by: mike ryan <msr@elision.org>, with modifications by me.
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/fxp/if_fxp.c112
-rw-r--r--sys/dev/fxp/if_fxpvar.h6
-rw-r--r--sys/pci/if_fxp.c112
-rw-r--r--sys/pci/if_fxpvar.h6
4 files changed, 222 insertions, 14 deletions
diff --git a/sys/dev/fxp/if_fxp.c b/sys/dev/fxp/if_fxp.c
index c988786..e1539e1 100644
--- a/sys/dev/fxp/if_fxp.c
+++ b/sys/dev/fxp/if_fxp.c
@@ -221,6 +221,7 @@ static int fxp_mediachange __P((struct ifnet *));
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 *,
@@ -290,7 +291,23 @@ fxp_scb_wait(sc)
{
int i = 10000;
- while (CSR_READ_1(sc, FXP_CSR_SCB_COMMAND) && --i);
+ 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));
}
/*************************************************************
@@ -687,12 +704,85 @@ fxp_shutdown(device_t dev)
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;
+
+ 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);
+
+ sc->suspended = 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;
+
+ 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);
+
+ sc->suspended = 0;
+
+ 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 }
};
@@ -1126,7 +1216,7 @@ fxp_intr(arg)
FXP_LOCK(sc, s);
#endif
- while ((statack = CSR_READ_1(sc, FXP_CSR_SCB_STATACK)) != 0) {
+ while (!sc->suspended && (statack = CSR_READ_1(sc, FXP_CSR_SCB_STATACK)) != 0) {
#if defined(__NetBSD__)
claimed = 1;
#endif
@@ -1364,6 +1454,9 @@ fxp_stop(sc)
FXP_LOCK(sc, s);
#endif
+ ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+ ifp->if_timer = 0;
+
/*
* Cancel stats updater.
*/
@@ -1407,8 +1500,6 @@ fxp_stop(sc)
}
}
- ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
- ifp->if_timer = 0;
#if !defined(__NetBSD__)
FXP_UNLOCK(sc, s);
#endif
@@ -1526,7 +1617,7 @@ fxp_init(xsc)
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));
+ fxp_dma_wait(&cbp->cb_status, sc);
/*
* Now initialize the station address. Temporarily use the TxCB
@@ -1550,7 +1641,7 @@ fxp_init(xsc)
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));
+ fxp_dma_wait(&cb_ias->cb_status, sc);
/*
* Initialize transmit control block (TxCB) list.
@@ -1990,6 +2081,7 @@ fxp_mc_setup(sc)
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
@@ -2072,8 +2164,14 @@ fxp_mc_setup(sc)
* 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) ;
+ 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.
diff --git a/sys/dev/fxp/if_fxpvar.h b/sys/dev/fxp/if_fxpvar.h
index 8a0353b..82b8e80 100644
--- a/sys/dev/fxp/if_fxpvar.h
+++ b/sys/dev/fxp/if_fxpvar.h
@@ -69,6 +69,12 @@ struct fxp_softc {
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 */
+ int suspended; /* 0 = normal 1 = suspended (APM) */
+ 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. */
diff --git a/sys/pci/if_fxp.c b/sys/pci/if_fxp.c
index c988786..e1539e1 100644
--- a/sys/pci/if_fxp.c
+++ b/sys/pci/if_fxp.c
@@ -221,6 +221,7 @@ static int fxp_mediachange __P((struct ifnet *));
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 *,
@@ -290,7 +291,23 @@ fxp_scb_wait(sc)
{
int i = 10000;
- while (CSR_READ_1(sc, FXP_CSR_SCB_COMMAND) && --i);
+ 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));
}
/*************************************************************
@@ -687,12 +704,85 @@ fxp_shutdown(device_t dev)
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;
+
+ 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);
+
+ sc->suspended = 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;
+
+ 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);
+
+ sc->suspended = 0;
+
+ 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 }
};
@@ -1126,7 +1216,7 @@ fxp_intr(arg)
FXP_LOCK(sc, s);
#endif
- while ((statack = CSR_READ_1(sc, FXP_CSR_SCB_STATACK)) != 0) {
+ while (!sc->suspended && (statack = CSR_READ_1(sc, FXP_CSR_SCB_STATACK)) != 0) {
#if defined(__NetBSD__)
claimed = 1;
#endif
@@ -1364,6 +1454,9 @@ fxp_stop(sc)
FXP_LOCK(sc, s);
#endif
+ ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+ ifp->if_timer = 0;
+
/*
* Cancel stats updater.
*/
@@ -1407,8 +1500,6 @@ fxp_stop(sc)
}
}
- ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
- ifp->if_timer = 0;
#if !defined(__NetBSD__)
FXP_UNLOCK(sc, s);
#endif
@@ -1526,7 +1617,7 @@ fxp_init(xsc)
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));
+ fxp_dma_wait(&cbp->cb_status, sc);
/*
* Now initialize the station address. Temporarily use the TxCB
@@ -1550,7 +1641,7 @@ fxp_init(xsc)
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));
+ fxp_dma_wait(&cb_ias->cb_status, sc);
/*
* Initialize transmit control block (TxCB) list.
@@ -1990,6 +2081,7 @@ fxp_mc_setup(sc)
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
@@ -2072,8 +2164,14 @@ fxp_mc_setup(sc)
* 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) ;
+ 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.
diff --git a/sys/pci/if_fxpvar.h b/sys/pci/if_fxpvar.h
index 8a0353b..82b8e80 100644
--- a/sys/pci/if_fxpvar.h
+++ b/sys/pci/if_fxpvar.h
@@ -69,6 +69,12 @@ struct fxp_softc {
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 */
+ int suspended; /* 0 = normal 1 = suspended (APM) */
+ 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. */
OpenPOWER on IntegriCloud