Skip to content

Commit

Permalink
iwlwifi: load firmware in chunks
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Johannes Berg committed Sep 10, 2012
1 parent 7439046 commit 83f84d7
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 28 deletions.
4 changes: 2 additions & 2 deletions drivers/net/wireless/iwlwifi/dvm/ucode.c
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ static int iwl_alive_notify(struct iwl_priv *priv)
static int iwl_verify_sec_sparse(struct iwl_priv *priv,
const struct fw_desc *fw_desc)
{
__le32 *image = (__le32 *)fw_desc->v_addr;
__le32 *image = (__le32 *)fw_desc->data;
u32 len = fw_desc->len;
u32 val;
u32 i;
Expand All @@ -319,7 +319,7 @@ static int iwl_verify_sec_sparse(struct iwl_priv *priv,
static void iwl_print_mismatch_sec(struct iwl_priv *priv,
const struct fw_desc *fw_desc)
{
__le32 *image = (__le32 *)fw_desc->v_addr;
__le32 *image = (__le32 *)fw_desc->data;
u32 len = fw_desc->len;
u32 val;
u32 offs;
Expand Down
26 changes: 14 additions & 12 deletions drivers/net/wireless/iwlwifi/iwl-drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
#include <linux/dma-mapping.h>
#include <linux/firmware.h>
#include <linux/module.h>
#include <linux/vmalloc.h>

#include "iwl-drv.h"
#include "iwl-debug.h"
Expand Down Expand Up @@ -164,10 +165,8 @@ struct fw_sec {

static void iwl_free_fw_desc(struct iwl_drv *drv, struct fw_desc *desc)
{
if (desc->v_addr)
dma_free_coherent(drv->trans->dev, desc->len,
desc->v_addr, desc->p_addr);
desc->v_addr = NULL;
vfree(desc->data);
desc->data = NULL;
desc->len = 0;
}

Expand All @@ -186,21 +185,24 @@ static void iwl_dealloc_ucode(struct iwl_drv *drv)
}

static int iwl_alloc_fw_desc(struct iwl_drv *drv, struct fw_desc *desc,
struct fw_sec *sec)
struct fw_sec *sec)
{
if (!sec || !sec->size) {
desc->v_addr = NULL;
void *data;

desc->data = NULL;

if (!sec || !sec->size)
return -EINVAL;
}

desc->v_addr = dma_alloc_coherent(drv->trans->dev, sec->size,
&desc->p_addr, GFP_KERNEL);
if (!desc->v_addr)
data = vmalloc(sec->size);
if (!data)
return -ENOMEM;

desc->len = sec->size;
desc->offset = sec->offset;
memcpy(desc->v_addr, sec->data, sec->size);
memcpy(data, sec->data, desc->len);
desc->data = data;

return 0;
}

Expand Down
3 changes: 1 addition & 2 deletions drivers/net/wireless/iwlwifi/iwl-fw.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,7 @@ struct iwl_ucode_capabilities {

/* one for each uCode image (inst/data, init/runtime/wowlan) */
struct fw_desc {
dma_addr_t p_addr; /* hardware address */
void *v_addr; /* software address */
const void *data; /* vmalloc'ed data */
u32 len; /* size in bytes */
u32 offset; /* offset in the device */
};
Expand Down
53 changes: 41 additions & 12 deletions drivers/net/wireless/iwlwifi/pcie/trans.c
Original file line number Diff line number Diff line change
Expand Up @@ -923,13 +923,10 @@ static int iwl_prepare_card_hw(struct iwl_trans *trans)
/*
* ucode
*/
static int iwl_load_section(struct iwl_trans *trans, u8 section_num,
const struct fw_desc *section)
static int iwl_load_firmware_chunk(struct iwl_trans *trans, u32 dst_addr,
dma_addr_t phy_addr, u32 byte_cnt)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
dma_addr_t phy_addr = section->p_addr;
u32 byte_cnt = section->len;
u32 dst_addr = section->offset;
int ret;

trans_pcie->ucode_write_complete = false;
Expand All @@ -943,8 +940,8 @@ static int iwl_load_section(struct iwl_trans *trans, u8 section_num,
dst_addr);

iwl_write_direct32(trans,
FH_TFDIB_CTRL0_REG(FH_SRVC_CHNL),
phy_addr & FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK);
FH_TFDIB_CTRL0_REG(FH_SRVC_CHNL),
phy_addr & FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK);

iwl_write_direct32(trans,
FH_TFDIB_CTRL1_REG(FH_SRVC_CHNL),
Expand All @@ -963,26 +960,58 @@ static int iwl_load_section(struct iwl_trans *trans, u8 section_num,
FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE |
FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD);

IWL_DEBUG_FW(trans, "[%d] uCode section being loaded...\n",
section_num);
ret = wait_event_timeout(trans_pcie->ucode_write_waitq,
trans_pcie->ucode_write_complete, 5 * HZ);
if (!ret) {
IWL_ERR(trans, "Could not load the [%d] uCode section\n",
section_num);
IWL_ERR(trans, "Failed to load firmware chunk!\n");
return -ETIMEDOUT;
}

return 0;
}

static int iwl_load_section(struct iwl_trans *trans, u8 section_num,
const struct fw_desc *section)
{
u8 *v_addr;
dma_addr_t p_addr;
u32 offset;
int ret = 0;

IWL_DEBUG_FW(trans, "[%d] uCode section being loaded...\n",
section_num);

v_addr = dma_alloc_coherent(trans->dev, PAGE_SIZE, &p_addr, GFP_KERNEL);
if (!v_addr)
return -ENOMEM;

for (offset = 0; offset < section->len; offset += PAGE_SIZE) {
u32 copy_size;

copy_size = min_t(u32, PAGE_SIZE, section->len - offset);

memcpy(v_addr, (u8 *)section->data + offset, copy_size);
ret = iwl_load_firmware_chunk(trans, section->offset + offset,
p_addr, copy_size);
if (ret) {
IWL_ERR(trans,
"Could not load the [%d] uCode section\n",
section_num);
break;
}
}

dma_free_coherent(trans->dev, PAGE_SIZE, v_addr, p_addr);
return ret;
}

static int iwl_load_given_ucode(struct iwl_trans *trans,
const struct fw_img *image)
{
int i, ret = 0;

for (i = 0; i < IWL_UCODE_SECTION_MAX; i++) {
if (!image->sec[i].p_addr)
if (!image->sec[i].data)
break;

ret = iwl_load_section(trans, i, &image->sec[i]);
Expand Down

0 comments on commit 83f84d7

Please sign in to comment.