The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/contrib/dev/iwlwifi/fw/paging.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
    2 /*
    3  * Copyright (C) 2012-2014, 2018-2019, 2021 Intel Corporation
    4  * Copyright (C) 2013-2015 Intel Mobile Communications GmbH
    5  * Copyright (C) 2016-2017 Intel Deutschland GmbH
    6  */
    7 #include "iwl-drv.h"
    8 #include "runtime.h"
    9 #include "fw/api/commands.h"
   10 
   11 void iwl_free_fw_paging(struct iwl_fw_runtime *fwrt)
   12 {
   13         int i;
   14 
   15         if (!fwrt->fw_paging_db[0].fw_paging_block)
   16                 return;
   17 
   18         for (i = 0; i < NUM_OF_FW_PAGING_BLOCKS; i++) {
   19                 struct iwl_fw_paging *paging = &fwrt->fw_paging_db[i];
   20 
   21                 if (!paging->fw_paging_block) {
   22                         IWL_DEBUG_FW(fwrt,
   23                                      "Paging: block %d already freed, continue to next page\n",
   24                                      i);
   25 
   26                         continue;
   27                 }
   28                 dma_unmap_page(fwrt->trans->dev, paging->fw_paging_phys,
   29                                paging->fw_paging_size, DMA_BIDIRECTIONAL);
   30 
   31                 __free_pages(paging->fw_paging_block,
   32                              get_order(paging->fw_paging_size));
   33                 paging->fw_paging_block = NULL;
   34         }
   35 
   36         memset(fwrt->fw_paging_db, 0, sizeof(fwrt->fw_paging_db));
   37 }
   38 IWL_EXPORT_SYMBOL(iwl_free_fw_paging);
   39 
   40 static int iwl_alloc_fw_paging_mem(struct iwl_fw_runtime *fwrt,
   41                                    const struct fw_img *image)
   42 {
   43         struct page *block;
   44         dma_addr_t phys = 0;
   45         int blk_idx, order, num_of_pages, size;
   46 
   47         if (fwrt->fw_paging_db[0].fw_paging_block)
   48                 return 0;
   49 
   50         /* ensure BLOCK_2_EXP_SIZE is power of 2 of PAGING_BLOCK_SIZE */
   51         BUILD_BUG_ON(BIT(BLOCK_2_EXP_SIZE) != PAGING_BLOCK_SIZE);
   52 
   53         num_of_pages = image->paging_mem_size / FW_PAGING_SIZE;
   54         fwrt->num_of_paging_blk =
   55                 DIV_ROUND_UP(num_of_pages, NUM_OF_PAGE_PER_GROUP);
   56         fwrt->num_of_pages_in_last_blk =
   57                 num_of_pages -
   58                 NUM_OF_PAGE_PER_GROUP * (fwrt->num_of_paging_blk - 1);
   59 
   60         IWL_DEBUG_FW(fwrt,
   61                      "Paging: allocating mem for %d paging blocks, each block holds 8 pages, last block holds %d pages\n",
   62                      fwrt->num_of_paging_blk,
   63                      fwrt->num_of_pages_in_last_blk);
   64 
   65         /*
   66          * Allocate CSS and paging blocks in dram.
   67          */
   68         for (blk_idx = 0; blk_idx < fwrt->num_of_paging_blk + 1; blk_idx++) {
   69                 /* For CSS allocate 4KB, for others PAGING_BLOCK_SIZE (32K) */
   70                 size = blk_idx ? PAGING_BLOCK_SIZE : FW_PAGING_SIZE;
   71                 order = get_order(size);
   72                 block = alloc_pages(GFP_KERNEL, order);
   73                 if (!block) {
   74                         /* free all the previous pages since we failed */
   75                         iwl_free_fw_paging(fwrt);
   76                         return -ENOMEM;
   77                 }
   78 
   79                 fwrt->fw_paging_db[blk_idx].fw_paging_block = block;
   80                 fwrt->fw_paging_db[blk_idx].fw_paging_size = size;
   81 
   82                 phys = dma_map_page(fwrt->trans->dev, block, 0,
   83                                     PAGE_SIZE << order,
   84                                     DMA_BIDIRECTIONAL);
   85                 if (dma_mapping_error(fwrt->trans->dev, phys)) {
   86                         /*
   87                          * free the previous pages and the current one
   88                          * since we failed to map_page.
   89                          */
   90                         iwl_free_fw_paging(fwrt);
   91                         return -ENOMEM;
   92                 }
   93                 fwrt->fw_paging_db[blk_idx].fw_paging_phys = phys;
   94 
   95                 if (!blk_idx)
   96                         IWL_DEBUG_FW(fwrt,
   97                                      "Paging: allocated 4K(CSS) bytes (order %d) for firmware paging.\n",
   98                                      order);
   99                 else
  100                         IWL_DEBUG_FW(fwrt,
  101                                      "Paging: allocated 32K bytes (order %d) for firmware paging.\n",
  102                                      order);
  103         }
  104 
  105         return 0;
  106 }
  107 
  108 static int iwl_fill_paging_mem(struct iwl_fw_runtime *fwrt,
  109                                const struct fw_img *image)
  110 {
  111         int sec_idx, idx, ret;
  112         u32 offset = 0;
  113 
  114         /*
  115          * find where is the paging image start point:
  116          * if CPU2 exist and it's in paging format, then the image looks like:
  117          * CPU1 sections (2 or more)
  118          * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between CPU1 to CPU2
  119          * CPU2 sections (not paged)
  120          * PAGING_SEPARATOR_SECTION delimiter - separate between CPU2
  121          * non paged to CPU2 paging sec
  122          * CPU2 paging CSS
  123          * CPU2 paging image (including instruction and data)
  124          */
  125         for (sec_idx = 0; sec_idx < image->num_sec; sec_idx++) {
  126                 if (image->sec[sec_idx].offset == PAGING_SEPARATOR_SECTION) {
  127                         sec_idx++;
  128                         break;
  129                 }
  130         }
  131 
  132         /*
  133          * If paging is enabled there should be at least 2 more sections left
  134          * (one for CSS and one for Paging data)
  135          */
  136         if (sec_idx >= image->num_sec - 1) {
  137                 IWL_ERR(fwrt, "Paging: Missing CSS and/or paging sections\n");
  138                 ret = -EINVAL;
  139                 goto err;
  140         }
  141 
  142         /* copy the CSS block to the dram */
  143         IWL_DEBUG_FW(fwrt, "Paging: load paging CSS to FW, sec = %d\n",
  144                      sec_idx);
  145 
  146         if (image->sec[sec_idx].len > fwrt->fw_paging_db[0].fw_paging_size) {
  147                 IWL_ERR(fwrt, "CSS block is larger than paging size\n");
  148                 ret = -EINVAL;
  149                 goto err;
  150         }
  151 
  152         memcpy(page_address(fwrt->fw_paging_db[0].fw_paging_block),
  153                image->sec[sec_idx].data,
  154                image->sec[sec_idx].len);
  155         fwrt->fw_paging_db[0].fw_offs = image->sec[sec_idx].offset;
  156         dma_sync_single_for_device(fwrt->trans->dev,
  157                                    fwrt->fw_paging_db[0].fw_paging_phys,
  158                                    fwrt->fw_paging_db[0].fw_paging_size,
  159                                    DMA_BIDIRECTIONAL);
  160 
  161         IWL_DEBUG_FW(fwrt,
  162                      "Paging: copied %d CSS bytes to first block\n",
  163                      fwrt->fw_paging_db[0].fw_paging_size);
  164 
  165         sec_idx++;
  166 
  167         /*
  168          * Copy the paging blocks to the dram.  The loop index starts
  169          * from 1 since the CSS block (index 0) was already copied to
  170          * dram.  We use num_of_paging_blk + 1 to account for that.
  171          */
  172         for (idx = 1; idx < fwrt->num_of_paging_blk + 1; idx++) {
  173                 struct iwl_fw_paging *block = &fwrt->fw_paging_db[idx];
  174                 int remaining = image->sec[sec_idx].len - offset;
  175                 int len = block->fw_paging_size;
  176 
  177                 /*
  178                  * For the last block, we copy all that is remaining,
  179                  * for all other blocks, we copy fw_paging_size at a
  180                  * time. */
  181                 if (idx == fwrt->num_of_paging_blk) {
  182                         len = remaining;
  183                         if (remaining !=
  184                             fwrt->num_of_pages_in_last_blk * FW_PAGING_SIZE) {
  185                                 IWL_ERR(fwrt,
  186                                         "Paging: last block contains more data than expected %d\n",
  187                                         remaining);
  188                                 ret = -EINVAL;
  189                                 goto err;
  190                         }
  191                 } else if (block->fw_paging_size > remaining) {
  192                         IWL_ERR(fwrt,
  193                                 "Paging: not enough data in other in block %d (%d)\n",
  194                                 idx, remaining);
  195                         ret = -EINVAL;
  196                         goto err;
  197                 }
  198 
  199                 memcpy(page_address(block->fw_paging_block),
  200                        (const u8 *)image->sec[sec_idx].data + offset, len);
  201                 block->fw_offs = image->sec[sec_idx].offset + offset;
  202                 dma_sync_single_for_device(fwrt->trans->dev,
  203                                            block->fw_paging_phys,
  204                                            block->fw_paging_size,
  205                                            DMA_BIDIRECTIONAL);
  206 
  207                 IWL_DEBUG_FW(fwrt,
  208                              "Paging: copied %d paging bytes to block %d\n",
  209                              len, idx);
  210 
  211                 offset += block->fw_paging_size;
  212         }
  213 
  214         return 0;
  215 
  216 err:
  217         iwl_free_fw_paging(fwrt);
  218         return ret;
  219 }
  220 
  221 static int iwl_save_fw_paging(struct iwl_fw_runtime *fwrt,
  222                               const struct fw_img *fw)
  223 {
  224         int ret;
  225 
  226         ret = iwl_alloc_fw_paging_mem(fwrt, fw);
  227         if (ret)
  228                 return ret;
  229 
  230         return iwl_fill_paging_mem(fwrt, fw);
  231 }
  232 
  233 /* send paging cmd to FW in case CPU2 has paging image */
  234 static int iwl_send_paging_cmd(struct iwl_fw_runtime *fwrt,
  235                                const struct fw_img *fw)
  236 {
  237         struct iwl_fw_paging_cmd paging_cmd = {
  238                 .flags = cpu_to_le32(PAGING_CMD_IS_SECURED |
  239                                      PAGING_CMD_IS_ENABLED |
  240                                      (fwrt->num_of_pages_in_last_blk <<
  241                                       PAGING_CMD_NUM_OF_PAGES_IN_LAST_GRP_POS)),
  242                 .block_size = cpu_to_le32(BLOCK_2_EXP_SIZE),
  243                 .block_num = cpu_to_le32(fwrt->num_of_paging_blk),
  244         };
  245         struct iwl_host_cmd hcmd = {
  246                 .id = WIDE_ID(IWL_ALWAYS_LONG_GROUP, FW_PAGING_BLOCK_CMD),
  247                 .len = { sizeof(paging_cmd), },
  248                 .data = { &paging_cmd, },
  249         };
  250         int blk_idx;
  251 
  252         /* loop for for all paging blocks + CSS block */
  253         for (blk_idx = 0; blk_idx < fwrt->num_of_paging_blk + 1; blk_idx++) {
  254                 dma_addr_t addr = fwrt->fw_paging_db[blk_idx].fw_paging_phys;
  255                 __le32 phy_addr;
  256 
  257                 addr = addr >> PAGE_2_EXP_SIZE;
  258                 phy_addr = cpu_to_le32(addr);
  259                 paging_cmd.device_phy_addr[blk_idx] = phy_addr;
  260         }
  261 
  262         return iwl_trans_send_cmd(fwrt->trans, &hcmd);
  263 }
  264 
  265 int iwl_init_paging(struct iwl_fw_runtime *fwrt, enum iwl_ucode_type type)
  266 {
  267         const struct fw_img *fw = &fwrt->fw->img[type];
  268         int ret;
  269 
  270         if (fwrt->trans->trans_cfg->gen2)
  271                 return 0;
  272 
  273         /*
  274          * Configure and operate fw paging mechanism.
  275          * The driver configures the paging flow only once.
  276          * The CPU2 paging image is included in the IWL_UCODE_INIT image.
  277          */
  278         if (!fw->paging_mem_size)
  279                 return 0;
  280 
  281         ret = iwl_save_fw_paging(fwrt, fw);
  282         if (ret) {
  283                 IWL_ERR(fwrt, "failed to save the FW paging image\n");
  284                 return ret;
  285         }
  286 
  287         ret = iwl_send_paging_cmd(fwrt, fw);
  288         if (ret) {
  289                 IWL_ERR(fwrt, "failed to send the paging cmd\n");
  290                 iwl_free_fw_paging(fwrt);
  291                 return ret;
  292         }
  293 
  294         return 0;
  295 }
  296 IWL_EXPORT_SYMBOL(iwl_init_paging);

Cache object: bdf23bc4592808c954b8eef4b2b84e80


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.