Path: blob/main/sys/contrib/dev/iwlwifi/fw/paging.c
48287 views
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause1/*2* Copyright (C) 2012-2014, 2018-2019, 2021, 2025 Intel Corporation3* Copyright (C) 2013-2015 Intel Mobile Communications GmbH4* Copyright (C) 2016-2017 Intel Deutschland GmbH5*/6#include "iwl-drv.h"7#include "runtime.h"8#include "fw/api/commands.h"910void iwl_free_fw_paging(struct iwl_fw_runtime *fwrt)11{12int i;1314if (!fwrt->fw_paging_db[0].fw_paging_block)15return;1617for (i = 0; i < NUM_OF_FW_PAGING_BLOCKS; i++) {18struct iwl_fw_paging *paging = &fwrt->fw_paging_db[i];1920if (!paging->fw_paging_block) {21IWL_DEBUG_FW(fwrt,22"Paging: block %d already freed, continue to next page\n",23i);2425continue;26}27dma_unmap_page(fwrt->trans->dev, paging->fw_paging_phys,28paging->fw_paging_size, DMA_BIDIRECTIONAL);2930__free_pages(paging->fw_paging_block,31get_order(paging->fw_paging_size));32paging->fw_paging_block = NULL;33}3435memset(fwrt->fw_paging_db, 0, sizeof(fwrt->fw_paging_db));36}37IWL_EXPORT_SYMBOL(iwl_free_fw_paging);3839static int iwl_alloc_fw_paging_mem(struct iwl_fw_runtime *fwrt,40const struct fw_img *image)41{42struct page *block;43dma_addr_t phys = 0;44int blk_idx, order, num_of_pages, size;4546if (fwrt->fw_paging_db[0].fw_paging_block)47return 0;4849/* ensure BLOCK_2_EXP_SIZE is power of 2 of PAGING_BLOCK_SIZE */50BUILD_BUG_ON(BIT(BLOCK_2_EXP_SIZE) != PAGING_BLOCK_SIZE);5152num_of_pages = image->paging_mem_size / FW_PAGING_SIZE;53fwrt->num_of_paging_blk =54DIV_ROUND_UP(num_of_pages, NUM_OF_PAGE_PER_GROUP);55fwrt->num_of_pages_in_last_blk =56num_of_pages -57NUM_OF_PAGE_PER_GROUP * (fwrt->num_of_paging_blk - 1);5859IWL_DEBUG_FW(fwrt,60"Paging: allocating mem for %d paging blocks, each block holds 8 pages, last block holds %d pages\n",61fwrt->num_of_paging_blk,62fwrt->num_of_pages_in_last_blk);6364/*65* Allocate CSS and paging blocks in dram.66*/67for (blk_idx = 0; blk_idx < fwrt->num_of_paging_blk + 1; blk_idx++) {68/* For CSS allocate 4KB, for others PAGING_BLOCK_SIZE (32K) */69size = blk_idx ? PAGING_BLOCK_SIZE : FW_PAGING_SIZE;70order = get_order(size);71block = alloc_pages(GFP_KERNEL, order);72if (!block) {73/* free all the previous pages since we failed */74iwl_free_fw_paging(fwrt);75return -ENOMEM;76}7778fwrt->fw_paging_db[blk_idx].fw_paging_block = block;79fwrt->fw_paging_db[blk_idx].fw_paging_size = size;8081phys = dma_map_page(fwrt->trans->dev, block, 0,82PAGE_SIZE << order,83DMA_BIDIRECTIONAL);84if (dma_mapping_error(fwrt->trans->dev, phys)) {85/*86* free the previous pages and the current one87* since we failed to map_page.88*/89iwl_free_fw_paging(fwrt);90return -ENOMEM;91}92fwrt->fw_paging_db[blk_idx].fw_paging_phys = phys;9394if (!blk_idx)95IWL_DEBUG_FW(fwrt,96"Paging: allocated 4K(CSS) bytes (order %d) for firmware paging.\n",97order);98else99IWL_DEBUG_FW(fwrt,100"Paging: allocated 32K bytes (order %d) for firmware paging.\n",101order);102}103104return 0;105}106107static int iwl_fill_paging_mem(struct iwl_fw_runtime *fwrt,108const struct fw_img *image)109{110int sec_idx, idx, ret;111u32 offset = 0;112113/*114* find where is the paging image start point:115* if CPU2 exist and it's in paging format, then the image looks like:116* CPU1 sections (2 or more)117* CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between CPU1 to CPU2118* CPU2 sections (not paged)119* PAGING_SEPARATOR_SECTION delimiter - separate between CPU2120* non paged to CPU2 paging sec121* CPU2 paging CSS122* CPU2 paging image (including instruction and data)123*/124for (sec_idx = 0; sec_idx < image->num_sec; sec_idx++) {125if (image->sec[sec_idx].offset == PAGING_SEPARATOR_SECTION) {126sec_idx++;127break;128}129}130131/*132* If paging is enabled there should be at least 2 more sections left133* (one for CSS and one for Paging data)134*/135if (sec_idx >= image->num_sec - 1) {136IWL_ERR(fwrt, "Paging: Missing CSS and/or paging sections\n");137ret = -EINVAL;138goto err;139}140141/* copy the CSS block to the dram */142IWL_DEBUG_FW(fwrt, "Paging: load paging CSS to FW, sec = %d\n",143sec_idx);144145if (image->sec[sec_idx].len > fwrt->fw_paging_db[0].fw_paging_size) {146IWL_ERR(fwrt, "CSS block is larger than paging size\n");147ret = -EINVAL;148goto err;149}150151memcpy(page_address(fwrt->fw_paging_db[0].fw_paging_block),152image->sec[sec_idx].data,153image->sec[sec_idx].len);154fwrt->fw_paging_db[0].fw_offs = image->sec[sec_idx].offset;155dma_sync_single_for_device(fwrt->trans->dev,156fwrt->fw_paging_db[0].fw_paging_phys,157fwrt->fw_paging_db[0].fw_paging_size,158DMA_BIDIRECTIONAL);159160IWL_DEBUG_FW(fwrt,161"Paging: copied %d CSS bytes to first block\n",162fwrt->fw_paging_db[0].fw_paging_size);163164sec_idx++;165166/*167* Copy the paging blocks to the dram. The loop index starts168* from 1 since the CSS block (index 0) was already copied to169* dram. We use num_of_paging_blk + 1 to account for that.170*/171for (idx = 1; idx < fwrt->num_of_paging_blk + 1; idx++) {172struct iwl_fw_paging *block = &fwrt->fw_paging_db[idx];173int remaining = image->sec[sec_idx].len - offset;174int len = block->fw_paging_size;175176/*177* For the last block, we copy all that is remaining,178* for all other blocks, we copy fw_paging_size at a179* time. */180if (idx == fwrt->num_of_paging_blk) {181len = remaining;182if (remaining !=183fwrt->num_of_pages_in_last_blk * FW_PAGING_SIZE) {184IWL_ERR(fwrt,185"Paging: last block contains more data than expected %d\n",186remaining);187ret = -EINVAL;188goto err;189}190} else if (block->fw_paging_size > remaining) {191IWL_ERR(fwrt,192"Paging: not enough data in other in block %d (%d)\n",193idx, remaining);194ret = -EINVAL;195goto err;196}197198memcpy(page_address(block->fw_paging_block),199(const u8 *)image->sec[sec_idx].data + offset, len);200block->fw_offs = image->sec[sec_idx].offset + offset;201dma_sync_single_for_device(fwrt->trans->dev,202block->fw_paging_phys,203block->fw_paging_size,204DMA_BIDIRECTIONAL);205206IWL_DEBUG_FW(fwrt,207"Paging: copied %d paging bytes to block %d\n",208len, idx);209210offset += block->fw_paging_size;211}212213return 0;214215err:216iwl_free_fw_paging(fwrt);217return ret;218}219220static int iwl_save_fw_paging(struct iwl_fw_runtime *fwrt,221const struct fw_img *fw)222{223int ret;224225ret = iwl_alloc_fw_paging_mem(fwrt, fw);226if (ret)227return ret;228229return iwl_fill_paging_mem(fwrt, fw);230}231232/* send paging cmd to FW in case CPU2 has paging image */233static int iwl_send_paging_cmd(struct iwl_fw_runtime *fwrt,234const struct fw_img *fw)235{236struct iwl_fw_paging_cmd paging_cmd = {237.flags = cpu_to_le32(PAGING_CMD_IS_SECURED |238PAGING_CMD_IS_ENABLED |239(fwrt->num_of_pages_in_last_blk <<240PAGING_CMD_NUM_OF_PAGES_IN_LAST_GRP_POS)),241.block_size = cpu_to_le32(BLOCK_2_EXP_SIZE),242.block_num = cpu_to_le32(fwrt->num_of_paging_blk),243};244struct iwl_host_cmd hcmd = {245.id = WIDE_ID(IWL_ALWAYS_LONG_GROUP, FW_PAGING_BLOCK_CMD),246.len = { sizeof(paging_cmd), },247.data = { &paging_cmd, },248};249int blk_idx;250251/* loop for all paging blocks + CSS block */252for (blk_idx = 0; blk_idx < fwrt->num_of_paging_blk + 1; blk_idx++) {253dma_addr_t addr = fwrt->fw_paging_db[blk_idx].fw_paging_phys;254__le32 phy_addr;255256addr = addr >> PAGE_2_EXP_SIZE;257phy_addr = cpu_to_le32(addr);258paging_cmd.device_phy_addr[blk_idx] = phy_addr;259}260261return iwl_trans_send_cmd(fwrt->trans, &hcmd);262}263264int iwl_init_paging(struct iwl_fw_runtime *fwrt, enum iwl_ucode_type type)265{266const struct fw_img *fw = &fwrt->fw->img[type];267int ret;268269if (fwrt->trans->mac_cfg->gen2)270return 0;271272/*273* Configure and operate fw paging mechanism.274* The driver configures the paging flow only once.275* The CPU2 paging image is included in the IWL_UCODE_INIT image.276*/277if (!fw->paging_mem_size)278return 0;279280ret = iwl_save_fw_paging(fwrt, fw);281if (ret) {282IWL_ERR(fwrt, "failed to save the FW paging image\n");283return ret;284}285286ret = iwl_send_paging_cmd(fwrt, fw);287if (ret) {288IWL_ERR(fwrt, "failed to send the paging cmd\n");289iwl_free_fw_paging(fwrt);290return ret;291}292293return 0;294}295IWL_EXPORT_SYMBOL(iwl_init_paging);296297298