paging.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. /******************************************************************************
  2. *
  3. * This file is provided under a dual BSD/GPLv2 license. When using or
  4. * redistributing this file, you may do so under either license.
  5. *
  6. * GPL LICENSE SUMMARY
  7. *
  8. * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  9. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  10. * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
  11. *
  12. * This program is free software; you can redistribute it and/or modify
  13. * it under the terms of version 2 of the GNU General Public License as
  14. * published by the Free Software Foundation.
  15. *
  16. * This program is distributed in the hope that it will be useful, but
  17. * WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  19. * General Public License for more details.
  20. *
  21. * The full GNU General Public License is included in this distribution
  22. * in the file called COPYING.
  23. *
  24. * Contact Information:
  25. * Intel Linux Wireless <linuxwifi@intel.com>
  26. * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
  27. *
  28. * BSD LICENSE
  29. *
  30. * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  31. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  32. * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
  33. * All rights reserved.
  34. *
  35. * Redistribution and use in source and binary forms, with or without
  36. * modification, are permitted provided that the following conditions
  37. * are met:
  38. *
  39. * * Redistributions of source code must retain the above copyright
  40. * notice, this list of conditions and the following disclaimer.
  41. * * Redistributions in binary form must reproduce the above copyright
  42. * notice, this list of conditions and the following disclaimer in
  43. * the documentation and/or other materials provided with the
  44. * distribution.
  45. * * Neither the name Intel Corporation nor the names of its
  46. * contributors may be used to endorse or promote products derived
  47. * from this software without specific prior written permission.
  48. *
  49. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  50. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  51. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  52. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  53. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  54. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  55. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  56. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  57. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  58. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  59. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  60. *
  61. *****************************************************************************/
  62. #include "iwl-drv.h"
  63. #include "runtime.h"
  64. #include "fw/api/commands.h"
  65. void iwl_free_fw_paging(struct iwl_fw_runtime *fwrt)
  66. {
  67. int i;
  68. if (!fwrt->fw_paging_db[0].fw_paging_block)
  69. return;
  70. for (i = 0; i < NUM_OF_FW_PAGING_BLOCKS; i++) {
  71. struct iwl_fw_paging *paging = &fwrt->fw_paging_db[i];
  72. if (!paging->fw_paging_block) {
  73. IWL_DEBUG_FW(fwrt,
  74. "Paging: block %d already freed, continue to next page\n",
  75. i);
  76. continue;
  77. }
  78. dma_unmap_page(fwrt->trans->dev, paging->fw_paging_phys,
  79. paging->fw_paging_size, DMA_BIDIRECTIONAL);
  80. __free_pages(paging->fw_paging_block,
  81. get_order(paging->fw_paging_size));
  82. paging->fw_paging_block = NULL;
  83. }
  84. memset(fwrt->fw_paging_db, 0, sizeof(fwrt->fw_paging_db));
  85. }
  86. IWL_EXPORT_SYMBOL(iwl_free_fw_paging);
  87. static int iwl_alloc_fw_paging_mem(struct iwl_fw_runtime *fwrt,
  88. const struct fw_img *image)
  89. {
  90. struct page *block;
  91. dma_addr_t phys = 0;
  92. int blk_idx, order, num_of_pages, size;
  93. if (fwrt->fw_paging_db[0].fw_paging_block)
  94. return 0;
  95. /* ensure BLOCK_2_EXP_SIZE is power of 2 of PAGING_BLOCK_SIZE */
  96. BUILD_BUG_ON(BIT(BLOCK_2_EXP_SIZE) != PAGING_BLOCK_SIZE);
  97. num_of_pages = image->paging_mem_size / FW_PAGING_SIZE;
  98. fwrt->num_of_paging_blk =
  99. DIV_ROUND_UP(num_of_pages, NUM_OF_PAGE_PER_GROUP);
  100. fwrt->num_of_pages_in_last_blk =
  101. num_of_pages -
  102. NUM_OF_PAGE_PER_GROUP * (fwrt->num_of_paging_blk - 1);
  103. IWL_DEBUG_FW(fwrt,
  104. "Paging: allocating mem for %d paging blocks, each block holds 8 pages, last block holds %d pages\n",
  105. fwrt->num_of_paging_blk,
  106. fwrt->num_of_pages_in_last_blk);
  107. /*
  108. * Allocate CSS and paging blocks in dram.
  109. */
  110. for (blk_idx = 0; blk_idx < fwrt->num_of_paging_blk + 1; blk_idx++) {
  111. /* For CSS allocate 4KB, for others PAGING_BLOCK_SIZE (32K) */
  112. size = blk_idx ? PAGING_BLOCK_SIZE : FW_PAGING_SIZE;
  113. order = get_order(size);
  114. block = alloc_pages(GFP_KERNEL, order);
  115. if (!block) {
  116. /* free all the previous pages since we failed */
  117. iwl_free_fw_paging(fwrt);
  118. return -ENOMEM;
  119. }
  120. fwrt->fw_paging_db[blk_idx].fw_paging_block = block;
  121. fwrt->fw_paging_db[blk_idx].fw_paging_size = size;
  122. phys = dma_map_page(fwrt->trans->dev, block, 0,
  123. PAGE_SIZE << order,
  124. DMA_BIDIRECTIONAL);
  125. if (dma_mapping_error(fwrt->trans->dev, phys)) {
  126. /*
  127. * free the previous pages and the current one
  128. * since we failed to map_page.
  129. */
  130. iwl_free_fw_paging(fwrt);
  131. return -ENOMEM;
  132. }
  133. fwrt->fw_paging_db[blk_idx].fw_paging_phys = phys;
  134. if (!blk_idx)
  135. IWL_DEBUG_FW(fwrt,
  136. "Paging: allocated 4K(CSS) bytes (order %d) for firmware paging.\n",
  137. order);
  138. else
  139. IWL_DEBUG_FW(fwrt,
  140. "Paging: allocated 32K bytes (order %d) for firmware paging.\n",
  141. order);
  142. }
  143. return 0;
  144. }
  145. static int iwl_fill_paging_mem(struct iwl_fw_runtime *fwrt,
  146. const struct fw_img *image)
  147. {
  148. int sec_idx, idx;
  149. u32 offset = 0;
  150. /*
  151. * find where is the paging image start point:
  152. * if CPU2 exist and it's in paging format, then the image looks like:
  153. * CPU1 sections (2 or more)
  154. * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between CPU1 to CPU2
  155. * CPU2 sections (not paged)
  156. * PAGING_SEPARATOR_SECTION delimiter - separate between CPU2
  157. * non paged to CPU2 paging sec
  158. * CPU2 paging CSS
  159. * CPU2 paging image (including instruction and data)
  160. */
  161. for (sec_idx = 0; sec_idx < image->num_sec; sec_idx++) {
  162. if (image->sec[sec_idx].offset == PAGING_SEPARATOR_SECTION) {
  163. sec_idx++;
  164. break;
  165. }
  166. }
  167. /*
  168. * If paging is enabled there should be at least 2 more sections left
  169. * (one for CSS and one for Paging data)
  170. */
  171. if (sec_idx >= image->num_sec - 1) {
  172. IWL_ERR(fwrt, "Paging: Missing CSS and/or paging sections\n");
  173. iwl_free_fw_paging(fwrt);
  174. return -EINVAL;
  175. }
  176. /* copy the CSS block to the dram */
  177. IWL_DEBUG_FW(fwrt, "Paging: load paging CSS to FW, sec = %d\n",
  178. sec_idx);
  179. memcpy(page_address(fwrt->fw_paging_db[0].fw_paging_block),
  180. image->sec[sec_idx].data,
  181. fwrt->fw_paging_db[0].fw_paging_size);
  182. dma_sync_single_for_device(fwrt->trans->dev,
  183. fwrt->fw_paging_db[0].fw_paging_phys,
  184. fwrt->fw_paging_db[0].fw_paging_size,
  185. DMA_BIDIRECTIONAL);
  186. IWL_DEBUG_FW(fwrt,
  187. "Paging: copied %d CSS bytes to first block\n",
  188. fwrt->fw_paging_db[0].fw_paging_size);
  189. sec_idx++;
  190. /*
  191. * copy the paging blocks to the dram
  192. * loop index start from 1 since that CSS block already copied to dram
  193. * and CSS index is 0.
  194. * loop stop at num_of_paging_blk since that last block is not full.
  195. */
  196. for (idx = 1; idx < fwrt->num_of_paging_blk; idx++) {
  197. struct iwl_fw_paging *block = &fwrt->fw_paging_db[idx];
  198. memcpy(page_address(block->fw_paging_block),
  199. image->sec[sec_idx].data + offset,
  200. block->fw_paging_size);
  201. dma_sync_single_for_device(fwrt->trans->dev,
  202. block->fw_paging_phys,
  203. block->fw_paging_size,
  204. DMA_BIDIRECTIONAL);
  205. IWL_DEBUG_FW(fwrt,
  206. "Paging: copied %d paging bytes to block %d\n",
  207. fwrt->fw_paging_db[idx].fw_paging_size,
  208. idx);
  209. offset += fwrt->fw_paging_db[idx].fw_paging_size;
  210. }
  211. /* copy the last paging block */
  212. if (fwrt->num_of_pages_in_last_blk > 0) {
  213. struct iwl_fw_paging *block = &fwrt->fw_paging_db[idx];
  214. memcpy(page_address(block->fw_paging_block),
  215. image->sec[sec_idx].data + offset,
  216. FW_PAGING_SIZE * fwrt->num_of_pages_in_last_blk);
  217. dma_sync_single_for_device(fwrt->trans->dev,
  218. block->fw_paging_phys,
  219. block->fw_paging_size,
  220. DMA_BIDIRECTIONAL);
  221. IWL_DEBUG_FW(fwrt,
  222. "Paging: copied %d pages in the last block %d\n",
  223. fwrt->num_of_pages_in_last_blk, idx);
  224. }
  225. return 0;
  226. }
  227. static int iwl_save_fw_paging(struct iwl_fw_runtime *fwrt,
  228. const struct fw_img *fw)
  229. {
  230. int ret;
  231. ret = iwl_alloc_fw_paging_mem(fwrt, fw);
  232. if (ret)
  233. return ret;
  234. return iwl_fill_paging_mem(fwrt, fw);
  235. }
  236. /* send paging cmd to FW in case CPU2 has paging image */
  237. static int iwl_send_paging_cmd(struct iwl_fw_runtime *fwrt,
  238. const struct fw_img *fw)
  239. {
  240. struct iwl_fw_paging_cmd paging_cmd = {
  241. .flags = cpu_to_le32(PAGING_CMD_IS_SECURED |
  242. PAGING_CMD_IS_ENABLED |
  243. (fwrt->num_of_pages_in_last_blk <<
  244. PAGING_CMD_NUM_OF_PAGES_IN_LAST_GRP_POS)),
  245. .block_size = cpu_to_le32(BLOCK_2_EXP_SIZE),
  246. .block_num = cpu_to_le32(fwrt->num_of_paging_blk),
  247. };
  248. struct iwl_host_cmd hcmd = {
  249. .id = iwl_cmd_id(FW_PAGING_BLOCK_CMD, IWL_ALWAYS_LONG_GROUP, 0),
  250. .len = { sizeof(paging_cmd), },
  251. .data = { &paging_cmd, },
  252. };
  253. int blk_idx;
  254. /* loop for for all paging blocks + CSS block */
  255. for (blk_idx = 0; blk_idx < fwrt->num_of_paging_blk + 1; blk_idx++) {
  256. dma_addr_t addr = fwrt->fw_paging_db[blk_idx].fw_paging_phys;
  257. __le32 phy_addr;
  258. addr = addr >> PAGE_2_EXP_SIZE;
  259. phy_addr = cpu_to_le32(addr);
  260. paging_cmd.device_phy_addr[blk_idx] = phy_addr;
  261. }
  262. return iwl_trans_send_cmd(fwrt->trans, &hcmd);
  263. }
  264. int iwl_init_paging(struct iwl_fw_runtime *fwrt, enum iwl_ucode_type type)
  265. {
  266. const struct fw_img *fw = &fwrt->fw->img[type];
  267. int ret;
  268. if (fwrt->trans->cfg->gen2)
  269. return 0;
  270. /*
  271. * Configure and operate fw paging mechanism.
  272. * The driver configures the paging flow only once.
  273. * The CPU2 paging image is included in the IWL_UCODE_INIT image.
  274. */
  275. if (!fw->paging_mem_size)
  276. return 0;
  277. ret = iwl_save_fw_paging(fwrt, fw);
  278. if (ret) {
  279. IWL_ERR(fwrt, "failed to save the FW paging image\n");
  280. return ret;
  281. }
  282. ret = iwl_send_paging_cmd(fwrt, fw);
  283. if (ret) {
  284. IWL_ERR(fwrt, "failed to send the paging cmd\n");
  285. iwl_free_fw_paging(fwrt);
  286. return ret;
  287. }
  288. return 0;
  289. }
  290. IWL_EXPORT_SYMBOL(iwl_init_paging);