mirror of https://gitee.com/openkylin/linux.git
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>
This commit is contained in:
parent
7439046d97
commit
83f84d7bd4
|
@ -295,7 +295,7 @@ static int iwl_alive_notify(struct iwl_priv *priv)
|
||||||
static int iwl_verify_sec_sparse(struct iwl_priv *priv,
|
static int iwl_verify_sec_sparse(struct iwl_priv *priv,
|
||||||
const struct fw_desc *fw_desc)
|
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 len = fw_desc->len;
|
||||||
u32 val;
|
u32 val;
|
||||||
u32 i;
|
u32 i;
|
||||||
|
@ -319,7 +319,7 @@ static int iwl_verify_sec_sparse(struct iwl_priv *priv,
|
||||||
static void iwl_print_mismatch_sec(struct iwl_priv *priv,
|
static void iwl_print_mismatch_sec(struct iwl_priv *priv,
|
||||||
const struct fw_desc *fw_desc)
|
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 len = fw_desc->len;
|
||||||
u32 val;
|
u32 val;
|
||||||
u32 offs;
|
u32 offs;
|
||||||
|
|
|
@ -64,6 +64,7 @@
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
#include <linux/firmware.h>
|
#include <linux/firmware.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/vmalloc.h>
|
||||||
|
|
||||||
#include "iwl-drv.h"
|
#include "iwl-drv.h"
|
||||||
#include "iwl-debug.h"
|
#include "iwl-debug.h"
|
||||||
|
@ -164,10 +165,8 @@ struct fw_sec {
|
||||||
|
|
||||||
static void iwl_free_fw_desc(struct iwl_drv *drv, struct fw_desc *desc)
|
static void iwl_free_fw_desc(struct iwl_drv *drv, struct fw_desc *desc)
|
||||||
{
|
{
|
||||||
if (desc->v_addr)
|
vfree(desc->data);
|
||||||
dma_free_coherent(drv->trans->dev, desc->len,
|
desc->data = NULL;
|
||||||
desc->v_addr, desc->p_addr);
|
|
||||||
desc->v_addr = NULL;
|
|
||||||
desc->len = 0;
|
desc->len = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
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) {
|
void *data;
|
||||||
desc->v_addr = NULL;
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
desc->v_addr = dma_alloc_coherent(drv->trans->dev, sec->size,
|
desc->data = NULL;
|
||||||
&desc->p_addr, GFP_KERNEL);
|
|
||||||
if (!desc->v_addr)
|
if (!sec || !sec->size)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
data = vmalloc(sec->size);
|
||||||
|
if (!data)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
desc->len = sec->size;
|
desc->len = sec->size;
|
||||||
desc->offset = sec->offset;
|
desc->offset = sec->offset;
|
||||||
memcpy(desc->v_addr, sec->data, sec->size);
|
memcpy(data, sec->data, desc->len);
|
||||||
|
desc->data = data;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -124,8 +124,7 @@ struct iwl_ucode_capabilities {
|
||||||
|
|
||||||
/* one for each uCode image (inst/data, init/runtime/wowlan) */
|
/* one for each uCode image (inst/data, init/runtime/wowlan) */
|
||||||
struct fw_desc {
|
struct fw_desc {
|
||||||
dma_addr_t p_addr; /* hardware address */
|
const void *data; /* vmalloc'ed data */
|
||||||
void *v_addr; /* software address */
|
|
||||||
u32 len; /* size in bytes */
|
u32 len; /* size in bytes */
|
||||||
u32 offset; /* offset in the device */
|
u32 offset; /* offset in the device */
|
||||||
};
|
};
|
||||||
|
|
|
@ -923,13 +923,10 @@ static int iwl_prepare_card_hw(struct iwl_trans *trans)
|
||||||
/*
|
/*
|
||||||
* ucode
|
* ucode
|
||||||
*/
|
*/
|
||||||
static int iwl_load_section(struct iwl_trans *trans, u8 section_num,
|
static int iwl_load_firmware_chunk(struct iwl_trans *trans, u32 dst_addr,
|
||||||
const struct fw_desc *section)
|
dma_addr_t phy_addr, u32 byte_cnt)
|
||||||
{
|
{
|
||||||
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
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;
|
int ret;
|
||||||
|
|
||||||
trans_pcie->ucode_write_complete = false;
|
trans_pcie->ucode_write_complete = false;
|
||||||
|
@ -943,8 +940,8 @@ static int iwl_load_section(struct iwl_trans *trans, u8 section_num,
|
||||||
dst_addr);
|
dst_addr);
|
||||||
|
|
||||||
iwl_write_direct32(trans,
|
iwl_write_direct32(trans,
|
||||||
FH_TFDIB_CTRL0_REG(FH_SRVC_CHNL),
|
FH_TFDIB_CTRL0_REG(FH_SRVC_CHNL),
|
||||||
phy_addr & FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK);
|
phy_addr & FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK);
|
||||||
|
|
||||||
iwl_write_direct32(trans,
|
iwl_write_direct32(trans,
|
||||||
FH_TFDIB_CTRL1_REG(FH_SRVC_CHNL),
|
FH_TFDIB_CTRL1_REG(FH_SRVC_CHNL),
|
||||||
|
@ -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_DMA_CREDIT_DISABLE |
|
||||||
FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD);
|
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,
|
ret = wait_event_timeout(trans_pcie->ucode_write_waitq,
|
||||||
trans_pcie->ucode_write_complete, 5 * HZ);
|
trans_pcie->ucode_write_complete, 5 * HZ);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
IWL_ERR(trans, "Could not load the [%d] uCode section\n",
|
IWL_ERR(trans, "Failed to load firmware chunk!\n");
|
||||||
section_num);
|
|
||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
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,
|
static int iwl_load_given_ucode(struct iwl_trans *trans,
|
||||||
const struct fw_img *image)
|
const struct fw_img *image)
|
||||||
{
|
{
|
||||||
int i, ret = 0;
|
int i, ret = 0;
|
||||||
|
|
||||||
for (i = 0; i < IWL_UCODE_SECTION_MAX; i++) {
|
for (i = 0; i < IWL_UCODE_SECTION_MAX; i++) {
|
||||||
if (!image->sec[i].p_addr)
|
if (!image->sec[i].data)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
ret = iwl_load_section(trans, i, &image->sec[i]);
|
ret = iwl_load_section(trans, i, &image->sec[i]);
|
||||||
|
|
Loading…
Reference in New Issue