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 746e84c..06e599c 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 22657da..a3a53de 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 b30abd6..6e351e7 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 802461d..dd3122b 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 0d7441e..b182d61 100644 (file)
@@ -1680,15 +1680,56 @@ 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;
 
        for (i = 0, idx = 0; i < ab->qmi.mem_seg_count; i++) {
                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 dd9e498..cd484a4 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 {