Skip to content

Commit

Permalink
[PATCH] ipw2200: Scale firmware loading watchdog with the firmware size
Browse files Browse the repository at this point in the history
I can't really help with why restarts happen, but the following patch
greatly increases the likelihood that a firmware reload will succeed
afterward on my thinkpad. It addresses two issues. First, sysfs module
loading and hotplug are asynchronous, and as such file operations on the
"loading" and "data" files are racy when you load 2 firmwares in quick
succession. Second, the timeout for DMAing the firmware needs to scale
with the size of the firmware being loaded. That is, the watchdog needs
to be on throughput, not on time alone.

I no longer get the firmware load errors, though this is at best a hacky
workaround for a racy interface. (Obviously, this does nothing to address
the fatal errors in firmware which cause reloads; it just causes the
initial loading and the reloads to work more often.)

Signed-off-by: Peter Jones <pjones@redhat.com>
Signed-off-by: Ben M Cahill <ben.m.cahill@intel.com>
Signed-off-by: Zhu Yi <yi.zhu@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Zhu Yi authored and John W. Linville committed Jan 31, 2006
1 parent 4bfdb91 commit 397ae12
Showing 1 changed file with 63 additions and 51 deletions.
114 changes: 63 additions & 51 deletions drivers/net/wireless/ipw2200.c
Original file line number Diff line number Diff line change
Expand Up @@ -2772,22 +2772,25 @@ static int ipw_fw_dma_add_buffer(struct ipw_priv *priv,

static int ipw_fw_dma_wait(struct ipw_priv *priv)
{
u32 current_index = 0;
u32 current_index = 0, previous_index;
u32 watchdog = 0;

IPW_DEBUG_FW(">> : \n");

current_index = ipw_fw_dma_command_block_index(priv);
IPW_DEBUG_FW_INFO("sram_desc.last_cb_index:0x%8X\n",
IPW_DEBUG_FW_INFO("sram_desc.last_cb_index:0x%08X\n",
(int)priv->sram_desc.last_cb_index);

while (current_index < priv->sram_desc.last_cb_index) {
udelay(50);
previous_index = current_index;
current_index = ipw_fw_dma_command_block_index(priv);

watchdog++;

if (watchdog > 400) {
if (previous_index < current_index) {
watchdog = 0;
continue;
}
if (++watchdog > 400) {
IPW_DEBUG_FW_INFO("Timeout\n");
ipw_fw_dma_dump_command_block(priv);
ipw_fw_dma_abort(priv);
Expand Down Expand Up @@ -3276,55 +3279,31 @@ static int ipw_load(struct ipw_priv *priv)
const struct firmware *firmware = NULL;
const struct firmware *ucode = NULL;
#endif
char *ucode_name;
char *fw_name;
int rc = 0, retries = 3;

#ifdef CONFIG_PM
if (!fw_loaded) {
#endif
rc = ipw_get_fw(priv, &bootfw, IPW_FW_NAME("boot"));
if (rc)
goto error;

switch (priv->ieee->iw_mode) {
case IW_MODE_ADHOC:
rc = ipw_get_fw(priv, &ucode,
IPW_FW_NAME("ibss_ucode"));
if (rc)
goto error;

rc = ipw_get_fw(priv, &firmware, IPW_FW_NAME("ibss"));
break;

switch (priv->ieee->iw_mode) {
case IW_MODE_ADHOC:
ucode_name = IPW_FW_NAME("ibss_ucode");
fw_name = IPW_FW_NAME("ibss");
break;
#ifdef CONFIG_IPW2200_MONITOR
case IW_MODE_MONITOR:
rc = ipw_get_fw(priv, &ucode,
IPW_FW_NAME("sniffer_ucode"));
if (rc)
goto error;

rc = ipw_get_fw(priv, &firmware,
IPW_FW_NAME("sniffer"));
break;
case IW_MODE_MONITOR:
ucode_name = IPW_FW_NAME("sniffer_ucode");
fw_name = IPW_FW_NAME("sniffer");
break;
#endif
case IW_MODE_INFRA:
rc = ipw_get_fw(priv, &ucode, IPW_FW_NAME("bss_ucode"));
if (rc)
goto error;

rc = ipw_get_fw(priv, &firmware, IPW_FW_NAME("bss"));
break;

default:
rc = -EINVAL;
}

if (rc)
goto error;

#ifdef CONFIG_PM
fw_loaded = 1;
case IW_MODE_INFRA:
ucode_name = IPW_FW_NAME("bss_ucode");
fw_name = IPW_FW_NAME("bss");
break;
default:
rc = -EINVAL;
}
#endif

if (rc < 0)
goto error;

if (!priv->rxq)
priv->rxq = ipw_rx_queue_alloc(priv);
Expand All @@ -3346,14 +3325,23 @@ static int ipw_load(struct ipw_priv *priv)
ipw_stop_nic(priv);

rc = ipw_reset_nic(priv);
if (rc) {
if (rc < 0) {
IPW_ERROR("Unable to reset NIC\n");
goto error;
}

ipw_zero_memory(priv, IPW_NIC_SRAM_LOWER_BOUND,
IPW_NIC_SRAM_UPPER_BOUND - IPW_NIC_SRAM_LOWER_BOUND);

#ifdef CONFIG_PM
if (!fw_loaded) {
#endif
rc = ipw_get_fw(priv, &bootfw, IPW_FW_NAME("boot"));
if (rc < 0)
goto error;
#ifdef CONFIG_PM
}
#endif
/* DMA the initial boot firmware into the device */
rc = ipw_load_firmware(priv, bootfw->data + sizeof(struct fw_header),
bootfw->size - sizeof(struct fw_header));
Expand All @@ -3377,6 +3365,16 @@ static int ipw_load(struct ipw_priv *priv)
/* ack fw init done interrupt */
ipw_write32(priv, IPW_INTA_RW, IPW_INTA_BIT_FW_INITIALIZATION_DONE);

#ifdef CONFIG_PM
if (!fw_loaded) {
#endif
rc = ipw_get_fw(priv, &ucode, ucode_name);
if (rc < 0)
goto error;
#ifdef CONFIG_PM
}
#endif

/* DMA the ucode into the device */
rc = ipw_load_ucode(priv, ucode->data + sizeof(struct fw_header),
ucode->size - sizeof(struct fw_header));
Expand All @@ -3388,6 +3386,16 @@ static int ipw_load(struct ipw_priv *priv)
/* stop nic */
ipw_stop_nic(priv);

#ifdef CONFIG_PM
if (!fw_loaded) {
#endif
rc = ipw_get_fw(priv, &firmware, fw_name);
if (rc < 0)
goto error;
#ifdef CONFIG_PM
}
#endif

/* DMA bss firmware into the device */
rc = ipw_load_firmware(priv, firmware->data +
sizeof(struct fw_header),
Expand All @@ -3397,10 +3405,14 @@ static int ipw_load(struct ipw_priv *priv)
goto error;
}

#ifdef CONFIG_PM
fw_loaded = 1;
#endif

ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 0);

rc = ipw_queue_reset(priv);
if (rc) {
if (rc < 0) {
IPW_ERROR("Unable to initialize queues\n");
goto error;
}
Expand Down

0 comments on commit 397ae12

Please sign in to comment.