iwlwifi: refactor out paging code
authorJohannes Berg <johannes.berg@intel.com>
Thu, 1 Jun 2017 10:10:32 +0000 (12:10 +0200)
committerLuca Coelho <luciano.coelho@intel.com>
Tue, 1 Aug 2017 09:41:43 +0000 (12:41 +0300)
Refactor the paging code from mvm to be used by different opmodes.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
drivers/net/wireless/intel/iwlwifi/Makefile
drivers/net/wireless/intel/iwlwifi/fw/api.h
drivers/net/wireless/intel/iwlwifi/fw/paging.c [new file with mode: 0644]
drivers/net/wireless/intel/iwlwifi/fw/runtime.h [new file with mode: 0644]
drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h
drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c
drivers/net/wireless/intel/iwlwifi/mvm/fw.c
drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
drivers/net/wireless/intel/iwlwifi/mvm/ops.c

index 20bd261..186a5b2 100644 (file)
@@ -11,6 +11,7 @@ iwlwifi-$(CONFIG_IWLDVM) += cfg/1000.o cfg/2000.o cfg/5000.o cfg/6000.o
 iwlwifi-$(CONFIG_IWLMVM) += cfg/7000.o cfg/8000.o cfg/9000.o cfg/a000.o
 iwlwifi-objs           += iwl-trans.o
 iwlwifi-objs           += fw/notif-wait.o
+iwlwifi-$(CONFIG_IWLMVM) += fw/paging.o
 
 iwlwifi-objs += $(iwlwifi-m)
 
index 0e107f9..f9bcbaf 100644 (file)
@@ -226,4 +226,47 @@ struct iwl_phy_db_cmd {
        u8 data[];
 } __packed;
 
+#define NUM_OF_FW_PAGING_BLOCKS        33 /* 32 for data and 1 block for CSS */
+
+/**
+ * struct iwl_fw_paging_cmd - paging layout
+ *
+ * Send to FW the paging layout in the driver.
+ *
+ * @flags: various flags for the command
+ * @block_size: the block size in powers of 2
+ * @block_num: number of blocks specified in the command.
+ * @device_phy_addr: virtual addresses from device side
+ */
+struct iwl_fw_paging_cmd {
+       __le32 flags;
+       __le32 block_size;
+       __le32 block_num;
+       __le32 device_phy_addr[NUM_OF_FW_PAGING_BLOCKS];
+} __packed; /* FW_PAGING_BLOCK_CMD_API_S_VER_1 */
+
+/**
+ * enum iwl_fw_item_id - FW item IDs
+ *
+ * @IWL_FW_ITEM_ID_PAGING: Address of the pages that the FW will upload
+ *     download
+ */
+enum iwl_fw_item_id {
+       IWL_FW_ITEM_ID_PAGING = 3,
+};
+
+/**
+ * struct iwl_fw_get_item_cmd - get an item from the fw
+ * @item_id: ID of item to obtain, see &enum iwl_fw_item_id
+ */
+struct iwl_fw_get_item_cmd {
+       __le32 item_id;
+} __packed; /* FW_GET_ITEM_CMD_API_S_VER_1 */
+
+struct iwl_fw_get_item_resp {
+       __le32 item_id;
+       __le32 item_byte_cnt;
+       __le32 item_val;
+} __packed; /* FW_GET_ITEM_RSP_S_VER_1 */
+
 #endif /* __iwl_fw_api_h__*/
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/paging.c b/drivers/net/wireless/intel/iwlwifi/fw/paging.c
new file mode 100644 (file)
index 0000000..2f40449
--- /dev/null
@@ -0,0 +1,417 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include "iwl-drv.h"
+#include "runtime.h"
+
+/* FIXME */
+#define FW_PAGING_BLOCK_CMD 0x4f
+#define FW_GET_ITEM_CMD 0x1a
+
+void iwl_free_fw_paging(struct iwl_fw_runtime *fwrt)
+{
+       int i;
+
+       if (!fwrt->fw_paging_db[0].fw_paging_block)
+               return;
+
+       for (i = 0; i < NUM_OF_FW_PAGING_BLOCKS; i++) {
+               struct iwl_fw_paging *paging = &fwrt->fw_paging_db[i];
+
+               if (!paging->fw_paging_block) {
+                       IWL_DEBUG_FW(fwrt,
+                                    "Paging: block %d already freed, continue to next page\n",
+                                    i);
+
+                       continue;
+               }
+               dma_unmap_page(fwrt->trans->dev, paging->fw_paging_phys,
+                              paging->fw_paging_size, DMA_BIDIRECTIONAL);
+
+               __free_pages(paging->fw_paging_block,
+                            get_order(paging->fw_paging_size));
+               paging->fw_paging_block = NULL;
+       }
+       kfree(fwrt->trans->paging_download_buf);
+       fwrt->trans->paging_download_buf = NULL;
+       fwrt->trans->paging_db = NULL;
+
+       memset(fwrt->fw_paging_db, 0, sizeof(fwrt->fw_paging_db));
+}
+IWL_EXPORT_SYMBOL(iwl_free_fw_paging);
+
+static int iwl_alloc_fw_paging_mem(struct iwl_fw_runtime *fwrt,
+                                  const struct fw_img *image)
+{
+       struct page *block;
+       dma_addr_t phys = 0;
+       int blk_idx, order, num_of_pages, size, dma_enabled;
+
+       if (fwrt->fw_paging_db[0].fw_paging_block)
+               return 0;
+
+       dma_enabled = is_device_dma_capable(fwrt->trans->dev);
+
+       /* ensure BLOCK_2_EXP_SIZE is power of 2 of PAGING_BLOCK_SIZE */
+       BUILD_BUG_ON(BIT(BLOCK_2_EXP_SIZE) != PAGING_BLOCK_SIZE);
+
+       num_of_pages = image->paging_mem_size / FW_PAGING_SIZE;
+       fwrt->num_of_paging_blk =
+               DIV_ROUND_UP(num_of_pages, NUM_OF_PAGE_PER_GROUP);
+       fwrt->num_of_pages_in_last_blk =
+               num_of_pages -
+               NUM_OF_PAGE_PER_GROUP * (fwrt->num_of_paging_blk - 1);
+
+       IWL_DEBUG_FW(fwrt,
+                    "Paging: allocating mem for %d paging blocks, each block holds 8 pages, last block holds %d pages\n",
+                    fwrt->num_of_paging_blk,
+                    fwrt->num_of_pages_in_last_blk);
+
+       /*
+        * Allocate CSS and paging blocks in dram.
+        */
+       for (blk_idx = 0; blk_idx < fwrt->num_of_paging_blk + 1; blk_idx++) {
+               /* For CSS allocate 4KB, for others PAGING_BLOCK_SIZE (32K) */
+               size = blk_idx ? PAGING_BLOCK_SIZE : FW_PAGING_SIZE;
+               order = get_order(size);
+               block = alloc_pages(GFP_KERNEL, order);
+               if (!block) {
+                       /* free all the previous pages since we failed */
+                       iwl_free_fw_paging(fwrt);
+                       return -ENOMEM;
+               }
+
+               fwrt->fw_paging_db[blk_idx].fw_paging_block = block;
+               fwrt->fw_paging_db[blk_idx].fw_paging_size = size;
+
+               if (dma_enabled) {
+                       phys = dma_map_page(fwrt->trans->dev, block, 0,
+                                           PAGE_SIZE << order,
+                                           DMA_BIDIRECTIONAL);
+                       if (dma_mapping_error(fwrt->trans->dev, phys)) {
+                               /*
+                                * free the previous pages and the current one
+                                * since we failed to map_page.
+                                */
+                               iwl_free_fw_paging(fwrt);
+                               return -ENOMEM;
+                       }
+                       fwrt->fw_paging_db[blk_idx].fw_paging_phys = phys;
+               } else {
+                       fwrt->fw_paging_db[blk_idx].fw_paging_phys =
+                               PAGING_ADDR_SIG |
+                               blk_idx << BLOCK_2_EXP_SIZE;
+               }
+
+               if (!blk_idx)
+                       IWL_DEBUG_FW(fwrt,
+                                    "Paging: allocated 4K(CSS) bytes (order %d) for firmware paging.\n",
+                                    order);
+               else
+                       IWL_DEBUG_FW(fwrt,
+                                    "Paging: allocated 32K bytes (order %d) for firmware paging.\n",
+                                    order);
+       }
+
+       return 0;
+}
+
+static int iwl_fill_paging_mem(struct iwl_fw_runtime *fwrt,
+                              const struct fw_img *image)
+{
+       int sec_idx, idx;
+       u32 offset = 0;
+
+       /*
+        * find where is the paging image start point:
+        * if CPU2 exist and it's in paging format, then the image looks like:
+        * CPU1 sections (2 or more)
+        * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between CPU1 to CPU2
+        * CPU2 sections (not paged)
+        * PAGING_SEPARATOR_SECTION delimiter - separate between CPU2
+        * non paged to CPU2 paging sec
+        * CPU2 paging CSS
+        * CPU2 paging image (including instruction and data)
+        */
+       for (sec_idx = 0; sec_idx < image->num_sec; sec_idx++) {
+               if (image->sec[sec_idx].offset == PAGING_SEPARATOR_SECTION) {
+                       sec_idx++;
+                       break;
+               }
+       }
+
+       /*
+        * If paging is enabled there should be at least 2 more sections left
+        * (one for CSS and one for Paging data)
+        */
+       if (sec_idx >= image->num_sec - 1) {
+               IWL_ERR(fwrt, "Paging: Missing CSS and/or paging sections\n");
+               iwl_free_fw_paging(fwrt);
+               return -EINVAL;
+       }
+
+       /* copy the CSS block to the dram */
+       IWL_DEBUG_FW(fwrt, "Paging: load paging CSS to FW, sec = %d\n",
+                    sec_idx);
+
+       memcpy(page_address(fwrt->fw_paging_db[0].fw_paging_block),
+              image->sec[sec_idx].data,
+              fwrt->fw_paging_db[0].fw_paging_size);
+       dma_sync_single_for_device(fwrt->trans->dev,
+                                  fwrt->fw_paging_db[0].fw_paging_phys,
+                                  fwrt->fw_paging_db[0].fw_paging_size,
+                                  DMA_BIDIRECTIONAL);
+
+       IWL_DEBUG_FW(fwrt,
+                    "Paging: copied %d CSS bytes to first block\n",
+                    fwrt->fw_paging_db[0].fw_paging_size);
+
+       sec_idx++;
+
+       /*
+        * copy the paging blocks to the dram
+        * loop index start from 1 since that CSS block already copied to dram
+        * and CSS index is 0.
+        * loop stop at num_of_paging_blk since that last block is not full.
+        */
+       for (idx = 1; idx < fwrt->num_of_paging_blk; idx++) {
+               struct iwl_fw_paging *block = &fwrt->fw_paging_db[idx];
+
+               memcpy(page_address(block->fw_paging_block),
+                      image->sec[sec_idx].data + offset,
+                      block->fw_paging_size);
+               dma_sync_single_for_device(fwrt->trans->dev,
+                                          block->fw_paging_phys,
+                                          block->fw_paging_size,
+                                          DMA_BIDIRECTIONAL);
+
+               IWL_DEBUG_FW(fwrt,
+                            "Paging: copied %d paging bytes to block %d\n",
+                            fwrt->fw_paging_db[idx].fw_paging_size,
+                            idx);
+
+               offset += fwrt->fw_paging_db[idx].fw_paging_size;
+       }
+
+       /* copy the last paging block */
+       if (fwrt->num_of_pages_in_last_blk > 0) {
+               struct iwl_fw_paging *block = &fwrt->fw_paging_db[idx];
+
+               memcpy(page_address(block->fw_paging_block),
+                      image->sec[sec_idx].data + offset,
+                      FW_PAGING_SIZE * fwrt->num_of_pages_in_last_blk);
+               dma_sync_single_for_device(fwrt->trans->dev,
+                                          block->fw_paging_phys,
+                                          block->fw_paging_size,
+                                          DMA_BIDIRECTIONAL);
+
+               IWL_DEBUG_FW(fwrt,
+                            "Paging: copied %d pages in the last block %d\n",
+                            fwrt->num_of_pages_in_last_blk, idx);
+       }
+
+       return 0;
+}
+
+static int iwl_save_fw_paging(struct iwl_fw_runtime *fwrt,
+                             const struct fw_img *fw)
+{
+       int ret;
+
+       ret = iwl_alloc_fw_paging_mem(fwrt, fw);
+       if (ret)
+               return ret;
+
+       return iwl_fill_paging_mem(fwrt, fw);
+}
+
+/* send paging cmd to FW in case CPU2 has paging image */
+static int iwl_send_paging_cmd(struct iwl_fw_runtime *fwrt,
+                              const struct fw_img *fw)
+{
+       struct iwl_fw_paging_cmd paging_cmd = {
+               .flags = cpu_to_le32(PAGING_CMD_IS_SECURED |
+                                    PAGING_CMD_IS_ENABLED |
+                                    (fwrt->num_of_pages_in_last_blk <<
+                                     PAGING_CMD_NUM_OF_PAGES_IN_LAST_GRP_POS)),
+               .block_size = cpu_to_le32(BLOCK_2_EXP_SIZE),
+               .block_num = cpu_to_le32(fwrt->num_of_paging_blk),
+       };
+       struct iwl_host_cmd hcmd = {
+               .id = iwl_cmd_id(FW_PAGING_BLOCK_CMD, IWL_ALWAYS_LONG_GROUP, 0),
+               .len = { sizeof(paging_cmd), },
+               .data = { &paging_cmd, },
+       };
+       int blk_idx;
+
+       /* loop for for all paging blocks + CSS block */
+       for (blk_idx = 0; blk_idx < fwrt->num_of_paging_blk + 1; blk_idx++) {
+               dma_addr_t addr = fwrt->fw_paging_db[blk_idx].fw_paging_phys;
+               __le32 phy_addr;
+
+               addr = addr >> PAGE_2_EXP_SIZE;
+               phy_addr = cpu_to_le32(addr);
+               paging_cmd.device_phy_addr[blk_idx] = phy_addr;
+       }
+
+       return iwl_trans_send_cmd(fwrt->trans, &hcmd);
+}
+
+/*
+ * Send paging item cmd to FW in case CPU2 has paging image
+ */
+static int iwl_trans_get_paging_item(struct iwl_fw_runtime *fwrt)
+{
+       int ret;
+       struct iwl_fw_get_item_cmd fw_get_item_cmd = {
+               .item_id = cpu_to_le32(IWL_FW_ITEM_ID_PAGING),
+       };
+       struct iwl_fw_get_item_resp *item_resp;
+       struct iwl_host_cmd cmd = {
+               .id = iwl_cmd_id(FW_GET_ITEM_CMD, IWL_ALWAYS_LONG_GROUP, 0),
+               .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL,
+               .data = { &fw_get_item_cmd, },
+               .len = { sizeof(fw_get_item_cmd), },
+       };
+
+       ret = iwl_trans_send_cmd(fwrt->trans, &cmd);
+       if (ret) {
+               IWL_ERR(fwrt,
+                       "Paging: Failed to send FW_GET_ITEM_CMD cmd (err = %d)\n",
+                       ret);
+               return ret;
+       }
+
+       item_resp = (void *)((struct iwl_rx_packet *)cmd.resp_pkt)->data;
+       if (item_resp->item_id != cpu_to_le32(IWL_FW_ITEM_ID_PAGING)) {
+               IWL_ERR(fwrt,
+                       "Paging: got wrong item in FW_GET_ITEM_CMD resp (item_id = %u)\n",
+                       le32_to_cpu(item_resp->item_id));
+               ret = -EIO;
+               goto exit;
+       }
+
+       /* Add an extra page for headers */
+       fwrt->trans->paging_download_buf = kzalloc(PAGING_BLOCK_SIZE +
+                                                 FW_PAGING_SIZE,
+                                                 GFP_KERNEL);
+       if (!fwrt->trans->paging_download_buf) {
+               ret = -ENOMEM;
+               goto exit;
+       }
+       fwrt->trans->paging_req_addr = le32_to_cpu(item_resp->item_val);
+       fwrt->trans->paging_db = fwrt->fw_paging_db;
+       IWL_DEBUG_FW(fwrt,
+                    "Paging: got paging request address (paging_req_addr 0x%08x)\n",
+                    fwrt->trans->paging_req_addr);
+
+exit:
+       iwl_free_resp(&cmd);
+
+       return ret;
+}
+
+int iwl_init_paging(struct iwl_fw_runtime *fwrt, enum iwl_ucode_type type)
+{
+       const struct fw_img *fw = &fwrt->fw->img[type];
+       int ret;
+
+       if (fwrt->trans->cfg->gen2)
+               return 0;
+
+       /*
+        * Configure and operate fw paging mechanism.
+        * The driver configures the paging flow only once.
+        * The CPU2 paging image is included in the IWL_UCODE_INIT image.
+        */
+       if (!fw->paging_mem_size)
+               return 0;
+
+       /*
+        * When dma is not enabled, the driver needs to copy / write
+        * the downloaded / uploaded page to / from the smem.
+        * This gets the location of the place were the pages are
+        * stored.
+        */
+       if (!is_device_dma_capable(fwrt->trans->dev)) {
+               ret = iwl_trans_get_paging_item(fwrt);
+               if (ret) {
+                       IWL_ERR(fwrt, "failed to get FW paging item\n");
+                       return ret;
+               }
+       }
+
+       ret = iwl_save_fw_paging(fwrt, fw);
+       if (ret) {
+               IWL_ERR(fwrt, "failed to save the FW paging image\n");
+               return ret;
+       }
+
+       ret = iwl_send_paging_cmd(fwrt, fw);
+       if (ret) {
+               IWL_ERR(fwrt, "failed to send the paging cmd\n");
+               iwl_free_fw_paging(fwrt);
+               return ret;
+       }
+
+       return 0;
+}
+IWL_EXPORT_SYMBOL(iwl_init_paging);
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h
new file mode 100644 (file)
index 0000000..c5d5643
--- /dev/null
@@ -0,0 +1,99 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2017 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2017 Intel Deutschland GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#ifndef __iwl_fw_runtime_h__
+#define __iwl_fw_runtime_h__
+
+#include "iwl-config.h"
+#include "iwl-trans.h"
+#include "img.h"
+#include "api.h"
+
+/**
+ * struct iwl_fw_runtime - runtime data for firmware
+ * @fw: firmware image
+ * @cfg: NIC configuration
+ * @dev: device pointer
+ * @fw_paging_db: paging database
+ * @num_of_paging_blk: number of paging blocks
+ * @num_of_pages_in_last_blk: number of pages in the last block
+ */
+struct iwl_fw_runtime {
+       struct iwl_trans *trans;
+       const struct iwl_fw *fw;
+       struct device *dev;
+
+       /* Paging */
+       struct iwl_fw_paging fw_paging_db[NUM_OF_FW_PAGING_BLOCKS];
+       u16 num_of_paging_blk;
+       u16 num_of_pages_in_last_blk;
+};
+
+static inline void iwl_fw_runtime_init(struct iwl_fw_runtime *fwrt,
+                                      struct iwl_trans *trans,
+                                      const struct iwl_fw *fw)
+{
+       memset(fwrt, 0, sizeof(*fwrt));
+       fwrt->trans = trans;
+       fwrt->fw = fw;
+       fwrt->dev = trans->dev;
+}
+
+int iwl_init_paging(struct iwl_fw_runtime *fwrt, enum iwl_ucode_type type);
+void iwl_free_fw_paging(struct iwl_fw_runtime *fwrt);
+
+#endif /* __iwl_fw_runtime_h__ */
index aad265d..e1a29cd 100644 (file)
@@ -1000,44 +1000,6 @@ struct iwl_nvm_access_cmd {
        u8 data[];
 } __packed; /* NVM_ACCESS_CMD_API_S_VER_2 */
 
-#define NUM_OF_FW_PAGING_BLOCKS        33 /* 32 for data and 1 block for CSS */
-
-/**
- * struct iwl_fw_paging_cmd - paging layout
- *
- * (FW_PAGING_BLOCK_CMD = 0x4f)
- *
- * Send to FW the paging layout in the driver.
- *
- * @flags: various flags for the command
- * @block_size: the block size in powers of 2
- * @block_num: number of blocks specified in the command.
- * @device_phy_addr: virtual addresses from device side
- */
-struct iwl_fw_paging_cmd {
-       __le32 flags;
-       __le32 block_size;
-       __le32 block_num;
-       __le32 device_phy_addr[NUM_OF_FW_PAGING_BLOCKS];
-} __packed; /* FW_PAGING_BLOCK_CMD_API_S_VER_1 */
-
-/*
- * Fw items ID's
- *
- * @IWL_FW_ITEM_ID_PAGING: Address of the pages that the FW will upload
- *     download
- */
-enum iwl_fw_item_id {
-       IWL_FW_ITEM_ID_PAGING = 3,
-};
-
-/*
- * struct iwl_fw_get_item_cmd - get an item from the fw
- */
-struct iwl_fw_get_item_cmd {
-       __le32 item_id;
-} __packed; /* FW_GET_ITEM_CMD_API_S_VER_1 */
-
 #define CONT_REC_COMMAND_SIZE  80
 #define ENABLE_CONT_RECORDING  0x15
 #define DISABLE_CONT_RECORDING 0x16
@@ -1058,12 +1020,6 @@ struct iwl_continuous_record_cmd {
                sizeof(struct iwl_continuous_record_mode)];
 } __packed;
 
-struct iwl_fw_get_item_resp {
-       __le32 item_id;
-       __le32 item_byte_cnt;
-       __le32 item_val;
-} __packed; /* FW_GET_ITEM_RSP_S_VER_1 */
-
 /**
  * struct iwl_nvm_access_resp_ver2 - response to NVM_ACCESS_CMD
  * @offset: offset in bytes into the section
index 1602b36..ba32753 100644 (file)
@@ -694,8 +694,8 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
        /* Make room for fw's virtual image pages, if it exists */
        if (!mvm->trans->cfg->gen2 &&
            mvm->fw->img[mvm->cur_ucode].paging_mem_size &&
-           mvm->fw_paging_db[0].fw_paging_block)
-               file_len += mvm->num_of_paging_blk *
+           mvm->fwrt.fw_paging_db[0].fw_paging_block)
+               file_len += mvm->fwrt.num_of_paging_blk *
                        (sizeof(*dump_data) +
                         sizeof(struct iwl_fw_error_dump_paging) +
                         PAGING_BLOCK_SIZE);
@@ -833,12 +833,12 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
        /* Dump fw's virtual image */
        if (!mvm->trans->cfg->gen2 &&
            mvm->fw->img[mvm->cur_ucode].paging_mem_size &&
-           mvm->fw_paging_db[0].fw_paging_block) {
-               for (i = 1; i < mvm->num_of_paging_blk + 1; i++) {
+           mvm->fwrt.fw_paging_db[0].fw_paging_block) {
+               for (i = 1; i < mvm->fwrt.num_of_paging_blk + 1; i++) {
                        struct iwl_fw_error_dump_paging *paging;
                        struct page *pages =
-                               mvm->fw_paging_db[i].fw_paging_block;
-                       dma_addr_t addr = mvm->fw_paging_db[i].fw_paging_phys;
+                               mvm->fwrt.fw_paging_db[i].fw_paging_block;
+                       dma_addr_t addr = mvm->fwrt.fw_paging_db[i].fw_paging_phys;
 
                        dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING);
                        dump_data->len = cpu_to_le32(sizeof(*paging) +
index 79e7a7a..47715ee 100644 (file)
@@ -144,134 +144,6 @@ static int iwl_mvm_send_dqa_cmd(struct iwl_mvm *mvm)
        return ret;
 }
 
-void iwl_free_fw_paging(struct iwl_mvm *mvm)
-{
-       int i;
-
-       if (!mvm->fw_paging_db[0].fw_paging_block)
-               return;
-
-       for (i = 0; i < NUM_OF_FW_PAGING_BLOCKS; i++) {
-               struct iwl_fw_paging *paging = &mvm->fw_paging_db[i];
-
-               if (!paging->fw_paging_block) {
-                       IWL_DEBUG_FW(mvm,
-                                    "Paging: block %d already freed, continue to next page\n",
-                                    i);
-
-                       continue;
-               }
-               dma_unmap_page(mvm->trans->dev, paging->fw_paging_phys,
-                              paging->fw_paging_size, DMA_BIDIRECTIONAL);
-
-               __free_pages(paging->fw_paging_block,
-                            get_order(paging->fw_paging_size));
-               paging->fw_paging_block = NULL;
-       }
-       kfree(mvm->trans->paging_download_buf);
-       mvm->trans->paging_download_buf = NULL;
-       mvm->trans->paging_db = NULL;
-
-       memset(mvm->fw_paging_db, 0, sizeof(mvm->fw_paging_db));
-}
-
-static int iwl_fill_paging_mem(struct iwl_mvm *mvm, const struct fw_img *image)
-{
-       int sec_idx, idx;
-       u32 offset = 0;
-
-       /*
-        * find where is the paging image start point:
-        * if CPU2 exist and it's in paging format, then the image looks like:
-        * CPU1 sections (2 or more)
-        * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between CPU1 to CPU2
-        * CPU2 sections (not paged)
-        * PAGING_SEPARATOR_SECTION delimiter - separate between CPU2
-        * non paged to CPU2 paging sec
-        * CPU2 paging CSS
-        * CPU2 paging image (including instruction and data)
-        */
-       for (sec_idx = 0; sec_idx < image->num_sec; sec_idx++) {
-               if (image->sec[sec_idx].offset == PAGING_SEPARATOR_SECTION) {
-                       sec_idx++;
-                       break;
-               }
-       }
-
-       /*
-        * If paging is enabled there should be at least 2 more sections left
-        * (one for CSS and one for Paging data)
-        */
-       if (sec_idx >= image->num_sec - 1) {
-               IWL_ERR(mvm, "Paging: Missing CSS and/or paging sections\n");
-               iwl_free_fw_paging(mvm);
-               return -EINVAL;
-       }
-
-       /* copy the CSS block to the dram */
-       IWL_DEBUG_FW(mvm, "Paging: load paging CSS to FW, sec = %d\n",
-                    sec_idx);
-
-       memcpy(page_address(mvm->fw_paging_db[0].fw_paging_block),
-              image->sec[sec_idx].data,
-              mvm->fw_paging_db[0].fw_paging_size);
-       dma_sync_single_for_device(mvm->trans->dev,
-                                  mvm->fw_paging_db[0].fw_paging_phys,
-                                  mvm->fw_paging_db[0].fw_paging_size,
-                                  DMA_BIDIRECTIONAL);
-
-       IWL_DEBUG_FW(mvm,
-                    "Paging: copied %d CSS bytes to first block\n",
-                    mvm->fw_paging_db[0].fw_paging_size);
-
-       sec_idx++;
-
-       /*
-        * copy the paging blocks to the dram
-        * loop index start from 1 since that CSS block already copied to dram
-        * and CSS index is 0.
-        * loop stop at num_of_paging_blk since that last block is not full.
-        */
-       for (idx = 1; idx < mvm->num_of_paging_blk; idx++) {
-               struct iwl_fw_paging *block = &mvm->fw_paging_db[idx];
-
-               memcpy(page_address(block->fw_paging_block),
-                      image->sec[sec_idx].data + offset,
-                      block->fw_paging_size);
-               dma_sync_single_for_device(mvm->trans->dev,
-                                          block->fw_paging_phys,
-                                          block->fw_paging_size,
-                                          DMA_BIDIRECTIONAL);
-
-
-               IWL_DEBUG_FW(mvm,
-                            "Paging: copied %d paging bytes to block %d\n",
-                            mvm->fw_paging_db[idx].fw_paging_size,
-                            idx);
-
-               offset += mvm->fw_paging_db[idx].fw_paging_size;
-       }
-
-       /* copy the last paging block */
-       if (mvm->num_of_pages_in_last_blk > 0) {
-               struct iwl_fw_paging *block = &mvm->fw_paging_db[idx];
-
-               memcpy(page_address(block->fw_paging_block),
-                      image->sec[sec_idx].data + offset,
-                      FW_PAGING_SIZE * mvm->num_of_pages_in_last_blk);
-               dma_sync_single_for_device(mvm->trans->dev,
-                                          block->fw_paging_phys,
-                                          block->fw_paging_size,
-                                          DMA_BIDIRECTIONAL);
-
-               IWL_DEBUG_FW(mvm,
-                            "Paging: copied %d pages in the last block %d\n",
-                            mvm->num_of_pages_in_last_blk, idx);
-       }
-
-       return 0;
-}
-
 void iwl_mvm_mfu_assert_dump_notif(struct iwl_mvm *mvm,
                                   struct iwl_rx_cmd_buffer *rxb)
 {
@@ -293,178 +165,6 @@ void iwl_mvm_mfu_assert_dump_notif(struct iwl_mvm *mvm,
                               le32_to_cpu(dump_data[i]));
 }
 
-static int iwl_alloc_fw_paging_mem(struct iwl_mvm *mvm,
-                                  const struct fw_img *image)
-{
-       struct page *block;
-       dma_addr_t phys = 0;
-       int blk_idx, order, num_of_pages, size, dma_enabled;
-
-       if (mvm->fw_paging_db[0].fw_paging_block)
-               return 0;
-
-       dma_enabled = is_device_dma_capable(mvm->trans->dev);
-
-       /* ensure BLOCK_2_EXP_SIZE is power of 2 of PAGING_BLOCK_SIZE */
-       BUILD_BUG_ON(BIT(BLOCK_2_EXP_SIZE) != PAGING_BLOCK_SIZE);
-
-       num_of_pages = image->paging_mem_size / FW_PAGING_SIZE;
-       mvm->num_of_paging_blk =
-               DIV_ROUND_UP(num_of_pages, NUM_OF_PAGE_PER_GROUP);
-       mvm->num_of_pages_in_last_blk =
-               num_of_pages -
-               NUM_OF_PAGE_PER_GROUP * (mvm->num_of_paging_blk - 1);
-
-       IWL_DEBUG_FW(mvm,
-                    "Paging: allocating mem for %d paging blocks, each block holds 8 pages, last block holds %d pages\n",
-                    mvm->num_of_paging_blk,
-                    mvm->num_of_pages_in_last_blk);
-
-       /*
-        * Allocate CSS and paging blocks in dram.
-        */
-       for (blk_idx = 0; blk_idx < mvm->num_of_paging_blk + 1; blk_idx++) {
-               /* For CSS allocate 4KB, for others PAGING_BLOCK_SIZE (32K) */
-               size = blk_idx ? PAGING_BLOCK_SIZE : FW_PAGING_SIZE;
-               order = get_order(size);
-               block = alloc_pages(GFP_KERNEL, order);
-               if (!block) {
-                       /* free all the previous pages since we failed */
-                       iwl_free_fw_paging(mvm);
-                       return -ENOMEM;
-               }
-
-               mvm->fw_paging_db[blk_idx].fw_paging_block = block;
-               mvm->fw_paging_db[blk_idx].fw_paging_size = size;
-
-               if (dma_enabled) {
-                       phys = dma_map_page(mvm->trans->dev, block, 0,
-                                           PAGE_SIZE << order,
-                                           DMA_BIDIRECTIONAL);
-                       if (dma_mapping_error(mvm->trans->dev, phys)) {
-                               /*
-                                * free the previous pages and the current one
-                                * since we failed to map_page.
-                                */
-                               iwl_free_fw_paging(mvm);
-                               return -ENOMEM;
-                       }
-                       mvm->fw_paging_db[blk_idx].fw_paging_phys = phys;
-               } else {
-                       mvm->fw_paging_db[blk_idx].fw_paging_phys =
-                               PAGING_ADDR_SIG |
-                               blk_idx << BLOCK_2_EXP_SIZE;
-               }
-
-               if (!blk_idx)
-                       IWL_DEBUG_FW(mvm,
-                                    "Paging: allocated 4K(CSS) bytes (order %d) for firmware paging.\n",
-                                    order);
-               else
-                       IWL_DEBUG_FW(mvm,
-                                    "Paging: allocated 32K bytes (order %d) for firmware paging.\n",
-                                    order);
-       }
-
-       return 0;
-}
-
-static int iwl_save_fw_paging(struct iwl_mvm *mvm,
-                             const struct fw_img *fw)
-{
-       int ret;
-
-       ret = iwl_alloc_fw_paging_mem(mvm, fw);
-       if (ret)
-               return ret;
-
-       return iwl_fill_paging_mem(mvm, fw);
-}
-
-/* send paging cmd to FW in case CPU2 has paging image */
-static int iwl_send_paging_cmd(struct iwl_mvm *mvm, const struct fw_img *fw)
-{
-       struct iwl_fw_paging_cmd paging_cmd = {
-               .flags = cpu_to_le32(PAGING_CMD_IS_SECURED |
-                                    PAGING_CMD_IS_ENABLED |
-                                    (mvm->num_of_pages_in_last_blk <<
-                                     PAGING_CMD_NUM_OF_PAGES_IN_LAST_GRP_POS)),
-               .block_size = cpu_to_le32(BLOCK_2_EXP_SIZE),
-               .block_num = cpu_to_le32(mvm->num_of_paging_blk),
-       };
-       int blk_idx;
-
-       /* loop for for all paging blocks + CSS block */
-       for (blk_idx = 0; blk_idx < mvm->num_of_paging_blk + 1; blk_idx++) {
-               dma_addr_t addr = mvm->fw_paging_db[blk_idx].fw_paging_phys;
-               __le32 phy_addr;
-
-               addr = addr >> PAGE_2_EXP_SIZE;
-               phy_addr = cpu_to_le32(addr);
-               paging_cmd.device_phy_addr[blk_idx] = phy_addr;
-       }
-
-       return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(FW_PAGING_BLOCK_CMD,
-                                                   IWL_ALWAYS_LONG_GROUP, 0),
-                                   0, sizeof(paging_cmd), &paging_cmd);
-}
-
-/*
- * Send paging item cmd to FW in case CPU2 has paging image
- */
-static int iwl_trans_get_paging_item(struct iwl_mvm *mvm)
-{
-       int ret;
-       struct iwl_fw_get_item_cmd fw_get_item_cmd = {
-               .item_id = cpu_to_le32(IWL_FW_ITEM_ID_PAGING),
-       };
-
-       struct iwl_fw_get_item_resp *item_resp;
-       struct iwl_host_cmd cmd = {
-               .id = iwl_cmd_id(FW_GET_ITEM_CMD, IWL_ALWAYS_LONG_GROUP, 0),
-               .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL,
-               .data = { &fw_get_item_cmd, },
-       };
-
-       cmd.len[0] = sizeof(struct iwl_fw_get_item_cmd);
-
-       ret = iwl_mvm_send_cmd(mvm, &cmd);
-       if (ret) {
-               IWL_ERR(mvm,
-                       "Paging: Failed to send FW_GET_ITEM_CMD cmd (err = %d)\n",
-                       ret);
-               return ret;
-       }
-
-       item_resp = (void *)((struct iwl_rx_packet *)cmd.resp_pkt)->data;
-       if (item_resp->item_id != cpu_to_le32(IWL_FW_ITEM_ID_PAGING)) {
-               IWL_ERR(mvm,
-                       "Paging: got wrong item in FW_GET_ITEM_CMD resp (item_id = %u)\n",
-                       le32_to_cpu(item_resp->item_id));
-               ret = -EIO;
-               goto exit;
-       }
-
-       /* Add an extra page for headers */
-       mvm->trans->paging_download_buf = kzalloc(PAGING_BLOCK_SIZE +
-                                                 FW_PAGING_SIZE,
-                                                 GFP_KERNEL);
-       if (!mvm->trans->paging_download_buf) {
-               ret = -ENOMEM;
-               goto exit;
-       }
-       mvm->trans->paging_req_addr = le32_to_cpu(item_resp->item_val);
-       mvm->trans->paging_db = mvm->fw_paging_db;
-       IWL_DEBUG_FW(mvm,
-                    "Paging: got paging request address (paging_req_addr 0x%08x)\n",
-                    mvm->trans->paging_req_addr);
-
-exit:
-       iwl_free_resp(&cmd);
-
-       return ret;
-}
-
 static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
                         struct iwl_rx_packet *pkt, void *data)
 {
@@ -544,48 +244,6 @@ static bool iwl_wait_phy_db_entry(struct iwl_notif_wait_data *notif_wait,
        return false;
 }
 
-static int iwl_mvm_init_paging(struct iwl_mvm *mvm)
-{
-       const struct fw_img *fw = &mvm->fw->img[mvm->cur_ucode];
-       int ret;
-
-       /*
-        * Configure and operate fw paging mechanism.
-        * The driver configures the paging flow only once.
-        * The CPU2 paging image is included in the IWL_UCODE_INIT image.
-        */
-       if (!fw->paging_mem_size)
-               return 0;
-
-       /*
-        * When dma is not enabled, the driver needs to copy / write
-        * the downloaded / uploaded page to / from the smem.
-        * This gets the location of the place were the pages are
-        * stored.
-        */
-       if (!is_device_dma_capable(mvm->trans->dev)) {
-               ret = iwl_trans_get_paging_item(mvm);
-               if (ret) {
-                       IWL_ERR(mvm, "failed to get FW paging item\n");
-                       return ret;
-               }
-       }
-
-       ret = iwl_save_fw_paging(mvm, fw);
-       if (ret) {
-               IWL_ERR(mvm, "failed to save the FW paging image\n");
-               return ret;
-       }
-
-       ret = iwl_send_paging_cmd(mvm, fw);
-       if (ret) {
-               IWL_ERR(mvm, "failed to send the paging cmd\n");
-               iwl_free_fw_paging(mvm);
-               return ret;
-       }
-
-       return 0;
-}
 static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
                                         enum iwl_ucode_type ucode_type)
 {
@@ -1495,7 +1153,7 @@ static int iwl_mvm_load_rt_fw(struct iwl_mvm *mvm)
        if (ret)
                return ret;
 
-       return iwl_mvm_init_paging(mvm);
+       return iwl_init_paging(&mvm->fwrt, mvm->cur_ucode);
 }
 
 int iwl_mvm_up(struct iwl_mvm *mvm)
index eaacfaf..f8fe15c 100644 (file)
@@ -87,6 +87,7 @@
 #include "fw-api.h"
 #include "constants.h"
 #include "tof.h"
+#include "fw/runtime.h"
 
 #define IWL_MVM_MAX_ADDRESSES          5
 /* RSSI offset for WkP */
@@ -815,10 +816,7 @@ struct iwl_mvm {
        /* NVM sections */
        struct iwl_nvm_section nvm_sections[NVM_MAX_NUM_SECTIONS];
 
-       /* Paging section */
-       struct iwl_fw_paging fw_paging_db[NUM_OF_FW_PAGING_BLOCKS];
-       u16 num_of_paging_blk;
-       u16 num_of_pages_in_last_blk;
+       struct iwl_fw_runtime fwrt;
 
        /* EEPROM MAC addresses */
        struct mac_address addresses[IWL_MVM_MAX_ADDRESSES];
@@ -1571,9 +1569,6 @@ void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm,
 void iwl_mvm_rx_umac_scan_iter_complete_notif(struct iwl_mvm *mvm,
                                              struct iwl_rx_cmd_buffer *rxb);
 
-/* Paging */
-void iwl_free_fw_paging(struct iwl_mvm *mvm);
-
 /* MVM debugfs */
 #ifdef CONFIG_IWLWIFI_DEBUGFS
 int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir);
@@ -1798,8 +1793,7 @@ void iwl_mvm_enable_ac_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue,
 
 static inline void iwl_mvm_stop_device(struct iwl_mvm *mvm)
 {
-       if (!iwl_mvm_has_new_tx_api(mvm))
-               iwl_free_fw_paging(mvm);
+       iwl_free_fw_paging(&mvm->fwrt);
        clear_bit(IWL_MVM_STATUS_FIRMWARE_RUNNING, &mvm->status);
        mvm->fw_dbg_conf = FW_DBG_INVALID;
        iwl_trans_stop_device(mvm->trans);
index 4d1188b..8c49ac9 100644 (file)
@@ -580,6 +580,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        mvm->fw = fw;
        mvm->hw = hw;
 
+       iwl_fw_runtime_init(&mvm->fwrt, trans, fw);
+
        mvm->init_status = 0;
 
        if (iwl_mvm_has_new_rx_api(mvm)) {