diff options
author | Ahmad Fatoum <a.fatoum@pengutronix.de> | 2020-06-11 19:18:38 +0200 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2020-06-17 10:28:08 +0200 |
commit | 04aa7be82a2df5bec926d6e426e36accd44808a2 (patch) | |
tree | 702a0fb0d7fce878ef0874d9344b2791cda95859 /drivers/watchdog | |
parent | 6c1e7688df94a9d4dfebeb62266f7fc91c209d8a (diff) | |
download | barebox-04aa7be82a2df5bec926d6e426e36accd44808a2.tar.gz barebox-04aa7be82a2df5bec926d6e426e36accd44808a2.tar.xz |
watchdog: f71808e: maintain watchdog timeout occurred flag
If we experience a watchdog reset, the indicating flag should persist
till the Linux driver had a chance to see it.
The flag bit is special however in that writing 1 clears the bit and
writing 0 keeps it intact, i.e. :
Bit
read written result
0 0 = 0
1 0 = 1
0 1 = 0
1 1 = 0
So in the bootloader, we should write a zero always. The OS on the
other hand can either write 1 or the old value after reading to
clear the flag.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers/watchdog')
-rw-r--r-- | drivers/watchdog/f71808e_wdt.c | 59 |
1 files changed, 40 insertions, 19 deletions
diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c index 6f2d30ec77..925c2f809d 100644 --- a/drivers/watchdog/f71808e_wdt.c +++ b/drivers/watchdog/f71808e_wdt.c @@ -11,7 +11,6 @@ #include <init.h> #include <asm/io.h> -#include <linux/bitops.h> #include <driver.h> #include <watchdog.h> #include <printk.h> @@ -114,30 +113,50 @@ static inline void superio_exit(u16 base) outb(SIO_LOCK_KEY, base); } +static inline u8 f71808e_wdt_conf_in(struct f71808e_wdt *wd) +{ + return superio_inb(wd->sioaddr, F71808FG_REG_WDT_CONF); +} + +static inline void f71808e_wdt_conf_out(struct f71808e_wdt *wd, u8 wdt_conf) +{ + /* + * Writing 1 to WDTMOUT_STS clears it. Writing 0 keeps the old state. + * We want the latter, so the OS driver can check it later on. + */ + wdt_conf &= ~BIT(F71808FG_FLAG_WDTMOUT_STS); + superio_outb(wd->sioaddr, F71808FG_REG_WDT_CONF, wdt_conf); +} + static void f71808e_wdt_keepalive(struct f71808e_wdt *wd) { + u8 wdt_conf; + superio_enter(wd->sioaddr); superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT); + wdt_conf = f71808e_wdt_conf_in(wd); + if (wd->minutes_mode) /* select minutes for timer units */ - superio_set_bit(wd->sioaddr, F71808FG_REG_WDT_CONF, - F71808FG_FLAG_WD_UNIT); + wdt_conf |= BIT(F71808FG_FLAG_WD_UNIT); else /* select seconds for timer units */ - superio_clear_bit(wd->sioaddr, F71808FG_REG_WDT_CONF, - F71808FG_FLAG_WD_UNIT); + wdt_conf &= ~BIT(F71808FG_FLAG_WD_UNIT); + + f71808e_wdt_conf_out(wd, wdt_conf); /* Set timer value */ - superio_outb(wd->sioaddr, F71808FG_REG_WD_TIME, - wd->timer_val); + superio_outb(wd->sioaddr, F71808FG_REG_WD_TIME, wd->timer_val); superio_exit(wd->sioaddr); } static void f71808e_wdt_start(struct f71808e_wdt *wd) { + u8 wdt_conf; + /* Make sure we don't die as soon as the watchdog is enabled below */ f71808e_wdt_keepalive(wd); @@ -158,36 +177,38 @@ static void f71808e_wdt_start(struct f71808e_wdt *wd) superio_set_bit(wd->sioaddr, F71808FG_REG_WDO_CONF, F71808FG_FLAG_WDOUT_EN); - superio_set_bit(wd->sioaddr, F71808FG_REG_WDT_CONF, - F71808FG_FLAG_WD_EN); + wdt_conf = f71808e_wdt_conf_in(wd); + wdt_conf |= BIT(F71808FG_FLAG_WD_EN); + f71808e_wdt_conf_out(wd, wdt_conf); if (wd->pulse_width > 0) { /* Select "pulse" output mode with given duration */ - u8 wdt_conf = superio_inb(wd->sioaddr, F71808FG_REG_WDT_CONF); - /* Set WD_PSWIDTH bits (1:0) */ wdt_conf = (wdt_conf & 0xfc) | (wd->pulse_width & 0x03); /* Set WD_PULSE to "pulse" mode */ wdt_conf |= BIT(F71808FG_FLAG_WD_PULSE); - superio_outb(wd->sioaddr, F71808FG_REG_WDT_CONF, wdt_conf); } else { /* Select "level" output mode */ - superio_clear_bit(wd->sioaddr, F71808FG_REG_WDT_CONF, - F71808FG_FLAG_WD_PULSE); + wdt_conf &= ~BIT(F71808FG_FLAG_WD_PULSE); } + f71808e_wdt_conf_out(wd, wdt_conf); + superio_exit(wd->sioaddr); } static void f71808e_wdt_stop(struct f71808e_wdt *wd) { + u8 wdt_conf; + superio_enter(wd->sioaddr); superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT); - superio_clear_bit(wd->sioaddr, F71808FG_REG_WDT_CONF, - F71808FG_FLAG_WD_EN); + wdt_conf = f71808e_wdt_conf_in(wd); + wdt_conf &= ~BIT(F71808FG_FLAG_WD_EN); + f71808e_wdt_conf_out(wd, wdt_conf); superio_exit(wd->sioaddr); } @@ -222,14 +243,14 @@ static int f71808e_wdt_init(struct f71808e_wdt *wd, struct device_d *dev) { struct watchdog *wdd = &wd->wdd; const char * const *names = pulse_width_names; - unsigned long wdt_conf; + u8 wdt_conf; int ret; superio_enter(wd->sioaddr); superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT); - wdt_conf = superio_inb(wd->sioaddr, F71808FG_REG_WDT_CONF); + wdt_conf = f71808e_wdt_conf_in(wd); superio_exit(wd->sioaddr); @@ -262,7 +283,7 @@ static int f71808e_wdt_init(struct f71808e_wdt *wd, struct device_d *dev) } - if (test_bit(F71808FG_FLAG_WD_EN, &wdt_conf)) + if (wdt_conf & BIT(F71808FG_FLAG_WD_EN)) wdd->running = WDOG_HW_RUNNING; else wdd->running = WDOG_HW_NOT_RUNNING; |