net: wireless: bcm4329: Fix watchdog syncronization during start/stop
authorDmitry Shmidt <dimitrysh@google.com>
Wed, 1 Dec 2010 22:22:52 +0000 (14:22 -0800)
committerDmitry Shmidt <dimitrysh@google.com>
Thu, 2 Dec 2010 23:46:54 +0000 (15:46 -0800)
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
drivers/net/wireless/bcm4329/dhd_linux.c
drivers/net/wireless/bcm4329/dhd_sdio.c

index 472b992751e766cc01402522f23784b89a7e9a56..b6bb7d11b79f96e2a6672a78ed0c1811850ba8d5 100644 (file)
@@ -1399,22 +1399,24 @@ dhd_watchdog_thread(void *data)
        /* Run until signal received */
        while (1) {
                if (down_interruptible (&dhd->watchdog_sem) == 0) {
-
+                       dhd_os_sdlock(&dhd->pub);
                        if (dhd->pub.dongle_reset == FALSE) {
+                               DHD_TIMER(("%s:\n", __FUNCTION__));
                                /* Call the bus module watchdog */
                                dhd_bus_watchdog(&dhd->pub);
-                       }
-                       /* Count the tick for reference */
-                       dhd->pub.tickcnt++;
 
-                       /* Reschedule the watchdog */
-                       if (dhd->wd_timer_valid)
-                               mod_timer(&dhd->timer, jiffies + dhd_watchdog_ms * HZ / 1000);
+                               /* Count the tick for reference */
+                               dhd->pub.tickcnt++;
 
+                               /* Reschedule the watchdog */
+                               if (dhd->wd_timer_valid)
+                                       mod_timer(&dhd->timer, jiffies + dhd_watchdog_ms * HZ / 1000);
+                       }
+                       dhd_os_sdunlock(&dhd->pub);
                        dhd_os_wake_unlock(&dhd->pub);
-               }
-               else
+               } else {
                        break;
+               }
        }
 
        complete_and_exit(&dhd->watchdog_exited, 0);
@@ -1426,11 +1428,17 @@ dhd_watchdog(ulong data)
        dhd_info_t *dhd = (dhd_info_t *)data;
 
        dhd_os_wake_lock(&dhd->pub);
+       if (dhd->pub.dongle_reset) {
+               dhd_os_wake_unlock(&dhd->pub);
+               return;
+       }
+
        if (dhd->watchdog_pid >= 0) {
                up(&dhd->watchdog_sem);
                return;
        }
 
+       dhd_os_sdlock(&dhd->pub);
        /* Call the bus module watchdog */
        dhd_bus_watchdog(&dhd->pub);
 
@@ -1440,6 +1448,7 @@ dhd_watchdog(ulong data)
        /* Reschedule the watchdog */
        if (dhd->wd_timer_valid)
                mod_timer(&dhd->timer, jiffies + dhd_watchdog_ms * HZ / 1000);
+       dhd_os_sdunlock(&dhd->pub);
        dhd_os_wake_unlock(&dhd->pub);
 }
 
@@ -2198,8 +2207,8 @@ dhd_bus_start(dhd_pub_t *dhdp)
 #if defined(OOB_INTR_ONLY)
        /* Host registration for OOB interrupt */
        if (bcmsdh_register_oob_intr(dhdp)) {
-               del_timer_sync(&dhd->timer);
                dhd->wd_timer_valid = FALSE;
+               del_timer_sync(&dhd->timer);
                DHD_ERROR(("%s Host failed to resgister for OOB\n", __FUNCTION__));
                return -ENODEV;
        }
@@ -2210,8 +2219,8 @@ dhd_bus_start(dhd_pub_t *dhdp)
 
        /* If bus is not ready, can't come up */
        if (dhd->pub.busstate != DHD_BUS_DATA) {
-               del_timer_sync(&dhd->timer);
                dhd->wd_timer_valid = FALSE;
+               del_timer_sync(&dhd->timer);
                DHD_ERROR(("%s failed bus is not ready\n", __FUNCTION__));
                return -ENODEV;
        }
@@ -2420,8 +2429,8 @@ dhd_bus_detach(dhd_pub_t *dhdp)
 #endif /* defined(OOB_INTR_ONLY) */
 
                        /* Clear the watchdog timer */
-                       del_timer_sync(&dhd->timer);
                        dhd->wd_timer_valid = FALSE;
+                       del_timer_sync(&dhd->timer);
                }
        }
 }
@@ -2687,29 +2696,28 @@ void
 dhd_os_wd_timer(void *bus, uint wdtick)
 {
        dhd_pub_t *pub = bus;
-       static uint save_dhd_watchdog_ms = 0;
        dhd_info_t *dhd = (dhd_info_t *)pub->info;
+       unsigned long flags;
+       int del_timer_flag = FALSE;
 
-       /* don't start the wd until fw is loaded */
-       if (pub->busstate == DHD_BUS_DOWN)
-               return;
+       flags = dhd_os_spin_lock(pub);
 
-       /* Totally stop the timer */
-       if (!wdtick && dhd->wd_timer_valid == TRUE) {
-               del_timer_sync(&dhd->timer);
-               dhd->wd_timer_valid = FALSE;
-               save_dhd_watchdog_ms = wdtick;
-               return;
+       /* don't start the wd until fw is loaded */
+       if (pub->busstate != DHD_BUS_DOWN) {
+               if (wdtick) {
+                       dhd_watchdog_ms = (uint)wdtick;
+                       dhd->wd_timer_valid = TRUE;
+                       /* Re arm the timer, at last watchdog period */
+                       mod_timer(&dhd->timer, jiffies + dhd_watchdog_ms * HZ / 1000);
+               } else if (dhd->wd_timer_valid == TRUE) {
+                       /* Totally stop the timer */
+                       dhd->wd_timer_valid = FALSE;
+                       del_timer_flag = TRUE;
+               }
        }
-
-       if (wdtick) {
-               dhd_watchdog_ms = (uint)wdtick;
-
-               /* Re arm the timer, at last watchdog period */
-               mod_timer(&dhd->timer, jiffies + dhd_watchdog_ms * HZ / 1000);
-
-               dhd->wd_timer_valid = TRUE;
-               save_dhd_watchdog_ms = wdtick;
+       dhd_os_spin_unlock(pub, flags);
+       if (del_timer_flag) {
+               del_timer_sync(&dhd->timer);
        }
 }
 
@@ -2927,20 +2935,12 @@ dhd_dev_reset(struct net_device *dev, uint8 flag)
 
        dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
 
-       /* Turning off watchdog */
-       if (flag)
-               dhd_os_wd_timer(&dhd->pub, 0);
-
        ret = dhd_bus_devreset(&dhd->pub, flag);
        if (ret) {
                DHD_ERROR(("%s: dhd_bus_devreset: %d\n", __FUNCTION__, ret));
                return ret;
        }
-
-       /* Turning on watchdog back */
-       if (!flag)
-               dhd_os_wd_timer(&dhd->pub, dhd_watchdog_ms);
-       DHD_ERROR(("%s: WLAN OFF DONE\n", __FUNCTION__));
+       DHD_ERROR(("%s: WLAN %s DONE\n", __FUNCTION__, flag ? "OFF" : "ON"));
 
        return ret;
 }
index 7494e38367329ee5e7b181d07dc8507d68b3b417..88b656a0f8bfb5d4c3e997d3167dcb5f2790347c 100644 (file)
@@ -705,6 +705,7 @@ dhdsdio_sdclk(dhd_bus_t *bus, bool on)
 static int
 dhdsdio_clkctl(dhd_bus_t *bus, uint target, bool pendok)
 {
+       int ret = BCME_OK;
 #ifdef DHD_DEBUG
        uint oldstate = bus->clkstate;
 #endif /* DHD_DEBUG */
@@ -717,7 +718,7 @@ dhdsdio_clkctl(dhd_bus_t *bus, uint target, bool pendok)
                        dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms);
                        bus->activity = TRUE;
                }
-               return BCME_OK;
+               return ret;
        }
 
        switch (target) {
@@ -726,29 +727,32 @@ dhdsdio_clkctl(dhd_bus_t *bus, uint target, bool pendok)
                if (bus->clkstate == CLK_NONE)
                        dhdsdio_sdclk(bus, TRUE);
                /* Now request HT Avail on the backplane */
-               dhdsdio_htclk(bus, TRUE, pendok);
-               dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms);
-               bus->activity = TRUE;
+               ret = dhdsdio_htclk(bus, TRUE, pendok);
+               if (ret == BCME_OK) {
+                       dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms);
+                       bus->activity = TRUE;
+               }
                break;
 
        case CLK_SDONLY:
                /* Remove HT request, or bring up SD clock */
                if (bus->clkstate == CLK_NONE)
-                       dhdsdio_sdclk(bus, TRUE);
+                       ret = dhdsdio_sdclk(bus, TRUE);
                else if (bus->clkstate == CLK_AVAIL)
-                       dhdsdio_htclk(bus, FALSE, FALSE);
+                       ret = dhdsdio_htclk(bus, FALSE, FALSE);
                else
                        DHD_ERROR(("dhdsdio_clkctl: request for %d -> %d\n",
                                   bus->clkstate, target));
-               dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms);
+               if (ret == BCME_OK)
+                       dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms);
                break;
 
        case CLK_NONE:
                /* Make sure to remove HT request */
                if (bus->clkstate == CLK_AVAIL)
-                       dhdsdio_htclk(bus, FALSE, FALSE);
+                       ret = dhdsdio_htclk(bus, FALSE, FALSE);
                /* Now remove the SD clock */
-               dhdsdio_sdclk(bus, FALSE);
+               ret = dhdsdio_sdclk(bus, FALSE);
                dhd_os_wd_timer(bus->dhd, 0);
                break;
        }
@@ -756,7 +760,7 @@ dhdsdio_clkctl(dhd_bus_t *bus, uint target, bool pendok)
        DHD_INFO(("dhdsdio_clkctl: %d -> %d\n", oldstate, bus->clkstate));
 #endif /* DHD_DEBUG */
 
-       return BCME_OK;
+       return ret;
 }
 
 int
@@ -4631,8 +4635,6 @@ dhd_bus_watchdog(dhd_pub_t *dhdp)
        if (bus->sleeping)
                return FALSE;
 
-       dhd_os_sdlock(bus->dhd);
-
        /* Poll period: check device if appropriate. */
        if (bus->poll && (++bus->polltick >= bus->pollrate)) {
                uint32 intstatus = 0;
@@ -4702,8 +4704,6 @@ dhd_bus_watchdog(dhd_pub_t *dhdp)
                }
        }
 
-       dhd_os_sdunlock(bus->dhd);
-
        return bus->ipend;
 }
 
@@ -5797,13 +5797,15 @@ dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag)
 
        if (flag == TRUE) {
                if (!bus->dhd->dongle_reset) {
+                       dhd_os_sdlock(dhdp);
+                       /* Turning off watchdog */
+                       dhd_os_wd_timer(dhdp, 0);
 #if !defined(IGNORE_ETH0_DOWN)
                        /* Force flow control as protection when stop come before ifconfig_down */
                        dhd_txflowcontrol(bus->dhd, 0, ON);
 #endif /* !defined(IGNORE_ETH0_DOWN) */
                        /* Expect app to have torn down any connection before calling */
                        /* Stop the bus, disable F2 */
-                       dhd_os_sdlock(dhdp);
                        dhd_bus_stop(bus, FALSE);
 
                        /* Clean tx/rx buffer pointers, detach from the dongle */
@@ -5850,7 +5852,9 @@ dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag)
 #if !defined(IGNORE_ETH0_DOWN)
                                        /* Restore flow control  */
                                        dhd_txflowcontrol(bus->dhd, 0, OFF);
-#endif 
+#endif
+                                       /* Turning on watchdog back */
+                                       dhd_os_wd_timer(dhdp, dhd_watchdog_ms);
 
                                        DHD_TRACE(("%s: WLAN ON DONE\n", __FUNCTION__));
                                } else