diff options
author | Damien Bergamini <damien@cvs.openbsd.org> | 2004-10-27 21:21:17 +0000 |
---|---|---|
committer | Damien Bergamini <damien@cvs.openbsd.org> | 2004-10-27 21:21:17 +0000 |
commit | 8e090bce28179d99854dbc51a3e4fc7f18375d67 (patch) | |
tree | 7fb467992d96c51ca4ab7b186b622e3526a23c20 /sys/dev/pci | |
parent | 79b9558ec83d1c184913b4ba0dd0ea6eb497c5f4 (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.c | 262 | ||||
-rw-r--r-- | sys/dev/pci/if_ipwreg.h | 8 | ||||
-rw-r--r-- | sys/dev/pci/if_ipwvar.h | 13 |
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; |