The ARM Server Base System Architecture is a specification for ARM-based server systems. Among other things, it defines the behavior and register interface for a watchdog timer.
Signed-off-by: Timur Tabi timur@codeaurora.org ---
[v4] Removed COMPILE_TEST pm_status is now bool removed some #includes use do_div instead display arch version if unsupported remove watchdog_set_drvdata
drivers/watchdog/Kconfig | 9 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/arm_sbsa_wdt.c | 295 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 305 insertions(+) create mode 100644 drivers/watchdog/arm_sbsa_wdt.c
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index e5e7c55..7720190 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -514,6 +514,15 @@ config MEDIATEK_WATCHDOG To compile this driver as a module, choose M here: the module will be called mtk_wdt.
+config ARM_SBSA_WDT + tristate "ARM Server Base System Architecture watchdog" + depends on ARM64 + depends on ARM_ARCH_TIMER + select WATCHDOG_CORE + help + Say Y here to include watchdog timer support for ARM Server Base + System Architecture (SBSA) systems. + # AVR32 Architecture
config AT32AP700X_WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 5c19294..063ab8c 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -64,6 +64,7 @@ obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o obj-$(CONFIG_MESON_WATCHDOG) += meson_wdt.o obj-$(CONFIG_MEDIATEK_WATCHDOG) += mtk_wdt.o +obj-$(CONFIG_ARM_SBSA_WDT) += arm_sbsa_wdt.o
# AVR32 Architecture obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o diff --git a/drivers/watchdog/arm_sbsa_wdt.c b/drivers/watchdog/arm_sbsa_wdt.c new file mode 100644 index 0000000..12ed520e --- /dev/null +++ b/drivers/watchdog/arm_sbsa_wdt.c @@ -0,0 +1,295 @@ +/* + * Watchdog driver for SBSA-compliant watchdog timers + * + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ARM Server Base System Architecture watchdog driver. + * + * Register descriptions are taken from the ARM Server Base System + * Architecture document (ARM-DEN-0029) + */ + +#define pr_fmt(fmt) "sbsa-gwdt: " fmt + +#include <linux/module.h> +#include <linux/watchdog.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/reboot.h> + +#include <asm/arch_timer.h> + +/* Watchdog Interface Identification Registers */ +struct arm_sbsa_watchdog_ident { + __le32 w_iidr; /* Watchdog Interface Identification Register */ + uint8_t res2[0xFE8 - 0xFD0]; + __le32 w_pidr2; /* Peripheral ID2 Register */ +}; + +/* Watchdog Refresh Frame */ +struct arm_sbsa_watchdog_refresh { + __le32 wrr; /* Watchdog Refresh Register */ + uint8_t res1[0xFCC - 0x004]; + struct arm_sbsa_watchdog_ident ident; +}; + +/* Watchdog Control Frame */ +struct arm_sbsa_watchdog_control { + __le32 wcs; + __le32 res1; + __le32 wor; + __le32 res2; + __le64 wcv; + uint8_t res3[0xFCC - 0x018]; + struct arm_sbsa_watchdog_ident ident; +}; + +struct arm_sbsa_watchdog_data { + struct watchdog_device wdev; + bool pm_status; + struct arm_sbsa_watchdog_refresh __iomem *refresh; + struct arm_sbsa_watchdog_control __iomem *control; +}; + +static int arm_sbsa_wdt_start(struct watchdog_device *wdev) +{ + struct arm_sbsa_watchdog_data *data = + container_of(wdev, struct arm_sbsa_watchdog_data, wdev); + + /* Writing to the control register will also reset the counter */ + writel(1, &data->control->wcs); + + return 0; +} + +static int arm_sbsa_wdt_stop(struct watchdog_device *wdev) +{ + struct arm_sbsa_watchdog_data *data = + container_of(wdev, struct arm_sbsa_watchdog_data, wdev); + + writel(0, &data->control->wcs); + + return 0; +} + +static int arm_sbsa_wdt_set_timeout(struct watchdog_device *wdev, + unsigned int timeout) +{ + struct arm_sbsa_watchdog_data *data = + container_of(wdev, struct arm_sbsa_watchdog_data, wdev); + + wdev->timeout = timeout; + writel(arch_timer_get_cntfrq() * wdev->timeout, &data->control->wor); + + return 0; +} + +static int arm_sbsa_wdt_ping(struct watchdog_device *wdev) +{ + struct arm_sbsa_watchdog_data *data = + container_of(wdev, struct arm_sbsa_watchdog_data, wdev); + + writel(1, &data->refresh->wrr); + + return 0; +} + +static unsigned int arm_sbsa_wdt_status(struct watchdog_device *wdev) +{ + struct arm_sbsa_watchdog_data *data = + container_of(wdev, struct arm_sbsa_watchdog_data, wdev); + + return (readl(&data->control->wcs) & 1) << WDOG_ACTIVE; +} + +static unsigned int arm_sbsa_wdt_timeleft(struct watchdog_device *wdev) +{ + struct arm_sbsa_watchdog_data *data = + container_of(wdev, struct arm_sbsa_watchdog_data, wdev); + uint64_t diff = readq(&data->control->wcv) - arch_counter_get_cntvct(); + + do_div(diff, arch_timer_get_cntfrq()); + + return diff; +} + +static irqreturn_t arm_sbsa_wdt_interrupt(int irq, void *p) +{ + /* + * The WS0 interrupt occurs after the first timeout, so we attempt + * a manual reboot. If this doesn't work, the WS1 timeout will + * cause a hardware reset. + */ + pr_crit("Initiating system reboot\n"); + emergency_restart(); + + return IRQ_HANDLED; +} + +/* + * Disable watchdog if it is active during suspend + */ +static int __maybe_unused arm_sbsa_wdt_suspend(struct device *dev) +{ + struct arm_sbsa_watchdog_data *data = dev_get_drvdata(dev); + + data->pm_status = !!(readl(&data->control->wcs) & 1); + + if (data->pm_status) + writel(0, &data->control->wcs); + + return 0; +} + +/* + * Enable watchdog and configure it if necessary + */ +static int __maybe_unused arm_sbsa_wdt_resume(struct device *dev) +{ + struct arm_sbsa_watchdog_data *data = dev_get_drvdata(dev); + + if (data->pm_status) + writel(1, &data->control->wcs); + + return 0; +} + +static const struct dev_pm_ops arm_sbsa_wdt_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(arm_sbsa_wdt_suspend, arm_sbsa_wdt_resume) +}; + +static struct watchdog_info arm_sbsa_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, + .identity = "ARM SBSA watchdog", +}; + +static struct watchdog_ops arm_sbsa_wdt_ops = { + .owner = THIS_MODULE, + .start = arm_sbsa_wdt_start, + .stop = arm_sbsa_wdt_stop, + .ping = arm_sbsa_wdt_ping, + .set_timeout = arm_sbsa_wdt_set_timeout, + .status = arm_sbsa_wdt_status, + .get_timeleft = arm_sbsa_wdt_timeleft, +}; + +static int __init arm_sbsa_wdt_probe(struct platform_device *pdev) +{ + struct arm_sbsa_watchdog_data *data; + struct resource *res; + uint32_t iidr; + unsigned int arch; + int irq, ret; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "control"); + data->control = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(data->control)) + return PTR_ERR(data->control); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "refresh"); + data->refresh = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(data->refresh)) + return PTR_ERR(data->refresh); + + /* We only support architecture version 0 */ + iidr = readl(&data->control->ident.w_iidr); + arch = (iidr >> 16) & 0xf; + if (arch != 0) { + dev_err(&pdev->dev, + "architecture version %u is not supported\n", arch); + return -ENODEV; + } + + irq = platform_get_irq_byname(pdev, "ws0"); + if (irq < 0) { + dev_err(&pdev->dev, "could not get interrupt\n"); + return irq; + } + + ret = devm_request_irq(&pdev->dev, irq, arm_sbsa_wdt_interrupt, + 0, pdev->name, NULL); + if (ret < 0) { + dev_err(&pdev->dev, "could not request irq %i (ret=%i)\n", + irq, ret); + return ret; + } + + data->wdev.info = &arm_sbsa_wdt_info; + data->wdev.ops = &arm_sbsa_wdt_ops; + data->wdev.min_timeout = 1; + data->wdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS; + + /* Calculate the maximum timeout in seconds that we can support */ + data->wdev.max_timeout = U32_MAX / arch_timer_get_cntfrq(); + + /* + * Bits [15:12] are an implementation-defined revision number + * for the component. + */ + arm_sbsa_wdt_info.firmware_version = (iidr >> 12) & 0xf; + + ret = watchdog_register_device(&data->wdev); + if (ret < 0) { + dev_err(&pdev->dev, + "could not register watchdog device (ret=%i)\n", ret); + return ret; + } + + dev_dbg(&pdev->dev, "implementer code is %03x\n", + (iidr & 0xf00) >> 1 | (iidr & 0x7f)); + dev_info(&pdev->dev, "maximum timeout is %u seconds\n", + data->wdev.max_timeout); + + platform_set_drvdata(pdev, data); + + return 0; +} + +static int __exit arm_sbsa_wdt_remove(struct platform_device *pdev) +{ + struct arm_sbsa_watchdog_data *data = platform_get_drvdata(pdev); + + watchdog_unregister_device(&data->wdev); + + return 0; +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id arm_sbsa_wdt_of_match[] = { + { .compatible = "arm,sbsa-gwdt" }, + {}, +}; +MODULE_DEVICE_TABLE(of, arm_sbsa_wdt_of_match); +#endif + +static struct platform_driver arm_sbsa_wdt_driver = { + .driver = { + .name = "sbsa-gwdt", + .owner = THIS_MODULE, + .pm = &arm_sbsa_wdt_pm_ops, + .of_match_table = of_match_ptr(arm_sbsa_wdt_of_match), + }, + .probe = arm_sbsa_wdt_probe, + .remove = __exit_p(arm_sbsa_wdt_remove), +}; + +module_platform_driver(arm_sbsa_wdt_driver); + +MODULE_DESCRIPTION("ARM Server Base System Architecture Watchdog Driver"); +MODULE_LICENSE("GPL v2");
On 05/22/2015 04:52 PM, Timur Tabi wrote:
The ARM Server Base System Architecture is a specification for ARM-based server systems. Among other things, it defines the behavior and register interface for a watchdog timer.
Signed-off-by: Timur Tabi timur@codeaurora.org
My apologies for not getting a chance to think through earlier versions before now.
So, this is meant to be SBSA compliant; is it also meant to be SBBR compliant? I suspect not, since there is no ACPI initialization and SBBR requires both UEFI and ACPI. Is there any reason for not being SBBR compliant? I'm not saying this is good or bad; I'm only trying to understand the reasoning.
I'll also admit that I'm not an expert in ARM timers. Could I ask a really big favor, please? When I read the SBSA (section 5.2, specifically), that implies to me that there are two interrupts: a first interrupt for the timer itself set to go off after the timeout expires, and a second interrupt that is required when the timeout expires to force some "executive action". I only see one IRQ in the patch; what am I missing?
Thanks.
[v4] Removed COMPILE_TEST pm_status is now bool removed some #includes use do_div instead display arch version if unsupported remove watchdog_set_drvdata
drivers/watchdog/Kconfig | 9 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/arm_sbsa_wdt.c | 295 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 305 insertions(+) create mode 100644 drivers/watchdog/arm_sbsa_wdt.c
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index e5e7c55..7720190 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -514,6 +514,15 @@ config MEDIATEK_WATCHDOG To compile this driver as a module, choose M here: the module will be called mtk_wdt. +config ARM_SBSA_WDT
- tristate "ARM Server Base System Architecture watchdog"
- depends on ARM64
- depends on ARM_ARCH_TIMER
- select WATCHDOG_CORE
- help
Say Y here to include watchdog timer support for ARM Server Base
System Architecture (SBSA) systems.
# AVR32 Architecture config AT32AP700X_WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 5c19294..063ab8c 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -64,6 +64,7 @@ obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o obj-$(CONFIG_MESON_WATCHDOG) += meson_wdt.o obj-$(CONFIG_MEDIATEK_WATCHDOG) += mtk_wdt.o +obj-$(CONFIG_ARM_SBSA_WDT) += arm_sbsa_wdt.o # AVR32 Architecture obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o diff --git a/drivers/watchdog/arm_sbsa_wdt.c b/drivers/watchdog/arm_sbsa_wdt.c new file mode 100644 index 0000000..12ed520e --- /dev/null +++ b/drivers/watchdog/arm_sbsa_wdt.c @@ -0,0 +1,295 @@ +/*
- Watchdog driver for SBSA-compliant watchdog timers
- Copyright (c) 2015, The Linux Foundation. All rights reserved.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 and
- only version 2 as published by the Free Software Foundation.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- ARM Server Base System Architecture watchdog driver.
- Register descriptions are taken from the ARM Server Base System
- Architecture document (ARM-DEN-0029)
- */
+#define pr_fmt(fmt) "sbsa-gwdt: " fmt
+#include <linux/module.h> +#include <linux/watchdog.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/reboot.h>
+#include <asm/arch_timer.h>
+/* Watchdog Interface Identification Registers */ +struct arm_sbsa_watchdog_ident {
- __le32 w_iidr; /* Watchdog Interface Identification Register */
- uint8_t res2[0xFE8 - 0xFD0];
- __le32 w_pidr2; /* Peripheral ID2 Register */
+};
+/* Watchdog Refresh Frame */ +struct arm_sbsa_watchdog_refresh {
- __le32 wrr; /* Watchdog Refresh Register */
- uint8_t res1[0xFCC - 0x004];
- struct arm_sbsa_watchdog_ident ident;
+};
+/* Watchdog Control Frame */ +struct arm_sbsa_watchdog_control {
- __le32 wcs;
- __le32 res1;
- __le32 wor;
- __le32 res2;
- __le64 wcv;
- uint8_t res3[0xFCC - 0x018];
- struct arm_sbsa_watchdog_ident ident;
+};
+struct arm_sbsa_watchdog_data {
- struct watchdog_device wdev;
- bool pm_status;
- struct arm_sbsa_watchdog_refresh __iomem *refresh;
- struct arm_sbsa_watchdog_control __iomem *control;
+};
+static int arm_sbsa_wdt_start(struct watchdog_device *wdev) +{
- struct arm_sbsa_watchdog_data *data =
container_of(wdev, struct arm_sbsa_watchdog_data, wdev);
- /* Writing to the control register will also reset the counter */
- writel(1, &data->control->wcs);
- return 0;
+}
+static int arm_sbsa_wdt_stop(struct watchdog_device *wdev) +{
- struct arm_sbsa_watchdog_data *data =
container_of(wdev, struct arm_sbsa_watchdog_data, wdev);
- writel(0, &data->control->wcs);
- return 0;
+}
+static int arm_sbsa_wdt_set_timeout(struct watchdog_device *wdev,
- unsigned int timeout)
+{
- struct arm_sbsa_watchdog_data *data =
container_of(wdev, struct arm_sbsa_watchdog_data, wdev);
- wdev->timeout = timeout;
- writel(arch_timer_get_cntfrq() * wdev->timeout, &data->control->wor);
- return 0;
+}
+static int arm_sbsa_wdt_ping(struct watchdog_device *wdev) +{
- struct arm_sbsa_watchdog_data *data =
container_of(wdev, struct arm_sbsa_watchdog_data, wdev);
- writel(1, &data->refresh->wrr);
- return 0;
+}
+static unsigned int arm_sbsa_wdt_status(struct watchdog_device *wdev) +{
- struct arm_sbsa_watchdog_data *data =
container_of(wdev, struct arm_sbsa_watchdog_data, wdev);
- return (readl(&data->control->wcs) & 1) << WDOG_ACTIVE;
+}
+static unsigned int arm_sbsa_wdt_timeleft(struct watchdog_device *wdev) +{
- struct arm_sbsa_watchdog_data *data =
container_of(wdev, struct arm_sbsa_watchdog_data, wdev);
- uint64_t diff = readq(&data->control->wcv) - arch_counter_get_cntvct();
- do_div(diff, arch_timer_get_cntfrq());
- return diff;
+}
+static irqreturn_t arm_sbsa_wdt_interrupt(int irq, void *p) +{
- /*
* The WS0 interrupt occurs after the first timeout, so we attempt
* a manual reboot. If this doesn't work, the WS1 timeout will
* cause a hardware reset.
*/
- pr_crit("Initiating system reboot\n");
- emergency_restart();
- return IRQ_HANDLED;
+}
+/*
- Disable watchdog if it is active during suspend
- */
+static int __maybe_unused arm_sbsa_wdt_suspend(struct device *dev) +{
- struct arm_sbsa_watchdog_data *data = dev_get_drvdata(dev);
- data->pm_status = !!(readl(&data->control->wcs) & 1);
- if (data->pm_status)
writel(0, &data->control->wcs);
- return 0;
+}
+/*
- Enable watchdog and configure it if necessary
- */
+static int __maybe_unused arm_sbsa_wdt_resume(struct device *dev) +{
- struct arm_sbsa_watchdog_data *data = dev_get_drvdata(dev);
- if (data->pm_status)
writel(1, &data->control->wcs);
- return 0;
+}
+static const struct dev_pm_ops arm_sbsa_wdt_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(arm_sbsa_wdt_suspend, arm_sbsa_wdt_resume)
+};
+static struct watchdog_info arm_sbsa_wdt_info = {
- .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
- .identity = "ARM SBSA watchdog",
+};
+static struct watchdog_ops arm_sbsa_wdt_ops = {
- .owner = THIS_MODULE,
- .start = arm_sbsa_wdt_start,
- .stop = arm_sbsa_wdt_stop,
- .ping = arm_sbsa_wdt_ping,
- .set_timeout = arm_sbsa_wdt_set_timeout,
- .status = arm_sbsa_wdt_status,
- .get_timeleft = arm_sbsa_wdt_timeleft,
+};
+static int __init arm_sbsa_wdt_probe(struct platform_device *pdev) +{
- struct arm_sbsa_watchdog_data *data;
- struct resource *res;
- uint32_t iidr;
- unsigned int arch;
- int irq, ret;
- data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
- if (!data)
return -ENOMEM;
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "control");
- data->control = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(data->control))
return PTR_ERR(data->control);
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "refresh");
- data->refresh = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(data->refresh))
return PTR_ERR(data->refresh);
- /* We only support architecture version 0 */
- iidr = readl(&data->control->ident.w_iidr);
- arch = (iidr >> 16) & 0xf;
- if (arch != 0) {
dev_err(&pdev->dev,
"architecture version %u is not supported\n", arch);
return -ENODEV;
- }
- irq = platform_get_irq_byname(pdev, "ws0");
- if (irq < 0) {
dev_err(&pdev->dev, "could not get interrupt\n");
return irq;
- }
- ret = devm_request_irq(&pdev->dev, irq, arm_sbsa_wdt_interrupt,
0, pdev->name, NULL);
- if (ret < 0) {
dev_err(&pdev->dev, "could not request irq %i (ret=%i)\n",
irq, ret);
return ret;
- }
- data->wdev.info = &arm_sbsa_wdt_info;
- data->wdev.ops = &arm_sbsa_wdt_ops;
- data->wdev.min_timeout = 1;
- data->wdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
- /* Calculate the maximum timeout in seconds that we can support */
- data->wdev.max_timeout = U32_MAX / arch_timer_get_cntfrq();
- /*
* Bits [15:12] are an implementation-defined revision number
* for the component.
*/
- arm_sbsa_wdt_info.firmware_version = (iidr >> 12) & 0xf;
- ret = watchdog_register_device(&data->wdev);
- if (ret < 0) {
dev_err(&pdev->dev,
"could not register watchdog device (ret=%i)\n", ret);
return ret;
- }
- dev_dbg(&pdev->dev, "implementer code is %03x\n",
(iidr & 0xf00) >> 1 | (iidr & 0x7f));
- dev_info(&pdev->dev, "maximum timeout is %u seconds\n",
data->wdev.max_timeout);
- platform_set_drvdata(pdev, data);
- return 0;
+}
+static int __exit arm_sbsa_wdt_remove(struct platform_device *pdev) +{
- struct arm_sbsa_watchdog_data *data = platform_get_drvdata(pdev);
- watchdog_unregister_device(&data->wdev);
- return 0;
+}
+#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id arm_sbsa_wdt_of_match[] = {
- { .compatible = "arm,sbsa-gwdt" },
- {},
+}; +MODULE_DEVICE_TABLE(of, arm_sbsa_wdt_of_match); +#endif
+static struct platform_driver arm_sbsa_wdt_driver = {
- .driver = {
.name = "sbsa-gwdt",
.owner = THIS_MODULE,
.pm = &arm_sbsa_wdt_pm_ops,
.of_match_table = of_match_ptr(arm_sbsa_wdt_of_match),
- },
- .probe = arm_sbsa_wdt_probe,
- .remove = __exit_p(arm_sbsa_wdt_remove),
+};
+module_platform_driver(arm_sbsa_wdt_driver);
+MODULE_DESCRIPTION("ARM Server Base System Architecture Watchdog Driver"); +MODULE_LICENSE("GPL v2");
On 05/26/2015 12:11 PM, Al Stone wrote:
So, this is meant to be SBSA compliant; is it also meant to be SBBR compliant? I suspect not, since there is no ACPI initialization and SBBR requires both UEFI and ACPI. Is there any reason for not being SBBR compliant? I'm not saying this is good or bad; I'm only trying to understand the reasoning.
The driver expects some platform code to read the ACPI tables and initialize a platform device.
I'll also admit that I'm not an expert in ARM timers. Could I ask a really big favor, please? When I read the SBSA (section 5.2, specifically), that implies to me that there are two interrupts: a first interrupt for the timer itself set to go off after the timeout expires, and a second interrupt that is required when the timeout expires to force some "executive action". I only see one IRQ in the patch; what am I missing?
My driver just uses the first interrupt as a software reset. The second reset is treated as a "backup" hardware reset, in case the software reset didn't work.
Fu's driver, which I admit is better at handling this, uses the first interrupt as an optional pre-timeout that can be used for debugging. The second timeout, which is a hardware reset, is the "real" timeout.
Note that the ACPI specification for the watchdog device only allows for one interrupt to be specified. For these drivers, we expect the first interrupt (WS0) to be specified in the ACPI tables. We assume that the second timeout (WS1) will just cause an immediate hardware reset, without an interrupt.
Also, Fu and I have discussed this, and I think it makes sense to pick up his driver instead of mine. So I'm withdrawing my driver.
On 05/26/2015 11:58 AM, Timur Tabi wrote:
On 05/26/2015 12:11 PM, Al Stone wrote:
So, this is meant to be SBSA compliant; is it also meant to be SBBR compliant? I suspect not, since there is no ACPI initialization and SBBR requires both UEFI and ACPI. Is there any reason for not being SBBR compliant? I'm not saying this is good or bad; I'm only trying to understand the reasoning.
The driver expects some platform code to read the ACPI tables and initialize a platform device.
Ah. I have to think on that a bit; it sounds reasonable. I guess I was wondering about how the DT usage would work vs the ACPI usage and trying to make them very similar in my head. That may just give me a headache, however.
I'll also admit that I'm not an expert in ARM timers. Could I ask a really big favor, please? When I read the SBSA (section 5.2, specifically), that implies to me that there are two interrupts: a first interrupt for the timer itself set to go off after the timeout expires, and a second interrupt that is required when the timeout expires to force some "executive action". I only see one IRQ in the patch; what am I missing?
My driver just uses the first interrupt as a software reset. The second reset is treated as a "backup" hardware reset, in case the software reset didn't work.
Fu's driver, which I admit is better at handling this, uses the first interrupt as an optional pre-timeout that can be used for debugging. The second timeout, which is a hardware reset, is the "real" timeout.
Oh, I see. I think I was misunderstanding the SBSA, too; I saw the word "signal" and was thinking that had to be an interrupt, which is not correct.
Note that the ACPI specification for the watchdog device only allows for one interrupt to be specified. For these drivers, we expect the first interrupt (WS0) to be specified in the ACPI tables. We assume that the second timeout (WS1) will just cause an immediate hardware reset, without an interrupt.
Right. I think I've got the mapping now. Thanks, that helped clarify things for me. I appreciate the time spent.
Also, Fu and I have discussed this, and I think it makes sense to pick up his driver instead of mine. So I'm withdrawing my driver.
On 05/26/2015 03:38 PM, Al Stone wrote:
Ah. I have to think on that a bit; it sounds reasonable. I guess I was wondering about how the DT usage would work vs the ACPI usage and trying to make them very similar in my head. That may just give me a headache, however.
I haven't tested the code myself, but the driver uses named properties to get the base address for the device, so as long as the device tree has those property names, it should work automatically.
Hi
On 5/26/15, 6:11 PM, "Al Stone" al.stone@linaro.org wrote:
On 05/22/2015 04:52 PM, Timur Tabi wrote:
The ARM Server Base System Architecture is a specification for ARM-based server systems. Among other things, it defines the behavior and register interface for a watchdog timer.
Signed-off-by: Timur Tabi timur@codeaurora.org
My apologies for not getting a chance to think through earlier versions before now.
So, this is meant to be SBSA compliant; is it also meant to be SBBR compliant? I suspect not, since there is no ACPI initialization and SBBR requires both UEFI and ACPI. Is there any reason for not being SBBR compliant? I'm not saying this is good or bad; I'm only trying to understand the reasoning.
I'll also admit that I'm not an expert in ARM timers. Could I ask a really big favor, please? When I read the SBSA (section 5.2, specifically), that implies to me that there are two interrupts: a first interrupt for the timer itself set to go off after the timeout expires, and a second interrupt that is required when the timeout expires to force some "executive action". I only see one IRQ in the patch; what am I missing?
The sbsa watchdog has two signals, WS0 and WS1, or as some have now renamed them, the bark and bite. At a kernel level the first is going to come in as an interrupt, and second will be a essentially a reset.This gives you two chances at recovery. One reason for having this second chance is to allow a debug agent to step in an gather crash information.
HTH
Charles
Thanks.
[v4] Removed COMPILE_TEST pm_status is now bool removed some #includes use do_div instead display arch version if unsupported remove watchdog_set_drvdata
drivers/watchdog/Kconfig | 9 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/arm_sbsa_wdt.c | 295 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 305 insertions(+) create mode 100644 drivers/watchdog/arm_sbsa_wdt.c
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index e5e7c55..7720190 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -514,6 +514,15 @@ config MEDIATEK_WATCHDOG To compile this driver as a module, choose M here: the module will be called mtk_wdt. +config ARM_SBSA_WDT
- tristate "ARM Server Base System Architecture watchdog"
- depends on ARM64
- depends on ARM_ARCH_TIMER
- select WATCHDOG_CORE
- help
Say Y here to include watchdog timer support for ARM Server Base
System Architecture (SBSA) systems.
# AVR32 Architecture config AT32AP700X_WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 5c19294..063ab8c 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -64,6 +64,7 @@ obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o obj-$(CONFIG_MESON_WATCHDOG) += meson_wdt.o obj-$(CONFIG_MEDIATEK_WATCHDOG) += mtk_wdt.o +obj-$(CONFIG_ARM_SBSA_WDT) += arm_sbsa_wdt.o # AVR32 Architecture obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o diff --git a/drivers/watchdog/arm_sbsa_wdt.c b/drivers/watchdog/arm_sbsa_wdt.c new file mode 100644 index 0000000..12ed520e --- /dev/null +++ b/drivers/watchdog/arm_sbsa_wdt.c @@ -0,0 +1,295 @@ +/*
- Watchdog driver for SBSA-compliant watchdog timers
- Copyright (c) 2015, The Linux Foundation. All rights reserved.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 and
- only version 2 as published by the Free Software Foundation.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- ARM Server Base System Architecture watchdog driver.
- Register descriptions are taken from the ARM Server Base System
- Architecture document (ARM-DEN-0029)
- */
+#define pr_fmt(fmt) "sbsa-gwdt: " fmt
+#include <linux/module.h> +#include <linux/watchdog.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/reboot.h>
+#include <asm/arch_timer.h>
+/* Watchdog Interface Identification Registers */ +struct arm_sbsa_watchdog_ident {
- __le32 w_iidr; /* Watchdog Interface Identification Register */
- uint8_t res2[0xFE8 - 0xFD0];
- __le32 w_pidr2; /* Peripheral ID2 Register */
+};
+/* Watchdog Refresh Frame */ +struct arm_sbsa_watchdog_refresh {
- __le32 wrr; /* Watchdog Refresh Register */
- uint8_t res1[0xFCC - 0x004];
- struct arm_sbsa_watchdog_ident ident;
+};
+/* Watchdog Control Frame */ +struct arm_sbsa_watchdog_control {
- __le32 wcs;
- __le32 res1;
- __le32 wor;
- __le32 res2;
- __le64 wcv;
- uint8_t res3[0xFCC - 0x018];
- struct arm_sbsa_watchdog_ident ident;
+};
+struct arm_sbsa_watchdog_data {
- struct watchdog_device wdev;
- bool pm_status;
- struct arm_sbsa_watchdog_refresh __iomem *refresh;
- struct arm_sbsa_watchdog_control __iomem *control;
+};
+static int arm_sbsa_wdt_start(struct watchdog_device *wdev) +{
- struct arm_sbsa_watchdog_data *data =
container_of(wdev, struct arm_sbsa_watchdog_data, wdev);
- /* Writing to the control register will also reset the counter */
- writel(1, &data->control->wcs);
- return 0;
+}
+static int arm_sbsa_wdt_stop(struct watchdog_device *wdev) +{
- struct arm_sbsa_watchdog_data *data =
container_of(wdev, struct arm_sbsa_watchdog_data, wdev);
- writel(0, &data->control->wcs);
- return 0;
+}
+static int arm_sbsa_wdt_set_timeout(struct watchdog_device *wdev,
- unsigned int timeout)
+{
- struct arm_sbsa_watchdog_data *data =
container_of(wdev, struct arm_sbsa_watchdog_data, wdev);
- wdev->timeout = timeout;
- writel(arch_timer_get_cntfrq() * wdev->timeout, &data->control->wor);
- return 0;
+}
+static int arm_sbsa_wdt_ping(struct watchdog_device *wdev) +{
- struct arm_sbsa_watchdog_data *data =
container_of(wdev, struct arm_sbsa_watchdog_data, wdev);
- writel(1, &data->refresh->wrr);
- return 0;
+}
+static unsigned int arm_sbsa_wdt_status(struct watchdog_device *wdev) +{
- struct arm_sbsa_watchdog_data *data =
container_of(wdev, struct arm_sbsa_watchdog_data, wdev);
- return (readl(&data->control->wcs) & 1) << WDOG_ACTIVE;
+}
+static unsigned int arm_sbsa_wdt_timeleft(struct watchdog_device *wdev) +{
- struct arm_sbsa_watchdog_data *data =
container_of(wdev, struct arm_sbsa_watchdog_data, wdev);
- uint64_t diff = readq(&data->control->wcv) -
arch_counter_get_cntvct();
- do_div(diff, arch_timer_get_cntfrq());
- return diff;
+}
+static irqreturn_t arm_sbsa_wdt_interrupt(int irq, void *p) +{
- /*
* The WS0 interrupt occurs after the first timeout, so we attempt
* a manual reboot. If this doesn't work, the WS1 timeout will
* cause a hardware reset.
*/
- pr_crit("Initiating system reboot\n");
- emergency_restart();
- return IRQ_HANDLED;
+}
+/*
- Disable watchdog if it is active during suspend
- */
+static int __maybe_unused arm_sbsa_wdt_suspend(struct device *dev) +{
- struct arm_sbsa_watchdog_data *data = dev_get_drvdata(dev);
- data->pm_status = !!(readl(&data->control->wcs) & 1);
- if (data->pm_status)
writel(0, &data->control->wcs);
- return 0;
+}
+/*
- Enable watchdog and configure it if necessary
- */
+static int __maybe_unused arm_sbsa_wdt_resume(struct device *dev) +{
- struct arm_sbsa_watchdog_data *data = dev_get_drvdata(dev);
- if (data->pm_status)
writel(1, &data->control->wcs);
- return 0;
+}
+static const struct dev_pm_ops arm_sbsa_wdt_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(arm_sbsa_wdt_suspend, arm_sbsa_wdt_resume)
+};
+static struct watchdog_info arm_sbsa_wdt_info = {
- .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
- .identity = "ARM SBSA watchdog",
+};
+static struct watchdog_ops arm_sbsa_wdt_ops = {
- .owner = THIS_MODULE,
- .start = arm_sbsa_wdt_start,
- .stop = arm_sbsa_wdt_stop,
- .ping = arm_sbsa_wdt_ping,
- .set_timeout = arm_sbsa_wdt_set_timeout,
- .status = arm_sbsa_wdt_status,
- .get_timeleft = arm_sbsa_wdt_timeleft,
+};
+static int __init arm_sbsa_wdt_probe(struct platform_device *pdev) +{
- struct arm_sbsa_watchdog_data *data;
- struct resource *res;
- uint32_t iidr;
- unsigned int arch;
- int irq, ret;
- data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
- if (!data)
return -ENOMEM;
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "control");
- data->control = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(data->control))
return PTR_ERR(data->control);
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "refresh");
- data->refresh = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(data->refresh))
return PTR_ERR(data->refresh);
- /* We only support architecture version 0 */
- iidr = readl(&data->control->ident.w_iidr);
- arch = (iidr >> 16) & 0xf;
- if (arch != 0) {
dev_err(&pdev->dev,
"architecture version %u is not supported\n", arch);
return -ENODEV;
- }
- irq = platform_get_irq_byname(pdev, "ws0");
- if (irq < 0) {
dev_err(&pdev->dev, "could not get interrupt\n");
return irq;
- }
- ret = devm_request_irq(&pdev->dev, irq, arm_sbsa_wdt_interrupt,
0, pdev->name, NULL);
- if (ret < 0) {
dev_err(&pdev->dev, "could not request irq %i (ret=%i)\n",
irq, ret);
return ret;
- }
- data->wdev.info = &arm_sbsa_wdt_info;
- data->wdev.ops = &arm_sbsa_wdt_ops;
- data->wdev.min_timeout = 1;
- data->wdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
- /* Calculate the maximum timeout in seconds that we can support */
- data->wdev.max_timeout = U32_MAX / arch_timer_get_cntfrq();
- /*
* Bits [15:12] are an implementation-defined revision number
* for the component.
*/
- arm_sbsa_wdt_info.firmware_version = (iidr >> 12) & 0xf;
- ret = watchdog_register_device(&data->wdev);
- if (ret < 0) {
dev_err(&pdev->dev,
"could not register watchdog device (ret=%i)\n", ret);
return ret;
- }
- dev_dbg(&pdev->dev, "implementer code is %03x\n",
(iidr & 0xf00) >> 1 | (iidr & 0x7f));
- dev_info(&pdev->dev, "maximum timeout is %u seconds\n",
data->wdev.max_timeout);
- platform_set_drvdata(pdev, data);
- return 0;
+}
+static int __exit arm_sbsa_wdt_remove(struct platform_device *pdev) +{
- struct arm_sbsa_watchdog_data *data = platform_get_drvdata(pdev);
- watchdog_unregister_device(&data->wdev);
- return 0;
+}
+#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id arm_sbsa_wdt_of_match[] = {
- { .compatible = "arm,sbsa-gwdt" },
- {},
+}; +MODULE_DEVICE_TABLE(of, arm_sbsa_wdt_of_match); +#endif
+static struct platform_driver arm_sbsa_wdt_driver = {
- .driver = {
.name = "sbsa-gwdt",
.owner = THIS_MODULE,
.pm = &arm_sbsa_wdt_pm_ops,
.of_match_table = of_match_ptr(arm_sbsa_wdt_of_match),
- },
- .probe = arm_sbsa_wdt_probe,
- .remove = __exit_p(arm_sbsa_wdt_remove),
+};
+module_platform_driver(arm_sbsa_wdt_driver);
+MODULE_DESCRIPTION("ARM Server Base System Architecture Watchdog Driver"); +MODULE_LICENSE("GPL v2");
-- ciao, al
Al Stone Software Engineer Linaro Enterprise Group al.stone@linaro.org
Linaro-acpi mailing list Linaro-acpi@lists.linaro.org https://lists.linaro.org/mailman/listinfo/linaro-acpi
Hi Charles,
Great thanks for your info. Sorry, I just see this message.
Would you please provide some info about How to use the WS0/1. What is the original thought of this two stage timeout?
Those info is very important for implementing this driver. :-)
Thanks again!
On 28 May 2015 at 05:47, administrator charles.garcia-tobin@arm.com wrote:
Hi
On 5/26/15, 6:11 PM, "Al Stone" al.stone@linaro.org wrote:
On 05/22/2015 04:52 PM, Timur Tabi wrote:
The ARM Server Base System Architecture is a specification for ARM-based server systems. Among other things, it defines the behavior and register interface for a watchdog timer.
Signed-off-by: Timur Tabi timur@codeaurora.org
My apologies for not getting a chance to think through earlier versions before now.
So, this is meant to be SBSA compliant; is it also meant to be SBBR compliant? I suspect not, since there is no ACPI initialization and SBBR requires both UEFI and ACPI. Is there any reason for not being SBBR compliant? I'm not saying this is good or bad; I'm only trying to understand the reasoning.
I'll also admit that I'm not an expert in ARM timers. Could I ask a really big favor, please? When I read the SBSA (section 5.2, specifically), that implies to me that there are two interrupts: a first interrupt for the timer itself set to go off after the timeout expires, and a second interrupt that is required when the timeout expires to force some "executive action". I only see one IRQ in the patch; what am I missing?
The sbsa watchdog has two signals, WS0 and WS1, or as some have now renamed them, the bark and bite. At a kernel level the first is going to come in as an interrupt, and second will be a essentially a reset.This gives you two chances at recovery. One reason for having this second chance is to allow a debug agent to step in an gather crash information.
HTH
Charles
Thanks.
[v4] Removed COMPILE_TEST pm_status is now bool removed some #includes use do_div instead display arch version if unsupported remove watchdog_set_drvdata
drivers/watchdog/Kconfig | 9 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/arm_sbsa_wdt.c | 295 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 305 insertions(+) create mode 100644 drivers/watchdog/arm_sbsa_wdt.c
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index e5e7c55..7720190 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -514,6 +514,15 @@ config MEDIATEK_WATCHDOG To compile this driver as a module, choose M here: the module will be called mtk_wdt.
+config ARM_SBSA_WDT
- tristate "ARM Server Base System Architecture watchdog"
- depends on ARM64
- depends on ARM_ARCH_TIMER
- select WATCHDOG_CORE
- help
Say Y here to include watchdog timer support for ARM Server Base
System Architecture (SBSA) systems.
# AVR32 Architecture
config AT32AP700X_WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 5c19294..063ab8c 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -64,6 +64,7 @@ obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o obj-$(CONFIG_MESON_WATCHDOG) += meson_wdt.o obj-$(CONFIG_MEDIATEK_WATCHDOG) += mtk_wdt.o +obj-$(CONFIG_ARM_SBSA_WDT) += arm_sbsa_wdt.o
# AVR32 Architecture obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o diff --git a/drivers/watchdog/arm_sbsa_wdt.c b/drivers/watchdog/arm_sbsa_wdt.c new file mode 100644 index 0000000..12ed520e --- /dev/null +++ b/drivers/watchdog/arm_sbsa_wdt.c @@ -0,0 +1,295 @@ +/*
- Watchdog driver for SBSA-compliant watchdog timers
- Copyright (c) 2015, The Linux Foundation. All rights reserved.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 and
- only version 2 as published by the Free Software Foundation.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- ARM Server Base System Architecture watchdog driver.
- Register descriptions are taken from the ARM Server Base System
- Architecture document (ARM-DEN-0029)
- */
+#define pr_fmt(fmt) "sbsa-gwdt: " fmt
+#include <linux/module.h> +#include <linux/watchdog.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/reboot.h>
+#include <asm/arch_timer.h>
+/* Watchdog Interface Identification Registers */ +struct arm_sbsa_watchdog_ident {
- __le32 w_iidr; /* Watchdog Interface Identification Register */
- uint8_t res2[0xFE8 - 0xFD0];
- __le32 w_pidr2; /* Peripheral ID2 Register */
+};
+/* Watchdog Refresh Frame */ +struct arm_sbsa_watchdog_refresh {
- __le32 wrr; /* Watchdog Refresh Register */
- uint8_t res1[0xFCC - 0x004];
- struct arm_sbsa_watchdog_ident ident;
+};
+/* Watchdog Control Frame */ +struct arm_sbsa_watchdog_control {
- __le32 wcs;
- __le32 res1;
- __le32 wor;
- __le32 res2;
- __le64 wcv;
- uint8_t res3[0xFCC - 0x018];
- struct arm_sbsa_watchdog_ident ident;
+};
+struct arm_sbsa_watchdog_data {
- struct watchdog_device wdev;
- bool pm_status;
- struct arm_sbsa_watchdog_refresh __iomem *refresh;
- struct arm_sbsa_watchdog_control __iomem *control;
+};
+static int arm_sbsa_wdt_start(struct watchdog_device *wdev) +{
- struct arm_sbsa_watchdog_data *data =
container_of(wdev, struct arm_sbsa_watchdog_data, wdev);
- /* Writing to the control register will also reset the counter */
- writel(1, &data->control->wcs);
- return 0;
+}
+static int arm_sbsa_wdt_stop(struct watchdog_device *wdev) +{
- struct arm_sbsa_watchdog_data *data =
container_of(wdev, struct arm_sbsa_watchdog_data, wdev);
- writel(0, &data->control->wcs);
- return 0;
+}
+static int arm_sbsa_wdt_set_timeout(struct watchdog_device *wdev,
- unsigned int timeout)
+{
- struct arm_sbsa_watchdog_data *data =
container_of(wdev, struct arm_sbsa_watchdog_data, wdev);
- wdev->timeout = timeout;
- writel(arch_timer_get_cntfrq() * wdev->timeout, &data->control->wor);
- return 0;
+}
+static int arm_sbsa_wdt_ping(struct watchdog_device *wdev) +{
- struct arm_sbsa_watchdog_data *data =
container_of(wdev, struct arm_sbsa_watchdog_data, wdev);
- writel(1, &data->refresh->wrr);
- return 0;
+}
+static unsigned int arm_sbsa_wdt_status(struct watchdog_device *wdev) +{
- struct arm_sbsa_watchdog_data *data =
container_of(wdev, struct arm_sbsa_watchdog_data, wdev);
- return (readl(&data->control->wcs) & 1) << WDOG_ACTIVE;
+}
+static unsigned int arm_sbsa_wdt_timeleft(struct watchdog_device *wdev) +{
- struct arm_sbsa_watchdog_data *data =
container_of(wdev, struct arm_sbsa_watchdog_data, wdev);
- uint64_t diff = readq(&data->control->wcv) -
arch_counter_get_cntvct();
- do_div(diff, arch_timer_get_cntfrq());
- return diff;
+}
+static irqreturn_t arm_sbsa_wdt_interrupt(int irq, void *p) +{
- /*
* The WS0 interrupt occurs after the first timeout, so we attempt
* a manual reboot. If this doesn't work, the WS1 timeout will
* cause a hardware reset.
*/
- pr_crit("Initiating system reboot\n");
- emergency_restart();
- return IRQ_HANDLED;
+}
+/*
- Disable watchdog if it is active during suspend
- */
+static int __maybe_unused arm_sbsa_wdt_suspend(struct device *dev) +{
- struct arm_sbsa_watchdog_data *data = dev_get_drvdata(dev);
- data->pm_status = !!(readl(&data->control->wcs) & 1);
- if (data->pm_status)
writel(0, &data->control->wcs);
- return 0;
+}
+/*
- Enable watchdog and configure it if necessary
- */
+static int __maybe_unused arm_sbsa_wdt_resume(struct device *dev) +{
- struct arm_sbsa_watchdog_data *data = dev_get_drvdata(dev);
- if (data->pm_status)
writel(1, &data->control->wcs);
- return 0;
+}
+static const struct dev_pm_ops arm_sbsa_wdt_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(arm_sbsa_wdt_suspend, arm_sbsa_wdt_resume)
+};
+static struct watchdog_info arm_sbsa_wdt_info = {
- .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
- .identity = "ARM SBSA watchdog",
+};
+static struct watchdog_ops arm_sbsa_wdt_ops = {
- .owner = THIS_MODULE,
- .start = arm_sbsa_wdt_start,
- .stop = arm_sbsa_wdt_stop,
- .ping = arm_sbsa_wdt_ping,
- .set_timeout = arm_sbsa_wdt_set_timeout,
- .status = arm_sbsa_wdt_status,
- .get_timeleft = arm_sbsa_wdt_timeleft,
+};
+static int __init arm_sbsa_wdt_probe(struct platform_device *pdev) +{
- struct arm_sbsa_watchdog_data *data;
- struct resource *res;
- uint32_t iidr;
- unsigned int arch;
- int irq, ret;
- data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
- if (!data)
return -ENOMEM;
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "control");
- data->control = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(data->control))
return PTR_ERR(data->control);
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "refresh");
- data->refresh = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(data->refresh))
return PTR_ERR(data->refresh);
- /* We only support architecture version 0 */
- iidr = readl(&data->control->ident.w_iidr);
- arch = (iidr >> 16) & 0xf;
- if (arch != 0) {
dev_err(&pdev->dev,
"architecture version %u is not supported\n", arch);
return -ENODEV;
- }
- irq = platform_get_irq_byname(pdev, "ws0");
- if (irq < 0) {
dev_err(&pdev->dev, "could not get interrupt\n");
return irq;
- }
- ret = devm_request_irq(&pdev->dev, irq, arm_sbsa_wdt_interrupt,
0, pdev->name, NULL);
- if (ret < 0) {
dev_err(&pdev->dev, "could not request irq %i (ret=%i)\n",
irq, ret);
return ret;
- }
- data->wdev.info = &arm_sbsa_wdt_info;
- data->wdev.ops = &arm_sbsa_wdt_ops;
- data->wdev.min_timeout = 1;
- data->wdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
- /* Calculate the maximum timeout in seconds that we can support */
- data->wdev.max_timeout = U32_MAX / arch_timer_get_cntfrq();
- /*
* Bits [15:12] are an implementation-defined revision number
* for the component.
*/
- arm_sbsa_wdt_info.firmware_version = (iidr >> 12) & 0xf;
- ret = watchdog_register_device(&data->wdev);
- if (ret < 0) {
dev_err(&pdev->dev,
"could not register watchdog device (ret=%i)\n", ret);
return ret;
- }
- dev_dbg(&pdev->dev, "implementer code is %03x\n",
(iidr & 0xf00) >> 1 | (iidr & 0x7f));
- dev_info(&pdev->dev, "maximum timeout is %u seconds\n",
data->wdev.max_timeout);
- platform_set_drvdata(pdev, data);
- return 0;
+}
+static int __exit arm_sbsa_wdt_remove(struct platform_device *pdev) +{
- struct arm_sbsa_watchdog_data *data = platform_get_drvdata(pdev);
- watchdog_unregister_device(&data->wdev);
- return 0;
+}
+#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id arm_sbsa_wdt_of_match[] = {
- { .compatible = "arm,sbsa-gwdt" },
- {},
+}; +MODULE_DEVICE_TABLE(of, arm_sbsa_wdt_of_match); +#endif
+static struct platform_driver arm_sbsa_wdt_driver = {
- .driver = {
.name = "sbsa-gwdt",
.owner = THIS_MODULE,
.pm = &arm_sbsa_wdt_pm_ops,
.of_match_table = of_match_ptr(arm_sbsa_wdt_of_match),
- },
- .probe = arm_sbsa_wdt_probe,
- .remove = __exit_p(arm_sbsa_wdt_remove),
+};
+module_platform_driver(arm_sbsa_wdt_driver);
+MODULE_DESCRIPTION("ARM Server Base System Architecture Watchdog Driver"); +MODULE_LICENSE("GPL v2");
-- ciao, al
Al Stone Software Engineer Linaro Enterprise Group al.stone@linaro.org
Linaro-acpi mailing list Linaro-acpi@lists.linaro.org https://lists.linaro.org/mailman/listinfo/linaro-acpi