watchdog: aspeed: Add pre-timeout interrupt support
v2 cover watchdog: aspeed: Add pre-timeout interrupt support
Enable the core pre-timeout interrupt on AST2500 and AST2600.
Changes since v1:
- Use watchdog core support for pre-timeout interrupt, rather than new dts property
Eddie James (2):
watchdog: aspeed: Enable pre-timeout interrupt
ARM: dts: aspeed: Setup watchdog pre-timeout interrupt
arch/arm/boot/dts/aspeed-g5.dtsi | 3 + arch/arm/boot/dts/aspeed-g6.dtsi | 4 ++ drivers/watchdog/aspeed_wdt.c | 104 ++++++++++++++++++++++++++----- 3 files changed, 95 insertions(+), 16 deletions(-)
Signature · 1 line
2.31.1
PATCH v2 1/2 watchdog: aspeed: Enable pre-timeout interrupt
Enable the core pre-timeout interrupt on AST2500 and AST2600.
--- drivers/watchdog/aspeed_wdt.c | 104 ++++++++++++++++++++++++++++------ 1 file changed, 88 insertions(+), 16 deletions(-)
drivers/watchdog/aspeed_wdt.c +88 -16
@@ -5,11 +5,14 @@* Joel Stanley <[email protected]> */ +#include <linux/bits.h> #include <linux/delay.h> +#include <linux/interrupt.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_irq.h> #include <linux/platform_device.h> #include <linux/watchdog.h>
@@ -18,28 +21,41 @@ module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); +struct aspeed_wdt_config { +u32 ext_pulse_width_mask; +u32 irq_shift; +u32 irq_mask; +}; +struct aspeed_wdt {struct watchdog_device wdd;void __iomem *base;u32 ctrl; -}; - -struct aspeed_wdt_config { -u32 ext_pulse_width_mask; +const struct aspeed_wdt_config *cfg; };static const struct aspeed_wdt_config ast2400_config = { .ext_pulse_width_mask =0xff , + .irq_shift =0 , + .irq_mask =0 , };static const struct aspeed_wdt_config ast2500_config = { .ext_pulse_width_mask =0xfffff , + .irq_shift =12 , + .irq_mask =GENMASK (31 ,12 ), +}; + +static const struct aspeed_wdt_config ast2600_config = { + .ext_pulse_width_mask =0xfffff , + .irq_shift =0 , + .irq_mask =GENMASK (31 ,10 ), };static const struct of_device_id aspeed_wdt_of_table[] = { { .compatible ="aspeed,ast2400-wdt" , .data = &ast2400_config }, { .compatible ="aspeed,ast2500-wdt" , .data = &ast2500_config }, - { .compatible ="aspeed,ast2600-wdt" , .data = &ast2500_config }, + { .compatible ="aspeed,ast2600-wdt" , .data = &ast2600_config }, { }, };MODULE_DEVICE_TABLE (of, aspeed_wdt_of_table);
@@ -58,6 +74,7 @@ MODULE_DEVICE_TABLE(of, aspeed_wdt_of_table);#define WDT_CTRL_RESET_SYSTEM BIT(1)#define WDT_CTRL_ENABLE BIT(0)#define WDT_TIMEOUT_STATUS 0x10 +#define WDT_TIMEOUT_STATUS_IRQ BIT(2)#define WDT_TIMEOUT_STATUS_BOOT_SECONDARY BIT(1)#define WDT_CLEAR_TIMEOUT_STATUS 0x14#define WDT_CLEAR_TIMEOUT_AND_BOOT_CODE_SELECTION BIT(0)
@@ -160,6 +177,26 @@ static int aspeed_wdt_set_timeout(struct watchdog_device *wdd,return 0 ; } +static int aspeed_wdt_set_pretimeout (struct watchdog_device *wdd, +unsigned int pretimeout) +{ +struct aspeed_wdt *wdt =to_aspeed_wdt (wdd); +u32 actual = pretimeout *WDT_RATE_1MHZ ; +u32 s = wdt->cfg->irq_shift; +u32 m = wdt->cfg->irq_mask; + + wdd->pretimeout = pretimeout; + wdt->ctrl &= ~m; +if (pretimeout) + wdt->ctrl |= ((actual << s) & m) |WDT_CTRL_WDT_INTR ; +else + wdt->ctrl &= ~WDT_CTRL_WDT_INTR ; + +writel (wdt->ctrl, wdt->base +WDT_CTRL ); + +return 0 ; +} +static int aspeed_wdt_restart (struct watchdog_device *wdd,unsigned long action,void *data) {
@@ -232,6 +269,7 @@ static const struct watchdog_ops aspeed_wdt_ops = { .stop = aspeed_wdt_stop, .ping = aspeed_wdt_ping, .set_timeout = aspeed_wdt_set_timeout, + .set_pretimeout = aspeed_wdt_set_pretimeout, .restart = aspeed_wdt_restart, .owner = THIS_MODULE, };
@@ -243,10 +281,29 @@ static const struct watchdog_info aspeed_wdt_info = { .identity = KBUILD_MODNAME, }; +static const struct watchdog_info aspeed_wdt_pretimeout_info = { + .options =WDIOF_KEEPALIVEPING + |WDIOF_PRETIMEOUT + |WDIOF_MAGICCLOSE + |WDIOF_SETTIMEOUT , + .identity =KBUILD_MODNAME , +}; + +static irqreturn_t aspeed_wdt_irq (int irq,void *arg) +{ +struct watchdog_device *wdd = arg; +struct aspeed_wdt *wdt =to_aspeed_wdt (wdd); +u32 status =readl (wdt->base +WDT_TIMEOUT_STATUS ); + +if (status &WDT_TIMEOUT_STATUS_IRQ ) +watchdog_notify_pretimeout (wdd); + +return IRQ_HANDLED ; +} +static int aspeed_wdt_probe (struct platform_device *pdev) {struct device *dev = &pdev->dev; -const struct aspeed_wdt_config *config;const struct of_device_id *ofdid;struct aspeed_wdt *wdt;struct device_node *np;
@@ -259,11 +316,33 @@ static int aspeed_wdt_probe(struct platform_device *pdev)if (!wdt)return -ENOMEM ; + np = dev->of_node; + + ofdid =of_match_node (aspeed_wdt_of_table, np); +if (!ofdid) +return -EINVAL ; + wdt->cfg = ofdid->data; + wdt->base =devm_platform_ioremap_resource (pdev,0 );if (IS_ERR (wdt->base))return PTR_ERR (wdt->base); wdt->wdd.info = &aspeed_wdt_info; + +if (wdt->cfg->irq_mask) { +int irq =platform_get_irq_optional (pdev,0 ); + +if (irq >0 ) { + ret =devm_request_irq (dev, irq, aspeed_wdt_irq, +IRQF_SHARED ,dev_name (dev), + wdt); +if (ret) +return ret; + + wdt->wdd.info = &aspeed_wdt_pretimeout_info; + } + } + wdt->wdd.ops = &aspeed_wdt_ops; wdt->wdd.max_hw_heartbeat_ms =WDT_MAX_TIMEOUT_MS ; wdt->wdd.parent = dev;
@@ -273,13 +352,6 @@ static int aspeed_wdt_probe(struct platform_device *pdev)watchdog_set_nowayout (&wdt->wdd, nowayout); - np = dev->of_node; - - ofdid =of_match_node (aspeed_wdt_of_table, np); -if (!ofdid) -return -EINVAL ; - config = ofdid->data; -/* * On clock rates: * - ast2400 wdt can run at PCLK, or 1MHz
@@ -331,7 +403,7 @@ static int aspeed_wdt_probe(struct platform_device *pdev) (of_device_is_compatible(np, "aspeed,ast2600-wdt"))) {u32 reg =readl (wdt->base +WDT_RESET_WIDTH ); - reg &= config->ext_pulse_width_mask; + reg &= wdt->cfg->ext_pulse_width_mask;if (of_property_read_bool (np,"aspeed,ext-active-high" )) reg |=WDT_ACTIVE_HIGH_MAGIC ;else
@@ -339,7 +411,7 @@ static int aspeed_wdt_probe(struct platform_device *pdev)writel (reg, wdt->base +WDT_RESET_WIDTH ); - reg &= config->ext_pulse_width_mask; + reg &= wdt->cfg->ext_pulse_width_mask;if (of_property_read_bool (np,"aspeed,ext-push-pull" )) reg |=WDT_PUSH_PULL_MAGIC ;else
@@ -349,7 +421,7 @@ static int aspeed_wdt_probe(struct platform_device *pdev) }if (!of_property_read_u32 (np,"aspeed,ext-pulse-duration" , &duration)) { -u32 max_duration = config->ext_pulse_width_mask +1 ; +u32 max_duration = wdt->cfg->ext_pulse_width_mask +1 ;if (duration ==0 || duration > max_duration) { dev_err(dev, "Invalid pulse duration: %uus\n",
Signature · 1 line
2.31.1
Re: [PATCH v2 1/2] watchdog: aspeed: Enable pre-timeout interrupt
Quoted 3 lines · Eddie James
Enable the core pre-timeout interrupt on AST2500 and AST2600.
Signed-off-by Eddie James <[email protected]>
Quoted 219 lines
--- drivers/watchdog/aspeed_wdt.c | 104 ++++++++++++++++++++++++++++------ 1 file changed, 88 insertions(+), 16 deletions(-)drivers/watchdog/aspeed_wdt.c +88
-16@@ -5,11 +5,14 @@* Joel Stanley <[email protected]> */ +#include <linux/bits.h> #include <linux/delay.h> +#include <linux/interrupt.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_irq.h> #include <linux/platform_device.h> #include <linux/watchdog.h> @@ -18,28 +21,41 @@ module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); +struct aspeed_wdt_config { +u32 ext_pulse_width_mask; +u32 irq_shift; +u32 irq_mask; +}; +struct aspeed_wdt {struct watchdog_device wdd;void __iomem *base;u32 ctrl; -}; - -struct aspeed_wdt_config { -u32 ext_pulse_width_mask; +const struct aspeed_wdt_config *cfg; };static const struct aspeed_wdt_config ast2400_config = { .ext_pulse_width_mask =0xff , + .irq_shift =0 , + .irq_mask =0 , };static const struct aspeed_wdt_config ast2500_config = { .ext_pulse_width_mask =0xfffff , + .irq_shift =12 , + .irq_mask =GENMASK (31 ,12 ), +}; + +static const struct aspeed_wdt_config ast2600_config = { + .ext_pulse_width_mask =0xfffff , + .irq_shift =0 , + .irq_mask =GENMASK (31 ,10 ), };static const struct of_device_id aspeed_wdt_of_table[] = { { .compatible ="aspeed,ast2400-wdt" , .data = &ast2400_config }, { .compatible ="aspeed,ast2500-wdt" , .data = &ast2500_config }, - { .compatible ="aspeed,ast2600-wdt" , .data = &ast2500_config }, + { .compatible ="aspeed,ast2600-wdt" , .data = &ast2600_config }, { }, };MODULE_DEVICE_TABLE (of, aspeed_wdt_of_table);@@ -58,6 +74,7 @@ MODULE_DEVICE_TABLE(of, aspeed_wdt_of_table);#define WDT_CTRL_RESET_SYSTEM BIT(1)#define WDT_CTRL_ENABLE BIT(0)#define WDT_TIMEOUT_STATUS 0x10 +#define WDT_TIMEOUT_STATUS_IRQ BIT(2)#define WDT_TIMEOUT_STATUS_BOOT_SECONDARY BIT(1)#define WDT_CLEAR_TIMEOUT_STATUS 0x14#define WDT_CLEAR_TIMEOUT_AND_BOOT_CODE_SELECTION BIT(0)@@ -160,6 +177,26 @@ static int aspeed_wdt_set_timeout(struct watchdog_device *wdd,return 0 ; } +static int aspeed_wdt_set_pretimeout (struct watchdog_device *wdd, +unsigned int pretimeout) +{ +struct aspeed_wdt *wdt =to_aspeed_wdt (wdd); +u32 actual = pretimeout *WDT_RATE_1MHZ ; +u32 s = wdt->cfg->irq_shift; +u32 m = wdt->cfg->irq_mask; + + wdd->pretimeout = pretimeout; + wdt->ctrl &= ~m; +if (pretimeout) + wdt->ctrl |= ((actual << s) & m) |WDT_CTRL_WDT_INTR ; +else + wdt->ctrl &= ~WDT_CTRL_WDT_INTR ; + +writel (wdt->ctrl, wdt->base +WDT_CTRL ); + +return 0 ; +} +static int aspeed_wdt_restart (struct watchdog_device *wdd,unsigned long action,void *data) {@@ -232,6 +269,7 @@ static const struct watchdog_ops aspeed_wdt_ops = { .stop = aspeed_wdt_stop, .ping = aspeed_wdt_ping, .set_timeout = aspeed_wdt_set_timeout, + .set_pretimeout = aspeed_wdt_set_pretimeout, .restart = aspeed_wdt_restart, .owner = THIS_MODULE, };@@ -243,10 +281,29 @@ static const struct watchdog_info aspeed_wdt_info = { .identity = KBUILD_MODNAME, }; +static const struct watchdog_info aspeed_wdt_pretimeout_info = { + .options =WDIOF_KEEPALIVEPING + |WDIOF_PRETIMEOUT + |WDIOF_MAGICCLOSE + |WDIOF_SETTIMEOUT , + .identity =KBUILD_MODNAME , +}; + +static irqreturn_t aspeed_wdt_irq (int irq,void *arg) +{ +struct watchdog_device *wdd = arg; +struct aspeed_wdt *wdt =to_aspeed_wdt (wdd); +u32 status =readl (wdt->base +WDT_TIMEOUT_STATUS ); + +if (status &WDT_TIMEOUT_STATUS_IRQ ) +watchdog_notify_pretimeout (wdd); + +return IRQ_HANDLED ; +} +static int aspeed_wdt_probe (struct platform_device *pdev) {struct device *dev = &pdev->dev; -const struct aspeed_wdt_config *config;const struct of_device_id *ofdid;struct aspeed_wdt *wdt;struct device_node *np;@@ -259,11 +316,33 @@ static int aspeed_wdt_probe(struct platform_device *pdev)if (!wdt)return -ENOMEM ; + np = dev->of_node; + + ofdid =of_match_node (aspeed_wdt_of_table, np); +if (!ofdid) +return -EINVAL ; + wdt->cfg = ofdid->data; + wdt->base =devm_platform_ioremap_resource (pdev,0 );if (IS_ERR (wdt->base))return PTR_ERR (wdt->base); wdt->wdd.info = &aspeed_wdt_info; + +if (wdt->cfg->irq_mask) { +int irq =platform_get_irq_optional (pdev,0 ); + +if (irq >0 ) { + ret =devm_request_irq (dev, irq, aspeed_wdt_irq, +IRQF_SHARED ,dev_name (dev), + wdt); +if (ret) +return ret; + + wdt->wdd.info = &aspeed_wdt_pretimeout_info; + } + } + wdt->wdd.ops = &aspeed_wdt_ops; wdt->wdd.max_hw_heartbeat_ms =WDT_MAX_TIMEOUT_MS ; wdt->wdd.parent = dev;@@ -273,13 +352,6 @@ static int aspeed_wdt_probe(struct platform_device *pdev)watchdog_set_nowayout (&wdt->wdd, nowayout); - np = dev->of_node; - - ofdid =of_match_node (aspeed_wdt_of_table, np); -if (!ofdid) -return -EINVAL ; - config = ofdid->data; -/* * On clock rates: * - ast2400 wdt can run at PCLK, or 1MHz @@ -331,7 +403,7 @@ static int aspeed_wdt_probe(struct platform_device *pdev) (of_device_is_compatible(np, "aspeed,ast2600-wdt"))) {u32 reg =readl (wdt->base +WDT_RESET_WIDTH ); - reg &= config->ext_pulse_width_mask; + reg &= wdt->cfg->ext_pulse_width_mask;if (of_property_read_bool (np,"aspeed,ext-active-high" )) reg |=WDT_ACTIVE_HIGH_MAGIC ;else @@ -339,7 +411,7 @@ static int aspeed_wdt_probe(struct platform_device *pdev)writel (reg, wdt->base +WDT_RESET_WIDTH ); - reg &= config->ext_pulse_width_mask; + reg &= wdt->cfg->ext_pulse_width_mask;if (of_property_read_bool (np,"aspeed,ext-push-pull" )) reg |=WDT_PUSH_PULL_MAGIC ;else @@ -349,7 +421,7 @@ static int aspeed_wdt_probe(struct platform_device *pdev) }if (!of_property_read_u32 (np,"aspeed,ext-pulse-duration" , &duration)) { -u32 max_duration = config->ext_pulse_width_mask +1 ; +u32 max_duration = wdt->cfg->ext_pulse_width_mask +1 ;if (duration ==0 || duration > max_duration) { dev_err(dev, "Invalid pulse duration: %uus\n",
PATCH v2 2/2 ARM: dts: aspeed: Setup watchdog pre-timeout interrupt
In reply to Eddie JamesSpecify the interrupt lines for the base SOCs that support it.
--- arch/arm/boot/dts/aspeed-g5.dtsi | 3 +++ arch/arm/boot/dts/aspeed-g6.dtsi | 4 ++++ 2 files changed, 7 insertions(+)
arch/arm/boot/dts/aspeed-g5.dtsi +3 -0
@@ -410,18 +410,21 @@ wdt1: watchdog at 1e785000 { compatible = "aspeed,ast2500-wdt"; reg = <0x1e785000 0x20>; clocks = <&syscon ASPEED_CLK_APB>; + interrupts = <27>; }; wdt2: watchdog at 1e785020 { compatible = "aspeed,ast2500-wdt"; reg = <0x1e785020 0x20>; clocks = <&syscon ASPEED_CLK_APB>; + interrupts = <27>; }; wdt3: watchdog at 1e785040 { compatible = "aspeed,ast2500-wdt"; reg = <0x1e785040 0x20>; clocks = <&syscon ASPEED_CLK_APB>; + interrupts = <27>; status = "disabled"; };
arch/arm/boot/dts/aspeed-g6.dtsi +4 -0
@@ -539,23 +539,27 @@ uart5: serial at 1e784000 { wdt1: watchdog at 1e785000 { compatible = "aspeed,ast2600-wdt"; + interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>; reg = <0x1e785000 0x40>; }; wdt2: watchdog at 1e785040 { compatible = "aspeed,ast2600-wdt"; + interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>; reg = <0x1e785040 0x40>; status = "disabled"; }; wdt3: watchdog at 1e785080 { compatible = "aspeed,ast2600-wdt"; + interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>; reg = <0x1e785080 0x40>; status = "disabled"; }; wdt4: watchdog at 1e7850c0 { compatible = "aspeed,ast2600-wdt"; + interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>; reg = <0x1e7850C0 0x40>; status = "disabled"; };
Signature · 1 line
2.31.1
Re: [PATCH v2 2/2] ARM: dts: aspeed: Setup watchdog pre-timeout interrupt
Quoted 3 lines · Eddie James
Specify the interrupt lines for the base SOCs that support it.
Signed-off-by Eddie James <[email protected]>
Quoted 63 lines
--- arch/arm/boot/dts/aspeed-g5.dtsi | 3 +++ arch/arm/boot/dts/aspeed-g6.dtsi | 4 ++++ 2 files changed, 7 insertions(+)arch/arm/boot/dts/aspeed-g5.dtsi +3
-0@@ -410,18 +410,21 @@ wdt1: watchdog at 1e785000 { compatible = "aspeed,ast2500-wdt"; reg = <0x1e785000 0x20>; clocks = <&syscon ASPEED_CLK_APB>; + interrupts = <27>; }; wdt2: watchdog at 1e785020 { compatible = "aspeed,ast2500-wdt"; reg = <0x1e785020 0x20>; clocks = <&syscon ASPEED_CLK_APB>; + interrupts = <27>; }; wdt3: watchdog at 1e785040 { compatible = "aspeed,ast2500-wdt"; reg = <0x1e785040 0x20>; clocks = <&syscon ASPEED_CLK_APB>; + interrupts = <27>; status = "disabled"; };arch/arm/boot/dts/aspeed-g6.dtsi +4
-0@@ -539,23 +539,27 @@ uart5: serial at 1e784000 { wdt1: watchdog at 1e785000 { compatible = "aspeed,ast2600-wdt"; + interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>; reg = <0x1e785000 0x40>; }; wdt2: watchdog at 1e785040 { compatible = "aspeed,ast2600-wdt"; + interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>; reg = <0x1e785040 0x40>; status = "disabled"; }; wdt3: watchdog at 1e785080 { compatible = "aspeed,ast2600-wdt"; + interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>; reg = <0x1e785080 0x40>; status = "disabled"; }; wdt4: watchdog at 1e7850c0 { compatible = "aspeed,ast2600-wdt"; + interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>; reg = <0x1e7850C0 0x40>; status = "disabled"; };