staging: brcm80211: move fullmac watchdog timer code to dhd_sdio.c
The watchdog timer is used in bus interface layer in fullmac. Move
related code to dhd_sdio.c for clean up.
Signed-off-by: Franky Lin <frankyl@broadcom.com>
Reviewed-by: Roland Vossen <rvossen@broadcom.com>
Reviewed-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
diff --git a/drivers/staging/brcm80211/brcmfmac/dhd_sdio.c b/drivers/staging/brcm80211/brcmfmac/dhd_sdio.c
index ec2c744..aeef2dc 100644
--- a/drivers/staging/brcm80211/brcmfmac/dhd_sdio.c
+++ b/drivers/staging/brcm80211/brcmfmac/dhd_sdio.c
@@ -16,6 +16,7 @@
#include <linux/types.h>
#include <linux/kernel.h>
+#include <linux/kthread.h>
#include <linux/printk.h>
#include <linux/pci_ids.h>
#include <linux/netdevice.h>
@@ -587,6 +588,11 @@
spinlock_t txqlock;
wait_queue_head_t ctrl_wait;
+
+ struct timer_list timer;
+ struct completion watchdog_wait;
+ struct task_struct *watchdog_tsk;
+ bool wd_timer_valid;
} dhd_bus_t;
typedef volatile struct _sbconfig {
@@ -645,6 +651,14 @@
/* Deferred transmit */
const uint brcmf_deferred_tx = 1;
+/* Watchdog thread priority, -1 to use kernel timer */
+int brcmf_watchdog_prio = 97;
+module_param(brcmf_watchdog_prio, int, 0);
+
+/* Watchdog interval */
+uint brcmf_watchdog_ms = 10;
+module_param(brcmf_watchdog_ms, uint, 0);
+
/* Tx/Rx bounds */
uint brcmf_txbound;
uint brcmf_rxbound;
@@ -780,6 +794,8 @@
static void brcmf_sdbrcm_chip_detach(struct dhd_bus *bus);
static void brcmf_sdbrcm_wait_for_event(dhd_pub_t *dhd, bool *lockvar);
static void brcmf_sdbrcm_wait_event_wakeup(dhd_bus_t *bus);
+static void brcmf_sdbrcm_watchdog(unsigned long data);
+static int brcmf_sdbrcm_watchdog_thread(void *data);
/* Packet free applicable unconditionally for sdio and sdspi.
* Conditional if bufpool was present for gspi bus.
@@ -975,7 +991,7 @@
/* Early exit if we're already there */
if (bus->clkstate == target) {
if (target == CLK_AVAIL) {
- brcmf_os_wd_timer(bus->dhd, brcmf_watchdog_ms);
+ brcmf_sdbrcm_wd_timer(bus, brcmf_watchdog_ms);
bus->activity = true;
}
return 0;
@@ -988,7 +1004,7 @@
brcmf_sdbrcm_sdclk(bus, true);
/* Now request HT Avail on the backplane */
brcmf_sdbrcm_htclk(bus, true, pendok);
- brcmf_os_wd_timer(bus->dhd, brcmf_watchdog_ms);
+ brcmf_sdbrcm_wd_timer(bus, brcmf_watchdog_ms);
bus->activity = true;
break;
@@ -1001,7 +1017,7 @@
else
DHD_ERROR(("brcmf_sdbrcm_clkctl: request for %d -> %d"
"\n", bus->clkstate, target));
- brcmf_os_wd_timer(bus->dhd, brcmf_watchdog_ms);
+ brcmf_sdbrcm_wd_timer(bus, brcmf_watchdog_ms);
break;
case CLK_NONE:
@@ -1010,7 +1026,7 @@
brcmf_sdbrcm_htclk(bus, false, false);
/* Now remove the SD clock */
brcmf_sdbrcm_sdclk(bus, false);
- brcmf_os_wd_timer(bus->dhd, 0);
+ brcmf_sdbrcm_wd_timer(bus, 0);
break;
}
#ifdef BCMDBG
@@ -1671,6 +1687,7 @@
IOV_IDLECLOCK,
IOV_SD1IDLE,
IOV_SLEEP,
+ IOV_WDTICK,
IOV_VARS
};
@@ -1691,6 +1708,7 @@
{"alignctl", IOV_ALIGNCTL, 0, IOVT_BOOL, 0},
{"sdalign", IOV_SDALIGN, 0, IOVT_BOOL, 0},
{"devreset", IOV_DEVRESET, 0, IOVT_BOOL, 0},
+ {"wdtick", IOV_WDTICK, 0, IOVT_UINT32, 0},
#ifdef BCMDBG
{"sdreg", IOV_SDREG, 0, IOVT_BUFFER, sizeof(struct brcmf_sdreg)}
,
@@ -2703,6 +2721,19 @@
break;
+ case IOV_GVAL(IOV_WDTICK):
+ int_val = (s32) brcmf_watchdog_ms;
+ memcpy(arg, &int_val, val_size);
+ break;
+
+ case IOV_SVAL(IOV_WDTICK):
+ if (!bus->dhd->up) {
+ bcmerror = -ENOLINK;
+ break;
+ }
+ brcmf_sdbrcm_wd_timer(bus, (uint) int_val);
+ break;
+
default:
bcmerror = -ENOTSUPP;
break;
@@ -2967,6 +2998,12 @@
/* Enable clock for device interrupts */
brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
+ if (bus->watchdog_tsk) {
+ send_sig(SIGTERM, bus->watchdog_tsk, 1);
+ kthread_stop(bus->watchdog_tsk);
+ bus->watchdog_tsk = NULL;
+ }
+
/* Disable and clear interrupts at the chip level also */
W_SDREG(0, &bus->regs->hostintmask, retries);
local_hostintmask = bus->hostintmask;
@@ -3022,6 +3059,10 @@
if (enforce_mutex)
brcmf_os_sdunlock(bus->dhd);
+
+#if defined(OOB_INTR_ONLY)
+ brcmf_sdio_unregister_oob_intr();
+#endif /* defined(OOB_INTR_ONLY) */
}
int brcmf_sdbrcm_bus_init(dhd_pub_t *dhdp, bool enforce_mutex)
@@ -3039,6 +3080,10 @@
if (!bus->dhd)
return 0;
+ /* Start the watchdog timer */
+ bus->dhd->tickcnt = 0;
+ brcmf_sdbrcm_wd_timer(bus, brcmf_watchdog_ms);
+
if (enforce_mutex)
brcmf_os_sdlock(bus->dhd);
@@ -3121,6 +3166,19 @@
brcmf_sdcard_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
saveclk, &err);
+#if defined(OOB_INTR_ONLY)
+ /* Host registration for OOB interrupt */
+ if (brcmf_sdio_register_oob_intr(bus->dhd)) {
+ brcmf_sdbrcm_wd_timer(bus, 0);
+ DHD_ERROR(("%s Host failed to resgister for OOB\n", __func__));
+ ret = -ENODEV;
+ goto exit;
+ }
+
+ /* Enable oob at firmware */
+ brcmf_sdbrcm_enable_oob_intr(bus, true);
+#endif /* defined(OOB_INTR_ONLY) */
+
/* If we didn't come up, turn off backplane clock */
if (dhdp->busstate != DHD_BUS_DATA)
brcmf_sdbrcm_clkctl(bus, CLK_NONE, false);
@@ -5029,7 +5087,7 @@
bus->idlecount = 0;
if (bus->activity) {
bus->activity = false;
- brcmf_os_wd_timer(bus->dhd, brcmf_watchdog_ms);
+ brcmf_sdbrcm_wd_timer(bus, brcmf_watchdog_ms);
} else {
brcmf_sdbrcm_clkctl(bus, CLK_NONE, false);
}
@@ -5218,6 +5276,24 @@
spin_lock_init(&bus->txqlock);
init_waitqueue_head(&bus->ctrl_wait);
+ /* Set up the watchdog timer */
+ init_timer(&bus->timer);
+ bus->timer.data = (unsigned long)bus;
+ bus->timer.function = brcmf_sdbrcm_watchdog;
+
+ if (brcmf_dpc_prio >= 0) {
+ /* Initialize watchdog thread */
+ init_completion(&bus->watchdog_wait);
+ bus->watchdog_tsk = kthread_run(brcmf_sdbrcm_watchdog_thread,
+ bus, "brcmf_watchdog");
+ if (IS_ERR(bus->watchdog_tsk)) {
+ printk(KERN_WARNING
+ "brcmf_watchdog thread failed to start\n");
+ bus->watchdog_tsk = NULL;
+ }
+ } else
+ bus->watchdog_tsk = NULL;
+
/* Attach to the dhd/OS/network interface */
bus->dhd = brcmf_attach(bus, SDPCM_RESERVE);
if (!bus->dhd) {
@@ -5868,6 +5944,7 @@
bus = dhdp->bus;
if (flag == true) {
+ brcmf_sdbrcm_wd_timer(bus, 0);
if (!bus->dhd->dongle_reset) {
/* Expect app to have torn down any
connection before calling */
@@ -5924,6 +6001,7 @@
"is on\n", __func__));
bcmerror = -EIO;
}
+ brcmf_sdbrcm_wd_timer(bus, brcmf_watchdog_ms);
}
return bcmerror;
}
@@ -6339,3 +6417,99 @@
wake_up_interruptible(&bus->ctrl_wait);
return;
}
+
+static int
+brcmf_sdbrcm_watchdog_thread(void *data)
+{
+ dhd_bus_t *bus = (dhd_bus_t *)data;
+
+ /* This thread doesn't need any user-level access,
+ * so get rid of all our resources
+ */
+ if (brcmf_watchdog_prio > 0) {
+ struct sched_param param;
+ param.sched_priority = (brcmf_watchdog_prio < MAX_RT_PRIO) ?
+ brcmf_watchdog_prio : (MAX_RT_PRIO - 1);
+ sched_setscheduler(current, SCHED_FIFO, ¶m);
+ }
+
+ allow_signal(SIGTERM);
+ /* Run until signal received */
+ while (1) {
+ if (kthread_should_stop())
+ break;
+ if (!wait_for_completion_interruptible(&bus->watchdog_wait)) {
+ if (bus->dhd->dongle_reset == false)
+ brcmf_sdbrcm_bus_watchdog(bus->dhd);
+ /* Count the tick for reference */
+ bus->dhd->tickcnt++;
+ } else
+ break;
+ }
+ return 0;
+}
+
+static void
+brcmf_sdbrcm_watchdog(unsigned long data)
+{
+ dhd_bus_t *bus = (dhd_bus_t *)data;
+
+ if (brcmf_watchdog_prio >= 0) {
+ if (bus->watchdog_tsk)
+ complete(&bus->watchdog_wait);
+ else
+ return;
+ } else {
+ brcmf_sdbrcm_bus_watchdog(bus->dhd);
+
+ /* Count the tick for reference */
+ bus->dhd->tickcnt++;
+ }
+
+ /* Reschedule the watchdog */
+ if (bus->wd_timer_valid)
+ mod_timer(&bus->timer, jiffies + brcmf_watchdog_ms * HZ / 1000);
+}
+
+void
+brcmf_sdbrcm_wd_timer(struct dhd_bus *bus, uint wdtick)
+{
+ static uint save_ms;
+
+ /* don't start the wd until fw is loaded */
+ if (bus->dhd->busstate == DHD_BUS_DOWN)
+ return;
+
+ /* Totally stop the timer */
+ if (!wdtick && bus->wd_timer_valid == true) {
+ del_timer_sync(&bus->timer);
+ bus->wd_timer_valid = false;
+ save_ms = wdtick;
+ return;
+ }
+
+ if (wdtick) {
+ brcmf_watchdog_ms = (uint) wdtick;
+
+ if (save_ms != brcmf_watchdog_ms) {
+ if (bus->wd_timer_valid == true)
+ /* Stop timer and restart at new value */
+ del_timer_sync(&bus->timer);
+
+ /* Create timer again when watchdog period is
+ dynamically changed or in the first instance
+ */
+ bus->timer.expires =
+ jiffies + brcmf_watchdog_ms * HZ / 1000;
+ add_timer(&bus->timer);
+
+ } else {
+ /* Re arm the timer, at last watchdog period */
+ mod_timer(&bus->timer,
+ jiffies + brcmf_watchdog_ms * HZ / 1000);
+ }
+
+ bus->wd_timer_valid = true;
+ save_ms = wdtick;
+ }
+}