[ar71xx] fix watchdog to work on subsequent SoCs
The AR9341 AHB runs at 225MHz, much faster than the 33MHz of the AR71xx AHB. So not only is the math going to do weird things, it will also wrap rather than being clamped. So: * clamp! don't wrap! * tidy up some debugging * add an option to throw an NMI rather than reset! Tested: * AR9341 SoC (TP-Link TL-WDR4300), patting/not patting the watchdog!
This commit is contained in:
parent
ea64ebd08c
commit
0155d8f69d
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=363236
@ -50,6 +50,7 @@ struct ar71xx_wdog_softc {
|
|||||||
device_t dev;
|
device_t dev;
|
||||||
int armed;
|
int armed;
|
||||||
int reboot_from_watchdog;
|
int reboot_from_watchdog;
|
||||||
|
int watchdog_nmi;
|
||||||
int debug;
|
int debug;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -58,32 +59,54 @@ ar71xx_wdog_watchdog_fn(void *private, u_int cmd, int *error)
|
|||||||
{
|
{
|
||||||
struct ar71xx_wdog_softc *sc = private;
|
struct ar71xx_wdog_softc *sc = private;
|
||||||
uint64_t timer_val;
|
uint64_t timer_val;
|
||||||
|
int action;
|
||||||
|
|
||||||
|
action = RST_WDOG_ACTION_RESET;
|
||||||
|
if (sc->watchdog_nmi != 0)
|
||||||
|
action = RST_WDOG_ACTION_NMI;
|
||||||
|
|
||||||
cmd &= WD_INTERVAL;
|
cmd &= WD_INTERVAL;
|
||||||
if (sc->debug)
|
if (sc->debug)
|
||||||
device_printf(sc->dev, "ar71xx_wdog_watchdog_fn: cmd: %x\n", cmd);
|
device_printf(sc->dev, "%s: : cmd: %x\n", __func__, cmd);
|
||||||
if (cmd > 0) {
|
if (cmd > 0) {
|
||||||
timer_val = (uint64_t)(1ULL << cmd) * ar71xx_ahb_freq() /
|
timer_val = (uint64_t)(1ULL << cmd) * ar71xx_ahb_freq() /
|
||||||
1000000000;
|
1000000000;
|
||||||
if (sc->debug)
|
|
||||||
device_printf(sc->dev, "ar71xx_wdog_watchdog_fn: programming timer: %jx\n", (uintmax_t) timer_val);
|
|
||||||
/*
|
/*
|
||||||
* Load timer with large enough value to prevent spurious
|
* Clamp the timer value in case we overflow.
|
||||||
* reset
|
|
||||||
*/
|
*/
|
||||||
ATH_WRITE_REG(AR71XX_RST_WDOG_TIMER,
|
if (timer_val > 0xffffffff)
|
||||||
ar71xx_ahb_freq() * 10);
|
timer_val = 0xffffffff;
|
||||||
ATH_WRITE_REG(AR71XX_RST_WDOG_CONTROL,
|
if (sc->debug)
|
||||||
RST_WDOG_ACTION_RESET);
|
device_printf(sc->dev, "%s: programming timer: %jx\n",
|
||||||
ATH_WRITE_REG(AR71XX_RST_WDOG_TIMER,
|
__func__, (uintmax_t) timer_val);
|
||||||
(timer_val & 0xffffffff));
|
/*
|
||||||
|
* Make sure the watchdog is set to NOACTION and give it
|
||||||
|
* time to take.
|
||||||
|
*/
|
||||||
|
ATH_WRITE_REG(AR71XX_RST_WDOG_CONTROL, RST_WDOG_ACTION_NOACTION);
|
||||||
|
wmb();
|
||||||
|
DELAY(100);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update the timer value. It's already clamped at this
|
||||||
|
* point so we don't have to wrap/clamp it here.
|
||||||
|
*/
|
||||||
|
ATH_WRITE_REG(AR71XX_RST_WDOG_TIMER, timer_val);
|
||||||
|
wmb();
|
||||||
|
DELAY(100);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* And now, arm.
|
||||||
|
*/
|
||||||
|
ATH_WRITE_REG(AR71XX_RST_WDOG_CONTROL, action);
|
||||||
sc->armed = 1;
|
sc->armed = 1;
|
||||||
*error = 0;
|
*error = 0;
|
||||||
} else {
|
} else {
|
||||||
if (sc->debug)
|
if (sc->debug)
|
||||||
device_printf(sc->dev, "ar71xx_wdog_watchdog_fn: disarming\n");
|
device_printf(sc->dev, "%s: disarming\n", __func__);
|
||||||
if (sc->armed) {
|
if (sc->armed) {
|
||||||
ATH_WRITE_REG(AR71XX_RST_WDOG_CONTROL,
|
ATH_WRITE_REG(AR71XX_RST_WDOG_CONTROL,
|
||||||
RST_WDOG_ACTION_NOACTION);
|
RST_WDOG_ACTION_NOACTION);
|
||||||
sc->armed = 0;
|
sc->armed = 0;
|
||||||
}
|
}
|
||||||
@ -109,6 +132,9 @@ ar71xx_wdog_sysctl(device_t dev)
|
|||||||
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
||||||
"debug", CTLFLAG_RW, &sc->debug, 0,
|
"debug", CTLFLAG_RW, &sc->debug, 0,
|
||||||
"enable watchdog debugging");
|
"enable watchdog debugging");
|
||||||
|
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
||||||
|
"nmi", CTLFLAG_RW, &sc->watchdog_nmi, 0,
|
||||||
|
"watchdog triggers NMI instead of reset");
|
||||||
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
||||||
"armed", CTLFLAG_RD, &sc->armed, 0,
|
"armed", CTLFLAG_RD, &sc->armed, 0,
|
||||||
"whether the watchdog is armed");
|
"whether the watchdog is armed");
|
||||||
|
Loading…
Reference in New Issue
Block a user