aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/iwlwifi
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2012-09-10 05:50:18 -0400
committerJohannes Berg <johannes.berg@intel.com>2012-09-10 13:14:30 -0400
commit83f84d7bd49f9e4deea0a77a76c9882673ee1f3c (patch)
treefb2f84fa74450f89cbb429a7457ff0e80ece35b7 /drivers/net/wireless/iwlwifi
parent7439046d9747966bb805ca8bfd1086a876a4b8fd (diff)
iwlwifi: load firmware in chunks
Instead of allocating one big chunk of DMA-coherent memory for the firmware and keeping it around, only vmalloc() the firmware and copy it into a single page of DMA-coherent memory for the upload. The advantage is that we don't need DMA memory for the firmware image that is stored while the driver is operating, we only need it while uploading. This will make it easier for the driver to work if the system has fragmented memory. Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'drivers/net/wireless/iwlwifi')
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/ucode.c4
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-drv.c26
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-fw.h3
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/trans.c53
4 files changed, 58 insertions, 28 deletions
diff --git a/drivers/net/wireless/iwlwifi/dvm/ucode.c b/drivers/net/wireless/iwlwifi/dvm/ucode.c
index 6d8d6dd7943f..2cb1efbc5ed1 100644
--- a/drivers/net/wireless/iwlwifi/dvm/ucode.c
+++ b/drivers/net/wireless/iwlwifi/dvm/ucode.c
@@ -295,7 +295,7 @@ static int iwl_alive_notify(struct iwl_priv *priv)
295static int iwl_verify_sec_sparse(struct iwl_priv *priv, 295static int iwl_verify_sec_sparse(struct iwl_priv *priv,
296 const struct fw_desc *fw_desc) 296 const struct fw_desc *fw_desc)
297{ 297{
298 __le32 *image = (__le32 *)fw_desc->v_addr; 298 __le32 *image = (__le32 *)fw_desc->data;
299 u32 len = fw_desc->len; 299 u32 len = fw_desc->len;
300 u32 val; 300 u32 val;
301 u32 i; 301 u32 i;
@@ -319,7 +319,7 @@ static int iwl_verify_sec_sparse(struct iwl_priv *priv,
319static void iwl_print_mismatch_sec(struct iwl_priv *priv, 319static void iwl_print_mismatch_sec(struct iwl_priv *priv,
320 const struct fw_desc *fw_desc) 320 const struct fw_desc *fw_desc)
321{ 321{
322 __le32 *image = (__le32 *)fw_desc->v_addr; 322 __le32 *image = (__le32 *)fw_desc->data;
323 u32 len = fw_desc->len; 323 u32 len = fw_desc->len;
324 u32 val; 324 u32 val;
325 u32 offs; 325 u32 offs;
diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c
index 48d6d44c16d0..198634b75ed0 100644
--- a/drivers/net/wireless/iwlwifi/iwl-drv.c
+++ b/drivers/net/wireless/iwlwifi/iwl-drv.c
@@ -64,6 +64,7 @@
64#include <linux/dma-mapping.h> 64#include <linux/dma-mapping.h>
65#include <linux/firmware.h> 65#include <linux/firmware.h>
66#include <linux/module.h> 66#include <linux/module.h>
67#include <linux/vmalloc.h>
67 68
68#include "iwl-drv.h" 69#include "iwl-drv.h"
69#include "iwl-debug.h" 70#include "iwl-debug.h"
@@ -164,10 +165,8 @@ struct fw_sec {
164 165
165static void iwl_free_fw_desc(struct iwl_drv *drv, struct fw_desc *desc) 166static void iwl_free_fw_desc(struct iwl_drv *drv, struct fw_desc *desc)
166{ 167{
167 if (desc->v_addr) 168 vfree(desc->data);
168 dma_free_coherent(drv->trans->dev, desc->len, 169 desc->data = NULL;
169 desc->v_addr, desc->p_addr);
170 desc->v_addr = NULL;
171 desc->len = 0; 170 desc->len = 0;
172} 171}
173 172
@@ -186,21 +185,24 @@ static void iwl_dealloc_ucode(struct iwl_drv *drv)
186} 185}
187 186
188static int iwl_alloc_fw_desc(struct iwl_drv *drv, struct fw_desc *desc, 187static int iwl_alloc_fw_desc(struct iwl_drv *drv, struct fw_desc *desc,
189 struct fw_sec *sec) 188 struct fw_sec *sec)
190{ 189{
191 if (!sec || !sec->size) { 190 void *data;
192 desc->v_addr = NULL; 191
192 desc->data = NULL;
193
194 if (!sec || !sec->size)
193 return -EINVAL; 195 return -EINVAL;
194 }
195 196
196 desc->v_addr = dma_alloc_coherent(drv->trans->dev, sec->size, 197 data = vmalloc(sec->size);
197 &desc->p_addr, GFP_KERNEL); 198 if (!data)
198 if (!desc->v_addr)
199 return -ENOMEM; 199 return -ENOMEM;
200 200
201 desc->len = sec->size; 201 desc->len = sec->size;
202 desc->offset = sec->offset; 202 desc->offset = sec->offset;
203 memcpy(desc->v_addr, sec->data, sec->size); 203 memcpy(data, sec->data, desc->len);
204 desc->data = data;
205
204 return 0; 206 return 0;
205} 207}
206 208
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h
index 2153e4cc5572..d1a86b66bc51 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fw.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fw.h
@@ -124,8 +124,7 @@ struct iwl_ucode_capabilities {
124 124
125/* one for each uCode image (inst/data, init/runtime/wowlan) */ 125/* one for each uCode image (inst/data, init/runtime/wowlan) */
126struct fw_desc { 126struct fw_desc {
127 dma_addr_t p_addr; /* hardware address */ 127 const void *data; /* vmalloc'ed data */
128 void *v_addr; /* software address */
129 u32 len; /* size in bytes */ 128 u32 len; /* size in bytes */
130 u32 offset; /* offset in the device */ 129 u32 offset; /* offset in the device */
131}; 130};
diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c
index d44fa2a220f1..3cb4f858d4eb 100644
--- a/drivers/net/wireless/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/iwlwifi/pcie/trans.c
@@ -923,13 +923,10 @@ static int iwl_prepare_card_hw(struct iwl_trans *trans)
923/* 923/*
924 * ucode 924 * ucode
925 */ 925 */
926static int iwl_load_section(struct iwl_trans *trans, u8 section_num, 926static int iwl_load_firmware_chunk(struct iwl_trans *trans, u32 dst_addr,
927 const struct fw_desc *section) 927 dma_addr_t phy_addr, u32 byte_cnt)
928{ 928{
929 struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 929 struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
930 dma_addr_t phy_addr = section->p_addr;
931 u32 byte_cnt = section->len;
932 u32 dst_addr = section->offset;
933 int ret; 930 int ret;
934 931
935 trans_pcie->ucode_write_complete = false; 932 trans_pcie->ucode_write_complete = false;
@@ -943,8 +940,8 @@ static int iwl_load_section(struct iwl_trans *trans, u8 section_num,
943 dst_addr); 940 dst_addr);
944 941
945 iwl_write_direct32(trans, 942 iwl_write_direct32(trans,
946 FH_TFDIB_CTRL0_REG(FH_SRVC_CHNL), 943 FH_TFDIB_CTRL0_REG(FH_SRVC_CHNL),
947 phy_addr & FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK); 944 phy_addr & FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK);
948 945
949 iwl_write_direct32(trans, 946 iwl_write_direct32(trans,
950 FH_TFDIB_CTRL1_REG(FH_SRVC_CHNL), 947 FH_TFDIB_CTRL1_REG(FH_SRVC_CHNL),
@@ -963,26 +960,58 @@ static int iwl_load_section(struct iwl_trans *trans, u8 section_num,
963 FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE | 960 FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE |
964 FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD); 961 FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD);
965 962
966 IWL_DEBUG_FW(trans, "[%d] uCode section being loaded...\n",
967 section_num);
968 ret = wait_event_timeout(trans_pcie->ucode_write_waitq, 963 ret = wait_event_timeout(trans_pcie->ucode_write_waitq,
969 trans_pcie->ucode_write_complete, 5 * HZ); 964 trans_pcie->ucode_write_complete, 5 * HZ);
970 if (!ret) { 965 if (!ret) {
971 IWL_ERR(trans, "Could not load the [%d] uCode section\n", 966 IWL_ERR(trans, "Failed to load firmware chunk!\n");
972 section_num);
973 return -ETIMEDOUT; 967 return -ETIMEDOUT;
974 } 968 }
975 969
976 return 0; 970 return 0;
977} 971}
978 972
973static int iwl_load_section(struct iwl_trans *trans, u8 section_num,
974 const struct fw_desc *section)
975{
976 u8 *v_addr;
977 dma_addr_t p_addr;
978 u32 offset;
979 int ret = 0;
980
981 IWL_DEBUG_FW(trans, "[%d] uCode section being loaded...\n",
982 section_num);
983
984 v_addr = dma_alloc_coherent(trans->dev, PAGE_SIZE, &p_addr, GFP_KERNEL);
985 if (!v_addr)
986 return -ENOMEM;
987
988 for (offset = 0; offset < section->len; offset += PAGE_SIZE) {
989 u32 copy_size;
990
991 copy_size = min_t(u32, PAGE_SIZE, section->len - offset);
992
993 memcpy(v_addr, (u8 *)section->data + offset, copy_size);
994 ret = iwl_load_firmware_chunk(trans, section->offset + offset,
995 p_addr, copy_size);
996 if (ret) {
997 IWL_ERR(trans,
998 "Could not load the [%d] uCode section\n",
999 section_num);
1000 break;
1001 }
1002 }
1003
1004 dma_free_coherent(trans->dev, PAGE_SIZE, v_addr, p_addr);
1005 return ret;
1006}
1007
979static int iwl_load_given_ucode(struct iwl_trans *trans, 1008static int iwl_load_given_ucode(struct iwl_trans *trans,
980 const struct fw_img *image) 1009 const struct fw_img *image)
981{ 1010{
982 int i, ret = 0; 1011 int i, ret = 0;
983 1012
984 for (i = 0; i < IWL_UCODE_SECTION_MAX; i++) { 1013 for (i = 0; i < IWL_UCODE_SECTION_MAX; i++) {
985 if (!image->sec[i].p_addr) 1014 if (!image->sec[i].data)
986 break; 1015 break;
987 1016
988 ret = iwl_load_section(trans, i, &image->sec[i]); 1017 ret = iwl_load_section(trans, i, &image->sec[i]);