ath11k: add board file support for PCI devices
authorGovind Singh <govinds@codeaurora.org>
Fri, 14 Aug 2020 07:10:21 +0000 (10:10 +0300)
committerKalle Valo <kvalo@codeaurora.org>
Mon, 17 Aug 2020 10:18:13 +0000 (13:18 +0300)
PCI devices like QCA6390 load the board file differently, add support for that
and the method is chosen using bus_params variables.

Add support to create board name for different targets.  This board name is
used to parse the board data from board-2.bin for ahb/pci based targets.

As struct target_mem_chunk::vaddr was changed from 'u32' to 'u32 *' in
ath11k_qmi_assign_target_mem_chunk() vaddr assignments were changed to NULL to
avoid a compilation warning. IPQ8074 does not use the vaddr field for anything
so that change does not affect functionality.

At the moment this only supports board files with BIN type. Support for ELF
type, which seems to be more popular on QCA6390 devices, needs to be added later.

Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1
Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.1.0.1-01238-QCAHKSWPL_SILICONZ-2

Signed-off-by: Govind Singh <govinds@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/1597389030-13887-3-git-send-email-kvalo@codeaurora.org
drivers/net/wireless/ath/ath11k/ahb.c
drivers/net/wireless/ath/ath11k/core.c
drivers/net/wireless/ath/ath11k/core.h
drivers/net/wireless/ath/ath11k/pci.c
drivers/net/wireless/ath/ath11k/qmi.c
drivers/net/wireless/ath/ath11k/qmi.h

index 746e84c4526ca4ec3c9cac08f9735524c6964b1c..06e599cbfbf928df3c3d5166a9427e006d1b2eca 100644 (file)
@@ -28,6 +28,8 @@ MODULE_DEVICE_TABLE(of, ath11k_ahb_of_match);
 static const struct ath11k_bus_params ath11k_ahb_bus_params = {
        .mhi_support = false,
        .m3_fw_support = false,
+       .fixed_bdf_addr = true,
+       .fixed_mem_region = true,
 };
 
 /* Target firmware's Copy Engine configuration. */
index 22657dac7749943d1f2645524dccc5c0b190f7c9..a3a53debc24f16fd40d06755308b6c992a6bb6fa 100644 (file)
@@ -50,11 +50,9 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
 static int ath11k_core_create_board_name(struct ath11k_base *ab, char *name,
                                         size_t name_len)
 {
-       /* Note: bus is fixed to ahb. When other bus type supported,
-        * make it to dynamic.
-        */
        scnprintf(name, name_len,
-                 "bus=ahb,qmi-chip-id=%d,qmi-board-id=%d",
+                 "bus=%s,qmi-chip-id=%d,qmi-board-id=%d",
+                 ath11k_bus_str(ab->hif.bus),
                  ab->qmi.target.chip_id,
                  ab->qmi.target.board_id);
 
@@ -853,6 +851,7 @@ struct ath11k_base *ath11k_core_alloc(struct device *dev, size_t priv_size,
        timer_setup(&ab->rx_replenish_retry, ath11k_ce_rx_replenish_retry, 0);
        ab->dev = dev;
        ab->bus_params = *bus_params;
+       ab->hif.bus = bus;
 
        return ab;
 
index b30abd611f0dbf52eed5200dccc701ccf87ecae8..6e351e7bded8900281d0d3157afb6d309ba39a5f 100644 (file)
@@ -583,6 +583,8 @@ struct ath11k_board_data {
 struct ath11k_bus_params {
        bool mhi_support;
        bool m3_fw_support;
+       bool fixed_bdf_addr;
+       bool fixed_mem_region;
 };
 
 /* IPQ8074 HW channel counters frequency value in hertz */
@@ -647,6 +649,7 @@ struct ath11k_base {
        unsigned long mem_len;
 
        struct {
+               enum ath11k_bus bus;
                const struct ath11k_hif_ops *ops;
        } hif;
 
@@ -905,4 +908,16 @@ static inline void ath11k_core_create_firmware_path(struct ath11k_base *ab,
                 ab->hw_params.fw.dir, filename);
 }
 
+static inline const char *ath11k_bus_str(enum ath11k_bus bus)
+{
+       switch (bus) {
+       case ATH11K_BUS_PCI:
+               return "pci";
+       case ATH11K_BUS_AHB:
+               return "ahb";
+       }
+
+       return "unknown";
+}
+
 #endif /* _CORE_H_ */
index 802461d1261a6fae629a8c08ba30a9c377bc1406..dd3122b47d3573ea2a1591d7482c0bf205671155 100644 (file)
@@ -30,6 +30,8 @@ MODULE_DEVICE_TABLE(pci, ath11k_pci_id_table);
 static const struct ath11k_bus_params ath11k_pci_bus_params = {
        .mhi_support = true,
        .m3_fw_support = true,
+       .fixed_bdf_addr = false,
+       .fixed_mem_region = false,
 };
 
 static const struct ath11k_msi_config msi_config = {
index 0d7441e6ff177abc966efc9e2e44ea19547ae1c6..b182d618105773c36f26e50190054c9042b30837 100644 (file)
@@ -1680,7 +1680,48 @@ out:
        return ret;
 }
 
+static void ath11k_qmi_free_target_mem_chunk(struct ath11k_base *ab)
+{
+       int i;
+
+       if (ab->bus_params.fixed_mem_region)
+               return;
+
+       for (i = 0; i < ab->qmi.mem_seg_count; i++) {
+               if (!ab->qmi.target_mem[i].vaddr)
+                       continue;
+
+               dma_free_coherent(ab->dev,
+                                 ab->qmi.target_mem[i].size,
+                                 ab->qmi.target_mem[i].vaddr,
+                                 ab->qmi.target_mem[i].paddr);
+               ab->qmi.target_mem[i].vaddr = NULL;
+       }
+}
+
 static int ath11k_qmi_alloc_target_mem_chunk(struct ath11k_base *ab)
+{
+       int i;
+       struct target_mem_chunk *chunk;
+
+       for (i = 0; i < ab->qmi.mem_seg_count; i++) {
+               chunk = &ab->qmi.target_mem[i];
+               chunk->vaddr = dma_alloc_coherent(ab->dev,
+                                                 chunk->size,
+                                                 &chunk->paddr,
+                                                 GFP_KERNEL);
+               if (!chunk->vaddr) {
+                       ath11k_err(ab, "failed to alloc memory, size: 0x%x, type: %u\n",
+                                  chunk->size,
+                                  chunk->type);
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static int ath11k_qmi_assign_target_mem_chunk(struct ath11k_base *ab)
 {
        int i, idx;
 
@@ -1688,7 +1729,7 @@ static int ath11k_qmi_alloc_target_mem_chunk(struct ath11k_base *ab)
                switch (ab->qmi.target_mem[i].type) {
                case BDF_MEM_REGION_TYPE:
                        ab->qmi.target_mem[idx].paddr = ab->hw_params.bdf_addr;
-                       ab->qmi.target_mem[idx].vaddr = ab->hw_params.bdf_addr;
+                       ab->qmi.target_mem[idx].vaddr = NULL;
                        ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size;
                        ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type;
                        idx++;
@@ -1700,7 +1741,7 @@ static int ath11k_qmi_alloc_target_mem_chunk(struct ath11k_base *ab)
                        }
                        /* TODO ath11k does not support cold boot calibration */
                        ab->qmi.target_mem[idx].paddr = 0;
-                       ab->qmi.target_mem[idx].vaddr = 0;
+                       ab->qmi.target_mem[idx].vaddr = NULL;
                        ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size;
                        ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type;
                        idx++;
@@ -1842,7 +1883,7 @@ out:
        return ret;
 }
 
-static int ath11k_qmi_load_bdf(struct ath11k_base *ab)
+static int ath11k_qmi_load_bdf_fixed_addr(struct ath11k_base *ab)
 {
        struct qmi_wlanfw_bdf_download_req_msg_v01 *req;
        struct qmi_wlanfw_bdf_download_resp_msg_v01 resp;
@@ -1914,6 +1955,92 @@ out:
        return ret;
 }
 
+static int ath11k_qmi_load_bdf_qmi(struct ath11k_base *ab)
+{
+       struct qmi_wlanfw_bdf_download_req_msg_v01 *req;
+       struct qmi_wlanfw_bdf_download_resp_msg_v01 resp;
+       struct ath11k_board_data bd;
+       unsigned int remaining;
+       struct qmi_txn txn = {};
+       int ret;
+       const u8 *temp;
+
+       req = kzalloc(sizeof(*req), GFP_KERNEL);
+       if (!req)
+               return -ENOMEM;
+       memset(&resp, 0, sizeof(resp));
+
+       memset(&bd, 0, sizeof(bd));
+       ret = ath11k_core_fetch_bdf(ab, &bd);
+       if (ret) {
+               ath11k_warn(ab, "qmi failed to load bdf:\n");
+               goto out;
+       }
+
+       temp = bd.data;
+       remaining = bd.len;
+
+       while (remaining) {
+               req->valid = 1;
+               req->file_id_valid = 1;
+               req->file_id = ab->qmi.target.board_id;
+               req->total_size_valid = 1;
+               req->total_size = bd.len;
+               req->seg_id_valid = 1;
+               req->data_valid = 1;
+               req->data_len = ATH11K_QMI_MAX_BDF_FILE_NAME_SIZE;
+               req->bdf_type = ATH11K_QMI_BDF_TYPE_BIN;
+               req->bdf_type_valid = 1;
+               req->end_valid = 1;
+               req->end = 0;
+
+               if (remaining > QMI_WLANFW_MAX_DATA_SIZE_V01) {
+                       req->data_len = QMI_WLANFW_MAX_DATA_SIZE_V01;
+               } else {
+                       req->data_len = remaining;
+                       req->end = 1;
+               }
+
+               memcpy(req->data, temp, req->data_len);
+
+               ret = qmi_txn_init(&ab->qmi.handle, &txn,
+                                  qmi_wlanfw_bdf_download_resp_msg_v01_ei,
+                                  &resp);
+               if (ret < 0)
+                       goto out_qmi_bdf;
+
+               ret = qmi_send_request(&ab->qmi.handle, NULL, &txn,
+                                      QMI_WLANFW_BDF_DOWNLOAD_REQ_V01,
+                                      QMI_WLANFW_BDF_DOWNLOAD_REQ_MSG_V01_MAX_LEN,
+                                      qmi_wlanfw_bdf_download_req_msg_v01_ei, req);
+               if (ret < 0) {
+                       qmi_txn_cancel(&txn);
+                       goto out_qmi_bdf;
+               }
+
+               ret = qmi_txn_wait(&txn, msecs_to_jiffies(ATH11K_QMI_WLANFW_TIMEOUT_MS));
+               if (ret < 0)
+                       goto out_qmi_bdf;
+
+               if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
+                       ath11k_warn(ab, "qmi BDF download failed, result: %d, err: %d\n",
+                                   resp.resp.result, resp.resp.error);
+                       ret = resp.resp.result;
+                       goto out_qmi_bdf;
+               }
+               remaining -= req->data_len;
+               temp += req->data_len;
+               req->seg_id++;
+       }
+
+out_qmi_bdf:
+       ath11k_core_free_bdf(ab, &bd);
+
+out:
+       kfree(req);
+       return ret;
+}
+
 static int ath11k_qmi_m3_load(struct ath11k_base *ab)
 {
        struct m3_mem_region *m3_mem = &ab->qmi.m3_mem;
@@ -2242,7 +2369,10 @@ static void ath11k_qmi_event_load_bdf(struct ath11k_qmi *qmi)
                return;
        }
 
-       ret = ath11k_qmi_load_bdf(ab);
+       if (ab->bus_params.fixed_bdf_addr)
+               ret = ath11k_qmi_load_bdf_fixed_addr(ab);
+       else
+               ret = ath11k_qmi_load_bdf_qmi(ab);
        if (ret < 0) {
                ath11k_warn(ab, "qmi failed to load board data file:%d\n", ret);
                return;
@@ -2281,7 +2411,10 @@ static void ath11k_qmi_msg_mem_request_cb(struct qmi_handle *qmi_hdl,
                           msg->mem_seg[i].type, msg->mem_seg[i].size);
        }
 
-       ret = ath11k_qmi_alloc_target_mem_chunk(ab);
+       if (ab->bus_params.fixed_mem_region)
+               ret = ath11k_qmi_assign_target_mem_chunk(ab);
+       else
+               ret = ath11k_qmi_alloc_target_mem_chunk(ab);
        if (ret < 0) {
                ath11k_warn(ab, "qmi failed to alloc target memory:%d\n", ret);
                return;
@@ -2492,5 +2625,6 @@ void ath11k_qmi_deinit_service(struct ath11k_base *ab)
        cancel_work_sync(&ab->qmi.event_work);
        destroy_workqueue(ab->qmi.event_wq);
        ath11k_qmi_m3_free(ab);
+       ath11k_qmi_free_target_mem_chunk(ab);
 }
 
index dd9e498a20565200d11ca8c7399d9e7474ff98e3..cd484a4d0216fc9798bcd79fa04561562d1b7c4c 100644 (file)
@@ -40,6 +40,11 @@ enum ath11k_qmi_file_type {
        ATH11K_QMI_MAX_FILE_TYPE,
 };
 
+enum ath11k_qmi_bdf_type {
+       ATH11K_QMI_BDF_TYPE_BIN                 = 0,
+       ATH11K_QMI_BDF_TYPE_ELF                 = 1,
+};
+
 enum ath11k_qmi_event_type {
        ATH11K_QMI_EVENT_SERVER_ARRIVE,
        ATH11K_QMI_EVENT_SERVER_EXIT,
@@ -83,7 +88,7 @@ struct target_mem_chunk {
        u32 size;
        u32 type;
        dma_addr_t paddr;
-       u32 vaddr;
+       u32 *vaddr;
 };
 
 struct target_info {