cache qwx(4) firmware images in memory across suspend/resume cycles
authorstsp <stsp@openbsd.org>
Thu, 22 Feb 2024 09:08:08 +0000 (09:08 +0000)
committerstsp <stsp@openbsd.org>
Thu, 22 Feb 2024 09:08:08 +0000 (09:08 +0000)
testing + ok phessler@

sys/dev/ic/qwx.c
sys/dev/ic/qwxvar.h
sys/dev/pci/if_qwx_pci.c

index 68fea8b..bea8be2 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: qwx.c,v 1.49 2024/02/22 09:06:11 stsp Exp $   */
+/*     $OpenBSD: qwx.c,v 1.50 2024/02/22 09:08:08 stsp Exp $   */
 
 /*
  * Copyright 2023 Stefan Sperling <stsp@openbsd.org>
@@ -299,6 +299,18 @@ qwx_stop(struct ifnet *ifp)
        splx(s);
 }
 
+void
+qwx_free_firmware(struct qwx_softc *sc)
+{
+       int i;
+
+       for (i = 0; i < nitems(sc->fw_img); i++) {
+               free(sc->fw_img[i].data, M_DEVBUF, sc->fw_img[i].size);
+               sc->fw_img[i].data = NULL;
+               sc->fw_img[i].size = 0;
+       }
+}
+
 int
 qwx_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
 {
@@ -322,7 +334,7 @@ qwx_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
                if (ifp->if_flags & IFF_UP) {
                        if (!(ifp->if_flags & IFF_RUNNING)) {
                                /* Force reload of firmware image from disk. */
-                               sc->have_firmware = 0;
+                               qwx_free_firmware(sc);
                                err = qwx_init(ifp);
                        }
                } else {
@@ -8374,20 +8386,34 @@ err_free_req:
 int
 qwx_qmi_load_bdf_qmi(struct qwx_softc *sc, int regdb)
 {
-       u_char *data;
+       u_char *data = NULL;
        const u_char *boardfw;
-       size_t len, boardfw_len;
+       size_t len = 0, boardfw_len;
        uint32_t fw_size;
        int ret = 0, bdf_type;
 #ifdef notyet
        const uint8_t *tmp;
        uint32_t file_type;
 #endif
+       int fw_idx = regdb ? QWX_FW_REGDB : QWX_FW_BOARD;
 
-       ret = qwx_core_fetch_bdf(sc, &data, &len, &boardfw, &boardfw_len,
-           regdb ? ATH11K_REGDB_FILE : ATH11K_BOARD_API2_FILE);
-       if (ret)
-               return ret;
+       if (sc->fw_img[fw_idx].data) {
+               boardfw = sc->fw_img[fw_idx].data;
+               boardfw_len = sc->fw_img[fw_idx].size;
+       } else {
+               ret = qwx_core_fetch_bdf(sc, &data, &len,
+                   &boardfw, &boardfw_len,
+                   regdb ? ATH11K_REGDB_FILE : ATH11K_BOARD_API2_FILE);
+               if (ret)
+                       return ret;
+
+               sc->fw_img[fw_idx].data = malloc(boardfw_len, M_DEVBUF,
+                   M_NOWAIT);
+               if (sc->fw_img[fw_idx].data) {
+                       memcpy(sc->fw_img[fw_idx].data, boardfw, boardfw_len);
+                       sc->fw_img[fw_idx].size = boardfw_len;
+               }
+       }
 
        if (regdb)
                bdf_type = ATH11K_QMI_BDF_TYPE_REGDB;
@@ -8506,16 +8532,24 @@ qwx_qmi_m3_load(struct qwx_softc *sc)
        char path[PATH_MAX];
        int ret;
 
-       ret = snprintf(path, sizeof(path), "%s-%s-%s",
-           ATH11K_FW_DIR, sc->hw_params.fw.dir, ATH11K_M3_FILE);
-       if (ret < 0 || ret >= sizeof(path))
-               return ENOSPC;
+       if (sc->fw_img[QWX_FW_M3].data) {
+               data = sc->fw_img[QWX_FW_M3].data;
+               len = sc->fw_img[QWX_FW_M3].size;
+       } else {
+               ret = snprintf(path, sizeof(path), "%s-%s-%s",
+                   ATH11K_FW_DIR, sc->hw_params.fw.dir, ATH11K_M3_FILE);
+               if (ret < 0 || ret >= sizeof(path))
+                       return ENOSPC;
 
-       ret = loadfirmware(path, &data, &len);
-       if (ret) {
-               printf("%s: could not read %s (error %d)\n",
-                   sc->sc_dev.dv_xname, path, ret);
-               return ret;
+               ret = loadfirmware(path, &data, &len);
+               if (ret) {
+                       printf("%s: could not read %s (error %d)\n",
+                           sc->sc_dev.dv_xname, path, ret);
+                       return ret;
+               }
+
+               sc->fw_img[QWX_FW_M3].data = data;
+               sc->fw_img[QWX_FW_M3].size = len;
        }
 
        if (sc->m3_mem == NULL || QWX_DMA_LEN(sc->m3_mem) < len) {
@@ -8531,7 +8565,6 @@ qwx_qmi_m3_load(struct qwx_softc *sc)
        }
 
        memcpy(QWX_DMA_KVA(sc->m3_mem), data, len);
-       free(data, M_DEVBUF, len);
        return 0;
 }
 
@@ -24709,6 +24742,8 @@ qwx_detach(struct qwx_softc *sc)
                qwx_dmamem_free(sc->sc_dmat, sc->m3_mem);
                sc->m3_mem = NULL;
        }
+
+       qwx_free_firmware(sc);
 }
 
 struct qwx_dmamem *
index 89ab3c2..0399a43 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: qwxvar.h,v 1.21 2024/02/22 09:06:11 stsp Exp $        */
+/*     $OpenBSD: qwxvar.h,v 1.22 2024/02/22 09:08:08 stsp Exp $        */
 
 /*
  * Copyright (c) 2018-2019 The Linux Foundation.
@@ -1782,7 +1782,14 @@ struct qwx_softc {
        struct qwx_survey_info  survey[IEEE80211_CHAN_MAX];
 
        int                     attached;
-       int                     have_firmware;
+       struct {
+               u_char *data;
+               size_t size;
+       } fw_img[4];
+#define QWX_FW_AMSS    0
+#define QWX_FW_BOARD   1
+#define QWX_FW_M3      2
+#define QWX_FW_REGDB   3
 
        int                     sc_tx_timer;
        uint32_t                qfullmsk;
index 016b456..f896544 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: if_qwx_pci.c,v 1.11 2024/02/22 09:06:11 stsp Exp $    */
+/*     $OpenBSD: if_qwx_pci.c,v 1.12 2024/02/22 09:08:08 stsp Exp $    */
 
 /*
  * Copyright 2023 Stefan Sperling <stsp@openbsd.org>
@@ -371,7 +371,7 @@ struct qwx_pci_softc {
        struct taskq            *mhi_taskq;
 
        /*
-        * DMA memory for AMMS.bin firmware image.
+        * DMA memory for AMSS.bin firmware image.
         * This memory must remain available to the device until
         * the device is powered down.
         */
@@ -3032,23 +3032,31 @@ qwx_mhi_fw_load_handler(struct qwx_pci_softc *psc)
        u_char *data;
        size_t len;
 
-       ret = snprintf(amss_path, sizeof(amss_path), "%s-%s-%s",
-           ATH11K_FW_DIR, sc->hw_params.fw.dir, ATH11K_AMSS_FILE);
-       if (ret < 0 || ret >= sizeof(amss_path))
-               return ENOSPC;
+       if (sc->fw_img[QWX_FW_AMSS].data) {
+               data = sc->fw_img[QWX_FW_AMSS].data;
+               len = sc->fw_img[QWX_FW_AMSS].size;
+       } else {
+               ret = snprintf(amss_path, sizeof(amss_path), "%s-%s-%s",
+                   ATH11K_FW_DIR, sc->hw_params.fw.dir, ATH11K_AMSS_FILE);
+               if (ret < 0 || ret >= sizeof(amss_path))
+                       return ENOSPC;
+
+               ret = loadfirmware(amss_path, &data, &len);
+               if (ret) {
+                       printf("%s: could not read %s (error %d)\n",
+                           sc->sc_dev.dv_xname, amss_path, ret);
+                       return ret;
+               }
 
-       ret = loadfirmware(amss_path, &data, &len);
-       if (ret) {
-               printf("%s: could not read %s (error %d)\n",
-                   sc->sc_dev.dv_xname, amss_path, ret);
-               return ret;
-       }
+               if (len < MHI_DMA_VEC_CHUNK_SIZE) {
+                       printf("%s: %s is too short, have only %zu bytes\n",
+                           sc->sc_dev.dv_xname, amss_path, len);
+                       free(data, M_DEVBUF, len);
+                       return EINVAL;
+               }
 
-       if (len < MHI_DMA_VEC_CHUNK_SIZE) {
-               printf("%s: %s is too short, have only %zu bytes\n",
-                   sc->sc_dev.dv_xname, amss_path, len);
-               free(data, M_DEVBUF, len);
-               return EINVAL;
+               sc->fw_img[QWX_FW_AMSS].data = data;
+               sc->fw_img[QWX_FW_AMSS].size = len;
        }
 
        /* Second-stage boot loader sits in the first 512 KB of image. */
@@ -3056,7 +3064,6 @@ qwx_mhi_fw_load_handler(struct qwx_pci_softc *psc)
        if (ret != 0) {
                printf("%s: could not load firmware %s\n",
                    sc->sc_dev.dv_xname, amss_path);
-               free(data, M_DEVBUF, len);
                return ret;
        }
 
@@ -3065,6 +3072,7 @@ qwx_mhi_fw_load_handler(struct qwx_pci_softc *psc)
        if (ret != 0) {
                printf("%s: could not load firmware %s\n",
                    sc->sc_dev.dv_xname, amss_path);
+               return ret;
        }
 
        while (psc->bhi_ee < MHI_EE_AMSS) {
@@ -3076,11 +3084,8 @@ qwx_mhi_fw_load_handler(struct qwx_pci_softc *psc)
        if (ret != 0) {
                printf("%s: device failed to enter AMSS EE\n",
                    sc->sc_dev.dv_xname);
-               free(data, M_DEVBUF, len);
-               return ret;
        }
 
-       free(data, M_DEVBUF, len);
        return ret;
 }