[PATCH] ipw2200: Scale firmware loading watchdog with the firmware size.

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 <[EMAIL PROTECTED]>
Signed-off-by: Ben M Cahill <[EMAIL PROTECTED]>
Signed-off-by: Zhu Yi <[EMAIL PROTECTED]>
---

--- a/drivers/net/wireless/ipw2200.c
+++ b/drivers/net/wireless/ipw2200.c
@@ -2770,22 +2770,25 @@ static int ipw_fw_dma_add_buffer(struct 
 
 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);
@@ -3274,55 +3277,31 @@ static int ipw_load(struct ipw_priv *pri
        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);
@@ -3344,7 +3323,7 @@ static int ipw_load(struct ipw_priv *pri
        ipw_stop_nic(priv);
 
        rc = ipw_reset_nic(priv);
-       if (rc) {
+       if (rc < 0) {
                IPW_ERROR("Unable to reset NIC\n");
                goto error;
        }
@@ -3352,6 +3331,15 @@ static int ipw_load(struct ipw_priv *pri
        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));
@@ -3375,6 +3363,16 @@ static int ipw_load(struct ipw_priv *pri
        /* 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));
@@ -3386,6 +3384,16 @@ static int ipw_load(struct ipw_priv *pri
        /* 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),
@@ -3395,10 +3403,14 @@ static int ipw_load(struct ipw_priv *pri
                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;
        }
-
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to