linux-watchdog.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] [v4] watchdog: introduce the ARM64 SBSA watchdog driver
@ 2015-05-22 22:52 Timur Tabi
  2015-05-26 17:11 ` Al Stone
  0 siblings, 1 reply; 7+ messages in thread
From: Timur Tabi @ 2015-05-22 22:52 UTC (permalink / raw)
  To: linux-watchdog, Ashwin Chaugule, Vipul Gandhi, Fu Wei, Al Stone,
	Wim Van Sebroeck, Hanjun Guo, Arnd Bergmann, Guenter Roeck,
	Graeme Gregory, linaro-acpi

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");
-- 
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project.


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH] [v4] watchdog: introduce the ARM64 SBSA watchdog driver
  2015-05-22 22:52 [PATCH] [v4] watchdog: introduce the ARM64 SBSA watchdog driver Timur Tabi
@ 2015-05-26 17:11 ` Al Stone
  2015-05-26 17:58   ` Timur Tabi
  2015-05-27 21:47   ` [Linaro-acpi] " administrator
  0 siblings, 2 replies; 7+ messages in thread
From: Al Stone @ 2015-05-26 17:11 UTC (permalink / raw)
  To: Timur Tabi, linux-watchdog, Ashwin Chaugule, Vipul Gandhi, Fu Wei,
	Wim Van Sebroeck, Hanjun Guo, Arnd Bergmann, Guenter Roeck,
	Graeme Gregory, linaro-acpi

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");
> 


-- 
ciao,
al
-----------------------------------
Al Stone
Software Engineer
Linaro Enterprise Group
al.stone@linaro.org
-----------------------------------

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] [v4] watchdog: introduce the ARM64 SBSA watchdog driver
  2015-05-26 17:11 ` Al Stone
@ 2015-05-26 17:58   ` Timur Tabi
  2015-05-26 20:38     ` Al Stone
  2015-05-27 21:47   ` [Linaro-acpi] " administrator
  1 sibling, 1 reply; 7+ messages in thread
From: Timur Tabi @ 2015-05-26 17:58 UTC (permalink / raw)
  To: Al Stone, linux-watchdog, Ashwin Chaugule, Vipul Gandhi, Fu Wei,
	Wim Van Sebroeck, Hanjun Guo, Arnd Bergmann, Guenter Roeck,
	Graeme Gregory, linaro-acpi

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.

-- 
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the
Code Aurora Forum, a Linux Foundation Collaborative Project.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] [v4] watchdog: introduce the ARM64 SBSA watchdog driver
  2015-05-26 17:58   ` Timur Tabi
@ 2015-05-26 20:38     ` Al Stone
  2015-05-26 20:49       ` Timur Tabi
  0 siblings, 1 reply; 7+ messages in thread
From: Al Stone @ 2015-05-26 20:38 UTC (permalink / raw)
  To: Timur Tabi, linux-watchdog, Ashwin Chaugule, Vipul Gandhi, Fu Wei,
	Wim Van Sebroeck, Hanjun Guo, Arnd Bergmann, Guenter Roeck,
	Graeme Gregory, linaro-acpi

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.

-- 
ciao,
al
-----------------------------------
Al Stone
Software Engineer
Linaro Enterprise Group
al.stone@linaro.org
-----------------------------------

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] [v4] watchdog: introduce the ARM64 SBSA watchdog driver
  2015-05-26 20:38     ` Al Stone
@ 2015-05-26 20:49       ` Timur Tabi
  0 siblings, 0 replies; 7+ messages in thread
From: Timur Tabi @ 2015-05-26 20:49 UTC (permalink / raw)
  To: Al Stone, linux-watchdog, Ashwin Chaugule, Vipul Gandhi, Fu Wei,
	Wim Van Sebroeck, Hanjun Guo, Arnd Bergmann, Guenter Roeck,
	Graeme Gregory, linaro-acpi

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.

-- 
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the
Code Aurora Forum, a Linux Foundation Collaborative Project.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [Linaro-acpi] [PATCH] [v4] watchdog: introduce the ARM64 SBSA watchdog driver
  2015-05-26 17:11 ` Al Stone
  2015-05-26 17:58   ` Timur Tabi
@ 2015-05-27 21:47   ` administrator
  2015-06-10 14:55     ` Fu Wei
  1 sibling, 1 reply; 7+ messages in thread
From: administrator @ 2015-05-27 21:47 UTC (permalink / raw)
  To: Al Stone, Timur Tabi, linux-watchdog@vger.kernel.org,
	Ashwin Chaugule, Vipul Gandhi, Fu Wei, Wim Van Sebroeck,
	hanjun.guo@linaro.org, Arnd Bergmann, Guenter Roeck,
	graeme.gregory@linaro.org, linaro-acpi@lists.linaro.org

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

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [Linaro-acpi] [PATCH] [v4] watchdog: introduce the ARM64 SBSA watchdog driver
  2015-05-27 21:47   ` [Linaro-acpi] " administrator
@ 2015-06-10 14:55     ` Fu Wei
  0 siblings, 0 replies; 7+ messages in thread
From: Fu Wei @ 2015-06-10 14:55 UTC (permalink / raw)
  To: administrator
  Cc: Al Stone, Timur Tabi, linux-watchdog@vger.kernel.org,
	Ashwin Chaugule, Vipul Gandhi, Wim Van Sebroeck,
	hanjun.guo@linaro.org, Arnd Bergmann, Guenter Roeck,
	graeme.gregory@linaro.org, linaro-acpi@lists.linaro.org

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
>
>



-- 
Best regards,

Fu Wei
Software Engineer
Red Hat Software (Beijing) Co.,Ltd.Shanghai Branch
Ph: +86 21 61221326(direct)
Ph: +86 186 2020 4684 (mobile)
Room 1512, Regus One Corporate Avenue,Level 15,
One Corporate Avenue,222 Hubin Road,Huangpu District,
Shanghai,China 200021

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2015-06-10 14:55 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-05-22 22:52 [PATCH] [v4] watchdog: introduce the ARM64 SBSA watchdog driver Timur Tabi
2015-05-26 17:11 ` Al Stone
2015-05-26 17:58   ` Timur Tabi
2015-05-26 20:38     ` Al Stone
2015-05-26 20:49       ` Timur Tabi
2015-05-27 21:47   ` [Linaro-acpi] " administrator
2015-06-10 14:55     ` Fu Wei

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).