[PATCH v9 1/1] watchdog: aspeed: Revise handling of bootstatus
Andrew Jeffery
andrew at codeconstruct.com.au
Wed May 1 11:44:55 AEST 2024
Hi Peter,
Overall this seems okay, however I have some follow-ups to my naming
nitpicks on v8. Broadly, my preferences are that:
* It's clear from the macro name what SoC, controller and register
each macro applies to
* We have a consistent structure in the macro naming -
<soc>_<controller>_<register>_<description> - i.e. the values for
<soc> (AST, AST2400, AST2500, AST2600), <controller> (SCU), and
<register> (SYS_RESET) are consistent across the macro names
* I prefer consistent use of 'mask' instead of 'flag' for things that
are used as masks, as to me flag implies a constraint of a single
bit, where mask doesn't feel like it has such a constraint. However,
it's fine if a mask consists of a single bit, it's still a mask.
On Tue, 2024-04-30 at 22:31 +0800, Peter Yin wrote:
> Regarding the AST2600 specification, the WDTn Timeout Status Register
> (WDT10) has bit 1 reserved. Bit 1 of the status register indicates
> on ast2500 if the boot was from the second boot source.
> It does not indicate that the most recent reset was triggered by
> the watchdog. The code should just be changed to set WDIOF_CARDRESET
> if bit 0 of the status register is set. However, this bit can be clear when
> watchdog register 0x0c bit1(Reset System after timeout) is enabled.
> Thereforce include SCU register to veriy WDIOF_EXTERN1 and WDIOF_CARDRESET
> in ast2600 SCU74 or ast2400/ast2500 SCU3C.
>
> Fixes: 49d4d277ca54 ("aspeed: watchdog: Set bootstatus during probe")
> Signed-off-by: Peter Yin <peteryin.openbmc at gmail.com>
> ---
> drivers/watchdog/aspeed_wdt.c | 90 +++++++++++++++++++++++++++++++----
> 1 file changed, 82 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/watchdog/aspeed_wdt.c b/drivers/watchdog/aspeed_wdt.c
> index b4773a6aaf8c..556493763793 100644
> --- a/drivers/watchdog/aspeed_wdt.c
> +++ b/drivers/watchdog/aspeed_wdt.c
> @@ -11,10 +11,12 @@
> #include <linux/io.h>
> #include <linux/kernel.h>
> #include <linux/kstrtox.h>
> +#include <linux/mfd/syscon.h>
> #include <linux/module.h>
> #include <linux/of.h>
> #include <linux/of_irq.h>
> #include <linux/platform_device.h>
> +#include <linux/regmap.h>
> #include <linux/watchdog.h>
> static bool nowayout = WATCHDOG_NOWAYOUT;
> @@ -22,10 +24,38 @@ module_param(nowayout, bool, 0);
> MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
> __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
> +/* AST SCU Register for System Reset Event Log Register Set
> + * ast2600 is scu074 ast2400/2500 is scu03c
> + */
> +#define AST_SCU_SYS_RESET_POWERON_MASK BIT(0)
> +#define AST_SCU_SYS_RESET_EXTERN_FLAG BIT(1)
s/_FLAG/_MASK/ here too?
> +
> +#define AST2400_SYSTEM_RESET_STATUS 0x3C
You chose to use my suggestion of `..._SCU_SYS_RESET_...` for the
POWERON and EXTERN macros above, but here you've dropped `SCU` and also
used `SYSTEM_RESET` instead `SYS_RESET`. I'd prefer we pick a
consistent register name, so
```
#define AST2400_SCU_SYS_RESET_STATUS 0x3c
```
> +#define AST2400_WATCHDOG_RESET_MASK BIT(1)
Again, I'd prefer all these field macros at least have `SCU` in the
name, and preferably the register name too, so:
```
#define AST2400_SCU_SYS_RESET_WDT_MASK BIT(1)
```
> +#define AST2400_RESET_FLAG_CLEAR GENMASK(2, 0)
s/FLAG/FLAGS/ given it's defined over multiple bits? Also, to include
the register name in the macro name:
```
#define AST2400_SCU_SYS_RESET_FLAGS_CLEAR GENMASK(2, 0)
```
> +
> +#define AST2500_WATCHDOG_RESET_MASK GENMASK(4, 2)
> +#define AST2500_RESET_FLAG_CLEAR (AST2500_WATCHDOG_RESET_MASK | \
> + AST_SCU_SYS_RESET_POWERON_MASK | \
> + AST_SCU_SYS_RESET_EXTERN_FLAG)
The same comments above apply to the AST2500 macros.
> +
> +#define AST2600_SYSTEM_RESET_STATUS 0x74
> +#define AST2600_WATCHDOG_RESET_MASK GENMASK(31, 16)
> +#define AST2600_RESET_FLAG_CLEAR (AST2600_WATCHDOG_RESET_MASK | \
> + AST_SCU_SYS_RESET_POWERON_MASK | \
> + AST_SCU_SYS_RESET_EXTERN_FLAG)
... and the AST2600 macros.
> +
> struct aspeed_wdt_config {
> u32 ext_pulse_width_mask;
> u32 irq_shift;
> u32 irq_mask;
> + struct {
> + const char *compatible;
> + u32 reset_status_reg;
> + u32 watchdog_reset_mask;
> + u32 extern_reset_mask;
> + u32 reset_flag_clear;
> + } scu;
> };
> struct aspeed_wdt {
> @@ -39,18 +69,39 @@ static const struct aspeed_wdt_config ast2400_config = {
> .ext_pulse_width_mask = 0xff,
> .irq_shift = 0,
> .irq_mask = 0,
> + .scu = {
> + .compatible = "aspeed,ast2400-scu",
> + .reset_status_reg = AST2400_SYSTEM_RESET_STATUS,
> + .watchdog_reset_mask = AST2400_WATCHDOG_RESET_MASK,
> + .extern_reset_mask = 0,
> + .reset_flag_clear = AST2400_RESET_FLAG_CLEAR,
> + }
> };
> static const struct aspeed_wdt_config ast2500_config = {
> .ext_pulse_width_mask = 0xfffff,
> .irq_shift = 12,
> .irq_mask = GENMASK(31, 12),
> + .scu = {
> + .compatible = "aspeed,ast2500-scu",
> + .reset_status_reg = AST2400_SYSTEM_RESET_STATUS,
> + .watchdog_reset_mask = AST2500_WATCHDOG_RESET_MASK,
> + .extern_reset_mask = AST_SCU_SYS_RESET_EXTERN_FLAG,
> + .reset_flag_clear = AST2500_RESET_FLAG_CLEAR,
> + }
> };
> static const struct aspeed_wdt_config ast2600_config = {
> .ext_pulse_width_mask = 0xfffff,
> .irq_shift = 0,
> .irq_mask = GENMASK(31, 10),
> + .scu = {
> + .compatible = "aspeed,ast2600-scu",
> + .reset_status_reg = AST2600_SYSTEM_RESET_STATUS,
> + .watchdog_reset_mask = AST2600_WATCHDOG_RESET_MASK,
> + .extern_reset_mask = AST_SCU_SYS_RESET_EXTERN_FLAG,
> + .reset_flag_clear = AST2600_RESET_FLAG_CLEAR,
> + }
> };
> static const struct of_device_id aspeed_wdt_of_table[] = {
> @@ -310,6 +361,7 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
> const struct of_device_id *ofdid;
> struct aspeed_wdt *wdt;
> struct device_node *np;
> + struct regmap *scu;
> const char *reset_type;
> u32 duration;
> u32 status;
> @@ -458,14 +510,36 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
> writel(duration - 1, wdt->base + WDT_RESET_WIDTH);
> }
> - status = readl(wdt->base + WDT_TIMEOUT_STATUS);
> - if (status & WDT_TIMEOUT_STATUS_BOOT_SECONDARY) {
> - wdt->wdd.bootstatus = WDIOF_CARDRESET;
> -
> - if (of_device_is_compatible(np, "aspeed,ast2400-wdt") ||
> - of_device_is_compatible(np, "aspeed,ast2500-wdt"))
> - wdt->wdd.groups = bswitch_groups;
> - }
> + /*
> + * Power on reset is set when triggered by AC or SRSRST.
s/SRSRST/SRST/
> + * Thereforce, we clear flag to ensure
s/Thereforce/Therefore/
Also the line-wrapping for the comment seems a bit aggressive?
> + * next boot cause is a real watchdog case.
> + * We use the external reset flag to determine
> + * if it is an external reset or card reset.
> + * However, The ast2400 watchdog flag is cleared by an external reset,
> + * so it only supports WDIOF_CARDRESET.
> + */
> + scu = syscon_regmap_lookup_by_compatible(wdt->cfg->scu.compatible);
> + if (IS_ERR(scu))
> + return PTR_ERR(scu);
> +
> + ret = regmap_read(scu, wdt->cfg->scu.reset_status_reg, &status);
> + if (ret)
> + return ret;
> +
> + if (!(status & AST_SCU_SYS_RESET_POWERON_MASK) &&
> + status & wdt->cfg->scu.watchdog_reset_mask)
> + wdt->wdd.bootstatus = (status & wdt->cfg->scu.extern_reset_mask)
> + ? WDIOF_EXTERN1 : WDIOF_CARDRESET;
> +
> + status = wdt->cfg->scu.reset_flag_clear;
Seems unnecessary to assign the mask to clear the reset state into
status?
Andrew
> + ret = regmap_write(scu, wdt->cfg->scu.reset_status_reg, status);
> + if (ret)
> + return ret;
> +
> + if (of_device_is_compatible(np, "aspeed,ast2400-wdt") ||
> + of_device_is_compatible(np, "aspeed,ast2500-wdt"))
> + wdt->wdd.groups = bswitch_groups;
> dev_set_drvdata(dev, wdt);
More information about the Linux-aspeed
mailing list