summaryrefslogtreecommitdiff
path: root/sys/dev/pci
diff options
context:
space:
mode:
authorDamien Bergamini <damien@cvs.openbsd.org>2004-10-27 21:21:17 +0000
committerDamien Bergamini <damien@cvs.openbsd.org>2004-10-27 21:21:17 +0000
commit8e090bce28179d99854dbc51a3e4fc7f18375d67 (patch)
tree7fb467992d96c51ca4ab7b186b622e3526a23c20 /sys/dev/pci
parent79b9558ec83d1c184913b4ba0dd0ea6eb497c5f4 (diff)
Cache firmware into kernel memory instead of sending it directly. The
firmware is now sent only when the interface is turned up. This paves the way for proper suspend/resume support.
Diffstat (limited to 'sys/dev/pci')
-rw-r--r--sys/dev/pci/if_ipw.c262
-rw-r--r--sys/dev/pci/if_ipwreg.h8
-rw-r--r--sys/dev/pci/if_ipwvar.h13
3 files changed, 134 insertions, 149 deletions
diff --git a/sys/dev/pci/if_ipw.c b/sys/dev/pci/if_ipw.c
index 642fdb45236..1a7e16a5022 100644
--- a/sys/dev/pci/if_ipw.c
+++ b/sys/dev/pci/if_ipw.c
@@ -1,4 +1,4 @@
-/* $Id: if_ipw.c,v 1.11 2004/10/27 21:20:30 damien Exp $ */
+/* $Id: if_ipw.c,v 1.12 2004/10/27 21:21:16 damien Exp $ */
/*-
* Copyright (c) 2004
@@ -106,11 +106,11 @@ void ipw_tx_stop(struct ipw_softc *);
int ipw_rx_init(struct ipw_softc *);
void ipw_rx_stop(struct ipw_softc *);
void ipw_stop_master(struct ipw_softc *);
-void ipw_reset(struct ipw_softc *);
-int ipw_clock_sync(struct ipw_softc *);
+int ipw_reset(struct ipw_softc *);
int ipw_load_ucode(struct ipw_softc *, u_char *, int);
int ipw_load_firmware(struct ipw_softc *, u_char *, int);
-int ipw_firmware_init(struct ipw_softc *, u_char *);
+int ipw_cache_firmware(struct ipw_softc *, void *);
+void ipw_free_firmware(struct ipw_softc *);
int ipw_config(struct ipw_softc *);
int ipw_init(struct ifnet *);
void ipw_stop(struct ifnet *, int);
@@ -275,7 +275,10 @@ ipw_detach(struct device* self, int flags)
struct ipw_softc *sc = (struct ipw_softc *)self;
struct ifnet *ifp = &sc->sc_ic.ic_if;
- ipw_reset(sc);
+ ipw_stop(ifp, 1);
+
+ if (sc->flags & IPW_FLAG_FW_CACHED)
+ ipw_free_firmware(sc);
#if NBPFILTER > 0
bpfdetach(ifp);
@@ -408,14 +411,7 @@ ipw_command_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf)
letoh32(cmd->type), letoh32(cmd->subtype), letoh32(cmd->seq),
letoh32(cmd->len), letoh32(cmd->status)));
- /*
- * Wake up processes waiting for command ack. In the case of the
- * IPW_CMD_DISABLE command, wake up the process only when the adapter
- * enters the IPW_STATE_DISABLED state. This is notified in
- * ipw_newstate_intr().
- */
- if (letoh32(cmd->type) != IPW_CMD_DISABLE)
- wakeup(sc);
+ wakeup(sc);
}
void
@@ -444,10 +440,6 @@ ipw_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf)
ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
break;
- case IPW_STATE_DISABLED:
- wakeup(sc->cmd);
- break;
-
case IPW_STATE_RADIO_DISABLED:
/* XXX should turn the interface down */
break;
@@ -937,8 +929,10 @@ ipw_get_table1(struct ipw_softc *sc, u_int32_t *tbl)
{
u_int32_t i, size, buf[256];
- if (!(sc->flags & IPW_FLAG_FW_INITED))
- return ENOTTY;
+ if (!(sc->flags & IPW_FLAG_FW_INITED)) {
+ bzero(buf, sizeof buf);
+ return copyout(buf, tbl, sizeof buf);
+ }
CSR_WRITE_4(sc, IPW_CSR_AUTOINC_ADDR, sc->table1_base);
@@ -954,9 +948,6 @@ ipw_get_radio(struct ipw_softc *sc, int *ret)
{
int val;
- if (!(sc->flags & IPW_FLAG_FW_INITED))
- return ENOTTY;
-
val = (CSR_READ_4(sc, IPW_CSR_IO) & IPW_IO_RADIO_DISABLED) ? 0 : 1;
return copyout(&val, ret, sizeof val);
}
@@ -1013,7 +1004,7 @@ ipw_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
break;
ifr = (struct ifreq *)data;
- error = ipw_firmware_init(sc, (u_char *)ifr->ifr_data);
+ error = ipw_cache_firmware(sc, ifr->ifr_data);
break;
case SIOCSKILLFW:
@@ -1021,7 +1012,8 @@ ipw_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
if ((error = suser(curproc, 0)) != 0)
break;
- ipw_reset(sc);
+ ipw_stop(ifp, 1);
+ ipw_free_firmware(sc);
break;
case SIOCG80211AUTH:
@@ -1492,64 +1484,33 @@ ipw_stop_master(struct ipw_softc *sc)
sc->flags &= ~IPW_FLAG_FW_INITED;
}
-void
+int
ipw_reset(struct ipw_softc *sc)
{
- struct ifnet *ifp = &sc->sc_ic.ic_if;
int ntries;
- ipw_stop(ifp, 1);
-
- if (sc->flags & IPW_FLAG_FW_INITED) {
- ipw_cmd(sc, IPW_CMD_DISABLE_PHY, NULL, 0);
- ipw_cmd(sc, IPW_CMD_PREPARE_POWER_DOWN, NULL, 0);
-
- sc->flags &= ~IPW_FLAG_FW_INITED;
- }
-
- /* Disable interrupts */
- CSR_WRITE_4(sc, IPW_CSR_INTR_MASK, 0);
-
- CSR_WRITE_4(sc, IPW_CSR_RST, IPW_RST_STOP_MASTER);
- for (ntries = 0; ntries < 5; ntries++) {
- if (CSR_READ_4(sc, IPW_CSR_RST) & IPW_RST_MASTER_DISABLED)
- break;
- DELAY(10);
- }
-
- CSR_WRITE_4(sc, IPW_CSR_RST, IPW_RST_SW_RESET);
-
- ipw_rx_stop(sc);
- ipw_tx_stop(sc);
-
- ifp->if_flags &= ~IFF_UP;
-}
-
-int
-ipw_clock_sync(struct ipw_softc *sc)
-{
- int ntries;
- u_int32_t r;
+ ipw_stop_master(sc);
- CSR_WRITE_4(sc, IPW_CSR_RST, IPW_RST_SW_RESET);
- for (ntries = 0; ntries < 1000; ntries++) {
- if (CSR_READ_4(sc, IPW_CSR_RST) & IPW_RST_PRINCETON_RESET)
- break;
- DELAY(10);
- }
- if (ntries == 1000)
- return EIO;
+ /* Move adapter to D0 state */
+ CSR_WRITE_4(sc, IPW_CSR_CTL, CSR_READ_4(sc, IPW_CSR_CTL) |
+ IPW_CTL_INIT);
- CSR_WRITE_4(sc, IPW_CSR_CTL, IPW_CTL_INIT);
+ /* Wait for clock stabilization */
for (ntries = 0; ntries < 1000; ntries++) {
- if ((r = CSR_READ_4(sc, IPW_CSR_CTL)) & IPW_CTL_CLOCK_READY)
+ if (CSR_READ_4(sc, IPW_CSR_CTL) & IPW_CTL_CLOCK_READY)
break;
DELAY(200);
}
if (ntries == 1000)
return EIO;
- CSR_WRITE_4(sc, IPW_CSR_CTL, r | IPW_CTL_ALLOW_STANDBY);
+ CSR_WRITE_4(sc, IPW_CSR_RST, CSR_READ_4(sc, IPW_CSR_RST) |
+ IPW_RST_SW_RESET);
+
+ DELAY(10);
+
+ CSR_WRITE_4(sc, IPW_CSR_CTL, CSR_READ_4(sc, IPW_CSR_CTL) |
+ IPW_CTL_INIT);
return 0;
}
@@ -1635,6 +1596,8 @@ ipw_load_firmware(struct ipw_softc *sc, u_char *fw, int size)
/* Tell the adapter to initialize the firmware */
CSR_WRITE_4(sc, IPW_CSR_RST, 0);
+ CSR_WRITE_4(sc, IPW_CSR_CTL, CSR_READ_4(sc, IPW_CSR_CTL) |
+ IPW_CTL_ALLOW_STANDBY);
/* Wait at most one second for firmware initialization to complete */
if ((error = tsleep(sc, 0, "ipwinit", hz)) != 0) {
@@ -1649,98 +1612,66 @@ ipw_load_firmware(struct ipw_softc *sc, u_char *fw, int size)
return 0;
}
+/*
+ * Store firmware into kernel memory so we can download it when we need to,
+ * e.g when the adapter wakes up from suspend mode.
+ */
int
-ipw_firmware_init(struct ipw_softc *sc, u_char *data)
+ipw_cache_firmware(struct ipw_softc *sc, void *data)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = &ic->ic_if;
- struct ipw_fw_hdr hdr;
- u_int32_t len, fw_size, uc_size;
- u_char *fw, *uc;
+ struct ipw_firmware *fw = &sc->fw;
+ struct ipw_firmware_hdr hdr;
+ u_char *p = data;
int error;
- ipw_reset(sc);
+ if (sc->flags & IPW_FLAG_FW_CACHED)
+ ipw_free_firmware(sc);
if ((error = copyin(data, &hdr, sizeof hdr)) != 0)
goto fail1;
- fw_size = letoh32(hdr.fw_size);
- uc_size = letoh32(hdr.uc_size);
- data += sizeof hdr;
+ fw->main_size = letoh32(hdr.main_size);
+ fw->ucode_size = letoh32(hdr.ucode_size);
+ p += sizeof hdr;
- if ((fw = malloc(fw_size, M_DEVBUF, M_NOWAIT)) == NULL) {
+ fw->main = malloc(fw->main_size, M_DEVBUF, M_NOWAIT);
+ if (fw->main == NULL) {
error = ENOMEM;
goto fail1;
}
- if ((error = copyin(data, fw, fw_size)) != 0)
- goto fail2;
-
- data += fw_size;
-
- if ((uc = malloc(uc_size, M_DEVBUF, M_NOWAIT)) == NULL) {
+ fw->ucode = malloc(fw->ucode_size, M_DEVBUF, M_NOWAIT);
+ if (fw->ucode == NULL) {
error = ENOMEM;
goto fail2;
}
- if ((error = copyin(data, uc, uc_size)) != 0)
- goto fail3;
-
- if ((error = ipw_clock_sync(sc)) != 0) {
- printf("%s: clock synchronization failed\n",
- sc->sc_dev.dv_xname);
- goto fail3;
- }
-
- if ((error = ipw_load_ucode(sc, uc, uc_size)) != 0) {
- printf("%s: could not load microcode\n", sc->sc_dev.dv_xname);
- goto fail3;
- }
-
- ipw_stop_master(sc);
-
- if ((error = ipw_rx_init(sc)) != 0) {
- printf("%s: could not initialize rx queue\n",
- sc->sc_dev.dv_xname);
- goto fail3;
- }
-
- if ((error = ipw_tx_init(sc)) != 0) {
- printf("%s: could not initialize tx queue\n",
- sc->sc_dev.dv_xname);
+ if ((error = copyin(p, fw->main, fw->main_size)) != 0)
goto fail3;
- }
- if ((error = ipw_load_firmware(sc, fw, fw_size))) {
- printf("%s: could not load firmware\n", sc->sc_dev.dv_xname);
+ p += fw->main_size;
+ if ((error = copyin(p, fw->ucode, fw->ucode_size)) != 0)
goto fail3;
- }
-
- /* Firmware initialization completed */
- sc->flags |= IPW_FLAG_FW_INITED;
- free(uc, M_DEVBUF);
- free(fw, M_DEVBUF);
-
- /* Retrieve information tables base addresses */
- sc->table1_base = CSR_READ_4(sc, IPW_CSR_TABLE1_BASE);
- sc->table2_base = CSR_READ_4(sc, IPW_CSR_TABLE2_BASE);
-
- ipw_write_table1(sc, IPW_INFO_LOCK, 0);
-
- /* Retrieve adapter MAC address */
- len = IEEE80211_ADDR_LEN;
- ipw_read_table2(sc, IPW_INFO_ADAPTER_MAC, ic->ic_myaddr, &len);
+ DPRINTF(("Firmware cached: main %u, ucode %u\n", fw->main_size,
+ fw->ucode_size));
- IEEE80211_ADDR_COPY(LLADDR(ifp->if_sadl), ic->ic_myaddr);
+ sc->flags |= IPW_FLAG_FW_CACHED;
return 0;
-fail3: free(uc, M_DEVBUF);
-fail2: free(fw, M_DEVBUF);
-fail1: ipw_reset(sc);
+fail3: free(fw->ucode, M_DEVBUF);
+fail2: free(fw->main, M_DEVBUF);
+fail1: return error;
+}
- return error;
+void
+ipw_free_firmware(struct ipw_softc *sc)
+{
+ free(sc->fw.main, M_DEVBUF);
+ free(sc->fw.ucode, M_DEVBUF);
+
+ sc->flags &= ~IPW_FLAG_FW_CACHED;
}
int
@@ -1945,16 +1876,60 @@ int
ipw_init(struct ifnet *ifp)
{
struct ipw_softc *sc = ifp->if_softc;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ipw_firmware *fw = &sc->fw;
+ int error, len;
/* exit immediately if firmware has not been ioctl'd */
- if (!(sc->flags & IPW_FLAG_FW_INITED)) {
+ if (!(sc->flags & IPW_FLAG_FW_CACHED)) {
ifp->if_flags &= ~IFF_UP;
return EIO;
}
- ipw_stop(ifp, 0);
+ if ((error = ipw_reset(sc)) != 0) {
+ printf("%s: could not reset adapter\n", sc->sc_dev.dv_xname);
+ goto fail;
+ }
+
+ if ((error = ipw_load_ucode(sc, fw->ucode, fw->ucode_size)) != 0) {
+ printf("%s: could not load microcode\n", sc->sc_dev.dv_xname);
+ goto fail;
+ }
+
+ ipw_stop_master(sc);
+
+ if ((error = ipw_rx_init(sc)) != 0) {
+ printf("%s: could not initialize rx queue\n",
+ sc->sc_dev.dv_xname);
+ goto fail;
+ }
+
+ if ((error = ipw_tx_init(sc)) != 0) {
+ printf("%s: could not initialize tx queue\n",
+ sc->sc_dev.dv_xname);
+ goto fail;
+ }
+
+ if ((error = ipw_load_firmware(sc, fw->main, fw->main_size)) != 0) {
+ printf("%s: could not load firmware\n", sc->sc_dev.dv_xname);
+ goto fail;
+ }
+
+ sc->flags |= IPW_FLAG_FW_INITED;
+
+ /* Retrieve information tables base addresses */
+ sc->table1_base = CSR_READ_4(sc, IPW_CSR_TABLE1_BASE);
+ sc->table2_base = CSR_READ_4(sc, IPW_CSR_TABLE2_BASE);
+
+ ipw_write_table1(sc, IPW_INFO_LOCK, 0);
+
+ /* Retrieve adapter MAC address */
+ len = IEEE80211_ADDR_LEN;
+ ipw_read_table2(sc, IPW_INFO_ADAPTER_MAC, ic->ic_myaddr, &len);
- if (ipw_config(sc) != 0) {
+ IEEE80211_ADDR_COPY(LLADDR(ifp->if_sadl), ic->ic_myaddr);
+
+ if ((error = ipw_config(sc)) != 0) {
printf("%s: device configuration failed\n",
sc->sc_dev.dv_xname);
goto fail;
@@ -1967,7 +1942,7 @@ ipw_init(struct ifnet *ifp)
fail: ipw_stop(ifp, 0);
- return EIO;
+ return error;
}
void
@@ -1976,10 +1951,11 @@ ipw_stop(struct ifnet *ifp, int disable)
struct ipw_softc *sc = ifp->if_softc;
struct ieee80211com *ic = &sc->sc_ic;
- if (ifp->if_flags & IFF_RUNNING) {
- DPRINTF(("Disabling adapter\n"));
- ipw_cmd(sc, IPW_CMD_DISABLE, NULL, 0);
- }
+ ipw_stop_master(sc);
+ CSR_WRITE_4(sc, IPW_CSR_RST, IPW_RST_SW_RESET);
+
+ ipw_tx_stop(sc);
+ ipw_rx_stop(sc);
ifp->if_timer = 0;
ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
diff --git a/sys/dev/pci/if_ipwreg.h b/sys/dev/pci/if_ipwreg.h
index 7d82b2c3926..7ed3149e5f2 100644
--- a/sys/dev/pci/if_ipwreg.h
+++ b/sys/dev/pci/if_ipwreg.h
@@ -1,4 +1,4 @@
-/* $Id: if_ipwreg.h,v 1.4 2004/10/27 21:19:43 damien Exp $ */
+/* $Id: if_ipwreg.h,v 1.5 2004/10/27 21:21:16 damien Exp $ */
/*-
* Copyright (c) 2004
@@ -113,10 +113,10 @@
#define IPW_RATE_DS11 8
/* firmware binary image header */
-struct ipw_fw_hdr {
+struct ipw_firmware_hdr {
u_int32_t version;
- u_int32_t fw_size; /* firmware size */
- u_int32_t uc_size; /* microcode size */
+ u_int32_t main_size; /* firmware size */
+ u_int32_t ucode_size; /* microcode size */
} __attribute__((__packed__));
/* buffer descriptor */
diff --git a/sys/dev/pci/if_ipwvar.h b/sys/dev/pci/if_ipwvar.h
index d0c21a66b9e..c4ca8296ea1 100644
--- a/sys/dev/pci/if_ipwvar.h
+++ b/sys/dev/pci/if_ipwvar.h
@@ -1,4 +1,4 @@
-/* $Id: if_ipwvar.h,v 1.2 2004/10/27 21:17:18 damien Exp $ */
+/* $Id: if_ipwvar.h,v 1.3 2004/10/27 21:21:16 damien Exp $ */
/*-
* Copyright (c) 2004
@@ -27,6 +27,13 @@
* SUCH DAMAGE.
*/
+struct ipw_firmware {
+ void *main;
+ int main_size;
+ void *ucode;
+ int ucode_size;
+};
+
struct ipw_soft_bd {
struct ipw_bd *bd;
int type;
@@ -81,8 +88,10 @@ struct ipw_softc {
int (*sc_newstate)(struct ieee80211com *,
enum ieee80211_state, int);
+ struct ipw_firmware fw;
u_int32_t flags;
-#define IPW_FLAG_FW_INITED (1 << 0)
+#define IPW_FLAG_FW_CACHED (1 << 0)
+#define IPW_FLAG_FW_INITED (1 << 1)
struct resource *irq;
struct resource *mem;