All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] watchdog: add Alphascale asm9260-wdt driver
@ 2015-09-18  9:23 Oleksij Rempel
  2015-10-20  7:49 ` Oleksij Rempel
  2015-10-27  2:31 ` Guenter Roeck
  0 siblings, 2 replies; 43+ messages in thread
From: Oleksij Rempel @ 2015-09-18  9:23 UTC (permalink / raw)
  To: linux-watchdog, wim; +Cc: Oleksij Rempel

Add WD support for Alphascale asm9260 SoC. This driver
provide support for different function modes:
- HW mode to trigger SoC reset on timeout
- SW mode do soft reset if needed
- DEBUG mode

Optional support for stopping watchdog. If reset binding are not provided
this driver will work in nowayout mode.

Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
---
 .../bindings/watchdog/alphascale-asm9260.txt       |  38 ++
 drivers/watchdog/Kconfig                           |   9 +
 drivers/watchdog/Makefile                          |   1 +
 drivers/watchdog/asm9260_wdt.c                     | 405 +++++++++++++++++++++
 4 files changed, 453 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
 create mode 100644 drivers/watchdog/asm9260_wdt.c

diff --git a/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
new file mode 100644
index 0000000..cbe388b
--- /dev/null
+++ b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
@@ -0,0 +1,38 @@
+Alphascale asm9260 Watchdog timer
+
+Required properties:
+
+- compatible : should be "alphascale,asm9260-wdt".
+- reg : Specifies base physical address and size of the registers.
+- clocks : the clock feeding the watchdog timer.
+	Needed if platform uses clocks. See clock-bindings.txt
+- clock-names : should be set to
+	"mod" - source for tick counter.
+	"ahb" - ahb gate.
+
+Optional properties:
+- resets : phandle pointing to the system reset controller with correct
+	reset line index for watchdog controller reset. This propertie is
+	required if you need to disable "nowayout" and it works only with
+	CONFIG_WATCHDOG_NOWAYOUT=n.
+- reset-names : should be set to "wdt_rst" if "resets" is used.
+- timeout-sec : shall contain the default watchdog timeout in seconds,
+	if unset, the default timeout is 30 seconds.
+- alphascale,mode : tree modes are supported
+	"hw" - hw reset (defaul).
+	"sw" - sw reset.
+	"debug" - no action is taken.
+
+Example:
+
+watchdog0: watchdog@80048000 {
+	compatible = "alphascale,asm9260-wdt";
+	reg = <0x80048000 0x10>;
+	clocks = <&acc CLKID_SYS_WDT>, <&acc CLKID_AHB_WDT>;
+	clock-names = "mod", "ahb";
+	interrupts = <55>;
+	resets = <&rst WDT_RESET>;
+	reset-names = "wdt_rst";
+	timeout-sec = <30>;
+	alphascale,mode = "hw"
+};
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index c68edc1..cc5f675 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -173,6 +173,15 @@ config ARM_SP805_WATCHDOG
 	  ARM Primecell SP805 Watchdog timer. This will reboot your system when
 	  the timeout is reached.
 
+config ASM9260_WATCHDOG
+	tristate "Alphascale ASM9260 watchdog"
+	depends on MACH_ASM9260
+	depends on OF
+	select WATCHDOG_CORE
+	help
+	  Watchdog timer embedded into Alphascale asm9260 chips. This will reboot your
+	  system when the timeout is reached.
+
 config AT91RM9200_WATCHDOG
 	tristate "AT91RM9200 watchdog"
 	depends on SOC_AT91RM9200 && MFD_SYSCON
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 0c616e3..bd7b0cd 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
 
 # ARM Architecture
 obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o
+obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o
 obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
 obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
 obj-$(CONFIG_CADENCE_WATCHDOG) += cadence_wdt.o
diff --git a/drivers/watchdog/asm9260_wdt.c b/drivers/watchdog/asm9260_wdt.c
new file mode 100644
index 0000000..e5f859b
--- /dev/null
+++ b/drivers/watchdog/asm9260_wdt.c
@@ -0,0 +1,405 @@
+/*
+ * Watchdog driver for Alphascale ASM9260.
+ *
+ * Copyright (c) 2014 Oleksij Rempel <linux@rempel-privat.de>
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/reset.h>
+#include <linux/uaccess.h>
+#include <linux/watchdog.h>
+
+#define CLOCK_FREQ	1000000
+
+/* Watchdog Mode register */
+#define HW_WDMOD			0x00
+/* Wake interrupt. Set by HW, can't be cleared. */
+#define BM_MOD_WDINT			BIT(3)
+/* This bit set if timeout reached. Cleared by SW. */
+#define BM_MOD_WDTOF			BIT(2)
+/* HW Reset on timeout */
+#define BM_MOD_WDRESET			BIT(1)
+/* WD enable */
+#define BM_MOD_WDEN			BIT(0)
+
+/*
+ * Watchdog Timer Constant register
+ * Minimal value is 0xff, the meaning of this value
+ * depends on used clock: T = WDCLK * (0xff + 1) * 4
+ */
+#define HW_WDTC				0x04
+#define BM_WDTC_MAX(freq)		(0x7fffffff / (freq))
+
+/* Watchdog Feed register */
+#define HW_WDFEED			0x08
+
+/* Watchdog Timer Value register */
+#define HW_WDTV				0x0c
+
+#define ASM9260_WDT_DEFAULT_TIMEOUT	30
+
+enum asm9260_wdt_mode {
+	HW_RESET,
+	SW_RESET,
+	DEBUG,
+};
+
+struct asm9260_wdt_priv {
+	struct device		*dev;
+	struct watchdog_device	wdd;
+	struct clk		*clk;
+	struct clk		*clk_ahb;
+	struct reset_control	*rst;
+	struct notifier_block	restart_handler;
+
+	void __iomem		*iobase;
+	int			irq;
+	unsigned long		wdt_freq;
+	enum asm9260_wdt_mode	mode;
+};
+
+static int asm9260_wdt_feed(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+
+	iowrite32(0xaa, priv->iobase + HW_WDFEED);
+	iowrite32(0x55, priv->iobase + HW_WDFEED);
+
+	return 0;
+}
+
+static unsigned int asm9260_wdt_gettimeleft(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+	u32 counter;
+
+	counter = ioread32(priv->iobase + HW_WDTV);
+
+	return DIV_ROUND_CLOSEST(counter, priv->wdt_freq);
+}
+
+static int asm9260_wdt_updatetimeout(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+	u32 counter;
+
+	counter = wdd->timeout * priv->wdt_freq;
+
+	iowrite32(counter, priv->iobase + HW_WDTC);
+
+	return 0;
+}
+
+static int asm9260_wdt_enable(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+	u32 mode = 0;
+
+	if (priv->mode == HW_RESET)
+		mode = BM_MOD_WDRESET;
+
+	iowrite32(BM_MOD_WDEN | mode, priv->iobase + HW_WDMOD);
+
+	asm9260_wdt_updatetimeout(wdd);
+
+	asm9260_wdt_feed(wdd);
+
+	return 0;
+}
+
+static int asm9260_wdt_disable(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+
+	/* The only way to disable WD is to reset it. */
+	reset_control_assert(priv->rst);
+	reset_control_deassert(priv->rst);
+
+	return 0;
+}
+
+static int asm9260_wdt_settimeout(struct watchdog_device *wdd, unsigned int to)
+{
+	wdd->timeout = to;
+	asm9260_wdt_updatetimeout(wdd);
+
+	return 0;
+}
+
+static void asm9260_wdt_sys_reset(struct asm9260_wdt_priv *priv)
+{
+	/* init WD if it was not started */
+	priv->wdd.timeout = 1;
+
+	iowrite32(BM_MOD_WDEN | BM_MOD_WDRESET, priv->iobase + HW_WDMOD);
+
+	asm9260_wdt_updatetimeout(&priv->wdd);
+	/* first pass correct sequence */
+	asm9260_wdt_feed(&priv->wdd);
+	/*
+	 * Then write wrong pattern to the feed to trigger reset
+	 * ASAP.
+	 */
+	iowrite32(0xff, priv->iobase + HW_WDFEED);
+
+	mdelay(1000);
+}
+
+static irqreturn_t asm9260_wdt_irq(int irq, void *devid)
+{
+	struct asm9260_wdt_priv *priv = devid;
+	u32 stat;
+
+	stat = ioread32(priv->iobase + HW_WDMOD);
+	if (!(stat & BM_MOD_WDINT))
+		return IRQ_NONE;
+
+	/*
+	 * BM_MOD_WDINT flag can't be removed by SW. Only way is
+	 * to reset WD Controller.
+	 * TODO: add Controller reset if needed.
+	 */
+	if (priv->mode == DEBUG)
+		dev_info(priv->dev, "Watchdog Timeout. Do nothing.\n");
+	else {
+		dev_info(priv->dev, "Watchdog Timeout. Doing SW Reset.\n");
+		asm9260_wdt_sys_reset(priv);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int asm9260_restart_handler(struct notifier_block *this,
+				unsigned long mode, void *cmd)
+{
+	struct asm9260_wdt_priv *priv =
+		container_of(this, struct asm9260_wdt_priv, restart_handler);
+
+	asm9260_wdt_sys_reset(priv);
+
+	return NOTIFY_DONE;
+}
+
+static const struct watchdog_info asm9260_wdt_ident = {
+	.options          =     WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING
+				| WDIOF_MAGICCLOSE,
+	.firmware_version =	0,
+	.identity         =	"Alphascale asm9260 Watchdog",
+};
+
+static struct watchdog_ops asm9260_wdt_ops = {
+	.owner		= THIS_MODULE,
+	.start		= asm9260_wdt_enable,
+	.stop		= asm9260_wdt_disable,
+	.get_timeleft	= asm9260_wdt_gettimeleft,
+	.ping		= asm9260_wdt_feed,
+	.set_timeout	= asm9260_wdt_settimeout,
+};
+
+static int __init asm9260_wdt_get_dt_clks(struct asm9260_wdt_priv *priv)
+{
+	int clk_idx = 0, err;
+
+	priv->clk = devm_clk_get(priv->dev, "mod");
+	if (IS_ERR(priv->clk))
+		goto out_err;
+
+	/* configure AHB clock */
+	clk_idx = 1;
+	priv->clk_ahb = devm_clk_get(priv->dev, "ahb");
+	if (IS_ERR(priv->clk_ahb))
+		goto out_err;
+
+	err = clk_prepare_enable(priv->clk_ahb);
+	if (err)
+		dev_err(priv->dev, "Failed to enable ahb_clk!\n");
+
+	err = clk_set_rate(priv->clk, CLOCK_FREQ);
+	if (err) {
+		clk_disable_unprepare(priv->clk_ahb);
+		dev_err(priv->dev, "Failed to set rate!\n");
+	}
+
+	err = clk_prepare_enable(priv->clk);
+	if (err) {
+		clk_disable_unprepare(priv->clk_ahb);
+		dev_err(priv->dev, "Failed to enable clk!\n");
+	}
+
+	/* wdt has internal divider */
+	priv->wdt_freq = clk_get_rate(priv->clk) / 2;
+
+	return 0;
+out_err:
+	dev_err(priv->dev, "%s: Failed to get clk (%i)\n", __func__, clk_idx);
+	return 1;
+}
+
+static void __init asm9260_wdt_get_dt_mode(struct asm9260_wdt_priv *priv)
+{
+	const char *tmp;
+	int ret;
+
+	/* default mode */
+	priv->mode = HW_RESET;
+
+	ret = of_property_read_string(priv->dev->of_node,
+			"alphascale,mode", &tmp);
+	if (ret)
+		return;
+
+	if (!strcmp(tmp, "hw"))
+		priv->mode = HW_RESET;
+	else if (!strcmp(tmp, "sw"))
+		priv->mode = SW_RESET;
+	else if (!strcmp(tmp, "debug"))
+		priv->mode = DEBUG;
+	else {
+		dev_warn(priv->dev, "unknown reset-type: %s. Using defaul \"hw\" mode.",
+			tmp);
+		return;
+	}
+
+	dev_info(priv->dev, "using \"%s\" mode.", tmp);
+}
+
+static int __init asm9260_wdt_probe(struct platform_device *pdev)
+{
+	struct asm9260_wdt_priv *priv;
+	struct watchdog_device *wdd;
+	struct resource *res;
+	bool nowayout = WATCHDOG_NOWAYOUT;
+	int ret;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(struct asm9260_wdt_priv),
+			GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->dev = &pdev->dev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	priv->iobase = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(priv->iobase))
+		return PTR_ERR(priv->iobase);
+
+	ret = asm9260_wdt_get_dt_clks(priv);
+	if (ret)
+		return ret;
+
+	priv->rst = devm_reset_control_get(&pdev->dev, "wdt_rst");
+	if (IS_ERR(priv->rst)) {
+		dev_info(&pdev->dev, "No reset control found. Enabling \"nowayout\" mode\n");
+		nowayout = 1;
+		priv->rst = NULL;
+	}
+
+	wdd = &priv->wdd;
+	wdd->info = &asm9260_wdt_ident;
+	wdd->ops = &asm9260_wdt_ops;
+	wdd->min_timeout = 1;
+	wdd->max_timeout = BM_WDTC_MAX(priv->wdt_freq);
+	wdd->parent = &pdev->dev;
+
+	watchdog_set_drvdata(wdd, priv);
+
+	/*
+	 * If 'timeout-sec' unspecified in devicetree, assume a 30 second
+	 * default, unless the max timeout is less than 30 seconds, then use
+	 * the max instead.
+	 */
+	wdd->timeout = ASM9260_WDT_DEFAULT_TIMEOUT;
+	watchdog_init_timeout(wdd, 0, &pdev->dev);
+
+	watchdog_set_nowayout(wdd, nowayout);
+
+	asm9260_wdt_get_dt_mode(priv);
+
+	if (priv->mode != HW_RESET)
+		priv->irq = platform_get_irq(pdev, 0);
+
+	if (priv->irq > 0) {
+		/*
+		 * Not all supported platforms specify an interrupt for the
+		 * watchdog, so let's make it optional.
+		 */
+		ret = devm_request_irq(&pdev->dev, priv->irq,
+				asm9260_wdt_irq, IRQF_SHARED,
+				pdev->name, priv);
+		if (ret < 0)
+			dev_err(&pdev->dev, "failed to request IRQ\n");
+	}
+
+	ret = watchdog_register_device(wdd);
+	if (ret)
+		goto clk_off;
+
+	platform_set_drvdata(pdev, priv);
+
+	priv->restart_handler.notifier_call = asm9260_restart_handler;
+	priv->restart_handler.priority = 128;
+	ret = register_restart_handler(&priv->restart_handler);
+	if (ret)
+		dev_err(&pdev->dev, "cannot register restart handler\n");
+
+	dev_info(&pdev->dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)",
+		wdd->timeout, nowayout);
+	return 0;
+
+clk_off:
+	clk_disable_unprepare(priv->clk);
+	clk_disable_unprepare(priv->clk_ahb);
+	return ret;
+}
+
+static void asm9260_wdt_shutdown(struct platform_device *pdev)
+{
+	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
+
+	asm9260_wdt_disable(&priv->wdd);
+}
+
+static int __exit asm9260_wdt_remove(struct platform_device *pdev)
+{
+	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
+
+	asm9260_wdt_shutdown(pdev);
+
+	unregister_restart_handler(&priv->restart_handler);
+	clk_disable_unprepare(priv->clk);
+	clk_disable_unprepare(priv->clk_ahb);
+
+	return 0;
+}
+
+static const struct of_device_id asm9260_wdt_of_match[] = {
+	{ .compatible = "alphascale,asm9260-wdt"},
+	{},
+};
+MODULE_DEVICE_TABLE(of, asm9260_wdt_of_match);
+
+static struct platform_driver asm9260_wdt_driver = {
+	.driver = {
+		.name = "asm9260-wdt",
+		.owner = THIS_MODULE,
+		.of_match_table	= asm9260_wdt_of_match,
+	},
+	.probe = asm9260_wdt_probe,
+	.remove = asm9260_wdt_remove,
+	.shutdown = asm9260_wdt_shutdown,
+};
+module_platform_driver(asm9260_wdt_driver);
+
+MODULE_DESCRIPTION("asm9260 WatchDog Timer Driver");
+MODULE_AUTHOR("Oleksij Rempel <linux@rempel-privat.de>");
+MODULE_LICENSE("GPL");
-- 
2.5.0


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

* Re: [PATCH] watchdog: add Alphascale asm9260-wdt driver
  2015-09-18  9:23 [PATCH] watchdog: add Alphascale asm9260-wdt driver Oleksij Rempel
@ 2015-10-20  7:49 ` Oleksij Rempel
  2015-10-26 12:31   ` Guenter Roeck
  2015-10-27  2:31 ` Guenter Roeck
  1 sibling, 1 reply; 43+ messages in thread
From: Oleksij Rempel @ 2015-10-20  7:49 UTC (permalink / raw)
  To: linux-watchdog, wim

[-- Attachment #1: Type: text/plain, Size: 15292 bytes --]

Hallo all,

any feedback here? or it can be merged?

Am 18.09.2015 um 11:23 schrieb Oleksij Rempel:
> Add WD support for Alphascale asm9260 SoC. This driver
> provide support for different function modes:
> - HW mode to trigger SoC reset on timeout
> - SW mode do soft reset if needed
> - DEBUG mode
> 
> Optional support for stopping watchdog. If reset binding are not provided
> this driver will work in nowayout mode.
> 
> Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
> ---
>  .../bindings/watchdog/alphascale-asm9260.txt       |  38 ++
>  drivers/watchdog/Kconfig                           |   9 +
>  drivers/watchdog/Makefile                          |   1 +
>  drivers/watchdog/asm9260_wdt.c                     | 405 +++++++++++++++++++++
>  4 files changed, 453 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
>  create mode 100644 drivers/watchdog/asm9260_wdt.c
> 
> diff --git a/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
> new file mode 100644
> index 0000000..cbe388b
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
> @@ -0,0 +1,38 @@
> +Alphascale asm9260 Watchdog timer
> +
> +Required properties:
> +
> +- compatible : should be "alphascale,asm9260-wdt".
> +- reg : Specifies base physical address and size of the registers.
> +- clocks : the clock feeding the watchdog timer.
> +	Needed if platform uses clocks. See clock-bindings.txt
> +- clock-names : should be set to
> +	"mod" - source for tick counter.
> +	"ahb" - ahb gate.
> +
> +Optional properties:
> +- resets : phandle pointing to the system reset controller with correct
> +	reset line index for watchdog controller reset. This propertie is
> +	required if you need to disable "nowayout" and it works only with
> +	CONFIG_WATCHDOG_NOWAYOUT=n.
> +- reset-names : should be set to "wdt_rst" if "resets" is used.
> +- timeout-sec : shall contain the default watchdog timeout in seconds,
> +	if unset, the default timeout is 30 seconds.
> +- alphascale,mode : tree modes are supported
> +	"hw" - hw reset (defaul).
> +	"sw" - sw reset.
> +	"debug" - no action is taken.
> +
> +Example:
> +
> +watchdog0: watchdog@80048000 {
> +	compatible = "alphascale,asm9260-wdt";
> +	reg = <0x80048000 0x10>;
> +	clocks = <&acc CLKID_SYS_WDT>, <&acc CLKID_AHB_WDT>;
> +	clock-names = "mod", "ahb";
> +	interrupts = <55>;
> +	resets = <&rst WDT_RESET>;
> +	reset-names = "wdt_rst";
> +	timeout-sec = <30>;
> +	alphascale,mode = "hw"
> +};
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index c68edc1..cc5f675 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -173,6 +173,15 @@ config ARM_SP805_WATCHDOG
>  	  ARM Primecell SP805 Watchdog timer. This will reboot your system when
>  	  the timeout is reached.
>  
> +config ASM9260_WATCHDOG
> +	tristate "Alphascale ASM9260 watchdog"
> +	depends on MACH_ASM9260
> +	depends on OF
> +	select WATCHDOG_CORE
> +	help
> +	  Watchdog timer embedded into Alphascale asm9260 chips. This will reboot your
> +	  system when the timeout is reached.
> +
>  config AT91RM9200_WATCHDOG
>  	tristate "AT91RM9200 watchdog"
>  	depends on SOC_AT91RM9200 && MFD_SYSCON
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index 0c616e3..bd7b0cd 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -30,6 +30,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
>  
>  # ARM Architecture
>  obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o
> +obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o
>  obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
>  obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
>  obj-$(CONFIG_CADENCE_WATCHDOG) += cadence_wdt.o
> diff --git a/drivers/watchdog/asm9260_wdt.c b/drivers/watchdog/asm9260_wdt.c
> new file mode 100644
> index 0000000..e5f859b
> --- /dev/null
> +++ b/drivers/watchdog/asm9260_wdt.c
> @@ -0,0 +1,405 @@
> +/*
> + * Watchdog driver for Alphascale ASM9260.
> + *
> + * Copyright (c) 2014 Oleksij Rempel <linux@rempel-privat.de>
> + *
> + * Licensed under GPLv2 or later.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/clk.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/reboot.h>
> +#include <linux/reset.h>
> +#include <linux/uaccess.h>
> +#include <linux/watchdog.h>
> +
> +#define CLOCK_FREQ	1000000
> +
> +/* Watchdog Mode register */
> +#define HW_WDMOD			0x00
> +/* Wake interrupt. Set by HW, can't be cleared. */
> +#define BM_MOD_WDINT			BIT(3)
> +/* This bit set if timeout reached. Cleared by SW. */
> +#define BM_MOD_WDTOF			BIT(2)
> +/* HW Reset on timeout */
> +#define BM_MOD_WDRESET			BIT(1)
> +/* WD enable */
> +#define BM_MOD_WDEN			BIT(0)
> +
> +/*
> + * Watchdog Timer Constant register
> + * Minimal value is 0xff, the meaning of this value
> + * depends on used clock: T = WDCLK * (0xff + 1) * 4
> + */
> +#define HW_WDTC				0x04
> +#define BM_WDTC_MAX(freq)		(0x7fffffff / (freq))
> +
> +/* Watchdog Feed register */
> +#define HW_WDFEED			0x08
> +
> +/* Watchdog Timer Value register */
> +#define HW_WDTV				0x0c
> +
> +#define ASM9260_WDT_DEFAULT_TIMEOUT	30
> +
> +enum asm9260_wdt_mode {
> +	HW_RESET,
> +	SW_RESET,
> +	DEBUG,
> +};
> +
> +struct asm9260_wdt_priv {
> +	struct device		*dev;
> +	struct watchdog_device	wdd;
> +	struct clk		*clk;
> +	struct clk		*clk_ahb;
> +	struct reset_control	*rst;
> +	struct notifier_block	restart_handler;
> +
> +	void __iomem		*iobase;
> +	int			irq;
> +	unsigned long		wdt_freq;
> +	enum asm9260_wdt_mode	mode;
> +};
> +
> +static int asm9260_wdt_feed(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +
> +	iowrite32(0xaa, priv->iobase + HW_WDFEED);
> +	iowrite32(0x55, priv->iobase + HW_WDFEED);
> +
> +	return 0;
> +}
> +
> +static unsigned int asm9260_wdt_gettimeleft(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +	u32 counter;
> +
> +	counter = ioread32(priv->iobase + HW_WDTV);
> +
> +	return DIV_ROUND_CLOSEST(counter, priv->wdt_freq);
> +}
> +
> +static int asm9260_wdt_updatetimeout(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +	u32 counter;
> +
> +	counter = wdd->timeout * priv->wdt_freq;
> +
> +	iowrite32(counter, priv->iobase + HW_WDTC);
> +
> +	return 0;
> +}
> +
> +static int asm9260_wdt_enable(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +	u32 mode = 0;
> +
> +	if (priv->mode == HW_RESET)
> +		mode = BM_MOD_WDRESET;
> +
> +	iowrite32(BM_MOD_WDEN | mode, priv->iobase + HW_WDMOD);
> +
> +	asm9260_wdt_updatetimeout(wdd);
> +
> +	asm9260_wdt_feed(wdd);
> +
> +	return 0;
> +}
> +
> +static int asm9260_wdt_disable(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +
> +	/* The only way to disable WD is to reset it. */
> +	reset_control_assert(priv->rst);
> +	reset_control_deassert(priv->rst);
> +
> +	return 0;
> +}
> +
> +static int asm9260_wdt_settimeout(struct watchdog_device *wdd, unsigned int to)
> +{
> +	wdd->timeout = to;
> +	asm9260_wdt_updatetimeout(wdd);
> +
> +	return 0;
> +}
> +
> +static void asm9260_wdt_sys_reset(struct asm9260_wdt_priv *priv)
> +{
> +	/* init WD if it was not started */
> +	priv->wdd.timeout = 1;
> +
> +	iowrite32(BM_MOD_WDEN | BM_MOD_WDRESET, priv->iobase + HW_WDMOD);
> +
> +	asm9260_wdt_updatetimeout(&priv->wdd);
> +	/* first pass correct sequence */
> +	asm9260_wdt_feed(&priv->wdd);
> +	/*
> +	 * Then write wrong pattern to the feed to trigger reset
> +	 * ASAP.
> +	 */
> +	iowrite32(0xff, priv->iobase + HW_WDFEED);
> +
> +	mdelay(1000);
> +}
> +
> +static irqreturn_t asm9260_wdt_irq(int irq, void *devid)
> +{
> +	struct asm9260_wdt_priv *priv = devid;
> +	u32 stat;
> +
> +	stat = ioread32(priv->iobase + HW_WDMOD);
> +	if (!(stat & BM_MOD_WDINT))
> +		return IRQ_NONE;
> +
> +	/*
> +	 * BM_MOD_WDINT flag can't be removed by SW. Only way is
> +	 * to reset WD Controller.
> +	 * TODO: add Controller reset if needed.
> +	 */
> +	if (priv->mode == DEBUG)
> +		dev_info(priv->dev, "Watchdog Timeout. Do nothing.\n");
> +	else {
> +		dev_info(priv->dev, "Watchdog Timeout. Doing SW Reset.\n");
> +		asm9260_wdt_sys_reset(priv);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int asm9260_restart_handler(struct notifier_block *this,
> +				unsigned long mode, void *cmd)
> +{
> +	struct asm9260_wdt_priv *priv =
> +		container_of(this, struct asm9260_wdt_priv, restart_handler);
> +
> +	asm9260_wdt_sys_reset(priv);
> +
> +	return NOTIFY_DONE;
> +}
> +
> +static const struct watchdog_info asm9260_wdt_ident = {
> +	.options          =     WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING
> +				| WDIOF_MAGICCLOSE,
> +	.firmware_version =	0,
> +	.identity         =	"Alphascale asm9260 Watchdog",
> +};
> +
> +static struct watchdog_ops asm9260_wdt_ops = {
> +	.owner		= THIS_MODULE,
> +	.start		= asm9260_wdt_enable,
> +	.stop		= asm9260_wdt_disable,
> +	.get_timeleft	= asm9260_wdt_gettimeleft,
> +	.ping		= asm9260_wdt_feed,
> +	.set_timeout	= asm9260_wdt_settimeout,
> +};
> +
> +static int __init asm9260_wdt_get_dt_clks(struct asm9260_wdt_priv *priv)
> +{
> +	int clk_idx = 0, err;
> +
> +	priv->clk = devm_clk_get(priv->dev, "mod");
> +	if (IS_ERR(priv->clk))
> +		goto out_err;
> +
> +	/* configure AHB clock */
> +	clk_idx = 1;
> +	priv->clk_ahb = devm_clk_get(priv->dev, "ahb");
> +	if (IS_ERR(priv->clk_ahb))
> +		goto out_err;
> +
> +	err = clk_prepare_enable(priv->clk_ahb);
> +	if (err)
> +		dev_err(priv->dev, "Failed to enable ahb_clk!\n");
> +
> +	err = clk_set_rate(priv->clk, CLOCK_FREQ);
> +	if (err) {
> +		clk_disable_unprepare(priv->clk_ahb);
> +		dev_err(priv->dev, "Failed to set rate!\n");
> +	}
> +
> +	err = clk_prepare_enable(priv->clk);
> +	if (err) {
> +		clk_disable_unprepare(priv->clk_ahb);
> +		dev_err(priv->dev, "Failed to enable clk!\n");
> +	}
> +
> +	/* wdt has internal divider */
> +	priv->wdt_freq = clk_get_rate(priv->clk) / 2;
> +
> +	return 0;
> +out_err:
> +	dev_err(priv->dev, "%s: Failed to get clk (%i)\n", __func__, clk_idx);
> +	return 1;
> +}
> +
> +static void __init asm9260_wdt_get_dt_mode(struct asm9260_wdt_priv *priv)
> +{
> +	const char *tmp;
> +	int ret;
> +
> +	/* default mode */
> +	priv->mode = HW_RESET;
> +
> +	ret = of_property_read_string(priv->dev->of_node,
> +			"alphascale,mode", &tmp);
> +	if (ret)
> +		return;
> +
> +	if (!strcmp(tmp, "hw"))
> +		priv->mode = HW_RESET;
> +	else if (!strcmp(tmp, "sw"))
> +		priv->mode = SW_RESET;
> +	else if (!strcmp(tmp, "debug"))
> +		priv->mode = DEBUG;
> +	else {
> +		dev_warn(priv->dev, "unknown reset-type: %s. Using defaul \"hw\" mode.",
> +			tmp);
> +		return;
> +	}
> +
> +	dev_info(priv->dev, "using \"%s\" mode.", tmp);
> +}
> +
> +static int __init asm9260_wdt_probe(struct platform_device *pdev)
> +{
> +	struct asm9260_wdt_priv *priv;
> +	struct watchdog_device *wdd;
> +	struct resource *res;
> +	bool nowayout = WATCHDOG_NOWAYOUT;
> +	int ret;
> +
> +	priv = devm_kzalloc(&pdev->dev, sizeof(struct asm9260_wdt_priv),
> +			GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->dev = &pdev->dev;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	priv->iobase = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(priv->iobase))
> +		return PTR_ERR(priv->iobase);
> +
> +	ret = asm9260_wdt_get_dt_clks(priv);
> +	if (ret)
> +		return ret;
> +
> +	priv->rst = devm_reset_control_get(&pdev->dev, "wdt_rst");
> +	if (IS_ERR(priv->rst)) {
> +		dev_info(&pdev->dev, "No reset control found. Enabling \"nowayout\" mode\n");
> +		nowayout = 1;
> +		priv->rst = NULL;
> +	}
> +
> +	wdd = &priv->wdd;
> +	wdd->info = &asm9260_wdt_ident;
> +	wdd->ops = &asm9260_wdt_ops;
> +	wdd->min_timeout = 1;
> +	wdd->max_timeout = BM_WDTC_MAX(priv->wdt_freq);
> +	wdd->parent = &pdev->dev;
> +
> +	watchdog_set_drvdata(wdd, priv);
> +
> +	/*
> +	 * If 'timeout-sec' unspecified in devicetree, assume a 30 second
> +	 * default, unless the max timeout is less than 30 seconds, then use
> +	 * the max instead.
> +	 */
> +	wdd->timeout = ASM9260_WDT_DEFAULT_TIMEOUT;
> +	watchdog_init_timeout(wdd, 0, &pdev->dev);
> +
> +	watchdog_set_nowayout(wdd, nowayout);
> +
> +	asm9260_wdt_get_dt_mode(priv);
> +
> +	if (priv->mode != HW_RESET)
> +		priv->irq = platform_get_irq(pdev, 0);
> +
> +	if (priv->irq > 0) {
> +		/*
> +		 * Not all supported platforms specify an interrupt for the
> +		 * watchdog, so let's make it optional.
> +		 */
> +		ret = devm_request_irq(&pdev->dev, priv->irq,
> +				asm9260_wdt_irq, IRQF_SHARED,
> +				pdev->name, priv);
> +		if (ret < 0)
> +			dev_err(&pdev->dev, "failed to request IRQ\n");
> +	}
> +
> +	ret = watchdog_register_device(wdd);
> +	if (ret)
> +		goto clk_off;
> +
> +	platform_set_drvdata(pdev, priv);
> +
> +	priv->restart_handler.notifier_call = asm9260_restart_handler;
> +	priv->restart_handler.priority = 128;
> +	ret = register_restart_handler(&priv->restart_handler);
> +	if (ret)
> +		dev_err(&pdev->dev, "cannot register restart handler\n");
> +
> +	dev_info(&pdev->dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)",
> +		wdd->timeout, nowayout);
> +	return 0;
> +
> +clk_off:
> +	clk_disable_unprepare(priv->clk);
> +	clk_disable_unprepare(priv->clk_ahb);
> +	return ret;
> +}
> +
> +static void asm9260_wdt_shutdown(struct platform_device *pdev)
> +{
> +	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
> +
> +	asm9260_wdt_disable(&priv->wdd);
> +}
> +
> +static int __exit asm9260_wdt_remove(struct platform_device *pdev)
> +{
> +	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
> +
> +	asm9260_wdt_shutdown(pdev);
> +
> +	unregister_restart_handler(&priv->restart_handler);
> +	clk_disable_unprepare(priv->clk);
> +	clk_disable_unprepare(priv->clk_ahb);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id asm9260_wdt_of_match[] = {
> +	{ .compatible = "alphascale,asm9260-wdt"},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, asm9260_wdt_of_match);
> +
> +static struct platform_driver asm9260_wdt_driver = {
> +	.driver = {
> +		.name = "asm9260-wdt",
> +		.owner = THIS_MODULE,
> +		.of_match_table	= asm9260_wdt_of_match,
> +	},
> +	.probe = asm9260_wdt_probe,
> +	.remove = asm9260_wdt_remove,
> +	.shutdown = asm9260_wdt_shutdown,
> +};
> +module_platform_driver(asm9260_wdt_driver);
> +
> +MODULE_DESCRIPTION("asm9260 WatchDog Timer Driver");
> +MODULE_AUTHOR("Oleksij Rempel <linux@rempel-privat.de>");
> +MODULE_LICENSE("GPL");
> 


-- 
Regards,
Oleksij


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 213 bytes --]

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

* Re: [PATCH] watchdog: add Alphascale asm9260-wdt driver
  2015-10-20  7:49 ` Oleksij Rempel
@ 2015-10-26 12:31   ` Guenter Roeck
  0 siblings, 0 replies; 43+ messages in thread
From: Guenter Roeck @ 2015-10-26 12:31 UTC (permalink / raw)
  To: Oleksij Rempel; +Cc: linux-watchdog, wim

On Tue, Oct 20, 2015 at 09:49:10AM +0200, Oleksij Rempel wrote:
> Hallo all,
> 
> any feedback here? or it can be merged?
> 
>From my side just too much work and not enough time :-(

Guenter

> Am 18.09.2015 um 11:23 schrieb Oleksij Rempel:
> > Add WD support for Alphascale asm9260 SoC. This driver
> > provide support for different function modes:
> > - HW mode to trigger SoC reset on timeout
> > - SW mode do soft reset if needed
> > - DEBUG mode
> > 
> > Optional support for stopping watchdog. If reset binding are not provided
> > this driver will work in nowayout mode.
> > 
> > Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
> > ---
> >  .../bindings/watchdog/alphascale-asm9260.txt       |  38 ++
> >  drivers/watchdog/Kconfig                           |   9 +
> >  drivers/watchdog/Makefile                          |   1 +
> >  drivers/watchdog/asm9260_wdt.c                     | 405 +++++++++++++++++++++
> >  4 files changed, 453 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
> >  create mode 100644 drivers/watchdog/asm9260_wdt.c
> > 
> > diff --git a/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
> > new file mode 100644
> > index 0000000..cbe388b
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
> > @@ -0,0 +1,38 @@
> > +Alphascale asm9260 Watchdog timer
> > +
> > +Required properties:
> > +
> > +- compatible : should be "alphascale,asm9260-wdt".
> > +- reg : Specifies base physical address and size of the registers.
> > +- clocks : the clock feeding the watchdog timer.
> > +	Needed if platform uses clocks. See clock-bindings.txt
> > +- clock-names : should be set to
> > +	"mod" - source for tick counter.
> > +	"ahb" - ahb gate.
> > +
> > +Optional properties:
> > +- resets : phandle pointing to the system reset controller with correct
> > +	reset line index for watchdog controller reset. This propertie is
> > +	required if you need to disable "nowayout" and it works only with
> > +	CONFIG_WATCHDOG_NOWAYOUT=n.
> > +- reset-names : should be set to "wdt_rst" if "resets" is used.
> > +- timeout-sec : shall contain the default watchdog timeout in seconds,
> > +	if unset, the default timeout is 30 seconds.
> > +- alphascale,mode : tree modes are supported
> > +	"hw" - hw reset (defaul).
> > +	"sw" - sw reset.
> > +	"debug" - no action is taken.
> > +
> > +Example:
> > +
> > +watchdog0: watchdog@80048000 {
> > +	compatible = "alphascale,asm9260-wdt";
> > +	reg = <0x80048000 0x10>;
> > +	clocks = <&acc CLKID_SYS_WDT>, <&acc CLKID_AHB_WDT>;
> > +	clock-names = "mod", "ahb";
> > +	interrupts = <55>;
> > +	resets = <&rst WDT_RESET>;
> > +	reset-names = "wdt_rst";
> > +	timeout-sec = <30>;
> > +	alphascale,mode = "hw"
> > +};
> > diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> > index c68edc1..cc5f675 100644
> > --- a/drivers/watchdog/Kconfig
> > +++ b/drivers/watchdog/Kconfig
> > @@ -173,6 +173,15 @@ config ARM_SP805_WATCHDOG
> >  	  ARM Primecell SP805 Watchdog timer. This will reboot your system when
> >  	  the timeout is reached.
> >  
> > +config ASM9260_WATCHDOG
> > +	tristate "Alphascale ASM9260 watchdog"
> > +	depends on MACH_ASM9260
> > +	depends on OF
> > +	select WATCHDOG_CORE
> > +	help
> > +	  Watchdog timer embedded into Alphascale asm9260 chips. This will reboot your
> > +	  system when the timeout is reached.
> > +
> >  config AT91RM9200_WATCHDOG
> >  	tristate "AT91RM9200 watchdog"
> >  	depends on SOC_AT91RM9200 && MFD_SYSCON
> > diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> > index 0c616e3..bd7b0cd 100644
> > --- a/drivers/watchdog/Makefile
> > +++ b/drivers/watchdog/Makefile
> > @@ -30,6 +30,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
> >  
> >  # ARM Architecture
> >  obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o
> > +obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o
> >  obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
> >  obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
> >  obj-$(CONFIG_CADENCE_WATCHDOG) += cadence_wdt.o
> > diff --git a/drivers/watchdog/asm9260_wdt.c b/drivers/watchdog/asm9260_wdt.c
> > new file mode 100644
> > index 0000000..e5f859b
> > --- /dev/null
> > +++ b/drivers/watchdog/asm9260_wdt.c
> > @@ -0,0 +1,405 @@
> > +/*
> > + * Watchdog driver for Alphascale ASM9260.
> > + *
> > + * Copyright (c) 2014 Oleksij Rempel <linux@rempel-privat.de>
> > + *
> > + * Licensed under GPLv2 or later.
> > + */
> > +
> > +#include <linux/delay.h>
> > +#include <linux/clk.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/io.h>
> > +#include <linux/module.h>
> > +#include <linux/moduleparam.h>
> > +#include <linux/of.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/reboot.h>
> > +#include <linux/reset.h>
> > +#include <linux/uaccess.h>
> > +#include <linux/watchdog.h>
> > +
> > +#define CLOCK_FREQ	1000000
> > +
> > +/* Watchdog Mode register */
> > +#define HW_WDMOD			0x00
> > +/* Wake interrupt. Set by HW, can't be cleared. */
> > +#define BM_MOD_WDINT			BIT(3)
> > +/* This bit set if timeout reached. Cleared by SW. */
> > +#define BM_MOD_WDTOF			BIT(2)
> > +/* HW Reset on timeout */
> > +#define BM_MOD_WDRESET			BIT(1)
> > +/* WD enable */
> > +#define BM_MOD_WDEN			BIT(0)
> > +
> > +/*
> > + * Watchdog Timer Constant register
> > + * Minimal value is 0xff, the meaning of this value
> > + * depends on used clock: T = WDCLK * (0xff + 1) * 4
> > + */
> > +#define HW_WDTC				0x04
> > +#define BM_WDTC_MAX(freq)		(0x7fffffff / (freq))
> > +
> > +/* Watchdog Feed register */
> > +#define HW_WDFEED			0x08
> > +
> > +/* Watchdog Timer Value register */
> > +#define HW_WDTV				0x0c
> > +
> > +#define ASM9260_WDT_DEFAULT_TIMEOUT	30
> > +
> > +enum asm9260_wdt_mode {
> > +	HW_RESET,
> > +	SW_RESET,
> > +	DEBUG,
> > +};
> > +
> > +struct asm9260_wdt_priv {
> > +	struct device		*dev;
> > +	struct watchdog_device	wdd;
> > +	struct clk		*clk;
> > +	struct clk		*clk_ahb;
> > +	struct reset_control	*rst;
> > +	struct notifier_block	restart_handler;
> > +
> > +	void __iomem		*iobase;
> > +	int			irq;
> > +	unsigned long		wdt_freq;
> > +	enum asm9260_wdt_mode	mode;
> > +};
> > +
> > +static int asm9260_wdt_feed(struct watchdog_device *wdd)
> > +{
> > +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> > +
> > +	iowrite32(0xaa, priv->iobase + HW_WDFEED);
> > +	iowrite32(0x55, priv->iobase + HW_WDFEED);
> > +
> > +	return 0;
> > +}
> > +
> > +static unsigned int asm9260_wdt_gettimeleft(struct watchdog_device *wdd)
> > +{
> > +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> > +	u32 counter;
> > +
> > +	counter = ioread32(priv->iobase + HW_WDTV);
> > +
> > +	return DIV_ROUND_CLOSEST(counter, priv->wdt_freq);
> > +}
> > +
> > +static int asm9260_wdt_updatetimeout(struct watchdog_device *wdd)
> > +{
> > +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> > +	u32 counter;
> > +
> > +	counter = wdd->timeout * priv->wdt_freq;
> > +
> > +	iowrite32(counter, priv->iobase + HW_WDTC);
> > +
> > +	return 0;
> > +}
> > +
> > +static int asm9260_wdt_enable(struct watchdog_device *wdd)
> > +{
> > +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> > +	u32 mode = 0;
> > +
> > +	if (priv->mode == HW_RESET)
> > +		mode = BM_MOD_WDRESET;
> > +
> > +	iowrite32(BM_MOD_WDEN | mode, priv->iobase + HW_WDMOD);
> > +
> > +	asm9260_wdt_updatetimeout(wdd);
> > +
> > +	asm9260_wdt_feed(wdd);
> > +
> > +	return 0;
> > +}
> > +
> > +static int asm9260_wdt_disable(struct watchdog_device *wdd)
> > +{
> > +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> > +
> > +	/* The only way to disable WD is to reset it. */
> > +	reset_control_assert(priv->rst);
> > +	reset_control_deassert(priv->rst);
> > +
> > +	return 0;
> > +}
> > +
> > +static int asm9260_wdt_settimeout(struct watchdog_device *wdd, unsigned int to)
> > +{
> > +	wdd->timeout = to;
> > +	asm9260_wdt_updatetimeout(wdd);
> > +
> > +	return 0;
> > +}
> > +
> > +static void asm9260_wdt_sys_reset(struct asm9260_wdt_priv *priv)
> > +{
> > +	/* init WD if it was not started */
> > +	priv->wdd.timeout = 1;
> > +
> > +	iowrite32(BM_MOD_WDEN | BM_MOD_WDRESET, priv->iobase + HW_WDMOD);
> > +
> > +	asm9260_wdt_updatetimeout(&priv->wdd);
> > +	/* first pass correct sequence */
> > +	asm9260_wdt_feed(&priv->wdd);
> > +	/*
> > +	 * Then write wrong pattern to the feed to trigger reset
> > +	 * ASAP.
> > +	 */
> > +	iowrite32(0xff, priv->iobase + HW_WDFEED);
> > +
> > +	mdelay(1000);
> > +}
> > +
> > +static irqreturn_t asm9260_wdt_irq(int irq, void *devid)
> > +{
> > +	struct asm9260_wdt_priv *priv = devid;
> > +	u32 stat;
> > +
> > +	stat = ioread32(priv->iobase + HW_WDMOD);
> > +	if (!(stat & BM_MOD_WDINT))
> > +		return IRQ_NONE;
> > +
> > +	/*
> > +	 * BM_MOD_WDINT flag can't be removed by SW. Only way is
> > +	 * to reset WD Controller.
> > +	 * TODO: add Controller reset if needed.
> > +	 */
> > +	if (priv->mode == DEBUG)
> > +		dev_info(priv->dev, "Watchdog Timeout. Do nothing.\n");
> > +	else {
> > +		dev_info(priv->dev, "Watchdog Timeout. Doing SW Reset.\n");
> > +		asm9260_wdt_sys_reset(priv);
> > +	}
> > +
> > +	return IRQ_HANDLED;
> > +}
> > +
> > +static int asm9260_restart_handler(struct notifier_block *this,
> > +				unsigned long mode, void *cmd)
> > +{
> > +	struct asm9260_wdt_priv *priv =
> > +		container_of(this, struct asm9260_wdt_priv, restart_handler);
> > +
> > +	asm9260_wdt_sys_reset(priv);
> > +
> > +	return NOTIFY_DONE;
> > +}
> > +
> > +static const struct watchdog_info asm9260_wdt_ident = {
> > +	.options          =     WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING
> > +				| WDIOF_MAGICCLOSE,
> > +	.firmware_version =	0,
> > +	.identity         =	"Alphascale asm9260 Watchdog",
> > +};
> > +
> > +static struct watchdog_ops asm9260_wdt_ops = {
> > +	.owner		= THIS_MODULE,
> > +	.start		= asm9260_wdt_enable,
> > +	.stop		= asm9260_wdt_disable,
> > +	.get_timeleft	= asm9260_wdt_gettimeleft,
> > +	.ping		= asm9260_wdt_feed,
> > +	.set_timeout	= asm9260_wdt_settimeout,
> > +};
> > +
> > +static int __init asm9260_wdt_get_dt_clks(struct asm9260_wdt_priv *priv)
> > +{
> > +	int clk_idx = 0, err;
> > +
> > +	priv->clk = devm_clk_get(priv->dev, "mod");
> > +	if (IS_ERR(priv->clk))
> > +		goto out_err;
> > +
> > +	/* configure AHB clock */
> > +	clk_idx = 1;
> > +	priv->clk_ahb = devm_clk_get(priv->dev, "ahb");
> > +	if (IS_ERR(priv->clk_ahb))
> > +		goto out_err;
> > +
> > +	err = clk_prepare_enable(priv->clk_ahb);
> > +	if (err)
> > +		dev_err(priv->dev, "Failed to enable ahb_clk!\n");
> > +
> > +	err = clk_set_rate(priv->clk, CLOCK_FREQ);
> > +	if (err) {
> > +		clk_disable_unprepare(priv->clk_ahb);
> > +		dev_err(priv->dev, "Failed to set rate!\n");
> > +	}
> > +
> > +	err = clk_prepare_enable(priv->clk);
> > +	if (err) {
> > +		clk_disable_unprepare(priv->clk_ahb);
> > +		dev_err(priv->dev, "Failed to enable clk!\n");
> > +	}
> > +
> > +	/* wdt has internal divider */
> > +	priv->wdt_freq = clk_get_rate(priv->clk) / 2;
> > +
> > +	return 0;
> > +out_err:
> > +	dev_err(priv->dev, "%s: Failed to get clk (%i)\n", __func__, clk_idx);
> > +	return 1;
> > +}
> > +
> > +static void __init asm9260_wdt_get_dt_mode(struct asm9260_wdt_priv *priv)
> > +{
> > +	const char *tmp;
> > +	int ret;
> > +
> > +	/* default mode */
> > +	priv->mode = HW_RESET;
> > +
> > +	ret = of_property_read_string(priv->dev->of_node,
> > +			"alphascale,mode", &tmp);
> > +	if (ret)
> > +		return;
> > +
> > +	if (!strcmp(tmp, "hw"))
> > +		priv->mode = HW_RESET;
> > +	else if (!strcmp(tmp, "sw"))
> > +		priv->mode = SW_RESET;
> > +	else if (!strcmp(tmp, "debug"))
> > +		priv->mode = DEBUG;
> > +	else {
> > +		dev_warn(priv->dev, "unknown reset-type: %s. Using defaul \"hw\" mode.",
> > +			tmp);
> > +		return;
> > +	}
> > +
> > +	dev_info(priv->dev, "using \"%s\" mode.", tmp);
> > +}
> > +
> > +static int __init asm9260_wdt_probe(struct platform_device *pdev)
> > +{
> > +	struct asm9260_wdt_priv *priv;
> > +	struct watchdog_device *wdd;
> > +	struct resource *res;
> > +	bool nowayout = WATCHDOG_NOWAYOUT;
> > +	int ret;
> > +
> > +	priv = devm_kzalloc(&pdev->dev, sizeof(struct asm9260_wdt_priv),
> > +			GFP_KERNEL);
> > +	if (!priv)
> > +		return -ENOMEM;
> > +
> > +	priv->dev = &pdev->dev;
> > +
> > +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +	priv->iobase = devm_ioremap_resource(&pdev->dev, res);
> > +	if (IS_ERR(priv->iobase))
> > +		return PTR_ERR(priv->iobase);
> > +
> > +	ret = asm9260_wdt_get_dt_clks(priv);
> > +	if (ret)
> > +		return ret;
> > +
> > +	priv->rst = devm_reset_control_get(&pdev->dev, "wdt_rst");
> > +	if (IS_ERR(priv->rst)) {
> > +		dev_info(&pdev->dev, "No reset control found. Enabling \"nowayout\" mode\n");
> > +		nowayout = 1;
> > +		priv->rst = NULL;
> > +	}
> > +
> > +	wdd = &priv->wdd;
> > +	wdd->info = &asm9260_wdt_ident;
> > +	wdd->ops = &asm9260_wdt_ops;
> > +	wdd->min_timeout = 1;
> > +	wdd->max_timeout = BM_WDTC_MAX(priv->wdt_freq);
> > +	wdd->parent = &pdev->dev;
> > +
> > +	watchdog_set_drvdata(wdd, priv);
> > +
> > +	/*
> > +	 * If 'timeout-sec' unspecified in devicetree, assume a 30 second
> > +	 * default, unless the max timeout is less than 30 seconds, then use
> > +	 * the max instead.
> > +	 */
> > +	wdd->timeout = ASM9260_WDT_DEFAULT_TIMEOUT;
> > +	watchdog_init_timeout(wdd, 0, &pdev->dev);
> > +
> > +	watchdog_set_nowayout(wdd, nowayout);
> > +
> > +	asm9260_wdt_get_dt_mode(priv);
> > +
> > +	if (priv->mode != HW_RESET)
> > +		priv->irq = platform_get_irq(pdev, 0);
> > +
> > +	if (priv->irq > 0) {
> > +		/*
> > +		 * Not all supported platforms specify an interrupt for the
> > +		 * watchdog, so let's make it optional.
> > +		 */
> > +		ret = devm_request_irq(&pdev->dev, priv->irq,
> > +				asm9260_wdt_irq, IRQF_SHARED,
> > +				pdev->name, priv);
> > +		if (ret < 0)
> > +			dev_err(&pdev->dev, "failed to request IRQ\n");
> > +	}
> > +
> > +	ret = watchdog_register_device(wdd);
> > +	if (ret)
> > +		goto clk_off;
> > +
> > +	platform_set_drvdata(pdev, priv);
> > +
> > +	priv->restart_handler.notifier_call = asm9260_restart_handler;
> > +	priv->restart_handler.priority = 128;
> > +	ret = register_restart_handler(&priv->restart_handler);
> > +	if (ret)
> > +		dev_err(&pdev->dev, "cannot register restart handler\n");
> > +
> > +	dev_info(&pdev->dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)",
> > +		wdd->timeout, nowayout);
> > +	return 0;
> > +
> > +clk_off:
> > +	clk_disable_unprepare(priv->clk);
> > +	clk_disable_unprepare(priv->clk_ahb);
> > +	return ret;
> > +}
> > +
> > +static void asm9260_wdt_shutdown(struct platform_device *pdev)
> > +{
> > +	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
> > +
> > +	asm9260_wdt_disable(&priv->wdd);
> > +}
> > +
> > +static int __exit asm9260_wdt_remove(struct platform_device *pdev)
> > +{
> > +	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
> > +
> > +	asm9260_wdt_shutdown(pdev);
> > +
> > +	unregister_restart_handler(&priv->restart_handler);
> > +	clk_disable_unprepare(priv->clk);
> > +	clk_disable_unprepare(priv->clk_ahb);
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct of_device_id asm9260_wdt_of_match[] = {
> > +	{ .compatible = "alphascale,asm9260-wdt"},
> > +	{},
> > +};
> > +MODULE_DEVICE_TABLE(of, asm9260_wdt_of_match);
> > +
> > +static struct platform_driver asm9260_wdt_driver = {
> > +	.driver = {
> > +		.name = "asm9260-wdt",
> > +		.owner = THIS_MODULE,
> > +		.of_match_table	= asm9260_wdt_of_match,
> > +	},
> > +	.probe = asm9260_wdt_probe,
> > +	.remove = asm9260_wdt_remove,
> > +	.shutdown = asm9260_wdt_shutdown,
> > +};
> > +module_platform_driver(asm9260_wdt_driver);
> > +
> > +MODULE_DESCRIPTION("asm9260 WatchDog Timer Driver");
> > +MODULE_AUTHOR("Oleksij Rempel <linux@rempel-privat.de>");
> > +MODULE_LICENSE("GPL");
> > 
> 
> 
> -- 
> Regards,
> Oleksij
> 



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

* Re: [PATCH] watchdog: add Alphascale asm9260-wdt driver
  2015-09-18  9:23 [PATCH] watchdog: add Alphascale asm9260-wdt driver Oleksij Rempel
  2015-10-20  7:49 ` Oleksij Rempel
@ 2015-10-27  2:31 ` Guenter Roeck
  2015-10-29  7:20   ` Oleksij Rempel
       [not found]   ` <20151027023132.GA1270-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org>
  1 sibling, 2 replies; 43+ messages in thread
From: Guenter Roeck @ 2015-10-27  2:31 UTC (permalink / raw)
  To: Oleksij Rempel; +Cc: linux-watchdog, wim

On Fri, Sep 18, 2015 at 11:23:32AM +0200, Oleksij Rempel wrote:
> Add WD support for Alphascale asm9260 SoC. This driver
> provide support for different function modes:
> - HW mode to trigger SoC reset on timeout
> - SW mode do soft reset if needed
> - DEBUG mode
> 
> Optional support for stopping watchdog. If reset binding are not provided
> this driver will work in nowayout mode.

Why ?

> 
> Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
> ---
>  .../bindings/watchdog/alphascale-asm9260.txt       |  38 ++
>  drivers/watchdog/Kconfig                           |   9 +
>  drivers/watchdog/Makefile                          |   1 +
>  drivers/watchdog/asm9260_wdt.c                     | 405 +++++++++++++++++++++
>  4 files changed, 453 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
>  create mode 100644 drivers/watchdog/asm9260_wdt.c
> 
> diff --git a/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
> new file mode 100644
> index 0000000..cbe388b
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
> @@ -0,0 +1,38 @@
> +Alphascale asm9260 Watchdog timer
> +
> +Required properties:
> +
> +- compatible : should be "alphascale,asm9260-wdt".
> +- reg : Specifies base physical address and size of the registers.
> +- clocks : the clock feeding the watchdog timer.

There are two -> clocks.

> +	Needed if platform uses clocks. See clock-bindings.txt

The rest of the code suggests that the clocks are mandatory.
probe fails if asm9260_wdt_get_dt_clks() returns an error,
and the clock frequency would be 0.

> +- clock-names : should be set to
> +	"mod" - source for tick counter.
> +	"ahb" - ahb gate.
> +
> +Optional properties:
> +- resets : phandle pointing to the system reset controller with correct
> +	reset line index for watchdog controller reset. This propertie is
> +	required if you need to disable "nowayout" and it works only with
> +	CONFIG_WATCHDOG_NOWAYOUT=n.
> +- reset-names : should be set to "wdt_rst" if "resets" is used.
> +- timeout-sec : shall contain the default watchdog timeout in seconds,
> +	if unset, the default timeout is 30 seconds.
> +- alphascale,mode : tree modes are supported
> +	"hw" - hw reset (defaul).
> +	"sw" - sw reset.
> +	"debug" - no action is taken.
> +
> +Example:
> +
> +watchdog0: watchdog@80048000 {
> +	compatible = "alphascale,asm9260-wdt";
> +	reg = <0x80048000 0x10>;
> +	clocks = <&acc CLKID_SYS_WDT>, <&acc CLKID_AHB_WDT>;
> +	clock-names = "mod", "ahb";
> +	interrupts = <55>;
> +	resets = <&rst WDT_RESET>;
> +	reset-names = "wdt_rst";
> +	timeout-sec = <30>;
> +	alphascale,mode = "hw"

Seems to miss a ';'.

Did you get any feedback from dt maintainers on those bindings ?

> +};
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index c68edc1..cc5f675 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -173,6 +173,15 @@ config ARM_SP805_WATCHDOG
>  	  ARM Primecell SP805 Watchdog timer. This will reboot your system when
>  	  the timeout is reached.
>  
> +config ASM9260_WATCHDOG
> +	tristate "Alphascale ASM9260 watchdog"
> +	depends on MACH_ASM9260
> +	depends on OF
> +	select WATCHDOG_CORE
> +	help
> +	  Watchdog timer embedded into Alphascale asm9260 chips. This will reboot your
> +	  system when the timeout is reached.
> +
>  config AT91RM9200_WATCHDOG
>  	tristate "AT91RM9200 watchdog"
>  	depends on SOC_AT91RM9200 && MFD_SYSCON
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index 0c616e3..bd7b0cd 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -30,6 +30,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
>  
>  # ARM Architecture
>  obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o
> +obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o
>  obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
>  obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
>  obj-$(CONFIG_CADENCE_WATCHDOG) += cadence_wdt.o
> diff --git a/drivers/watchdog/asm9260_wdt.c b/drivers/watchdog/asm9260_wdt.c
> new file mode 100644
> index 0000000..e5f859b
> --- /dev/null
> +++ b/drivers/watchdog/asm9260_wdt.c
> @@ -0,0 +1,405 @@
> +/*
> + * Watchdog driver for Alphascale ASM9260.
> + *
> + * Copyright (c) 2014 Oleksij Rempel <linux@rempel-privat.de>
> + *
> + * Licensed under GPLv2 or later.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/clk.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/reboot.h>
> +#include <linux/reset.h>
> +#include <linux/uaccess.h>
> +#include <linux/watchdog.h>
> +
> +#define CLOCK_FREQ	1000000
> +
> +/* Watchdog Mode register */
> +#define HW_WDMOD			0x00
> +/* Wake interrupt. Set by HW, can't be cleared. */
> +#define BM_MOD_WDINT			BIT(3)

When using BIT macros, please include bitops.h.

> +/* This bit set if timeout reached. Cleared by SW. */
> +#define BM_MOD_WDTOF			BIT(2)
> +/* HW Reset on timeout */
> +#define BM_MOD_WDRESET			BIT(1)
> +/* WD enable */
> +#define BM_MOD_WDEN			BIT(0)
> +
> +/*
> + * Watchdog Timer Constant register
> + * Minimal value is 0xff, the meaning of this value
> + * depends on used clock: T = WDCLK * (0xff + 1) * 4
> + */
> +#define HW_WDTC				0x04
> +#define BM_WDTC_MAX(freq)		(0x7fffffff / (freq))
> +
> +/* Watchdog Feed register */
> +#define HW_WDFEED			0x08
> +
> +/* Watchdog Timer Value register */
> +#define HW_WDTV				0x0c
> +
> +#define ASM9260_WDT_DEFAULT_TIMEOUT	30
> +
> +enum asm9260_wdt_mode {
> +	HW_RESET,
> +	SW_RESET,
> +	DEBUG,
> +};
> +
> +struct asm9260_wdt_priv {
> +	struct device		*dev;
> +	struct watchdog_device	wdd;
> +	struct clk		*clk;
> +	struct clk		*clk_ahb;
> +	struct reset_control	*rst;
> +	struct notifier_block	restart_handler;
> +
> +	void __iomem		*iobase;
> +	int			irq;
> +	unsigned long		wdt_freq;
> +	enum asm9260_wdt_mode	mode;
> +};
> +
> +static int asm9260_wdt_feed(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +
> +	iowrite32(0xaa, priv->iobase + HW_WDFEED);
> +	iowrite32(0x55, priv->iobase + HW_WDFEED);
> +
> +	return 0;
> +}
> +
> +static unsigned int asm9260_wdt_gettimeleft(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +	u32 counter;
> +
> +	counter = ioread32(priv->iobase + HW_WDTV);
> +
> +	return DIV_ROUND_CLOSEST(counter, priv->wdt_freq);
> +}
> +
> +static int asm9260_wdt_updatetimeout(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +	u32 counter;
> +
> +	counter = wdd->timeout * priv->wdt_freq;
> +
> +	iowrite32(counter, priv->iobase + HW_WDTC);
> +
> +	return 0;
> +}
> +
> +static int asm9260_wdt_enable(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +	u32 mode = 0;
> +
> +	if (priv->mode == HW_RESET)
> +		mode = BM_MOD_WDRESET;
> +
> +	iowrite32(BM_MOD_WDEN | mode, priv->iobase + HW_WDMOD);
> +
> +	asm9260_wdt_updatetimeout(wdd);
> +
> +	asm9260_wdt_feed(wdd);
> +
> +	return 0;
> +}
> +
> +static int asm9260_wdt_disable(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +
> +	/* The only way to disable WD is to reset it. */
> +	reset_control_assert(priv->rst);
> +	reset_control_deassert(priv->rst);
> +
> +	return 0;
> +}
> +
> +static int asm9260_wdt_settimeout(struct watchdog_device *wdd, unsigned int to)
> +{
> +	wdd->timeout = to;
> +	asm9260_wdt_updatetimeout(wdd);
> +
> +	return 0;
> +}
> +
> +static void asm9260_wdt_sys_reset(struct asm9260_wdt_priv *priv)
> +{
> +	/* init WD if it was not started */
> +	priv->wdd.timeout = 1;
> +
> +	iowrite32(BM_MOD_WDEN | BM_MOD_WDRESET, priv->iobase + HW_WDMOD);
> +
> +	asm9260_wdt_updatetimeout(&priv->wdd);
> +	/* first pass correct sequence */
> +	asm9260_wdt_feed(&priv->wdd);
> +	/*
> +	 * Then write wrong pattern to the feed to trigger reset
> +	 * ASAP.
> +	 */
> +	iowrite32(0xff, priv->iobase + HW_WDFEED);
> +
> +	mdelay(1000);
> +}
> +
> +static irqreturn_t asm9260_wdt_irq(int irq, void *devid)
> +{
> +	struct asm9260_wdt_priv *priv = devid;
> +	u32 stat;
> +
> +	stat = ioread32(priv->iobase + HW_WDMOD);
> +	if (!(stat & BM_MOD_WDINT))
> +		return IRQ_NONE;
> +
> +	/*
> +	 * BM_MOD_WDINT flag can't be removed by SW. Only way is
> +	 * to reset WD Controller.
> +	 * TODO: add Controller reset if needed.

Is it needed or not ?

> +	 */
> +	if (priv->mode == DEBUG)
> +		dev_info(priv->dev, "Watchdog Timeout. Do nothing.\n");
> +	else {
> +		dev_info(priv->dev, "Watchdog Timeout. Doing SW Reset.\n");
> +		asm9260_wdt_sys_reset(priv);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int asm9260_restart_handler(struct notifier_block *this,
> +				unsigned long mode, void *cmd)
> +{
> +	struct asm9260_wdt_priv *priv =
> +		container_of(this, struct asm9260_wdt_priv, restart_handler);
> +
> +	asm9260_wdt_sys_reset(priv);
> +
> +	return NOTIFY_DONE;
> +}
> +
> +static const struct watchdog_info asm9260_wdt_ident = {
> +	.options          =     WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING
> +				| WDIOF_MAGICCLOSE,
> +	.firmware_version =	0,

Assigning 0 to a static variable is not necessary.

> +	.identity         =	"Alphascale asm9260 Watchdog",
> +};
> +
> +static struct watchdog_ops asm9260_wdt_ops = {
> +	.owner		= THIS_MODULE,
> +	.start		= asm9260_wdt_enable,
> +	.stop		= asm9260_wdt_disable,
> +	.get_timeleft	= asm9260_wdt_gettimeleft,
> +	.ping		= asm9260_wdt_feed,
> +	.set_timeout	= asm9260_wdt_settimeout,
> +};
> +
> +static int __init asm9260_wdt_get_dt_clks(struct asm9260_wdt_priv *priv)
> +{
> +	int clk_idx = 0, err;
> +
> +	priv->clk = devm_clk_get(priv->dev, "mod");
> +	if (IS_ERR(priv->clk))
> +		goto out_err;
> +
> +	/* configure AHB clock */
> +	clk_idx = 1;
> +	priv->clk_ahb = devm_clk_get(priv->dev, "ahb");
> +	if (IS_ERR(priv->clk_ahb))
> +		goto out_err;
> +
> +	err = clk_prepare_enable(priv->clk_ahb);
> +	if (err)
> +		dev_err(priv->dev, "Failed to enable ahb_clk!\n");

Not really clear why you ignore those errors. Please explain.

> +
> +	err = clk_set_rate(priv->clk, CLOCK_FREQ);
> +	if (err) {
> +		clk_disable_unprepare(priv->clk_ahb);
> +		dev_err(priv->dev, "Failed to set rate!\n");
> +	}
> +
> +	err = clk_prepare_enable(priv->clk);
> +	if (err) {
> +		clk_disable_unprepare(priv->clk_ahb);
> +		dev_err(priv->dev, "Failed to enable clk!\n");
> +	}
> +
> +	/* wdt has internal divider */
> +	priv->wdt_freq = clk_get_rate(priv->clk) / 2;

Can wdt_freq ever be 0 ?

... yes, if any of the prepare_enable functions failed.


> +
> +	return 0;
> +out_err:
> +	dev_err(priv->dev, "%s: Failed to get clk (%i)\n", __func__, clk_idx);

"clk_idx" does not really provide useful information to the user.
It might make sense to use a better description where the error
occurs, and drop clk_idx.

> +	return 1;

This is not a valid error code.

> +}
> +
> +static void __init asm9260_wdt_get_dt_mode(struct asm9260_wdt_priv *priv)
> +{
> +	const char *tmp;
> +	int ret;
> +
> +	/* default mode */
> +	priv->mode = HW_RESET;
> +
> +	ret = of_property_read_string(priv->dev->of_node,
> +			"alphascale,mode", &tmp);
> +	if (ret)
> +		return;
> +
> +	if (!strcmp(tmp, "hw"))
> +		priv->mode = HW_RESET;
> +	else if (!strcmp(tmp, "sw"))
> +		priv->mode = SW_RESET;
> +	else if (!strcmp(tmp, "debug"))
> +		priv->mode = DEBUG;
> +	else {
> +		dev_warn(priv->dev, "unknown reset-type: %s. Using defaul \"hw\" mode.",
> +			tmp);

Returning -EINVAL would be more appropriate here.

> +		return;
> +	}
> +
> +	dev_info(priv->dev, "using \"%s\" mode.", tmp);

Is this noise really necessary ?

If anything, a single message at the end of the probe function
would be easier to accept.

> +}
> +
> +static int __init asm9260_wdt_probe(struct platform_device *pdev)
> +{
> +	struct asm9260_wdt_priv *priv;
> +	struct watchdog_device *wdd;
> +	struct resource *res;
> +	bool nowayout = WATCHDOG_NOWAYOUT;
> +	int ret;
> +
> +	priv = devm_kzalloc(&pdev->dev, sizeof(struct asm9260_wdt_priv),
> +			GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->dev = &pdev->dev;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	priv->iobase = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(priv->iobase))
> +		return PTR_ERR(priv->iobase);
> +
> +	ret = asm9260_wdt_get_dt_clks(priv);
> +	if (ret)
> +		return ret;
> +
> +	priv->rst = devm_reset_control_get(&pdev->dev, "wdt_rst");
> +	if (IS_ERR(priv->rst)) {
> +		dev_info(&pdev->dev, "No reset control found. Enabling \"nowayout\" mode\n");

Noise. This should be obvious from the devicetree description.
I don't see value in repeating devicetree data.

> +		nowayout = 1;

Again, why ? nowayout should be independent.

> +		priv->rst = NULL;
> +	}
> +
> +	wdd = &priv->wdd;
> +	wdd->info = &asm9260_wdt_ident;
> +	wdd->ops = &asm9260_wdt_ops;
> +	wdd->min_timeout = 1;
> +	wdd->max_timeout = BM_WDTC_MAX(priv->wdt_freq);
> +	wdd->parent = &pdev->dev;
> +
> +	watchdog_set_drvdata(wdd, priv);
> +
> +	/*
> +	 * If 'timeout-sec' unspecified in devicetree, assume a 30 second
> +	 * default, unless the max timeout is less than 30 seconds, then use
> +	 * the max instead.
> +	 */
> +	wdd->timeout = ASM9260_WDT_DEFAULT_TIMEOUT;
> +	watchdog_init_timeout(wdd, 0, &pdev->dev);
> +
> +	watchdog_set_nowayout(wdd, nowayout);
> +
> +	asm9260_wdt_get_dt_mode(priv);
> +
> +	if (priv->mode != HW_RESET)
> +		priv->irq = platform_get_irq(pdev, 0);
> +
> +	if (priv->irq > 0) {

is_valid_irq() ?

> +		/*
> +		 * Not all supported platforms specify an interrupt for the
> +		 * watchdog, so let's make it optional.
> +		 */
> +		ret = devm_request_irq(&pdev->dev, priv->irq,
> +				asm9260_wdt_irq, IRQF_SHARED,
> +				pdev->name, priv);
> +		if (ret < 0)
> +			dev_err(&pdev->dev, "failed to request IRQ\n");

Why is this error ignored ?

> +	}
> +
> +	ret = watchdog_register_device(wdd);
> +	if (ret)
> +		goto clk_off;
> +
> +	platform_set_drvdata(pdev, priv);
> +
> +	priv->restart_handler.notifier_call = asm9260_restart_handler;
> +	priv->restart_handler.priority = 128;
> +	ret = register_restart_handler(&priv->restart_handler);
> +	if (ret)
> +		dev_err(&pdev->dev, "cannot register restart handler\n");

dev_err and abort, or dev_warn.

> +
> +	dev_info(&pdev->dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)",
> +		wdd->timeout, nowayout);
> +	return 0;
> +
> +clk_off:
> +	clk_disable_unprepare(priv->clk);
> +	clk_disable_unprepare(priv->clk_ahb);

What if prepare_enable failed ?
Also, while some if not most implementations of clk_disable check
for clk==NULL and/or IS_ERR(clk), not all do.

> +	return ret;
> +}
> +
> +static void asm9260_wdt_shutdown(struct platform_device *pdev)
> +{
> +	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
> +
> +	asm9260_wdt_disable(&priv->wdd);
> +}
> +
> +static int __exit asm9260_wdt_remove(struct platform_device *pdev)
> +{
> +	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
> +
> +	asm9260_wdt_shutdown(pdev);
> +
> +	unregister_restart_handler(&priv->restart_handler);
> +	clk_disable_unprepare(priv->clk);
> +	clk_disable_unprepare(priv->clk_ahb);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id asm9260_wdt_of_match[] = {
> +	{ .compatible = "alphascale,asm9260-wdt"},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, asm9260_wdt_of_match);
> +
> +static struct platform_driver asm9260_wdt_driver = {
> +	.driver = {
> +		.name = "asm9260-wdt",
> +		.owner = THIS_MODULE,
> +		.of_match_table	= asm9260_wdt_of_match,
> +	},
> +	.probe = asm9260_wdt_probe,
> +	.remove = asm9260_wdt_remove,
> +	.shutdown = asm9260_wdt_shutdown,
> +};
> +module_platform_driver(asm9260_wdt_driver);
> +
> +MODULE_DESCRIPTION("asm9260 WatchDog Timer Driver");
> +MODULE_AUTHOR("Oleksij Rempel <linux@rempel-privat.de>");
> +MODULE_LICENSE("GPL");
> -- 
> 2.5.0
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH] watchdog: add Alphascale asm9260-wdt driver
  2015-10-27  2:31 ` Guenter Roeck
@ 2015-10-29  7:20   ` Oleksij Rempel
       [not found]   ` <20151027023132.GA1270-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org>
  1 sibling, 0 replies; 43+ messages in thread
From: Oleksij Rempel @ 2015-10-29  7:20 UTC (permalink / raw)
  To: Guenter Roeck; +Cc: linux-watchdog, wim

[-- Attachment #1: Type: text/plain, Size: 3415 bytes --]

Hi,

thank you for your time.


Am 27.10.2015 um 03:31 schrieb Guenter Roeck:
> On Fri, Sep 18, 2015 at 11:23:32AM +0200, Oleksij Rempel wrote:
>> Add WD support for Alphascale asm9260 SoC. This driver
>> provide support for different function modes:
>> - HW mode to trigger SoC reset on timeout
>> - SW mode do soft reset if needed
>> - DEBUG mode
>>
>> Optional support for stopping watchdog. If reset binding are not provided
>> this driver will work in nowayout mode.
> 
> Why ?

Because this controller can't be stopped. Only way to stop it is to
reset it from reset controller, which is out of scope of this driver.
On other side i can make reset binding as required...

>>
>> Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
>> ---
>>  .../bindings/watchdog/alphascale-asm9260.txt       |  38 ++
>>  drivers/watchdog/Kconfig                           |   9 +
>>  drivers/watchdog/Makefile                          |   1 +
>>  drivers/watchdog/asm9260_wdt.c                     | 405 +++++++++++++++++++++
>>  4 files changed, 453 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
>>  create mode 100644 drivers/watchdog/asm9260_wdt.c
>>
>> diff --git a/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
>> new file mode 100644
>> index 0000000..cbe388b
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
>> @@ -0,0 +1,38 @@
>> +Alphascale asm9260 Watchdog timer
>> +
>> +Required properties:
>> +
>> +- compatible : should be "alphascale,asm9260-wdt".
>> +- reg : Specifies base physical address and size of the registers.
>> +- clocks : the clock feeding the watchdog timer.
> 
> There are two -> clocks.
> 
>> +	Needed if platform uses clocks. See clock-bindings.txt
> 
> The rest of the code suggests that the clocks are mandatory.
> probe fails if asm9260_wdt_get_dt_clks() returns an error,
> and the clock frequency would be 0.
> 
>> +- clock-names : should be set to
>> +	"mod" - source for tick counter.
>> +	"ahb" - ahb gate.
>> +
>> +Optional properties:
>> +- resets : phandle pointing to the system reset controller with correct
>> +	reset line index for watchdog controller reset. This propertie is
>> +	required if you need to disable "nowayout" and it works only with
>> +	CONFIG_WATCHDOG_NOWAYOUT=n.
>> +- reset-names : should be set to "wdt_rst" if "resets" is used.
>> +- timeout-sec : shall contain the default watchdog timeout in seconds,
>> +	if unset, the default timeout is 30 seconds.
>> +- alphascale,mode : tree modes are supported
>> +	"hw" - hw reset (defaul).
>> +	"sw" - sw reset.
>> +	"debug" - no action is taken.
>> +
>> +Example:
>> +
>> +watchdog0: watchdog@80048000 {
>> +	compatible = "alphascale,asm9260-wdt";
>> +	reg = <0x80048000 0x10>;
>> +	clocks = <&acc CLKID_SYS_WDT>, <&acc CLKID_AHB_WDT>;
>> +	clock-names = "mod", "ahb";
>> +	interrupts = <55>;
>> +	resets = <&rst WDT_RESET>;
>> +	reset-names = "wdt_rst";
>> +	timeout-sec = <30>;
>> +	alphascale,mode = "hw"
> 
> Seems to miss a ';'.
> 
> Did you get any feedback from dt maintainers on those bindings ?

No, i'll CC updated patch to devicetree list.

Thank you for other comments, i'll take care of it.

-- 
Regards,
Oleksij


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 213 bytes --]

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

* [PATCH v2] add Alphascale asm9260-wdt driver
  2015-10-27  2:31 ` Guenter Roeck
@ 2015-11-05  9:06       ` Oleksij Rempel
       [not found]   ` <20151027023132.GA1270-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org>
  1 sibling, 0 replies; 43+ messages in thread
From: Oleksij Rempel @ 2015-11-05  9:06 UTC (permalink / raw)
  To: linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	linux-0h96xk9xTtrk1uMJSBkQmQ, wim-IQzOog9fTRqzQB+pC5nmwQ,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: Oleksij Rempel

Hallo devicetree list,
this driver had already one rewiev round on watchdog list, please
take a look a devicetree part. Maybe some of you hase
comments or suggestions.

Thank you,

v2:
- fix DT types
- remove obsolet comments
- add clk error handling
- reduce log noise
- allow to return an error in asm9260_wdt_get_dt_mode

Oleksij Rempel (1):
  watchdog: add Alphascale asm9260-wdt driver

 .../bindings/watchdog/alphascale-asm9260.txt       |  39 ++
 drivers/watchdog/Kconfig                           |   9 +
 drivers/watchdog/Makefile                          |   1 +
 drivers/watchdog/asm9260_wdt.c                     | 415 +++++++++++++++++++++
 4 files changed, 464 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
 create mode 100644 drivers/watchdog/asm9260_wdt.c

-- 
2.5.0

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH v2] add Alphascale asm9260-wdt driver
@ 2015-11-05  9:06       ` Oleksij Rempel
  0 siblings, 0 replies; 43+ messages in thread
From: Oleksij Rempel @ 2015-11-05  9:06 UTC (permalink / raw)
  To: linux-watchdog, linux, wim, devicetree; +Cc: Oleksij Rempel

Hallo devicetree list,
this driver had already one rewiev round on watchdog list, please
take a look a devicetree part. Maybe some of you hase
comments or suggestions.

Thank you,

v2:
- fix DT types
- remove obsolet comments
- add clk error handling
- reduce log noise
- allow to return an error in asm9260_wdt_get_dt_mode

Oleksij Rempel (1):
  watchdog: add Alphascale asm9260-wdt driver

 .../bindings/watchdog/alphascale-asm9260.txt       |  39 ++
 drivers/watchdog/Kconfig                           |   9 +
 drivers/watchdog/Makefile                          |   1 +
 drivers/watchdog/asm9260_wdt.c                     | 415 +++++++++++++++++++++
 4 files changed, 464 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
 create mode 100644 drivers/watchdog/asm9260_wdt.c

-- 
2.5.0

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

* [PATCH v2] watchdog: add Alphascale asm9260-wdt driver
  2015-11-05  9:06       ` Oleksij Rempel
@ 2015-11-05  9:06           ` Oleksij Rempel
  -1 siblings, 0 replies; 43+ messages in thread
From: Oleksij Rempel @ 2015-11-05  9:06 UTC (permalink / raw)
  To: linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	linux-0h96xk9xTtrk1uMJSBkQmQ, wim-IQzOog9fTRqzQB+pC5nmwQ,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: Oleksij Rempel

Add WD support for Alphascale asm9260 SoC. This driver
provide support for different function modes:
- HW mode to trigger SoC reset on timeout
- SW mode do soft reset if needed
- DEBUG mode

Optional support for stopping watchdog. If reset binding are not provided
this driver will work in nowayout mode.

Signed-off-by: Oleksij Rempel <linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>
---
 .../bindings/watchdog/alphascale-asm9260.txt       |  39 ++
 drivers/watchdog/Kconfig                           |   9 +
 drivers/watchdog/Makefile                          |   1 +
 drivers/watchdog/asm9260_wdt.c                     | 415 +++++++++++++++++++++
 4 files changed, 464 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
 create mode 100644 drivers/watchdog/asm9260_wdt.c

diff --git a/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
new file mode 100644
index 0000000..6e54d1f
--- /dev/null
+++ b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
@@ -0,0 +1,39 @@
+Alphascale asm9260 Watchdog timer
+
+Required properties:
+
+- compatible : should be "alphascale,asm9260-wdt".
+- reg : Specifies base physical address and size of the registers.
+- clocks : the clocks feeding the watchdog timer. See clock-bindings.txt
+- clock-names : should be set to
+	"mod" - source for tick counter.
+	"ahb" - ahb gate.
+
+Optional properties:
+- resets : phandle pointing to the system reset controller with correct
+	reset line index for watchdog controller reset. This propertie is
+	required if you need to disable "nowayout" and it neened only with
+	CONFIG_WATCHDOG_NOWAYOUT=n.
+	Without reseting this WD controller, it is not possible to stop
+	counter.
+- reset-names : should be set to "wdt_rst" if "resets" is used.
+- timeout-sec : shall contain the default watchdog timeout in seconds,
+	if unset, the default timeout is 30 seconds.
+- alphascale,mode : tree modes are supported
+	"hw" - hw reset (defaul).
+	"sw" - sw reset.
+	"debug" - no action is taken.
+
+Example:
+
+watchdog0: watchdog@80048000 {
+	compatible = "alphascale,asm9260-wdt";
+	reg = <0x80048000 0x10>;
+	clocks = <&acc CLKID_SYS_WDT>, <&acc CLKID_AHB_WDT>;
+	clock-names = "mod", "ahb";
+	interrupts = <55>;
+	resets = <&rst WDT_RESET>;
+	reset-names = "wdt_rst";
+	timeout-sec = <30>;
+	alphascale,mode = "hw";
+};
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index c68edc1..cc5f675 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -173,6 +173,15 @@ config ARM_SP805_WATCHDOG
 	  ARM Primecell SP805 Watchdog timer. This will reboot your system when
 	  the timeout is reached.
 
+config ASM9260_WATCHDOG
+	tristate "Alphascale ASM9260 watchdog"
+	depends on MACH_ASM9260
+	depends on OF
+	select WATCHDOG_CORE
+	help
+	  Watchdog timer embedded into Alphascale asm9260 chips. This will reboot your
+	  system when the timeout is reached.
+
 config AT91RM9200_WATCHDOG
 	tristate "AT91RM9200 watchdog"
 	depends on SOC_AT91RM9200 && MFD_SYSCON
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 0c616e3..bd7b0cd 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
 
 # ARM Architecture
 obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o
+obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o
 obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
 obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
 obj-$(CONFIG_CADENCE_WATCHDOG) += cadence_wdt.o
diff --git a/drivers/watchdog/asm9260_wdt.c b/drivers/watchdog/asm9260_wdt.c
new file mode 100644
index 0000000..9f2c321
--- /dev/null
+++ b/drivers/watchdog/asm9260_wdt.c
@@ -0,0 +1,415 @@
+/*
+ * Watchdog driver for Alphascale ASM9260.
+ *
+ * Copyright (c) 2014 Oleksij Rempel <linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/reset.h>
+#include <linux/uaccess.h>
+#include <linux/watchdog.h>
+
+#define CLOCK_FREQ	1000000
+
+/* Watchdog Mode register */
+#define HW_WDMOD			0x00
+/* Wake interrupt. Set by HW, can't be cleared. */
+#define BM_MOD_WDINT			BIT(3)
+/* This bit set if timeout reached. Cleared by SW. */
+#define BM_MOD_WDTOF			BIT(2)
+/* HW Reset on timeout */
+#define BM_MOD_WDRESET			BIT(1)
+/* WD enable */
+#define BM_MOD_WDEN			BIT(0)
+
+/*
+ * Watchdog Timer Constant register
+ * Minimal value is 0xff, the meaning of this value
+ * depends on used clock: T = WDCLK * (0xff + 1) * 4
+ */
+#define HW_WDTC				0x04
+#define BM_WDTC_MAX(freq)		(0x7fffffff / (freq))
+
+/* Watchdog Feed register */
+#define HW_WDFEED			0x08
+
+/* Watchdog Timer Value register */
+#define HW_WDTV				0x0c
+
+#define ASM9260_WDT_DEFAULT_TIMEOUT	30
+
+enum asm9260_wdt_mode {
+	HW_RESET,
+	SW_RESET,
+	DEBUG,
+};
+
+struct asm9260_wdt_priv {
+	struct device		*dev;
+	struct watchdog_device	wdd;
+	struct clk		*clk;
+	struct clk		*clk_ahb;
+	struct reset_control	*rst;
+	struct notifier_block	restart_handler;
+
+	void __iomem		*iobase;
+	int			irq;
+	unsigned long		wdt_freq;
+	enum asm9260_wdt_mode	mode;
+};
+
+static int asm9260_wdt_feed(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+
+	iowrite32(0xaa, priv->iobase + HW_WDFEED);
+	iowrite32(0x55, priv->iobase + HW_WDFEED);
+
+	return 0;
+}
+
+static unsigned int asm9260_wdt_gettimeleft(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+	u32 counter;
+
+	counter = ioread32(priv->iobase + HW_WDTV);
+
+	return DIV_ROUND_CLOSEST(counter, priv->wdt_freq);
+}
+
+static int asm9260_wdt_updatetimeout(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+	u32 counter;
+
+	counter = wdd->timeout * priv->wdt_freq;
+
+	iowrite32(counter, priv->iobase + HW_WDTC);
+
+	return 0;
+}
+
+static int asm9260_wdt_enable(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+	u32 mode = 0;
+
+	if (priv->mode == HW_RESET)
+		mode = BM_MOD_WDRESET;
+
+	iowrite32(BM_MOD_WDEN | mode, priv->iobase + HW_WDMOD);
+
+	asm9260_wdt_updatetimeout(wdd);
+
+	asm9260_wdt_feed(wdd);
+
+	return 0;
+}
+
+static int asm9260_wdt_disable(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+
+	/* The only way to disable WD is to reset it. */
+	reset_control_assert(priv->rst);
+	reset_control_deassert(priv->rst);
+
+	return 0;
+}
+
+static int asm9260_wdt_settimeout(struct watchdog_device *wdd, unsigned int to)
+{
+	wdd->timeout = to;
+	asm9260_wdt_updatetimeout(wdd);
+
+	return 0;
+}
+
+static void asm9260_wdt_sys_reset(struct asm9260_wdt_priv *priv)
+{
+	/* init WD if it was not started */
+	priv->wdd.timeout = 1;
+
+	iowrite32(BM_MOD_WDEN | BM_MOD_WDRESET, priv->iobase + HW_WDMOD);
+
+	asm9260_wdt_updatetimeout(&priv->wdd);
+	/* first pass correct sequence */
+	asm9260_wdt_feed(&priv->wdd);
+	/*
+	 * Then write wrong pattern to the feed to trigger reset
+	 * ASAP.
+	 */
+	iowrite32(0xff, priv->iobase + HW_WDFEED);
+
+	mdelay(1000);
+}
+
+static irqreturn_t asm9260_wdt_irq(int irq, void *devid)
+{
+	struct asm9260_wdt_priv *priv = devid;
+	u32 stat;
+
+	stat = ioread32(priv->iobase + HW_WDMOD);
+	if (!(stat & BM_MOD_WDINT))
+		return IRQ_NONE;
+
+	if (priv->mode == DEBUG)
+		dev_info(priv->dev, "Watchdog Timeout. Do nothing.\n");
+	else {
+		dev_info(priv->dev, "Watchdog Timeout. Doing SW Reset.\n");
+		asm9260_wdt_sys_reset(priv);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int asm9260_restart_handler(struct notifier_block *this,
+				unsigned long mode, void *cmd)
+{
+	struct asm9260_wdt_priv *priv =
+		container_of(this, struct asm9260_wdt_priv, restart_handler);
+
+	asm9260_wdt_sys_reset(priv);
+
+	return NOTIFY_DONE;
+}
+
+static const struct watchdog_info asm9260_wdt_ident = {
+	.options          =     WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING
+				| WDIOF_MAGICCLOSE,
+	.identity         =	"Alphascale asm9260 Watchdog",
+};
+
+static struct watchdog_ops asm9260_wdt_ops = {
+	.owner		= THIS_MODULE,
+	.start		= asm9260_wdt_enable,
+	.stop		= asm9260_wdt_disable,
+	.get_timeleft	= asm9260_wdt_gettimeleft,
+	.ping		= asm9260_wdt_feed,
+	.set_timeout	= asm9260_wdt_settimeout,
+};
+
+static int __init asm9260_wdt_get_dt_clks(struct asm9260_wdt_priv *priv)
+{
+	int clk_idx = 0, err;
+	unsigned long clk;
+
+	priv->clk = devm_clk_get(priv->dev, "mod");
+	if (IS_ERR(priv->clk))
+		goto clk_err;
+
+	/* configure AHB clock */
+	clk_idx = 1;
+	priv->clk_ahb = devm_clk_get(priv->dev, "ahb");
+	if (IS_ERR(priv->clk_ahb))
+		goto clk_err;
+
+	err = clk_prepare_enable(priv->clk_ahb);
+	if (err) {
+		dev_err(priv->dev, "Failed to enable ahb_clk!\n");
+		goto out_err;
+	}
+
+	err = clk_set_rate(priv->clk, CLOCK_FREQ);
+	if (err) {
+		clk_disable_unprepare(priv->clk_ahb);
+		dev_err(priv->dev, "Failed to set rate!\n");
+		goto out_err;
+	}
+
+	err = clk_prepare_enable(priv->clk);
+	if (err) {
+		clk_disable_unprepare(priv->clk_ahb);
+		dev_err(priv->dev, "Failed to enable clk!\n");
+		goto out_err;
+	}
+
+	/* wdt has internal divider */
+	clk = clk_get_rate(priv->clk);
+	if (!clk) {
+		clk_disable_unprepare(priv->clk);
+		clk_disable_unprepare(priv->clk_ahb);
+		dev_err(priv->dev, "Failed, clk is 0!\n");
+		goto out_err;
+	}
+
+	priv->wdt_freq = clk / 2;
+
+	return 0;
+
+clk_err:
+	dev_err(priv->dev, "Failed to get clk (%s)\n",
+			clk_idx ? "ahb" : "mod");
+out_err:
+	return -EFAULT;
+}
+
+static int __init asm9260_wdt_get_dt_mode(struct asm9260_wdt_priv *priv)
+{
+	const char *tmp;
+	int ret;
+
+	/* default mode */
+	priv->mode = HW_RESET;
+
+	ret = of_property_read_string(priv->dev->of_node,
+			"alphascale,mode", &tmp);
+	if (ret < 0)
+		return ret;
+
+	if (!strcmp(tmp, "hw"))
+		priv->mode = HW_RESET;
+	else if (!strcmp(tmp, "sw"))
+		priv->mode = SW_RESET;
+	else if (!strcmp(tmp, "debug"))
+		priv->mode = DEBUG;
+	else {
+		dev_warn(priv->dev, "unknown reset-type: %s. Using defaul \"hw\" mode.",
+			tmp);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int __init asm9260_wdt_probe(struct platform_device *pdev)
+{
+	struct asm9260_wdt_priv *priv;
+	struct watchdog_device *wdd;
+	struct resource *res;
+	bool nowayout = WATCHDOG_NOWAYOUT;
+	int ret;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(struct asm9260_wdt_priv),
+			GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->dev = &pdev->dev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	priv->iobase = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(priv->iobase))
+		return PTR_ERR(priv->iobase);
+
+	ret = asm9260_wdt_get_dt_clks(priv);
+	if (ret)
+		return ret;
+
+	priv->rst = devm_reset_control_get(&pdev->dev, "wdt_rst");
+	if (IS_ERR(priv->rst)) {
+		nowayout = 1;
+		priv->rst = NULL;
+	}
+
+	wdd = &priv->wdd;
+	wdd->info = &asm9260_wdt_ident;
+	wdd->ops = &asm9260_wdt_ops;
+	wdd->min_timeout = 1;
+	wdd->max_timeout = BM_WDTC_MAX(priv->wdt_freq);
+	wdd->parent = &pdev->dev;
+
+	watchdog_set_drvdata(wdd, priv);
+
+	/*
+	 * If 'timeout-sec' unspecified in devicetree, assume a 30 second
+	 * default, unless the max timeout is less than 30 seconds, then use
+	 * the max instead.
+	 */
+	wdd->timeout = ASM9260_WDT_DEFAULT_TIMEOUT;
+	watchdog_init_timeout(wdd, 0, &pdev->dev);
+
+	watchdog_set_nowayout(wdd, nowayout);
+
+	asm9260_wdt_get_dt_mode(priv);
+
+	if (priv->mode != HW_RESET)
+		priv->irq = platform_get_irq(pdev, 0);
+
+	if (priv->irq > 0) {
+		/*
+		 * Not all supported platforms specify an interrupt for the
+		 * watchdog, so let's make it optional.
+		 */
+		ret = devm_request_irq(&pdev->dev, priv->irq,
+				asm9260_wdt_irq, IRQF_SHARED,
+				pdev->name, priv);
+		if (ret < 0)
+			dev_err(&pdev->dev, "failed to request IRQ\n");
+	}
+
+	ret = watchdog_register_device(wdd);
+	if (ret)
+		goto clk_off;
+
+	platform_set_drvdata(pdev, priv);
+
+	priv->restart_handler.notifier_call = asm9260_restart_handler;
+	priv->restart_handler.priority = 128;
+	ret = register_restart_handler(&priv->restart_handler);
+	if (ret)
+		dev_err(&pdev->dev, "cannot register restart handler\n");
+
+	dev_info(&pdev->dev, "Watchdog enabled (to=%d, nw=%d, mode=%d)\n",
+		wdd->timeout, nowayout, priv->mode);
+	return 0;
+
+clk_off:
+	clk_disable_unprepare(priv->clk);
+	clk_disable_unprepare(priv->clk_ahb);
+	return ret;
+}
+
+static void asm9260_wdt_shutdown(struct platform_device *pdev)
+{
+	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
+
+	asm9260_wdt_disable(&priv->wdd);
+}
+
+static int __exit asm9260_wdt_remove(struct platform_device *pdev)
+{
+	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
+
+	asm9260_wdt_shutdown(pdev);
+
+	unregister_restart_handler(&priv->restart_handler);
+	clk_disable_unprepare(priv->clk);
+	clk_disable_unprepare(priv->clk_ahb);
+
+	return 0;
+}
+
+static const struct of_device_id asm9260_wdt_of_match[] = {
+	{ .compatible = "alphascale,asm9260-wdt"},
+	{},
+};
+MODULE_DEVICE_TABLE(of, asm9260_wdt_of_match);
+
+static struct platform_driver asm9260_wdt_driver = {
+	.driver = {
+		.name = "asm9260-wdt",
+		.owner = THIS_MODULE,
+		.of_match_table	= asm9260_wdt_of_match,
+	},
+	.probe = asm9260_wdt_probe,
+	.remove = asm9260_wdt_remove,
+	.shutdown = asm9260_wdt_shutdown,
+};
+module_platform_driver(asm9260_wdt_driver);
+
+MODULE_DESCRIPTION("asm9260 WatchDog Timer Driver");
+MODULE_AUTHOR("Oleksij Rempel <linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>");
+MODULE_LICENSE("GPL");
-- 
2.5.0

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH v2] watchdog: add Alphascale asm9260-wdt driver
@ 2015-11-05  9:06           ` Oleksij Rempel
  0 siblings, 0 replies; 43+ messages in thread
From: Oleksij Rempel @ 2015-11-05  9:06 UTC (permalink / raw)
  To: linux-watchdog, linux, wim, devicetree; +Cc: Oleksij Rempel

Add WD support for Alphascale asm9260 SoC. This driver
provide support for different function modes:
- HW mode to trigger SoC reset on timeout
- SW mode do soft reset if needed
- DEBUG mode

Optional support for stopping watchdog. If reset binding are not provided
this driver will work in nowayout mode.

Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
---
 .../bindings/watchdog/alphascale-asm9260.txt       |  39 ++
 drivers/watchdog/Kconfig                           |   9 +
 drivers/watchdog/Makefile                          |   1 +
 drivers/watchdog/asm9260_wdt.c                     | 415 +++++++++++++++++++++
 4 files changed, 464 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
 create mode 100644 drivers/watchdog/asm9260_wdt.c

diff --git a/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
new file mode 100644
index 0000000..6e54d1f
--- /dev/null
+++ b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
@@ -0,0 +1,39 @@
+Alphascale asm9260 Watchdog timer
+
+Required properties:
+
+- compatible : should be "alphascale,asm9260-wdt".
+- reg : Specifies base physical address and size of the registers.
+- clocks : the clocks feeding the watchdog timer. See clock-bindings.txt
+- clock-names : should be set to
+	"mod" - source for tick counter.
+	"ahb" - ahb gate.
+
+Optional properties:
+- resets : phandle pointing to the system reset controller with correct
+	reset line index for watchdog controller reset. This propertie is
+	required if you need to disable "nowayout" and it neened only with
+	CONFIG_WATCHDOG_NOWAYOUT=n.
+	Without reseting this WD controller, it is not possible to stop
+	counter.
+- reset-names : should be set to "wdt_rst" if "resets" is used.
+- timeout-sec : shall contain the default watchdog timeout in seconds,
+	if unset, the default timeout is 30 seconds.
+- alphascale,mode : tree modes are supported
+	"hw" - hw reset (defaul).
+	"sw" - sw reset.
+	"debug" - no action is taken.
+
+Example:
+
+watchdog0: watchdog@80048000 {
+	compatible = "alphascale,asm9260-wdt";
+	reg = <0x80048000 0x10>;
+	clocks = <&acc CLKID_SYS_WDT>, <&acc CLKID_AHB_WDT>;
+	clock-names = "mod", "ahb";
+	interrupts = <55>;
+	resets = <&rst WDT_RESET>;
+	reset-names = "wdt_rst";
+	timeout-sec = <30>;
+	alphascale,mode = "hw";
+};
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index c68edc1..cc5f675 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -173,6 +173,15 @@ config ARM_SP805_WATCHDOG
 	  ARM Primecell SP805 Watchdog timer. This will reboot your system when
 	  the timeout is reached.
 
+config ASM9260_WATCHDOG
+	tristate "Alphascale ASM9260 watchdog"
+	depends on MACH_ASM9260
+	depends on OF
+	select WATCHDOG_CORE
+	help
+	  Watchdog timer embedded into Alphascale asm9260 chips. This will reboot your
+	  system when the timeout is reached.
+
 config AT91RM9200_WATCHDOG
 	tristate "AT91RM9200 watchdog"
 	depends on SOC_AT91RM9200 && MFD_SYSCON
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 0c616e3..bd7b0cd 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
 
 # ARM Architecture
 obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o
+obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o
 obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
 obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
 obj-$(CONFIG_CADENCE_WATCHDOG) += cadence_wdt.o
diff --git a/drivers/watchdog/asm9260_wdt.c b/drivers/watchdog/asm9260_wdt.c
new file mode 100644
index 0000000..9f2c321
--- /dev/null
+++ b/drivers/watchdog/asm9260_wdt.c
@@ -0,0 +1,415 @@
+/*
+ * Watchdog driver for Alphascale ASM9260.
+ *
+ * Copyright (c) 2014 Oleksij Rempel <linux@rempel-privat.de>
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/reset.h>
+#include <linux/uaccess.h>
+#include <linux/watchdog.h>
+
+#define CLOCK_FREQ	1000000
+
+/* Watchdog Mode register */
+#define HW_WDMOD			0x00
+/* Wake interrupt. Set by HW, can't be cleared. */
+#define BM_MOD_WDINT			BIT(3)
+/* This bit set if timeout reached. Cleared by SW. */
+#define BM_MOD_WDTOF			BIT(2)
+/* HW Reset on timeout */
+#define BM_MOD_WDRESET			BIT(1)
+/* WD enable */
+#define BM_MOD_WDEN			BIT(0)
+
+/*
+ * Watchdog Timer Constant register
+ * Minimal value is 0xff, the meaning of this value
+ * depends on used clock: T = WDCLK * (0xff + 1) * 4
+ */
+#define HW_WDTC				0x04
+#define BM_WDTC_MAX(freq)		(0x7fffffff / (freq))
+
+/* Watchdog Feed register */
+#define HW_WDFEED			0x08
+
+/* Watchdog Timer Value register */
+#define HW_WDTV				0x0c
+
+#define ASM9260_WDT_DEFAULT_TIMEOUT	30
+
+enum asm9260_wdt_mode {
+	HW_RESET,
+	SW_RESET,
+	DEBUG,
+};
+
+struct asm9260_wdt_priv {
+	struct device		*dev;
+	struct watchdog_device	wdd;
+	struct clk		*clk;
+	struct clk		*clk_ahb;
+	struct reset_control	*rst;
+	struct notifier_block	restart_handler;
+
+	void __iomem		*iobase;
+	int			irq;
+	unsigned long		wdt_freq;
+	enum asm9260_wdt_mode	mode;
+};
+
+static int asm9260_wdt_feed(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+
+	iowrite32(0xaa, priv->iobase + HW_WDFEED);
+	iowrite32(0x55, priv->iobase + HW_WDFEED);
+
+	return 0;
+}
+
+static unsigned int asm9260_wdt_gettimeleft(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+	u32 counter;
+
+	counter = ioread32(priv->iobase + HW_WDTV);
+
+	return DIV_ROUND_CLOSEST(counter, priv->wdt_freq);
+}
+
+static int asm9260_wdt_updatetimeout(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+	u32 counter;
+
+	counter = wdd->timeout * priv->wdt_freq;
+
+	iowrite32(counter, priv->iobase + HW_WDTC);
+
+	return 0;
+}
+
+static int asm9260_wdt_enable(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+	u32 mode = 0;
+
+	if (priv->mode == HW_RESET)
+		mode = BM_MOD_WDRESET;
+
+	iowrite32(BM_MOD_WDEN | mode, priv->iobase + HW_WDMOD);
+
+	asm9260_wdt_updatetimeout(wdd);
+
+	asm9260_wdt_feed(wdd);
+
+	return 0;
+}
+
+static int asm9260_wdt_disable(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+
+	/* The only way to disable WD is to reset it. */
+	reset_control_assert(priv->rst);
+	reset_control_deassert(priv->rst);
+
+	return 0;
+}
+
+static int asm9260_wdt_settimeout(struct watchdog_device *wdd, unsigned int to)
+{
+	wdd->timeout = to;
+	asm9260_wdt_updatetimeout(wdd);
+
+	return 0;
+}
+
+static void asm9260_wdt_sys_reset(struct asm9260_wdt_priv *priv)
+{
+	/* init WD if it was not started */
+	priv->wdd.timeout = 1;
+
+	iowrite32(BM_MOD_WDEN | BM_MOD_WDRESET, priv->iobase + HW_WDMOD);
+
+	asm9260_wdt_updatetimeout(&priv->wdd);
+	/* first pass correct sequence */
+	asm9260_wdt_feed(&priv->wdd);
+	/*
+	 * Then write wrong pattern to the feed to trigger reset
+	 * ASAP.
+	 */
+	iowrite32(0xff, priv->iobase + HW_WDFEED);
+
+	mdelay(1000);
+}
+
+static irqreturn_t asm9260_wdt_irq(int irq, void *devid)
+{
+	struct asm9260_wdt_priv *priv = devid;
+	u32 stat;
+
+	stat = ioread32(priv->iobase + HW_WDMOD);
+	if (!(stat & BM_MOD_WDINT))
+		return IRQ_NONE;
+
+	if (priv->mode == DEBUG)
+		dev_info(priv->dev, "Watchdog Timeout. Do nothing.\n");
+	else {
+		dev_info(priv->dev, "Watchdog Timeout. Doing SW Reset.\n");
+		asm9260_wdt_sys_reset(priv);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int asm9260_restart_handler(struct notifier_block *this,
+				unsigned long mode, void *cmd)
+{
+	struct asm9260_wdt_priv *priv =
+		container_of(this, struct asm9260_wdt_priv, restart_handler);
+
+	asm9260_wdt_sys_reset(priv);
+
+	return NOTIFY_DONE;
+}
+
+static const struct watchdog_info asm9260_wdt_ident = {
+	.options          =     WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING
+				| WDIOF_MAGICCLOSE,
+	.identity         =	"Alphascale asm9260 Watchdog",
+};
+
+static struct watchdog_ops asm9260_wdt_ops = {
+	.owner		= THIS_MODULE,
+	.start		= asm9260_wdt_enable,
+	.stop		= asm9260_wdt_disable,
+	.get_timeleft	= asm9260_wdt_gettimeleft,
+	.ping		= asm9260_wdt_feed,
+	.set_timeout	= asm9260_wdt_settimeout,
+};
+
+static int __init asm9260_wdt_get_dt_clks(struct asm9260_wdt_priv *priv)
+{
+	int clk_idx = 0, err;
+	unsigned long clk;
+
+	priv->clk = devm_clk_get(priv->dev, "mod");
+	if (IS_ERR(priv->clk))
+		goto clk_err;
+
+	/* configure AHB clock */
+	clk_idx = 1;
+	priv->clk_ahb = devm_clk_get(priv->dev, "ahb");
+	if (IS_ERR(priv->clk_ahb))
+		goto clk_err;
+
+	err = clk_prepare_enable(priv->clk_ahb);
+	if (err) {
+		dev_err(priv->dev, "Failed to enable ahb_clk!\n");
+		goto out_err;
+	}
+
+	err = clk_set_rate(priv->clk, CLOCK_FREQ);
+	if (err) {
+		clk_disable_unprepare(priv->clk_ahb);
+		dev_err(priv->dev, "Failed to set rate!\n");
+		goto out_err;
+	}
+
+	err = clk_prepare_enable(priv->clk);
+	if (err) {
+		clk_disable_unprepare(priv->clk_ahb);
+		dev_err(priv->dev, "Failed to enable clk!\n");
+		goto out_err;
+	}
+
+	/* wdt has internal divider */
+	clk = clk_get_rate(priv->clk);
+	if (!clk) {
+		clk_disable_unprepare(priv->clk);
+		clk_disable_unprepare(priv->clk_ahb);
+		dev_err(priv->dev, "Failed, clk is 0!\n");
+		goto out_err;
+	}
+
+	priv->wdt_freq = clk / 2;
+
+	return 0;
+
+clk_err:
+	dev_err(priv->dev, "Failed to get clk (%s)\n",
+			clk_idx ? "ahb" : "mod");
+out_err:
+	return -EFAULT;
+}
+
+static int __init asm9260_wdt_get_dt_mode(struct asm9260_wdt_priv *priv)
+{
+	const char *tmp;
+	int ret;
+
+	/* default mode */
+	priv->mode = HW_RESET;
+
+	ret = of_property_read_string(priv->dev->of_node,
+			"alphascale,mode", &tmp);
+	if (ret < 0)
+		return ret;
+
+	if (!strcmp(tmp, "hw"))
+		priv->mode = HW_RESET;
+	else if (!strcmp(tmp, "sw"))
+		priv->mode = SW_RESET;
+	else if (!strcmp(tmp, "debug"))
+		priv->mode = DEBUG;
+	else {
+		dev_warn(priv->dev, "unknown reset-type: %s. Using defaul \"hw\" mode.",
+			tmp);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int __init asm9260_wdt_probe(struct platform_device *pdev)
+{
+	struct asm9260_wdt_priv *priv;
+	struct watchdog_device *wdd;
+	struct resource *res;
+	bool nowayout = WATCHDOG_NOWAYOUT;
+	int ret;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(struct asm9260_wdt_priv),
+			GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->dev = &pdev->dev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	priv->iobase = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(priv->iobase))
+		return PTR_ERR(priv->iobase);
+
+	ret = asm9260_wdt_get_dt_clks(priv);
+	if (ret)
+		return ret;
+
+	priv->rst = devm_reset_control_get(&pdev->dev, "wdt_rst");
+	if (IS_ERR(priv->rst)) {
+		nowayout = 1;
+		priv->rst = NULL;
+	}
+
+	wdd = &priv->wdd;
+	wdd->info = &asm9260_wdt_ident;
+	wdd->ops = &asm9260_wdt_ops;
+	wdd->min_timeout = 1;
+	wdd->max_timeout = BM_WDTC_MAX(priv->wdt_freq);
+	wdd->parent = &pdev->dev;
+
+	watchdog_set_drvdata(wdd, priv);
+
+	/*
+	 * If 'timeout-sec' unspecified in devicetree, assume a 30 second
+	 * default, unless the max timeout is less than 30 seconds, then use
+	 * the max instead.
+	 */
+	wdd->timeout = ASM9260_WDT_DEFAULT_TIMEOUT;
+	watchdog_init_timeout(wdd, 0, &pdev->dev);
+
+	watchdog_set_nowayout(wdd, nowayout);
+
+	asm9260_wdt_get_dt_mode(priv);
+
+	if (priv->mode != HW_RESET)
+		priv->irq = platform_get_irq(pdev, 0);
+
+	if (priv->irq > 0) {
+		/*
+		 * Not all supported platforms specify an interrupt for the
+		 * watchdog, so let's make it optional.
+		 */
+		ret = devm_request_irq(&pdev->dev, priv->irq,
+				asm9260_wdt_irq, IRQF_SHARED,
+				pdev->name, priv);
+		if (ret < 0)
+			dev_err(&pdev->dev, "failed to request IRQ\n");
+	}
+
+	ret = watchdog_register_device(wdd);
+	if (ret)
+		goto clk_off;
+
+	platform_set_drvdata(pdev, priv);
+
+	priv->restart_handler.notifier_call = asm9260_restart_handler;
+	priv->restart_handler.priority = 128;
+	ret = register_restart_handler(&priv->restart_handler);
+	if (ret)
+		dev_err(&pdev->dev, "cannot register restart handler\n");
+
+	dev_info(&pdev->dev, "Watchdog enabled (to=%d, nw=%d, mode=%d)\n",
+		wdd->timeout, nowayout, priv->mode);
+	return 0;
+
+clk_off:
+	clk_disable_unprepare(priv->clk);
+	clk_disable_unprepare(priv->clk_ahb);
+	return ret;
+}
+
+static void asm9260_wdt_shutdown(struct platform_device *pdev)
+{
+	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
+
+	asm9260_wdt_disable(&priv->wdd);
+}
+
+static int __exit asm9260_wdt_remove(struct platform_device *pdev)
+{
+	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
+
+	asm9260_wdt_shutdown(pdev);
+
+	unregister_restart_handler(&priv->restart_handler);
+	clk_disable_unprepare(priv->clk);
+	clk_disable_unprepare(priv->clk_ahb);
+
+	return 0;
+}
+
+static const struct of_device_id asm9260_wdt_of_match[] = {
+	{ .compatible = "alphascale,asm9260-wdt"},
+	{},
+};
+MODULE_DEVICE_TABLE(of, asm9260_wdt_of_match);
+
+static struct platform_driver asm9260_wdt_driver = {
+	.driver = {
+		.name = "asm9260-wdt",
+		.owner = THIS_MODULE,
+		.of_match_table	= asm9260_wdt_of_match,
+	},
+	.probe = asm9260_wdt_probe,
+	.remove = asm9260_wdt_remove,
+	.shutdown = asm9260_wdt_shutdown,
+};
+module_platform_driver(asm9260_wdt_driver);
+
+MODULE_DESCRIPTION("asm9260 WatchDog Timer Driver");
+MODULE_AUTHOR("Oleksij Rempel <linux@rempel-privat.de>");
+MODULE_LICENSE("GPL");
-- 
2.5.0


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

* Re: [PATCH v2] watchdog: add Alphascale asm9260-wdt driver
  2015-11-05  9:06           ` Oleksij Rempel
@ 2015-11-05 16:32               ` Guenter Roeck
  -1 siblings, 0 replies; 43+ messages in thread
From: Guenter Roeck @ 2015-11-05 16:32 UTC (permalink / raw)
  To: Oleksij Rempel, linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	wim-IQzOog9fTRqzQB+pC5nmwQ, devicetree-u79uwXL29TY76Z2rM5mHXA

On 11/05/2015 01:06 AM, Oleksij Rempel wrote:
> Add WD support for Alphascale asm9260 SoC. This driver
> provide support for different function modes:
> - HW mode to trigger SoC reset on timeout
> - SW mode do soft reset if needed
> - DEBUG mode
>
> Optional support for stopping watchdog. If reset binding are not provided
> this driver will work in nowayout mode.
>

In general, this is considered to be orthogonal: If the user doesn't want
nowayout, and if the watchdog can not be stopped, other drivers issue a warning
that the watchdog was not stopped, and that the system will likely restart.

> Signed-off-by: Oleksij Rempel <linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>
> ---
>   .../bindings/watchdog/alphascale-asm9260.txt       |  39 ++
>   drivers/watchdog/Kconfig                           |   9 +
>   drivers/watchdog/Makefile                          |   1 +
>   drivers/watchdog/asm9260_wdt.c                     | 415 +++++++++++++++++++++
>   4 files changed, 464 insertions(+)
>   create mode 100644 Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
>   create mode 100644 drivers/watchdog/asm9260_wdt.c
>
> diff --git a/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
> new file mode 100644
> index 0000000..6e54d1f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
> @@ -0,0 +1,39 @@
> +Alphascale asm9260 Watchdog timer
> +
> +Required properties:
> +
> +- compatible : should be "alphascale,asm9260-wdt".
> +- reg : Specifies base physical address and size of the registers.
> +- clocks : the clocks feeding the watchdog timer. See clock-bindings.txt
> +- clock-names : should be set to
> +	"mod" - source for tick counter.
> +	"ahb" - ahb gate.
> +
> +Optional properties:
> +- resets : phandle pointing to the system reset controller with correct
> +	reset line index for watchdog controller reset. This propertie is
> +	required if you need to disable "nowayout" and it neened only with
> +	CONFIG_WATCHDOG_NOWAYOUT=n.
> +	Without reseting this WD controller, it is not possible to stop
> +	counter.
> +- reset-names : should be set to "wdt_rst" if "resets" is used.
> +- timeout-sec : shall contain the default watchdog timeout in seconds,
> +	if unset, the default timeout is 30 seconds.
> +- alphascale,mode : tree modes are supported
> +	"hw" - hw reset (defaul).
> +	"sw" - sw reset.
> +	"debug" - no action is taken.
> +
> +Example:
> +
> +watchdog0: watchdog@80048000 {
> +	compatible = "alphascale,asm9260-wdt";
> +	reg = <0x80048000 0x10>;
> +	clocks = <&acc CLKID_SYS_WDT>, <&acc CLKID_AHB_WDT>;
> +	clock-names = "mod", "ahb";
> +	interrupts = <55>;
> +	resets = <&rst WDT_RESET>;
> +	reset-names = "wdt_rst";
> +	timeout-sec = <30>;
> +	alphascale,mode = "hw";
> +};
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index c68edc1..cc5f675 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -173,6 +173,15 @@ config ARM_SP805_WATCHDOG
>   	  ARM Primecell SP805 Watchdog timer. This will reboot your system when
>   	  the timeout is reached.
>
> +config ASM9260_WATCHDOG
> +	tristate "Alphascale ASM9260 watchdog"
> +	depends on MACH_ASM9260
> +	depends on OF
> +	select WATCHDOG_CORE
> +	help
> +	  Watchdog timer embedded into Alphascale asm9260 chips. This will reboot your
> +	  system when the timeout is reached.
> +
>   config AT91RM9200_WATCHDOG
>   	tristate "AT91RM9200 watchdog"
>   	depends on SOC_AT91RM9200 && MFD_SYSCON
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index 0c616e3..bd7b0cd 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -30,6 +30,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
>
>   # ARM Architecture
>   obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o
> +obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o
>   obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
>   obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
>   obj-$(CONFIG_CADENCE_WATCHDOG) += cadence_wdt.o
> diff --git a/drivers/watchdog/asm9260_wdt.c b/drivers/watchdog/asm9260_wdt.c
> new file mode 100644
> index 0000000..9f2c321
> --- /dev/null
> +++ b/drivers/watchdog/asm9260_wdt.c
> @@ -0,0 +1,415 @@
> +/*
> + * Watchdog driver for Alphascale ASM9260.
> + *
> + * Copyright (c) 2014 Oleksij Rempel <linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>
> + *
> + * Licensed under GPLv2 or later.
> + */
> +
> +#include <linux/bitops.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>

I don't see any module parameters. Is this include needed ?

> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/reboot.h>
> +#include <linux/reset.h>
> +#include <linux/uaccess.h>

Is this include needed ?

> +#include <linux/watchdog.h>
> +
> +#define CLOCK_FREQ	1000000
> +
> +/* Watchdog Mode register */
> +#define HW_WDMOD			0x00
> +/* Wake interrupt. Set by HW, can't be cleared. */
> +#define BM_MOD_WDINT			BIT(3)
> +/* This bit set if timeout reached. Cleared by SW. */
> +#define BM_MOD_WDTOF			BIT(2)
> +/* HW Reset on timeout */
> +#define BM_MOD_WDRESET			BIT(1)
> +/* WD enable */
> +#define BM_MOD_WDEN			BIT(0)
> +
> +/*
> + * Watchdog Timer Constant register
> + * Minimal value is 0xff, the meaning of this value
> + * depends on used clock: T = WDCLK * (0xff + 1) * 4
> + */
> +#define HW_WDTC				0x04
> +#define BM_WDTC_MAX(freq)		(0x7fffffff / (freq))
> +
> +/* Watchdog Feed register */
> +#define HW_WDFEED			0x08
> +
> +/* Watchdog Timer Value register */
> +#define HW_WDTV				0x0c
> +
> +#define ASM9260_WDT_DEFAULT_TIMEOUT	30
> +
> +enum asm9260_wdt_mode {
> +	HW_RESET,
> +	SW_RESET,
> +	DEBUG,
> +};
> +
> +struct asm9260_wdt_priv {
> +	struct device		*dev;
> +	struct watchdog_device	wdd;
> +	struct clk		*clk;
> +	struct clk		*clk_ahb;
> +	struct reset_control	*rst;
> +	struct notifier_block	restart_handler;
> +
> +	void __iomem		*iobase;
> +	int			irq;
> +	unsigned long		wdt_freq;
> +	enum asm9260_wdt_mode	mode;
> +};
> +
> +static int asm9260_wdt_feed(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +
> +	iowrite32(0xaa, priv->iobase + HW_WDFEED);
> +	iowrite32(0x55, priv->iobase + HW_WDFEED);
> +
> +	return 0;
> +}
> +
> +static unsigned int asm9260_wdt_gettimeleft(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +	u32 counter;
> +
> +	counter = ioread32(priv->iobase + HW_WDTV);
> +
> +	return DIV_ROUND_CLOSEST(counter, priv->wdt_freq);
> +}
> +
> +static int asm9260_wdt_updatetimeout(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +	u32 counter;
> +
> +	counter = wdd->timeout * priv->wdt_freq;
> +
> +	iowrite32(counter, priv->iobase + HW_WDTC);
> +
> +	return 0;
> +}
> +
> +static int asm9260_wdt_enable(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +	u32 mode = 0;
> +
> +	if (priv->mode == HW_RESET)
> +		mode = BM_MOD_WDRESET;
> +
> +	iowrite32(BM_MOD_WDEN | mode, priv->iobase + HW_WDMOD);
> +
> +	asm9260_wdt_updatetimeout(wdd);
> +
> +	asm9260_wdt_feed(wdd);
> +
> +	return 0;
> +}
> +
> +static int asm9260_wdt_disable(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +
> +	/* The only way to disable WD is to reset it. */
> +	reset_control_assert(priv->rst);
> +	reset_control_deassert(priv->rst);
> +

I am a bit wary of depending on the infrastructure to never call this code
if priv->rst is NULL, due to having nowayout set.
You should at least add a comment here indicating that the code
depends on this behavior.

My preferred solution, though, would be to stop playing with nowayout and
dump a warning to the console if the watchdog could not be stopped.

> +	return 0;
> +}
> +
> +static int asm9260_wdt_settimeout(struct watchdog_device *wdd, unsigned int to)
> +{
> +	wdd->timeout = to;
> +	asm9260_wdt_updatetimeout(wdd);
> +
> +	return 0;
> +}
> +
> +static void asm9260_wdt_sys_reset(struct asm9260_wdt_priv *priv)
> +{
> +	/* init WD if it was not started */
> +	priv->wdd.timeout = 1;
> +
Wouldn't it make more sense to write a smaller value into HW_WDTC directly ?
Unless I am missing something, this creates a one-second delay which seems
unnecessary.

> +	iowrite32(BM_MOD_WDEN | BM_MOD_WDRESET, priv->iobase + HW_WDMOD);
> +
> +	asm9260_wdt_updatetimeout(&priv->wdd);
> +	/* first pass correct sequence */
> +	asm9260_wdt_feed(&priv->wdd);
> +	/*
> +	 * Then write wrong pattern to the feed to trigger reset
> +	 * ASAP.
> +	 */
> +	iowrite32(0xff, priv->iobase + HW_WDFEED);
> +
> +	mdelay(1000);
> +}
> +
> +static irqreturn_t asm9260_wdt_irq(int irq, void *devid)
> +{
> +	struct asm9260_wdt_priv *priv = devid;
> +	u32 stat;
> +
> +	stat = ioread32(priv->iobase + HW_WDMOD);
> +	if (!(stat & BM_MOD_WDINT))
> +		return IRQ_NONE;
> +
> +	if (priv->mode == DEBUG)
> +		dev_info(priv->dev, "Watchdog Timeout. Do nothing.\n");
> +	else {
> +		dev_info(priv->dev, "Watchdog Timeout. Doing SW Reset.\n");
> +		asm9260_wdt_sys_reset(priv);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int asm9260_restart_handler(struct notifier_block *this,
> +				unsigned long mode, void *cmd)
> +{
> +	struct asm9260_wdt_priv *priv =
> +		container_of(this, struct asm9260_wdt_priv, restart_handler);
> +
> +	asm9260_wdt_sys_reset(priv);
> +
> +	return NOTIFY_DONE;
> +}
> +
> +static const struct watchdog_info asm9260_wdt_ident = {
> +	.options          =     WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING
> +				| WDIOF_MAGICCLOSE,
> +	.identity         =	"Alphascale asm9260 Watchdog",
> +};
> +
> +static struct watchdog_ops asm9260_wdt_ops = {
> +	.owner		= THIS_MODULE,
> +	.start		= asm9260_wdt_enable,
> +	.stop		= asm9260_wdt_disable,
> +	.get_timeleft	= asm9260_wdt_gettimeleft,
> +	.ping		= asm9260_wdt_feed,
> +	.set_timeout	= asm9260_wdt_settimeout,
> +};
> +
> +static int __init asm9260_wdt_get_dt_clks(struct asm9260_wdt_priv *priv)
> +{
> +	int clk_idx = 0, err;
> +	unsigned long clk;
> +
> +	priv->clk = devm_clk_get(priv->dev, "mod");
> +	if (IS_ERR(priv->clk))
> +		goto clk_err;
> +
> +	/* configure AHB clock */
> +	clk_idx = 1;
> +	priv->clk_ahb = devm_clk_get(priv->dev, "ahb");
> +	if (IS_ERR(priv->clk_ahb))
> +		goto clk_err;
> +
Seems you are using clk_err only to display an error message.
This is not the intended use of error jump labels.

> +	err = clk_prepare_enable(priv->clk_ahb);
> +	if (err) {
> +		dev_err(priv->dev, "Failed to enable ahb_clk!\n");
> +		goto out_err;
> +	}
> +
> +	err = clk_set_rate(priv->clk, CLOCK_FREQ);
> +	if (err) {
> +		clk_disable_unprepare(priv->clk_ahb);
> +		dev_err(priv->dev, "Failed to set rate!\n");
> +		goto out_err;
> +	}
> +
> +	err = clk_prepare_enable(priv->clk);
> +	if (err) {
> +		clk_disable_unprepare(priv->clk_ahb);
> +		dev_err(priv->dev, "Failed to enable clk!\n");
> +		goto out_err;
> +	}
> +
> +	/* wdt has internal divider */
> +	clk = clk_get_rate(priv->clk);
> +	if (!clk) {
> +		clk_disable_unprepare(priv->clk);
> +		clk_disable_unprepare(priv->clk_ahb);

The basic idea with error exit jumps is to keep the error handling
there. This code defeats the purpose.

> +		dev_err(priv->dev, "Failed, clk is 0!\n");
> +		goto out_err;
> +	}
> +
> +	priv->wdt_freq = clk / 2;
> +
> +	return 0;
> +
> +clk_err:
> +	dev_err(priv->dev, "Failed to get clk (%s)\n",
> +			clk_idx ? "ahb" : "mod");

This is an odd use of an error return function. I would suggest to
create the error messages where they occur, and only handle cleanup here,
as suggested in the coding style document.

> +out_err:

This label is not needed. Any code jumping to it can return immediately.

> +	return -EFAULT;

EFAULT: /* Bad address */

Commonly used to identify a bad address passed from user space.
I don't think it is appropriate here. Besides, why override the already
known error in the first place ?


> +}
> +
> +static int __init asm9260_wdt_get_dt_mode(struct asm9260_wdt_priv *priv)
> +{
> +	const char *tmp;
> +	int ret;
> +
> +	/* default mode */
> +	priv->mode = HW_RESET;
> +
> +	ret = of_property_read_string(priv->dev->of_node,
> +			"alphascale,mode", &tmp);

Please align continuation lines with '('.

> +	if (ret < 0)
> +		return ret;
> +
> +	if (!strcmp(tmp, "hw"))
> +		priv->mode = HW_RESET;
> +	else if (!strcmp(tmp, "sw"))
> +		priv->mode = SW_RESET;
> +	else if (!strcmp(tmp, "debug"))
> +		priv->mode = DEBUG;
> +	else {
> +		dev_warn(priv->dev, "unknown reset-type: %s. Using defaul \"hw\" mode.",
> +			tmp);

s/defaul/default/

> +		return -EINVAL;

Why return an error (instead of void) if it isn't used ?

> +	}
> +
> +	return 0;
> +}
> +
> +static int __init asm9260_wdt_probe(struct platform_device *pdev)
> +{
> +	struct asm9260_wdt_priv *priv;
> +	struct watchdog_device *wdd;
> +	struct resource *res;
> +	bool nowayout = WATCHDOG_NOWAYOUT;
> +	int ret;
> +
> +	priv = devm_kzalloc(&pdev->dev, sizeof(struct asm9260_wdt_priv),
> +			GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->dev = &pdev->dev;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	priv->iobase = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(priv->iobase))
> +		return PTR_ERR(priv->iobase);
> +
> +	ret = asm9260_wdt_get_dt_clks(priv);
> +	if (ret)
> +		return ret;
> +
> +	priv->rst = devm_reset_control_get(&pdev->dev, "wdt_rst");
> +	if (IS_ERR(priv->rst)) {
> +		nowayout = 1;
> +		priv->rst = NULL;
> +	}
> +
> +	wdd = &priv->wdd;
> +	wdd->info = &asm9260_wdt_ident;
> +	wdd->ops = &asm9260_wdt_ops;
> +	wdd->min_timeout = 1;
> +	wdd->max_timeout = BM_WDTC_MAX(priv->wdt_freq);
> +	wdd->parent = &pdev->dev;
> +
> +	watchdog_set_drvdata(wdd, priv);
> +
> +	/*
> +	 * If 'timeout-sec' unspecified in devicetree, assume a 30 second
> +	 * default, unless the max timeout is less than 30 seconds, then use
> +	 * the max instead.
> +	 */
> +	wdd->timeout = ASM9260_WDT_DEFAULT_TIMEOUT;
> +	watchdog_init_timeout(wdd, 0, &pdev->dev);
> +
> +	watchdog_set_nowayout(wdd, nowayout);
> +
> +	asm9260_wdt_get_dt_mode(priv);
> +
> +	if (priv->mode != HW_RESET)
> +		priv->irq = platform_get_irq(pdev, 0);
> +
> +	if (priv->irq > 0) {
> +		/*
> +		 * Not all supported platforms specify an interrupt for the
> +		 * watchdog, so let's make it optional.
> +		 */
> +		ret = devm_request_irq(&pdev->dev, priv->irq,
> +				asm9260_wdt_irq, IRQF_SHARED,
> +				pdev->name, priv);
> +		if (ret < 0)
> +			dev_err(&pdev->dev, "failed to request IRQ\n");

dev_warn since you don't bail out.

> +	}
> +
> +	ret = watchdog_register_device(wdd);
> +	if (ret)
> +		goto clk_off;
> +
> +	platform_set_drvdata(pdev, priv);
> +
> +	priv->restart_handler.notifier_call = asm9260_restart_handler;
> +	priv->restart_handler.priority = 128;
> +	ret = register_restart_handler(&priv->restart_handler);
> +	if (ret)
> +		dev_err(&pdev->dev, "cannot register restart handler\n");

dev_warn since you don't bail out.

> +
> +	dev_info(&pdev->dev, "Watchdog enabled (to=%d, nw=%d, mode=%d)\n",
> +		wdd->timeout, nowayout, priv->mode);
> +	return 0;
> +
> +clk_off:
> +	clk_disable_unprepare(priv->clk);
> +	clk_disable_unprepare(priv->clk_ahb);
> +	return ret;
> +}
> +
> +static void asm9260_wdt_shutdown(struct platform_device *pdev)
> +{
> +	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
> +
> +	asm9260_wdt_disable(&priv->wdd);
> +}
> +
> +static int __exit asm9260_wdt_remove(struct platform_device *pdev)
> +{
> +	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
> +
> +	asm9260_wdt_shutdown(pdev);

You could just call asm9260_wdt_disable() directly.

> +
> +	unregister_restart_handler(&priv->restart_handler);
> +	clk_disable_unprepare(priv->clk);
> +	clk_disable_unprepare(priv->clk_ahb);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id asm9260_wdt_of_match[] = {
> +	{ .compatible = "alphascale,asm9260-wdt"},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, asm9260_wdt_of_match);
> +
> +static struct platform_driver asm9260_wdt_driver = {
> +	.driver = {
> +		.name = "asm9260-wdt",
> +		.owner = THIS_MODULE,
> +		.of_match_table	= asm9260_wdt_of_match,
> +	},
> +	.probe = asm9260_wdt_probe,
> +	.remove = asm9260_wdt_remove,
> +	.shutdown = asm9260_wdt_shutdown,
> +};
> +module_platform_driver(asm9260_wdt_driver);
> +
> +MODULE_DESCRIPTION("asm9260 WatchDog Timer Driver");
> +MODULE_AUTHOR("Oleksij Rempel <linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>");
> +MODULE_LICENSE("GPL");
>

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v2] watchdog: add Alphascale asm9260-wdt driver
@ 2015-11-05 16:32               ` Guenter Roeck
  0 siblings, 0 replies; 43+ messages in thread
From: Guenter Roeck @ 2015-11-05 16:32 UTC (permalink / raw)
  To: Oleksij Rempel, linux-watchdog, wim, devicetree

On 11/05/2015 01:06 AM, Oleksij Rempel wrote:
> Add WD support for Alphascale asm9260 SoC. This driver
> provide support for different function modes:
> - HW mode to trigger SoC reset on timeout
> - SW mode do soft reset if needed
> - DEBUG mode
>
> Optional support for stopping watchdog. If reset binding are not provided
> this driver will work in nowayout mode.
>

In general, this is considered to be orthogonal: If the user doesn't want
nowayout, and if the watchdog can not be stopped, other drivers issue a warning
that the watchdog was not stopped, and that the system will likely restart.

> Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
> ---
>   .../bindings/watchdog/alphascale-asm9260.txt       |  39 ++
>   drivers/watchdog/Kconfig                           |   9 +
>   drivers/watchdog/Makefile                          |   1 +
>   drivers/watchdog/asm9260_wdt.c                     | 415 +++++++++++++++++++++
>   4 files changed, 464 insertions(+)
>   create mode 100644 Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
>   create mode 100644 drivers/watchdog/asm9260_wdt.c
>
> diff --git a/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
> new file mode 100644
> index 0000000..6e54d1f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
> @@ -0,0 +1,39 @@
> +Alphascale asm9260 Watchdog timer
> +
> +Required properties:
> +
> +- compatible : should be "alphascale,asm9260-wdt".
> +- reg : Specifies base physical address and size of the registers.
> +- clocks : the clocks feeding the watchdog timer. See clock-bindings.txt
> +- clock-names : should be set to
> +	"mod" - source for tick counter.
> +	"ahb" - ahb gate.
> +
> +Optional properties:
> +- resets : phandle pointing to the system reset controller with correct
> +	reset line index for watchdog controller reset. This propertie is
> +	required if you need to disable "nowayout" and it neened only with
> +	CONFIG_WATCHDOG_NOWAYOUT=n.
> +	Without reseting this WD controller, it is not possible to stop
> +	counter.
> +- reset-names : should be set to "wdt_rst" if "resets" is used.
> +- timeout-sec : shall contain the default watchdog timeout in seconds,
> +	if unset, the default timeout is 30 seconds.
> +- alphascale,mode : tree modes are supported
> +	"hw" - hw reset (defaul).
> +	"sw" - sw reset.
> +	"debug" - no action is taken.
> +
> +Example:
> +
> +watchdog0: watchdog@80048000 {
> +	compatible = "alphascale,asm9260-wdt";
> +	reg = <0x80048000 0x10>;
> +	clocks = <&acc CLKID_SYS_WDT>, <&acc CLKID_AHB_WDT>;
> +	clock-names = "mod", "ahb";
> +	interrupts = <55>;
> +	resets = <&rst WDT_RESET>;
> +	reset-names = "wdt_rst";
> +	timeout-sec = <30>;
> +	alphascale,mode = "hw";
> +};
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index c68edc1..cc5f675 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -173,6 +173,15 @@ config ARM_SP805_WATCHDOG
>   	  ARM Primecell SP805 Watchdog timer. This will reboot your system when
>   	  the timeout is reached.
>
> +config ASM9260_WATCHDOG
> +	tristate "Alphascale ASM9260 watchdog"
> +	depends on MACH_ASM9260
> +	depends on OF
> +	select WATCHDOG_CORE
> +	help
> +	  Watchdog timer embedded into Alphascale asm9260 chips. This will reboot your
> +	  system when the timeout is reached.
> +
>   config AT91RM9200_WATCHDOG
>   	tristate "AT91RM9200 watchdog"
>   	depends on SOC_AT91RM9200 && MFD_SYSCON
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index 0c616e3..bd7b0cd 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -30,6 +30,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
>
>   # ARM Architecture
>   obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o
> +obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o
>   obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
>   obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
>   obj-$(CONFIG_CADENCE_WATCHDOG) += cadence_wdt.o
> diff --git a/drivers/watchdog/asm9260_wdt.c b/drivers/watchdog/asm9260_wdt.c
> new file mode 100644
> index 0000000..9f2c321
> --- /dev/null
> +++ b/drivers/watchdog/asm9260_wdt.c
> @@ -0,0 +1,415 @@
> +/*
> + * Watchdog driver for Alphascale ASM9260.
> + *
> + * Copyright (c) 2014 Oleksij Rempel <linux@rempel-privat.de>
> + *
> + * Licensed under GPLv2 or later.
> + */
> +
> +#include <linux/bitops.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>

I don't see any module parameters. Is this include needed ?

> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/reboot.h>
> +#include <linux/reset.h>
> +#include <linux/uaccess.h>

Is this include needed ?

> +#include <linux/watchdog.h>
> +
> +#define CLOCK_FREQ	1000000
> +
> +/* Watchdog Mode register */
> +#define HW_WDMOD			0x00
> +/* Wake interrupt. Set by HW, can't be cleared. */
> +#define BM_MOD_WDINT			BIT(3)
> +/* This bit set if timeout reached. Cleared by SW. */
> +#define BM_MOD_WDTOF			BIT(2)
> +/* HW Reset on timeout */
> +#define BM_MOD_WDRESET			BIT(1)
> +/* WD enable */
> +#define BM_MOD_WDEN			BIT(0)
> +
> +/*
> + * Watchdog Timer Constant register
> + * Minimal value is 0xff, the meaning of this value
> + * depends on used clock: T = WDCLK * (0xff + 1) * 4
> + */
> +#define HW_WDTC				0x04
> +#define BM_WDTC_MAX(freq)		(0x7fffffff / (freq))
> +
> +/* Watchdog Feed register */
> +#define HW_WDFEED			0x08
> +
> +/* Watchdog Timer Value register */
> +#define HW_WDTV				0x0c
> +
> +#define ASM9260_WDT_DEFAULT_TIMEOUT	30
> +
> +enum asm9260_wdt_mode {
> +	HW_RESET,
> +	SW_RESET,
> +	DEBUG,
> +};
> +
> +struct asm9260_wdt_priv {
> +	struct device		*dev;
> +	struct watchdog_device	wdd;
> +	struct clk		*clk;
> +	struct clk		*clk_ahb;
> +	struct reset_control	*rst;
> +	struct notifier_block	restart_handler;
> +
> +	void __iomem		*iobase;
> +	int			irq;
> +	unsigned long		wdt_freq;
> +	enum asm9260_wdt_mode	mode;
> +};
> +
> +static int asm9260_wdt_feed(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +
> +	iowrite32(0xaa, priv->iobase + HW_WDFEED);
> +	iowrite32(0x55, priv->iobase + HW_WDFEED);
> +
> +	return 0;
> +}
> +
> +static unsigned int asm9260_wdt_gettimeleft(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +	u32 counter;
> +
> +	counter = ioread32(priv->iobase + HW_WDTV);
> +
> +	return DIV_ROUND_CLOSEST(counter, priv->wdt_freq);
> +}
> +
> +static int asm9260_wdt_updatetimeout(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +	u32 counter;
> +
> +	counter = wdd->timeout * priv->wdt_freq;
> +
> +	iowrite32(counter, priv->iobase + HW_WDTC);
> +
> +	return 0;
> +}
> +
> +static int asm9260_wdt_enable(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +	u32 mode = 0;
> +
> +	if (priv->mode == HW_RESET)
> +		mode = BM_MOD_WDRESET;
> +
> +	iowrite32(BM_MOD_WDEN | mode, priv->iobase + HW_WDMOD);
> +
> +	asm9260_wdt_updatetimeout(wdd);
> +
> +	asm9260_wdt_feed(wdd);
> +
> +	return 0;
> +}
> +
> +static int asm9260_wdt_disable(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +
> +	/* The only way to disable WD is to reset it. */
> +	reset_control_assert(priv->rst);
> +	reset_control_deassert(priv->rst);
> +

I am a bit wary of depending on the infrastructure to never call this code
if priv->rst is NULL, due to having nowayout set.
You should at least add a comment here indicating that the code
depends on this behavior.

My preferred solution, though, would be to stop playing with nowayout and
dump a warning to the console if the watchdog could not be stopped.

> +	return 0;
> +}
> +
> +static int asm9260_wdt_settimeout(struct watchdog_device *wdd, unsigned int to)
> +{
> +	wdd->timeout = to;
> +	asm9260_wdt_updatetimeout(wdd);
> +
> +	return 0;
> +}
> +
> +static void asm9260_wdt_sys_reset(struct asm9260_wdt_priv *priv)
> +{
> +	/* init WD if it was not started */
> +	priv->wdd.timeout = 1;
> +
Wouldn't it make more sense to write a smaller value into HW_WDTC directly ?
Unless I am missing something, this creates a one-second delay which seems
unnecessary.

> +	iowrite32(BM_MOD_WDEN | BM_MOD_WDRESET, priv->iobase + HW_WDMOD);
> +
> +	asm9260_wdt_updatetimeout(&priv->wdd);
> +	/* first pass correct sequence */
> +	asm9260_wdt_feed(&priv->wdd);
> +	/*
> +	 * Then write wrong pattern to the feed to trigger reset
> +	 * ASAP.
> +	 */
> +	iowrite32(0xff, priv->iobase + HW_WDFEED);
> +
> +	mdelay(1000);
> +}
> +
> +static irqreturn_t asm9260_wdt_irq(int irq, void *devid)
> +{
> +	struct asm9260_wdt_priv *priv = devid;
> +	u32 stat;
> +
> +	stat = ioread32(priv->iobase + HW_WDMOD);
> +	if (!(stat & BM_MOD_WDINT))
> +		return IRQ_NONE;
> +
> +	if (priv->mode == DEBUG)
> +		dev_info(priv->dev, "Watchdog Timeout. Do nothing.\n");
> +	else {
> +		dev_info(priv->dev, "Watchdog Timeout. Doing SW Reset.\n");
> +		asm9260_wdt_sys_reset(priv);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int asm9260_restart_handler(struct notifier_block *this,
> +				unsigned long mode, void *cmd)
> +{
> +	struct asm9260_wdt_priv *priv =
> +		container_of(this, struct asm9260_wdt_priv, restart_handler);
> +
> +	asm9260_wdt_sys_reset(priv);
> +
> +	return NOTIFY_DONE;
> +}
> +
> +static const struct watchdog_info asm9260_wdt_ident = {
> +	.options          =     WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING
> +				| WDIOF_MAGICCLOSE,
> +	.identity         =	"Alphascale asm9260 Watchdog",
> +};
> +
> +static struct watchdog_ops asm9260_wdt_ops = {
> +	.owner		= THIS_MODULE,
> +	.start		= asm9260_wdt_enable,
> +	.stop		= asm9260_wdt_disable,
> +	.get_timeleft	= asm9260_wdt_gettimeleft,
> +	.ping		= asm9260_wdt_feed,
> +	.set_timeout	= asm9260_wdt_settimeout,
> +};
> +
> +static int __init asm9260_wdt_get_dt_clks(struct asm9260_wdt_priv *priv)
> +{
> +	int clk_idx = 0, err;
> +	unsigned long clk;
> +
> +	priv->clk = devm_clk_get(priv->dev, "mod");
> +	if (IS_ERR(priv->clk))
> +		goto clk_err;
> +
> +	/* configure AHB clock */
> +	clk_idx = 1;
> +	priv->clk_ahb = devm_clk_get(priv->dev, "ahb");
> +	if (IS_ERR(priv->clk_ahb))
> +		goto clk_err;
> +
Seems you are using clk_err only to display an error message.
This is not the intended use of error jump labels.

> +	err = clk_prepare_enable(priv->clk_ahb);
> +	if (err) {
> +		dev_err(priv->dev, "Failed to enable ahb_clk!\n");
> +		goto out_err;
> +	}
> +
> +	err = clk_set_rate(priv->clk, CLOCK_FREQ);
> +	if (err) {
> +		clk_disable_unprepare(priv->clk_ahb);
> +		dev_err(priv->dev, "Failed to set rate!\n");
> +		goto out_err;
> +	}
> +
> +	err = clk_prepare_enable(priv->clk);
> +	if (err) {
> +		clk_disable_unprepare(priv->clk_ahb);
> +		dev_err(priv->dev, "Failed to enable clk!\n");
> +		goto out_err;
> +	}
> +
> +	/* wdt has internal divider */
> +	clk = clk_get_rate(priv->clk);
> +	if (!clk) {
> +		clk_disable_unprepare(priv->clk);
> +		clk_disable_unprepare(priv->clk_ahb);

The basic idea with error exit jumps is to keep the error handling
there. This code defeats the purpose.

> +		dev_err(priv->dev, "Failed, clk is 0!\n");
> +		goto out_err;
> +	}
> +
> +	priv->wdt_freq = clk / 2;
> +
> +	return 0;
> +
> +clk_err:
> +	dev_err(priv->dev, "Failed to get clk (%s)\n",
> +			clk_idx ? "ahb" : "mod");

This is an odd use of an error return function. I would suggest to
create the error messages where they occur, and only handle cleanup here,
as suggested in the coding style document.

> +out_err:

This label is not needed. Any code jumping to it can return immediately.

> +	return -EFAULT;

EFAULT: /* Bad address */

Commonly used to identify a bad address passed from user space.
I don't think it is appropriate here. Besides, why override the already
known error in the first place ?


> +}
> +
> +static int __init asm9260_wdt_get_dt_mode(struct asm9260_wdt_priv *priv)
> +{
> +	const char *tmp;
> +	int ret;
> +
> +	/* default mode */
> +	priv->mode = HW_RESET;
> +
> +	ret = of_property_read_string(priv->dev->of_node,
> +			"alphascale,mode", &tmp);

Please align continuation lines with '('.

> +	if (ret < 0)
> +		return ret;
> +
> +	if (!strcmp(tmp, "hw"))
> +		priv->mode = HW_RESET;
> +	else if (!strcmp(tmp, "sw"))
> +		priv->mode = SW_RESET;
> +	else if (!strcmp(tmp, "debug"))
> +		priv->mode = DEBUG;
> +	else {
> +		dev_warn(priv->dev, "unknown reset-type: %s. Using defaul \"hw\" mode.",
> +			tmp);

s/defaul/default/

> +		return -EINVAL;

Why return an error (instead of void) if it isn't used ?

> +	}
> +
> +	return 0;
> +}
> +
> +static int __init asm9260_wdt_probe(struct platform_device *pdev)
> +{
> +	struct asm9260_wdt_priv *priv;
> +	struct watchdog_device *wdd;
> +	struct resource *res;
> +	bool nowayout = WATCHDOG_NOWAYOUT;
> +	int ret;
> +
> +	priv = devm_kzalloc(&pdev->dev, sizeof(struct asm9260_wdt_priv),
> +			GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->dev = &pdev->dev;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	priv->iobase = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(priv->iobase))
> +		return PTR_ERR(priv->iobase);
> +
> +	ret = asm9260_wdt_get_dt_clks(priv);
> +	if (ret)
> +		return ret;
> +
> +	priv->rst = devm_reset_control_get(&pdev->dev, "wdt_rst");
> +	if (IS_ERR(priv->rst)) {
> +		nowayout = 1;
> +		priv->rst = NULL;
> +	}
> +
> +	wdd = &priv->wdd;
> +	wdd->info = &asm9260_wdt_ident;
> +	wdd->ops = &asm9260_wdt_ops;
> +	wdd->min_timeout = 1;
> +	wdd->max_timeout = BM_WDTC_MAX(priv->wdt_freq);
> +	wdd->parent = &pdev->dev;
> +
> +	watchdog_set_drvdata(wdd, priv);
> +
> +	/*
> +	 * If 'timeout-sec' unspecified in devicetree, assume a 30 second
> +	 * default, unless the max timeout is less than 30 seconds, then use
> +	 * the max instead.
> +	 */
> +	wdd->timeout = ASM9260_WDT_DEFAULT_TIMEOUT;
> +	watchdog_init_timeout(wdd, 0, &pdev->dev);
> +
> +	watchdog_set_nowayout(wdd, nowayout);
> +
> +	asm9260_wdt_get_dt_mode(priv);
> +
> +	if (priv->mode != HW_RESET)
> +		priv->irq = platform_get_irq(pdev, 0);
> +
> +	if (priv->irq > 0) {
> +		/*
> +		 * Not all supported platforms specify an interrupt for the
> +		 * watchdog, so let's make it optional.
> +		 */
> +		ret = devm_request_irq(&pdev->dev, priv->irq,
> +				asm9260_wdt_irq, IRQF_SHARED,
> +				pdev->name, priv);
> +		if (ret < 0)
> +			dev_err(&pdev->dev, "failed to request IRQ\n");

dev_warn since you don't bail out.

> +	}
> +
> +	ret = watchdog_register_device(wdd);
> +	if (ret)
> +		goto clk_off;
> +
> +	platform_set_drvdata(pdev, priv);
> +
> +	priv->restart_handler.notifier_call = asm9260_restart_handler;
> +	priv->restart_handler.priority = 128;
> +	ret = register_restart_handler(&priv->restart_handler);
> +	if (ret)
> +		dev_err(&pdev->dev, "cannot register restart handler\n");

dev_warn since you don't bail out.

> +
> +	dev_info(&pdev->dev, "Watchdog enabled (to=%d, nw=%d, mode=%d)\n",
> +		wdd->timeout, nowayout, priv->mode);
> +	return 0;
> +
> +clk_off:
> +	clk_disable_unprepare(priv->clk);
> +	clk_disable_unprepare(priv->clk_ahb);
> +	return ret;
> +}
> +
> +static void asm9260_wdt_shutdown(struct platform_device *pdev)
> +{
> +	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
> +
> +	asm9260_wdt_disable(&priv->wdd);
> +}
> +
> +static int __exit asm9260_wdt_remove(struct platform_device *pdev)
> +{
> +	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
> +
> +	asm9260_wdt_shutdown(pdev);

You could just call asm9260_wdt_disable() directly.

> +
> +	unregister_restart_handler(&priv->restart_handler);
> +	clk_disable_unprepare(priv->clk);
> +	clk_disable_unprepare(priv->clk_ahb);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id asm9260_wdt_of_match[] = {
> +	{ .compatible = "alphascale,asm9260-wdt"},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, asm9260_wdt_of_match);
> +
> +static struct platform_driver asm9260_wdt_driver = {
> +	.driver = {
> +		.name = "asm9260-wdt",
> +		.owner = THIS_MODULE,
> +		.of_match_table	= asm9260_wdt_of_match,
> +	},
> +	.probe = asm9260_wdt_probe,
> +	.remove = asm9260_wdt_remove,
> +	.shutdown = asm9260_wdt_shutdown,
> +};
> +module_platform_driver(asm9260_wdt_driver);
> +
> +MODULE_DESCRIPTION("asm9260 WatchDog Timer Driver");
> +MODULE_AUTHOR("Oleksij Rempel <linux@rempel-privat.de>");
> +MODULE_LICENSE("GPL");
>


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

* Re: [PATCH v2] watchdog: add Alphascale asm9260-wdt driver
  2015-11-05  9:06           ` Oleksij Rempel
@ 2015-11-05 20:43               ` Rob Herring
  -1 siblings, 0 replies; 43+ messages in thread
From: Rob Herring @ 2015-11-05 20:43 UTC (permalink / raw)
  To: Oleksij Rempel
  Cc: linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	linux-0h96xk9xTtrk1uMJSBkQmQ, wim-IQzOog9fTRqzQB+pC5nmwQ,
	devicetree-u79uwXL29TY76Z2rM5mHXA

On Thu, Nov 05, 2015 at 10:06:56AM +0100, Oleksij Rempel wrote:
> Add WD support for Alphascale asm9260 SoC. This driver
> provide support for different function modes:
> - HW mode to trigger SoC reset on timeout
> - SW mode do soft reset if needed
> - DEBUG mode
> 
> Optional support for stopping watchdog. If reset binding are not provided
> this driver will work in nowayout mode.
> 
> Signed-off-by: Oleksij Rempel <linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>
> ---
>  .../bindings/watchdog/alphascale-asm9260.txt       |  39 ++

It is preferred that bindings are a separate patch.

>  drivers/watchdog/Kconfig                           |   9 +
>  drivers/watchdog/Makefile                          |   1 +
>  drivers/watchdog/asm9260_wdt.c                     | 415 +++++++++++++++++++++
>  4 files changed, 464 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
>  create mode 100644 drivers/watchdog/asm9260_wdt.c
> 
> diff --git a/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
> new file mode 100644
> index 0000000..6e54d1f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
> @@ -0,0 +1,39 @@
> +Alphascale asm9260 Watchdog timer
> +
> +Required properties:
> +
> +- compatible : should be "alphascale,asm9260-wdt".
> +- reg : Specifies base physical address and size of the registers.
> +- clocks : the clocks feeding the watchdog timer. See clock-bindings.txt
> +- clock-names : should be set to
> +	"mod" - source for tick counter.
> +	"ahb" - ahb gate.
> +
> +Optional properties:
> +- resets : phandle pointing to the system reset controller with correct
> +	reset line index for watchdog controller reset. This propertie is

s/propertie/property/

> +	required if you need to disable "nowayout" and it neened only with
> +	CONFIG_WATCHDOG_NOWAYOUT=n.

The DT cannot depend on certain kernel configs.

> +	Without reseting this WD controller, it is not possible to stop

s/reseting/resetting/

> +	counter.
> +- reset-names : should be set to "wdt_rst" if "resets" is used.
> +- timeout-sec : shall contain the default watchdog timeout in seconds,
> +	if unset, the default timeout is 30 seconds.
> +- alphascale,mode : tree modes are supported

s/tree/three/

> +	"hw" - hw reset (defaul).

s/defaul/default/

> +	"sw" - sw reset.
> +	"debug" - no action is taken.

Seems like this should be a generic wdog property.

> +
> +Example:
> +
> +watchdog0: watchdog@80048000 {
> +	compatible = "alphascale,asm9260-wdt";
> +	reg = <0x80048000 0x10>;
> +	clocks = <&acc CLKID_SYS_WDT>, <&acc CLKID_AHB_WDT>;
> +	clock-names = "mod", "ahb";
> +	interrupts = <55>;
> +	resets = <&rst WDT_RESET>;
> +	reset-names = "wdt_rst";
> +	timeout-sec = <30>;
> +	alphascale,mode = "hw";
> +};
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index c68edc1..cc5f675 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -173,6 +173,15 @@ config ARM_SP805_WATCHDOG
>  	  ARM Primecell SP805 Watchdog timer. This will reboot your system when
>  	  the timeout is reached.
>  
> +config ASM9260_WATCHDOG
> +	tristate "Alphascale ASM9260 watchdog"
> +	depends on MACH_ASM9260
> +	depends on OF
> +	select WATCHDOG_CORE
> +	help
> +	  Watchdog timer embedded into Alphascale asm9260 chips. This will reboot your
> +	  system when the timeout is reached.
> +
>  config AT91RM9200_WATCHDOG
>  	tristate "AT91RM9200 watchdog"
>  	depends on SOC_AT91RM9200 && MFD_SYSCON
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index 0c616e3..bd7b0cd 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -30,6 +30,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
>  
>  # ARM Architecture
>  obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o
> +obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o
>  obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
>  obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
>  obj-$(CONFIG_CADENCE_WATCHDOG) += cadence_wdt.o
> diff --git a/drivers/watchdog/asm9260_wdt.c b/drivers/watchdog/asm9260_wdt.c
> new file mode 100644
> index 0000000..9f2c321
> --- /dev/null
> +++ b/drivers/watchdog/asm9260_wdt.c
> @@ -0,0 +1,415 @@
> +/*
> + * Watchdog driver for Alphascale ASM9260.
> + *
> + * Copyright (c) 2014 Oleksij Rempel <linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>
> + *
> + * Licensed under GPLv2 or later.
> + */
> +
> +#include <linux/bitops.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/reboot.h>
> +#include <linux/reset.h>
> +#include <linux/uaccess.h>
> +#include <linux/watchdog.h>
> +
> +#define CLOCK_FREQ	1000000
> +
> +/* Watchdog Mode register */
> +#define HW_WDMOD			0x00
> +/* Wake interrupt. Set by HW, can't be cleared. */
> +#define BM_MOD_WDINT			BIT(3)
> +/* This bit set if timeout reached. Cleared by SW. */
> +#define BM_MOD_WDTOF			BIT(2)
> +/* HW Reset on timeout */
> +#define BM_MOD_WDRESET			BIT(1)
> +/* WD enable */
> +#define BM_MOD_WDEN			BIT(0)
> +
> +/*
> + * Watchdog Timer Constant register
> + * Minimal value is 0xff, the meaning of this value
> + * depends on used clock: T = WDCLK * (0xff + 1) * 4
> + */
> +#define HW_WDTC				0x04
> +#define BM_WDTC_MAX(freq)		(0x7fffffff / (freq))
> +
> +/* Watchdog Feed register */
> +#define HW_WDFEED			0x08
> +
> +/* Watchdog Timer Value register */
> +#define HW_WDTV				0x0c
> +
> +#define ASM9260_WDT_DEFAULT_TIMEOUT	30
> +
> +enum asm9260_wdt_mode {
> +	HW_RESET,
> +	SW_RESET,
> +	DEBUG,
> +};
> +
> +struct asm9260_wdt_priv {
> +	struct device		*dev;
> +	struct watchdog_device	wdd;
> +	struct clk		*clk;
> +	struct clk		*clk_ahb;
> +	struct reset_control	*rst;
> +	struct notifier_block	restart_handler;
> +
> +	void __iomem		*iobase;
> +	int			irq;
> +	unsigned long		wdt_freq;
> +	enum asm9260_wdt_mode	mode;
> +};
> +
> +static int asm9260_wdt_feed(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +
> +	iowrite32(0xaa, priv->iobase + HW_WDFEED);
> +	iowrite32(0x55, priv->iobase + HW_WDFEED);
> +
> +	return 0;
> +}
> +
> +static unsigned int asm9260_wdt_gettimeleft(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +	u32 counter;
> +
> +	counter = ioread32(priv->iobase + HW_WDTV);
> +
> +	return DIV_ROUND_CLOSEST(counter, priv->wdt_freq);
> +}
> +
> +static int asm9260_wdt_updatetimeout(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +	u32 counter;
> +
> +	counter = wdd->timeout * priv->wdt_freq;
> +
> +	iowrite32(counter, priv->iobase + HW_WDTC);
> +
> +	return 0;
> +}
> +
> +static int asm9260_wdt_enable(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +	u32 mode = 0;
> +
> +	if (priv->mode == HW_RESET)
> +		mode = BM_MOD_WDRESET;
> +
> +	iowrite32(BM_MOD_WDEN | mode, priv->iobase + HW_WDMOD);
> +
> +	asm9260_wdt_updatetimeout(wdd);
> +
> +	asm9260_wdt_feed(wdd);
> +
> +	return 0;
> +}
> +
> +static int asm9260_wdt_disable(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +
> +	/* The only way to disable WD is to reset it. */
> +	reset_control_assert(priv->rst);
> +	reset_control_deassert(priv->rst);
> +
> +	return 0;
> +}
> +
> +static int asm9260_wdt_settimeout(struct watchdog_device *wdd, unsigned int to)
> +{
> +	wdd->timeout = to;
> +	asm9260_wdt_updatetimeout(wdd);
> +
> +	return 0;
> +}
> +
> +static void asm9260_wdt_sys_reset(struct asm9260_wdt_priv *priv)
> +{
> +	/* init WD if it was not started */
> +	priv->wdd.timeout = 1;
> +
> +	iowrite32(BM_MOD_WDEN | BM_MOD_WDRESET, priv->iobase + HW_WDMOD);
> +
> +	asm9260_wdt_updatetimeout(&priv->wdd);
> +	/* first pass correct sequence */
> +	asm9260_wdt_feed(&priv->wdd);
> +	/*
> +	 * Then write wrong pattern to the feed to trigger reset
> +	 * ASAP.
> +	 */
> +	iowrite32(0xff, priv->iobase + HW_WDFEED);
> +
> +	mdelay(1000);
> +}
> +
> +static irqreturn_t asm9260_wdt_irq(int irq, void *devid)
> +{
> +	struct asm9260_wdt_priv *priv = devid;
> +	u32 stat;
> +
> +	stat = ioread32(priv->iobase + HW_WDMOD);
> +	if (!(stat & BM_MOD_WDINT))
> +		return IRQ_NONE;
> +
> +	if (priv->mode == DEBUG)
> +		dev_info(priv->dev, "Watchdog Timeout. Do nothing.\n");
> +	else {
> +		dev_info(priv->dev, "Watchdog Timeout. Doing SW Reset.\n");
> +		asm9260_wdt_sys_reset(priv);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int asm9260_restart_handler(struct notifier_block *this,
> +				unsigned long mode, void *cmd)
> +{
> +	struct asm9260_wdt_priv *priv =
> +		container_of(this, struct asm9260_wdt_priv, restart_handler);
> +
> +	asm9260_wdt_sys_reset(priv);
> +
> +	return NOTIFY_DONE;
> +}
> +
> +static const struct watchdog_info asm9260_wdt_ident = {
> +	.options          =     WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING
> +				| WDIOF_MAGICCLOSE,
> +	.identity         =	"Alphascale asm9260 Watchdog",
> +};
> +
> +static struct watchdog_ops asm9260_wdt_ops = {
> +	.owner		= THIS_MODULE,
> +	.start		= asm9260_wdt_enable,
> +	.stop		= asm9260_wdt_disable,
> +	.get_timeleft	= asm9260_wdt_gettimeleft,
> +	.ping		= asm9260_wdt_feed,
> +	.set_timeout	= asm9260_wdt_settimeout,
> +};
> +
> +static int __init asm9260_wdt_get_dt_clks(struct asm9260_wdt_priv *priv)
> +{
> +	int clk_idx = 0, err;
> +	unsigned long clk;
> +
> +	priv->clk = devm_clk_get(priv->dev, "mod");
> +	if (IS_ERR(priv->clk))
> +		goto clk_err;
> +
> +	/* configure AHB clock */
> +	clk_idx = 1;
> +	priv->clk_ahb = devm_clk_get(priv->dev, "ahb");
> +	if (IS_ERR(priv->clk_ahb))
> +		goto clk_err;
> +
> +	err = clk_prepare_enable(priv->clk_ahb);
> +	if (err) {
> +		dev_err(priv->dev, "Failed to enable ahb_clk!\n");
> +		goto out_err;
> +	}
> +
> +	err = clk_set_rate(priv->clk, CLOCK_FREQ);
> +	if (err) {
> +		clk_disable_unprepare(priv->clk_ahb);
> +		dev_err(priv->dev, "Failed to set rate!\n");
> +		goto out_err;
> +	}
> +
> +	err = clk_prepare_enable(priv->clk);
> +	if (err) {
> +		clk_disable_unprepare(priv->clk_ahb);
> +		dev_err(priv->dev, "Failed to enable clk!\n");
> +		goto out_err;
> +	}
> +
> +	/* wdt has internal divider */
> +	clk = clk_get_rate(priv->clk);
> +	if (!clk) {
> +		clk_disable_unprepare(priv->clk);
> +		clk_disable_unprepare(priv->clk_ahb);
> +		dev_err(priv->dev, "Failed, clk is 0!\n");
> +		goto out_err;
> +	}
> +
> +	priv->wdt_freq = clk / 2;
> +
> +	return 0;
> +
> +clk_err:
> +	dev_err(priv->dev, "Failed to get clk (%s)\n",
> +			clk_idx ? "ahb" : "mod");
> +out_err:
> +	return -EFAULT;
> +}
> +
> +static int __init asm9260_wdt_get_dt_mode(struct asm9260_wdt_priv *priv)
> +{
> +	const char *tmp;
> +	int ret;
> +
> +	/* default mode */
> +	priv->mode = HW_RESET;
> +
> +	ret = of_property_read_string(priv->dev->of_node,
> +			"alphascale,mode", &tmp);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (!strcmp(tmp, "hw"))
> +		priv->mode = HW_RESET;
> +	else if (!strcmp(tmp, "sw"))
> +		priv->mode = SW_RESET;
> +	else if (!strcmp(tmp, "debug"))
> +		priv->mode = DEBUG;
> +	else {
> +		dev_warn(priv->dev, "unknown reset-type: %s. Using defaul \"hw\" mode.",
> +			tmp);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int __init asm9260_wdt_probe(struct platform_device *pdev)
> +{
> +	struct asm9260_wdt_priv *priv;
> +	struct watchdog_device *wdd;
> +	struct resource *res;
> +	bool nowayout = WATCHDOG_NOWAYOUT;
> +	int ret;
> +
> +	priv = devm_kzalloc(&pdev->dev, sizeof(struct asm9260_wdt_priv),
> +			GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->dev = &pdev->dev;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	priv->iobase = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(priv->iobase))
> +		return PTR_ERR(priv->iobase);
> +
> +	ret = asm9260_wdt_get_dt_clks(priv);
> +	if (ret)
> +		return ret;
> +
> +	priv->rst = devm_reset_control_get(&pdev->dev, "wdt_rst");
> +	if (IS_ERR(priv->rst)) {
> +		nowayout = 1;
> +		priv->rst = NULL;
> +	}
> +
> +	wdd = &priv->wdd;
> +	wdd->info = &asm9260_wdt_ident;
> +	wdd->ops = &asm9260_wdt_ops;
> +	wdd->min_timeout = 1;
> +	wdd->max_timeout = BM_WDTC_MAX(priv->wdt_freq);
> +	wdd->parent = &pdev->dev;
> +
> +	watchdog_set_drvdata(wdd, priv);
> +
> +	/*
> +	 * If 'timeout-sec' unspecified in devicetree, assume a 30 second
> +	 * default, unless the max timeout is less than 30 seconds, then use
> +	 * the max instead.
> +	 */
> +	wdd->timeout = ASM9260_WDT_DEFAULT_TIMEOUT;
> +	watchdog_init_timeout(wdd, 0, &pdev->dev);
> +
> +	watchdog_set_nowayout(wdd, nowayout);
> +
> +	asm9260_wdt_get_dt_mode(priv);
> +
> +	if (priv->mode != HW_RESET)
> +		priv->irq = platform_get_irq(pdev, 0);
> +
> +	if (priv->irq > 0) {
> +		/*
> +		 * Not all supported platforms specify an interrupt for the
> +		 * watchdog, so let's make it optional.
> +		 */
> +		ret = devm_request_irq(&pdev->dev, priv->irq,
> +				asm9260_wdt_irq, IRQF_SHARED,
> +				pdev->name, priv);
> +		if (ret < 0)
> +			dev_err(&pdev->dev, "failed to request IRQ\n");
> +	}
> +
> +	ret = watchdog_register_device(wdd);
> +	if (ret)
> +		goto clk_off;
> +
> +	platform_set_drvdata(pdev, priv);
> +
> +	priv->restart_handler.notifier_call = asm9260_restart_handler;
> +	priv->restart_handler.priority = 128;
> +	ret = register_restart_handler(&priv->restart_handler);
> +	if (ret)
> +		dev_err(&pdev->dev, "cannot register restart handler\n");
> +
> +	dev_info(&pdev->dev, "Watchdog enabled (to=%d, nw=%d, mode=%d)\n",
> +		wdd->timeout, nowayout, priv->mode);
> +	return 0;
> +
> +clk_off:
> +	clk_disable_unprepare(priv->clk);
> +	clk_disable_unprepare(priv->clk_ahb);
> +	return ret;
> +}
> +
> +static void asm9260_wdt_shutdown(struct platform_device *pdev)
> +{
> +	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
> +
> +	asm9260_wdt_disable(&priv->wdd);
> +}
> +
> +static int __exit asm9260_wdt_remove(struct platform_device *pdev)
> +{
> +	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
> +
> +	asm9260_wdt_shutdown(pdev);
> +
> +	unregister_restart_handler(&priv->restart_handler);
> +	clk_disable_unprepare(priv->clk);
> +	clk_disable_unprepare(priv->clk_ahb);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id asm9260_wdt_of_match[] = {
> +	{ .compatible = "alphascale,asm9260-wdt"},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, asm9260_wdt_of_match);
> +
> +static struct platform_driver asm9260_wdt_driver = {
> +	.driver = {
> +		.name = "asm9260-wdt",
> +		.owner = THIS_MODULE,
> +		.of_match_table	= asm9260_wdt_of_match,
> +	},
> +	.probe = asm9260_wdt_probe,
> +	.remove = asm9260_wdt_remove,
> +	.shutdown = asm9260_wdt_shutdown,
> +};
> +module_platform_driver(asm9260_wdt_driver);
> +
> +MODULE_DESCRIPTION("asm9260 WatchDog Timer Driver");
> +MODULE_AUTHOR("Oleksij Rempel <linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>");
> +MODULE_LICENSE("GPL");
> -- 
> 2.5.0
> 
> --
> To unsubscribe from this list: send the line "unsubscribe devicetree" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v2] watchdog: add Alphascale asm9260-wdt driver
@ 2015-11-05 20:43               ` Rob Herring
  0 siblings, 0 replies; 43+ messages in thread
From: Rob Herring @ 2015-11-05 20:43 UTC (permalink / raw)
  To: Oleksij Rempel; +Cc: linux-watchdog, linux, wim, devicetree

On Thu, Nov 05, 2015 at 10:06:56AM +0100, Oleksij Rempel wrote:
> Add WD support for Alphascale asm9260 SoC. This driver
> provide support for different function modes:
> - HW mode to trigger SoC reset on timeout
> - SW mode do soft reset if needed
> - DEBUG mode
> 
> Optional support for stopping watchdog. If reset binding are not provided
> this driver will work in nowayout mode.
> 
> Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
> ---
>  .../bindings/watchdog/alphascale-asm9260.txt       |  39 ++

It is preferred that bindings are a separate patch.

>  drivers/watchdog/Kconfig                           |   9 +
>  drivers/watchdog/Makefile                          |   1 +
>  drivers/watchdog/asm9260_wdt.c                     | 415 +++++++++++++++++++++
>  4 files changed, 464 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
>  create mode 100644 drivers/watchdog/asm9260_wdt.c
> 
> diff --git a/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
> new file mode 100644
> index 0000000..6e54d1f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
> @@ -0,0 +1,39 @@
> +Alphascale asm9260 Watchdog timer
> +
> +Required properties:
> +
> +- compatible : should be "alphascale,asm9260-wdt".
> +- reg : Specifies base physical address and size of the registers.
> +- clocks : the clocks feeding the watchdog timer. See clock-bindings.txt
> +- clock-names : should be set to
> +	"mod" - source for tick counter.
> +	"ahb" - ahb gate.
> +
> +Optional properties:
> +- resets : phandle pointing to the system reset controller with correct
> +	reset line index for watchdog controller reset. This propertie is

s/propertie/property/

> +	required if you need to disable "nowayout" and it neened only with
> +	CONFIG_WATCHDOG_NOWAYOUT=n.

The DT cannot depend on certain kernel configs.

> +	Without reseting this WD controller, it is not possible to stop

s/reseting/resetting/

> +	counter.
> +- reset-names : should be set to "wdt_rst" if "resets" is used.
> +- timeout-sec : shall contain the default watchdog timeout in seconds,
> +	if unset, the default timeout is 30 seconds.
> +- alphascale,mode : tree modes are supported

s/tree/three/

> +	"hw" - hw reset (defaul).

s/defaul/default/

> +	"sw" - sw reset.
> +	"debug" - no action is taken.

Seems like this should be a generic wdog property.

> +
> +Example:
> +
> +watchdog0: watchdog@80048000 {
> +	compatible = "alphascale,asm9260-wdt";
> +	reg = <0x80048000 0x10>;
> +	clocks = <&acc CLKID_SYS_WDT>, <&acc CLKID_AHB_WDT>;
> +	clock-names = "mod", "ahb";
> +	interrupts = <55>;
> +	resets = <&rst WDT_RESET>;
> +	reset-names = "wdt_rst";
> +	timeout-sec = <30>;
> +	alphascale,mode = "hw";
> +};
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index c68edc1..cc5f675 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -173,6 +173,15 @@ config ARM_SP805_WATCHDOG
>  	  ARM Primecell SP805 Watchdog timer. This will reboot your system when
>  	  the timeout is reached.
>  
> +config ASM9260_WATCHDOG
> +	tristate "Alphascale ASM9260 watchdog"
> +	depends on MACH_ASM9260
> +	depends on OF
> +	select WATCHDOG_CORE
> +	help
> +	  Watchdog timer embedded into Alphascale asm9260 chips. This will reboot your
> +	  system when the timeout is reached.
> +
>  config AT91RM9200_WATCHDOG
>  	tristate "AT91RM9200 watchdog"
>  	depends on SOC_AT91RM9200 && MFD_SYSCON
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index 0c616e3..bd7b0cd 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -30,6 +30,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
>  
>  # ARM Architecture
>  obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o
> +obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o
>  obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
>  obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
>  obj-$(CONFIG_CADENCE_WATCHDOG) += cadence_wdt.o
> diff --git a/drivers/watchdog/asm9260_wdt.c b/drivers/watchdog/asm9260_wdt.c
> new file mode 100644
> index 0000000..9f2c321
> --- /dev/null
> +++ b/drivers/watchdog/asm9260_wdt.c
> @@ -0,0 +1,415 @@
> +/*
> + * Watchdog driver for Alphascale ASM9260.
> + *
> + * Copyright (c) 2014 Oleksij Rempel <linux@rempel-privat.de>
> + *
> + * Licensed under GPLv2 or later.
> + */
> +
> +#include <linux/bitops.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/reboot.h>
> +#include <linux/reset.h>
> +#include <linux/uaccess.h>
> +#include <linux/watchdog.h>
> +
> +#define CLOCK_FREQ	1000000
> +
> +/* Watchdog Mode register */
> +#define HW_WDMOD			0x00
> +/* Wake interrupt. Set by HW, can't be cleared. */
> +#define BM_MOD_WDINT			BIT(3)
> +/* This bit set if timeout reached. Cleared by SW. */
> +#define BM_MOD_WDTOF			BIT(2)
> +/* HW Reset on timeout */
> +#define BM_MOD_WDRESET			BIT(1)
> +/* WD enable */
> +#define BM_MOD_WDEN			BIT(0)
> +
> +/*
> + * Watchdog Timer Constant register
> + * Minimal value is 0xff, the meaning of this value
> + * depends on used clock: T = WDCLK * (0xff + 1) * 4
> + */
> +#define HW_WDTC				0x04
> +#define BM_WDTC_MAX(freq)		(0x7fffffff / (freq))
> +
> +/* Watchdog Feed register */
> +#define HW_WDFEED			0x08
> +
> +/* Watchdog Timer Value register */
> +#define HW_WDTV				0x0c
> +
> +#define ASM9260_WDT_DEFAULT_TIMEOUT	30
> +
> +enum asm9260_wdt_mode {
> +	HW_RESET,
> +	SW_RESET,
> +	DEBUG,
> +};
> +
> +struct asm9260_wdt_priv {
> +	struct device		*dev;
> +	struct watchdog_device	wdd;
> +	struct clk		*clk;
> +	struct clk		*clk_ahb;
> +	struct reset_control	*rst;
> +	struct notifier_block	restart_handler;
> +
> +	void __iomem		*iobase;
> +	int			irq;
> +	unsigned long		wdt_freq;
> +	enum asm9260_wdt_mode	mode;
> +};
> +
> +static int asm9260_wdt_feed(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +
> +	iowrite32(0xaa, priv->iobase + HW_WDFEED);
> +	iowrite32(0x55, priv->iobase + HW_WDFEED);
> +
> +	return 0;
> +}
> +
> +static unsigned int asm9260_wdt_gettimeleft(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +	u32 counter;
> +
> +	counter = ioread32(priv->iobase + HW_WDTV);
> +
> +	return DIV_ROUND_CLOSEST(counter, priv->wdt_freq);
> +}
> +
> +static int asm9260_wdt_updatetimeout(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +	u32 counter;
> +
> +	counter = wdd->timeout * priv->wdt_freq;
> +
> +	iowrite32(counter, priv->iobase + HW_WDTC);
> +
> +	return 0;
> +}
> +
> +static int asm9260_wdt_enable(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +	u32 mode = 0;
> +
> +	if (priv->mode == HW_RESET)
> +		mode = BM_MOD_WDRESET;
> +
> +	iowrite32(BM_MOD_WDEN | mode, priv->iobase + HW_WDMOD);
> +
> +	asm9260_wdt_updatetimeout(wdd);
> +
> +	asm9260_wdt_feed(wdd);
> +
> +	return 0;
> +}
> +
> +static int asm9260_wdt_disable(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +
> +	/* The only way to disable WD is to reset it. */
> +	reset_control_assert(priv->rst);
> +	reset_control_deassert(priv->rst);
> +
> +	return 0;
> +}
> +
> +static int asm9260_wdt_settimeout(struct watchdog_device *wdd, unsigned int to)
> +{
> +	wdd->timeout = to;
> +	asm9260_wdt_updatetimeout(wdd);
> +
> +	return 0;
> +}
> +
> +static void asm9260_wdt_sys_reset(struct asm9260_wdt_priv *priv)
> +{
> +	/* init WD if it was not started */
> +	priv->wdd.timeout = 1;
> +
> +	iowrite32(BM_MOD_WDEN | BM_MOD_WDRESET, priv->iobase + HW_WDMOD);
> +
> +	asm9260_wdt_updatetimeout(&priv->wdd);
> +	/* first pass correct sequence */
> +	asm9260_wdt_feed(&priv->wdd);
> +	/*
> +	 * Then write wrong pattern to the feed to trigger reset
> +	 * ASAP.
> +	 */
> +	iowrite32(0xff, priv->iobase + HW_WDFEED);
> +
> +	mdelay(1000);
> +}
> +
> +static irqreturn_t asm9260_wdt_irq(int irq, void *devid)
> +{
> +	struct asm9260_wdt_priv *priv = devid;
> +	u32 stat;
> +
> +	stat = ioread32(priv->iobase + HW_WDMOD);
> +	if (!(stat & BM_MOD_WDINT))
> +		return IRQ_NONE;
> +
> +	if (priv->mode == DEBUG)
> +		dev_info(priv->dev, "Watchdog Timeout. Do nothing.\n");
> +	else {
> +		dev_info(priv->dev, "Watchdog Timeout. Doing SW Reset.\n");
> +		asm9260_wdt_sys_reset(priv);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int asm9260_restart_handler(struct notifier_block *this,
> +				unsigned long mode, void *cmd)
> +{
> +	struct asm9260_wdt_priv *priv =
> +		container_of(this, struct asm9260_wdt_priv, restart_handler);
> +
> +	asm9260_wdt_sys_reset(priv);
> +
> +	return NOTIFY_DONE;
> +}
> +
> +static const struct watchdog_info asm9260_wdt_ident = {
> +	.options          =     WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING
> +				| WDIOF_MAGICCLOSE,
> +	.identity         =	"Alphascale asm9260 Watchdog",
> +};
> +
> +static struct watchdog_ops asm9260_wdt_ops = {
> +	.owner		= THIS_MODULE,
> +	.start		= asm9260_wdt_enable,
> +	.stop		= asm9260_wdt_disable,
> +	.get_timeleft	= asm9260_wdt_gettimeleft,
> +	.ping		= asm9260_wdt_feed,
> +	.set_timeout	= asm9260_wdt_settimeout,
> +};
> +
> +static int __init asm9260_wdt_get_dt_clks(struct asm9260_wdt_priv *priv)
> +{
> +	int clk_idx = 0, err;
> +	unsigned long clk;
> +
> +	priv->clk = devm_clk_get(priv->dev, "mod");
> +	if (IS_ERR(priv->clk))
> +		goto clk_err;
> +
> +	/* configure AHB clock */
> +	clk_idx = 1;
> +	priv->clk_ahb = devm_clk_get(priv->dev, "ahb");
> +	if (IS_ERR(priv->clk_ahb))
> +		goto clk_err;
> +
> +	err = clk_prepare_enable(priv->clk_ahb);
> +	if (err) {
> +		dev_err(priv->dev, "Failed to enable ahb_clk!\n");
> +		goto out_err;
> +	}
> +
> +	err = clk_set_rate(priv->clk, CLOCK_FREQ);
> +	if (err) {
> +		clk_disable_unprepare(priv->clk_ahb);
> +		dev_err(priv->dev, "Failed to set rate!\n");
> +		goto out_err;
> +	}
> +
> +	err = clk_prepare_enable(priv->clk);
> +	if (err) {
> +		clk_disable_unprepare(priv->clk_ahb);
> +		dev_err(priv->dev, "Failed to enable clk!\n");
> +		goto out_err;
> +	}
> +
> +	/* wdt has internal divider */
> +	clk = clk_get_rate(priv->clk);
> +	if (!clk) {
> +		clk_disable_unprepare(priv->clk);
> +		clk_disable_unprepare(priv->clk_ahb);
> +		dev_err(priv->dev, "Failed, clk is 0!\n");
> +		goto out_err;
> +	}
> +
> +	priv->wdt_freq = clk / 2;
> +
> +	return 0;
> +
> +clk_err:
> +	dev_err(priv->dev, "Failed to get clk (%s)\n",
> +			clk_idx ? "ahb" : "mod");
> +out_err:
> +	return -EFAULT;
> +}
> +
> +static int __init asm9260_wdt_get_dt_mode(struct asm9260_wdt_priv *priv)
> +{
> +	const char *tmp;
> +	int ret;
> +
> +	/* default mode */
> +	priv->mode = HW_RESET;
> +
> +	ret = of_property_read_string(priv->dev->of_node,
> +			"alphascale,mode", &tmp);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (!strcmp(tmp, "hw"))
> +		priv->mode = HW_RESET;
> +	else if (!strcmp(tmp, "sw"))
> +		priv->mode = SW_RESET;
> +	else if (!strcmp(tmp, "debug"))
> +		priv->mode = DEBUG;
> +	else {
> +		dev_warn(priv->dev, "unknown reset-type: %s. Using defaul \"hw\" mode.",
> +			tmp);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int __init asm9260_wdt_probe(struct platform_device *pdev)
> +{
> +	struct asm9260_wdt_priv *priv;
> +	struct watchdog_device *wdd;
> +	struct resource *res;
> +	bool nowayout = WATCHDOG_NOWAYOUT;
> +	int ret;
> +
> +	priv = devm_kzalloc(&pdev->dev, sizeof(struct asm9260_wdt_priv),
> +			GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->dev = &pdev->dev;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	priv->iobase = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(priv->iobase))
> +		return PTR_ERR(priv->iobase);
> +
> +	ret = asm9260_wdt_get_dt_clks(priv);
> +	if (ret)
> +		return ret;
> +
> +	priv->rst = devm_reset_control_get(&pdev->dev, "wdt_rst");
> +	if (IS_ERR(priv->rst)) {
> +		nowayout = 1;
> +		priv->rst = NULL;
> +	}
> +
> +	wdd = &priv->wdd;
> +	wdd->info = &asm9260_wdt_ident;
> +	wdd->ops = &asm9260_wdt_ops;
> +	wdd->min_timeout = 1;
> +	wdd->max_timeout = BM_WDTC_MAX(priv->wdt_freq);
> +	wdd->parent = &pdev->dev;
> +
> +	watchdog_set_drvdata(wdd, priv);
> +
> +	/*
> +	 * If 'timeout-sec' unspecified in devicetree, assume a 30 second
> +	 * default, unless the max timeout is less than 30 seconds, then use
> +	 * the max instead.
> +	 */
> +	wdd->timeout = ASM9260_WDT_DEFAULT_TIMEOUT;
> +	watchdog_init_timeout(wdd, 0, &pdev->dev);
> +
> +	watchdog_set_nowayout(wdd, nowayout);
> +
> +	asm9260_wdt_get_dt_mode(priv);
> +
> +	if (priv->mode != HW_RESET)
> +		priv->irq = platform_get_irq(pdev, 0);
> +
> +	if (priv->irq > 0) {
> +		/*
> +		 * Not all supported platforms specify an interrupt for the
> +		 * watchdog, so let's make it optional.
> +		 */
> +		ret = devm_request_irq(&pdev->dev, priv->irq,
> +				asm9260_wdt_irq, IRQF_SHARED,
> +				pdev->name, priv);
> +		if (ret < 0)
> +			dev_err(&pdev->dev, "failed to request IRQ\n");
> +	}
> +
> +	ret = watchdog_register_device(wdd);
> +	if (ret)
> +		goto clk_off;
> +
> +	platform_set_drvdata(pdev, priv);
> +
> +	priv->restart_handler.notifier_call = asm9260_restart_handler;
> +	priv->restart_handler.priority = 128;
> +	ret = register_restart_handler(&priv->restart_handler);
> +	if (ret)
> +		dev_err(&pdev->dev, "cannot register restart handler\n");
> +
> +	dev_info(&pdev->dev, "Watchdog enabled (to=%d, nw=%d, mode=%d)\n",
> +		wdd->timeout, nowayout, priv->mode);
> +	return 0;
> +
> +clk_off:
> +	clk_disable_unprepare(priv->clk);
> +	clk_disable_unprepare(priv->clk_ahb);
> +	return ret;
> +}
> +
> +static void asm9260_wdt_shutdown(struct platform_device *pdev)
> +{
> +	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
> +
> +	asm9260_wdt_disable(&priv->wdd);
> +}
> +
> +static int __exit asm9260_wdt_remove(struct platform_device *pdev)
> +{
> +	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
> +
> +	asm9260_wdt_shutdown(pdev);
> +
> +	unregister_restart_handler(&priv->restart_handler);
> +	clk_disable_unprepare(priv->clk);
> +	clk_disable_unprepare(priv->clk_ahb);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id asm9260_wdt_of_match[] = {
> +	{ .compatible = "alphascale,asm9260-wdt"},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, asm9260_wdt_of_match);
> +
> +static struct platform_driver asm9260_wdt_driver = {
> +	.driver = {
> +		.name = "asm9260-wdt",
> +		.owner = THIS_MODULE,
> +		.of_match_table	= asm9260_wdt_of_match,
> +	},
> +	.probe = asm9260_wdt_probe,
> +	.remove = asm9260_wdt_remove,
> +	.shutdown = asm9260_wdt_shutdown,
> +};
> +module_platform_driver(asm9260_wdt_driver);
> +
> +MODULE_DESCRIPTION("asm9260 WatchDog Timer Driver");
> +MODULE_AUTHOR("Oleksij Rempel <linux@rempel-privat.de>");
> +MODULE_LICENSE("GPL");
> -- 
> 2.5.0
> 
> --
> To unsubscribe from this list: send the line "unsubscribe devicetree" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v2] watchdog: add Alphascale asm9260-wdt driver
  2015-11-05 20:43               ` Rob Herring
@ 2015-11-23  7:11                 ` Oleksij Rempel
  -1 siblings, 0 replies; 43+ messages in thread
From: Oleksij Rempel @ 2015-11-23  7:11 UTC (permalink / raw)
  To: Rob Herring
  Cc: linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	linux-0h96xk9xTtrk1uMJSBkQmQ, wim-IQzOog9fTRqzQB+pC5nmwQ,
	devicetree-u79uwXL29TY76Z2rM5mHXA

[-- Attachment #1: Type: text/plain, Size: 2882 bytes --]

Hi,

thank you for review!

Am 05.11.2015 um 21:43 schrieb Rob Herring:
> On Thu, Nov 05, 2015 at 10:06:56AM +0100, Oleksij Rempel wrote:
>> Add WD support for Alphascale asm9260 SoC. This driver
>> provide support for different function modes:
>> - HW mode to trigger SoC reset on timeout
>> - SW mode do soft reset if needed
>> - DEBUG mode
>>
>> Optional support for stopping watchdog. If reset binding are not provided
>> this driver will work in nowayout mode.
>>
>> Signed-off-by: Oleksij Rempel <linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>
>> ---
>>  .../bindings/watchdog/alphascale-asm9260.txt       |  39 ++
> 
> It is preferred that bindings are a separate patch.
> 
>>  drivers/watchdog/Kconfig                           |   9 +
>>  drivers/watchdog/Makefile                          |   1 +
>>  drivers/watchdog/asm9260_wdt.c                     | 415 +++++++++++++++++++++
>>  4 files changed, 464 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
>>  create mode 100644 drivers/watchdog/asm9260_wdt.c
>>
>> diff --git a/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
>> new file mode 100644
>> index 0000000..6e54d1f
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
>> @@ -0,0 +1,39 @@
>> +Alphascale asm9260 Watchdog timer
>> +
>> +Required properties:
>> +
>> +- compatible : should be "alphascale,asm9260-wdt".
>> +- reg : Specifies base physical address and size of the registers.
>> +- clocks : the clocks feeding the watchdog timer. See clock-bindings.txt
>> +- clock-names : should be set to
>> +	"mod" - source for tick counter.
>> +	"ahb" - ahb gate.
>> +
>> +Optional properties:
>> +- resets : phandle pointing to the system reset controller with correct
>> +	reset line index for watchdog controller reset. This propertie is
> 
> s/propertie/property/
> 
>> +	required if you need to disable "nowayout" and it neened only with
>> +	CONFIG_WATCHDOG_NOWAYOUT=n.
> 
> The DT cannot depend on certain kernel configs.
> 
>> +	Without reseting this WD controller, it is not possible to stop
> 
> s/reseting/resetting/
> 
>> +	counter.
>> +- reset-names : should be set to "wdt_rst" if "resets" is used.
>> +- timeout-sec : shall contain the default watchdog timeout in seconds,
>> +	if unset, the default timeout is 30 seconds.
>> +- alphascale,mode : tree modes are supported
> 
> s/tree/three/
> 
>> +	"hw" - hw reset (defaul).
> 
> s/defaul/default/
> 
>> +	"sw" - sw reset.
>> +	"debug" - no action is taken.
> 
> Seems like this should be a generic wdog property.
> 

what do you mean?
Using "mode" instead of "alphascale,mode" and implement it in WD framework?

-- 
Regards,
Oleksij


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 213 bytes --]

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

* Re: [PATCH v2] watchdog: add Alphascale asm9260-wdt driver
@ 2015-11-23  7:11                 ` Oleksij Rempel
  0 siblings, 0 replies; 43+ messages in thread
From: Oleksij Rempel @ 2015-11-23  7:11 UTC (permalink / raw)
  To: Rob Herring; +Cc: linux-watchdog, linux, wim, devicetree

[-- Attachment #1: Type: text/plain, Size: 2859 bytes --]

Hi,

thank you for review!

Am 05.11.2015 um 21:43 schrieb Rob Herring:
> On Thu, Nov 05, 2015 at 10:06:56AM +0100, Oleksij Rempel wrote:
>> Add WD support for Alphascale asm9260 SoC. This driver
>> provide support for different function modes:
>> - HW mode to trigger SoC reset on timeout
>> - SW mode do soft reset if needed
>> - DEBUG mode
>>
>> Optional support for stopping watchdog. If reset binding are not provided
>> this driver will work in nowayout mode.
>>
>> Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
>> ---
>>  .../bindings/watchdog/alphascale-asm9260.txt       |  39 ++
> 
> It is preferred that bindings are a separate patch.
> 
>>  drivers/watchdog/Kconfig                           |   9 +
>>  drivers/watchdog/Makefile                          |   1 +
>>  drivers/watchdog/asm9260_wdt.c                     | 415 +++++++++++++++++++++
>>  4 files changed, 464 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
>>  create mode 100644 drivers/watchdog/asm9260_wdt.c
>>
>> diff --git a/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
>> new file mode 100644
>> index 0000000..6e54d1f
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
>> @@ -0,0 +1,39 @@
>> +Alphascale asm9260 Watchdog timer
>> +
>> +Required properties:
>> +
>> +- compatible : should be "alphascale,asm9260-wdt".
>> +- reg : Specifies base physical address and size of the registers.
>> +- clocks : the clocks feeding the watchdog timer. See clock-bindings.txt
>> +- clock-names : should be set to
>> +	"mod" - source for tick counter.
>> +	"ahb" - ahb gate.
>> +
>> +Optional properties:
>> +- resets : phandle pointing to the system reset controller with correct
>> +	reset line index for watchdog controller reset. This propertie is
> 
> s/propertie/property/
> 
>> +	required if you need to disable "nowayout" and it neened only with
>> +	CONFIG_WATCHDOG_NOWAYOUT=n.
> 
> The DT cannot depend on certain kernel configs.
> 
>> +	Without reseting this WD controller, it is not possible to stop
> 
> s/reseting/resetting/
> 
>> +	counter.
>> +- reset-names : should be set to "wdt_rst" if "resets" is used.
>> +- timeout-sec : shall contain the default watchdog timeout in seconds,
>> +	if unset, the default timeout is 30 seconds.
>> +- alphascale,mode : tree modes are supported
> 
> s/tree/three/
> 
>> +	"hw" - hw reset (defaul).
> 
> s/defaul/default/
> 
>> +	"sw" - sw reset.
>> +	"debug" - no action is taken.
> 
> Seems like this should be a generic wdog property.
> 

what do you mean?
Using "mode" instead of "alphascale,mode" and implement it in WD framework?

-- 
Regards,
Oleksij


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 213 bytes --]

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

* Re: [PATCH v2] watchdog: add Alphascale asm9260-wdt driver
  2015-11-23  7:11                 ` Oleksij Rempel
@ 2015-11-23  7:26                     ` Guenter Roeck
  -1 siblings, 0 replies; 43+ messages in thread
From: Guenter Roeck @ 2015-11-23  7:26 UTC (permalink / raw)
  To: Oleksij Rempel, Rob Herring
  Cc: linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	wim-IQzOog9fTRqzQB+pC5nmwQ, devicetree-u79uwXL29TY76Z2rM5mHXA

On 11/22/2015 11:11 PM, Oleksij Rempel wrote:
> Hi,
>
> thank you for review!
>
> Am 05.11.2015 um 21:43 schrieb Rob Herring:
>> On Thu, Nov 05, 2015 at 10:06:56AM +0100, Oleksij Rempel wrote:
>>> Add WD support for Alphascale asm9260 SoC. This driver
>>> provide support for different function modes:
>>> - HW mode to trigger SoC reset on timeout
>>> - SW mode do soft reset if needed
>>> - DEBUG mode
>>>
>>> Optional support for stopping watchdog. If reset binding are not provided
>>> this driver will work in nowayout mode.
>>>
>>> Signed-off-by: Oleksij Rempel <linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>
>>> ---
>>>   .../bindings/watchdog/alphascale-asm9260.txt       |  39 ++
>>
>> It is preferred that bindings are a separate patch.
>>
>>>   drivers/watchdog/Kconfig                           |   9 +
>>>   drivers/watchdog/Makefile                          |   1 +
>>>   drivers/watchdog/asm9260_wdt.c                     | 415 +++++++++++++++++++++
>>>   4 files changed, 464 insertions(+)
>>>   create mode 100644 Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
>>>   create mode 100644 drivers/watchdog/asm9260_wdt.c
>>>
>>> diff --git a/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
>>> new file mode 100644
>>> index 0000000..6e54d1f
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
>>> @@ -0,0 +1,39 @@
>>> +Alphascale asm9260 Watchdog timer
>>> +
>>> +Required properties:
>>> +
>>> +- compatible : should be "alphascale,asm9260-wdt".
>>> +- reg : Specifies base physical address and size of the registers.
>>> +- clocks : the clocks feeding the watchdog timer. See clock-bindings.txt
>>> +- clock-names : should be set to
>>> +	"mod" - source for tick counter.
>>> +	"ahb" - ahb gate.
>>> +
>>> +Optional properties:
>>> +- resets : phandle pointing to the system reset controller with correct
>>> +	reset line index for watchdog controller reset. This propertie is
>>
>> s/propertie/property/
>>
>>> +	required if you need to disable "nowayout" and it neened only with
>>> +	CONFIG_WATCHDOG_NOWAYOUT=n.
>>
>> The DT cannot depend on certain kernel configs.
>>
>>> +	Without reseting this WD controller, it is not possible to stop
>>
>> s/reseting/resetting/
>>
>>> +	counter.
>>> +- reset-names : should be set to "wdt_rst" if "resets" is used.
>>> +- timeout-sec : shall contain the default watchdog timeout in seconds,
>>> +	if unset, the default timeout is 30 seconds.
>>> +- alphascale,mode : tree modes are supported
>>
>> s/tree/three/
>>
>>> +	"hw" - hw reset (defaul).
>>
>> s/defaul/default/
>>
>>> +	"sw" - sw reset.
>>> +	"debug" - no action is taken.
>>
>> Seems like this should be a generic wdog property.
>>
>
> what do you mean?
> Using "mode" instead of "alphascale,mode" and implement it in WD framework?
>

You should still read the property in the driver, even it if is made generic.
The watchdog framework can't really use the information, most drivers don't
support selecting the mode. We can consider moving it into the framework
if/when it benefits other drivers.

On a side note, I would suggest "software" and "hardware" instead of sw / hw,
but that is just a personal preference.

Thanks,
Guenter

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v2] watchdog: add Alphascale asm9260-wdt driver
@ 2015-11-23  7:26                     ` Guenter Roeck
  0 siblings, 0 replies; 43+ messages in thread
From: Guenter Roeck @ 2015-11-23  7:26 UTC (permalink / raw)
  To: Oleksij Rempel, Rob Herring; +Cc: linux-watchdog, wim, devicetree

On 11/22/2015 11:11 PM, Oleksij Rempel wrote:
> Hi,
>
> thank you for review!
>
> Am 05.11.2015 um 21:43 schrieb Rob Herring:
>> On Thu, Nov 05, 2015 at 10:06:56AM +0100, Oleksij Rempel wrote:
>>> Add WD support for Alphascale asm9260 SoC. This driver
>>> provide support for different function modes:
>>> - HW mode to trigger SoC reset on timeout
>>> - SW mode do soft reset if needed
>>> - DEBUG mode
>>>
>>> Optional support for stopping watchdog. If reset binding are not provided
>>> this driver will work in nowayout mode.
>>>
>>> Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
>>> ---
>>>   .../bindings/watchdog/alphascale-asm9260.txt       |  39 ++
>>
>> It is preferred that bindings are a separate patch.
>>
>>>   drivers/watchdog/Kconfig                           |   9 +
>>>   drivers/watchdog/Makefile                          |   1 +
>>>   drivers/watchdog/asm9260_wdt.c                     | 415 +++++++++++++++++++++
>>>   4 files changed, 464 insertions(+)
>>>   create mode 100644 Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
>>>   create mode 100644 drivers/watchdog/asm9260_wdt.c
>>>
>>> diff --git a/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
>>> new file mode 100644
>>> index 0000000..6e54d1f
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
>>> @@ -0,0 +1,39 @@
>>> +Alphascale asm9260 Watchdog timer
>>> +
>>> +Required properties:
>>> +
>>> +- compatible : should be "alphascale,asm9260-wdt".
>>> +- reg : Specifies base physical address and size of the registers.
>>> +- clocks : the clocks feeding the watchdog timer. See clock-bindings.txt
>>> +- clock-names : should be set to
>>> +	"mod" - source for tick counter.
>>> +	"ahb" - ahb gate.
>>> +
>>> +Optional properties:
>>> +- resets : phandle pointing to the system reset controller with correct
>>> +	reset line index for watchdog controller reset. This propertie is
>>
>> s/propertie/property/
>>
>>> +	required if you need to disable "nowayout" and it neened only with
>>> +	CONFIG_WATCHDOG_NOWAYOUT=n.
>>
>> The DT cannot depend on certain kernel configs.
>>
>>> +	Without reseting this WD controller, it is not possible to stop
>>
>> s/reseting/resetting/
>>
>>> +	counter.
>>> +- reset-names : should be set to "wdt_rst" if "resets" is used.
>>> +- timeout-sec : shall contain the default watchdog timeout in seconds,
>>> +	if unset, the default timeout is 30 seconds.
>>> +- alphascale,mode : tree modes are supported
>>
>> s/tree/three/
>>
>>> +	"hw" - hw reset (defaul).
>>
>> s/defaul/default/
>>
>>> +	"sw" - sw reset.
>>> +	"debug" - no action is taken.
>>
>> Seems like this should be a generic wdog property.
>>
>
> what do you mean?
> Using "mode" instead of "alphascale,mode" and implement it in WD framework?
>

You should still read the property in the driver, even it if is made generic.
The watchdog framework can't really use the information, most drivers don't
support selecting the mode. We can consider moving it into the framework
if/when it benefits other drivers.

On a side note, I would suggest "software" and "hardware" instead of sw / hw,
but that is just a personal preference.

Thanks,
Guenter


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

* Re: [PATCH v2] watchdog: add Alphascale asm9260-wdt driver
  2015-11-23  7:26                     ` Guenter Roeck
@ 2015-11-23  9:51                         ` Oleksij Rempel
  -1 siblings, 0 replies; 43+ messages in thread
From: Oleksij Rempel @ 2015-11-23  9:51 UTC (permalink / raw)
  To: Guenter Roeck, Rob Herring
  Cc: linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	wim-IQzOog9fTRqzQB+pC5nmwQ, devicetree-u79uwXL29TY76Z2rM5mHXA

[-- Attachment #1: Type: text/plain, Size: 3771 bytes --]

Am 23.11.2015 um 08:26 schrieb Guenter Roeck:
> On 11/22/2015 11:11 PM, Oleksij Rempel wrote:
>> Hi,
>>
>> thank you for review!
>>
>> Am 05.11.2015 um 21:43 schrieb Rob Herring:
>>> On Thu, Nov 05, 2015 at 10:06:56AM +0100, Oleksij Rempel wrote:
>>>> Add WD support for Alphascale asm9260 SoC. This driver
>>>> provide support for different function modes:
>>>> - HW mode to trigger SoC reset on timeout
>>>> - SW mode do soft reset if needed
>>>> - DEBUG mode
>>>>
>>>> Optional support for stopping watchdog. If reset binding are not
>>>> provided
>>>> this driver will work in nowayout mode.
>>>>
>>>> Signed-off-by: Oleksij Rempel <linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>
>>>> ---
>>>>   .../bindings/watchdog/alphascale-asm9260.txt       |  39 ++
>>>
>>> It is preferred that bindings are a separate patch.
>>>
>>>>   drivers/watchdog/Kconfig                           |   9 +
>>>>   drivers/watchdog/Makefile                          |   1 +
>>>>   drivers/watchdog/asm9260_wdt.c                     | 415
>>>> +++++++++++++++++++++
>>>>   4 files changed, 464 insertions(+)
>>>>   create mode 100644
>>>> Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
>>>>   create mode 100644 drivers/watchdog/asm9260_wdt.c
>>>>
>>>> diff --git
>>>> a/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
>>>> b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
>>>> new file mode 100644
>>>> index 0000000..6e54d1f
>>>> --- /dev/null
>>>> +++ b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
>>>> @@ -0,0 +1,39 @@
>>>> +Alphascale asm9260 Watchdog timer
>>>> +
>>>> +Required properties:
>>>> +
>>>> +- compatible : should be "alphascale,asm9260-wdt".
>>>> +- reg : Specifies base physical address and size of the registers.
>>>> +- clocks : the clocks feeding the watchdog timer. See
>>>> clock-bindings.txt
>>>> +- clock-names : should be set to
>>>> +    "mod" - source for tick counter.
>>>> +    "ahb" - ahb gate.
>>>> +
>>>> +Optional properties:
>>>> +- resets : phandle pointing to the system reset controller with
>>>> correct
>>>> +    reset line index for watchdog controller reset. This propertie is
>>>
>>> s/propertie/property/
>>>
>>>> +    required if you need to disable "nowayout" and it neened only with
>>>> +    CONFIG_WATCHDOG_NOWAYOUT=n.
>>>
>>> The DT cannot depend on certain kernel configs.
>>>
>>>> +    Without reseting this WD controller, it is not possible to stop
>>>
>>> s/reseting/resetting/
>>>
>>>> +    counter.
>>>> +- reset-names : should be set to "wdt_rst" if "resets" is used.
>>>> +- timeout-sec : shall contain the default watchdog timeout in seconds,
>>>> +    if unset, the default timeout is 30 seconds.
>>>> +- alphascale,mode : tree modes are supported
>>>
>>> s/tree/three/
>>>
>>>> +    "hw" - hw reset (defaul).
>>>
>>> s/defaul/default/
>>>
>>>> +    "sw" - sw reset.
>>>> +    "debug" - no action is taken.
>>>
>>> Seems like this should be a generic wdog property.
>>>
>>
>> what do you mean?
>> Using "mode" instead of "alphascale,mode" and implement it in WD
>> framework?
>>
> 
> You should still read the property in the driver, even it if is made
> generic.
> The watchdog framework can't really use the information, most drivers don't
> support selecting the mode. We can consider moving it into the framework
> if/when it benefits other drivers.
> 
> On a side note, I would suggest "software" and "hardware" instead of sw
> / hw,
> but that is just a personal preference.

Ok, then i will leave it for now as is. If "mode" will be introduced, it
can brake compatibility. Using "alphascale,mode" feels save :)

-- 
Regards,
Oleksij


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 213 bytes --]

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

* Re: [PATCH v2] watchdog: add Alphascale asm9260-wdt driver
@ 2015-11-23  9:51                         ` Oleksij Rempel
  0 siblings, 0 replies; 43+ messages in thread
From: Oleksij Rempel @ 2015-11-23  9:51 UTC (permalink / raw)
  To: Guenter Roeck, Rob Herring; +Cc: linux-watchdog, wim, devicetree

[-- Attachment #1: Type: text/plain, Size: 3748 bytes --]

Am 23.11.2015 um 08:26 schrieb Guenter Roeck:
> On 11/22/2015 11:11 PM, Oleksij Rempel wrote:
>> Hi,
>>
>> thank you for review!
>>
>> Am 05.11.2015 um 21:43 schrieb Rob Herring:
>>> On Thu, Nov 05, 2015 at 10:06:56AM +0100, Oleksij Rempel wrote:
>>>> Add WD support for Alphascale asm9260 SoC. This driver
>>>> provide support for different function modes:
>>>> - HW mode to trigger SoC reset on timeout
>>>> - SW mode do soft reset if needed
>>>> - DEBUG mode
>>>>
>>>> Optional support for stopping watchdog. If reset binding are not
>>>> provided
>>>> this driver will work in nowayout mode.
>>>>
>>>> Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
>>>> ---
>>>>   .../bindings/watchdog/alphascale-asm9260.txt       |  39 ++
>>>
>>> It is preferred that bindings are a separate patch.
>>>
>>>>   drivers/watchdog/Kconfig                           |   9 +
>>>>   drivers/watchdog/Makefile                          |   1 +
>>>>   drivers/watchdog/asm9260_wdt.c                     | 415
>>>> +++++++++++++++++++++
>>>>   4 files changed, 464 insertions(+)
>>>>   create mode 100644
>>>> Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
>>>>   create mode 100644 drivers/watchdog/asm9260_wdt.c
>>>>
>>>> diff --git
>>>> a/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
>>>> b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
>>>> new file mode 100644
>>>> index 0000000..6e54d1f
>>>> --- /dev/null
>>>> +++ b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
>>>> @@ -0,0 +1,39 @@
>>>> +Alphascale asm9260 Watchdog timer
>>>> +
>>>> +Required properties:
>>>> +
>>>> +- compatible : should be "alphascale,asm9260-wdt".
>>>> +- reg : Specifies base physical address and size of the registers.
>>>> +- clocks : the clocks feeding the watchdog timer. See
>>>> clock-bindings.txt
>>>> +- clock-names : should be set to
>>>> +    "mod" - source for tick counter.
>>>> +    "ahb" - ahb gate.
>>>> +
>>>> +Optional properties:
>>>> +- resets : phandle pointing to the system reset controller with
>>>> correct
>>>> +    reset line index for watchdog controller reset. This propertie is
>>>
>>> s/propertie/property/
>>>
>>>> +    required if you need to disable "nowayout" and it neened only with
>>>> +    CONFIG_WATCHDOG_NOWAYOUT=n.
>>>
>>> The DT cannot depend on certain kernel configs.
>>>
>>>> +    Without reseting this WD controller, it is not possible to stop
>>>
>>> s/reseting/resetting/
>>>
>>>> +    counter.
>>>> +- reset-names : should be set to "wdt_rst" if "resets" is used.
>>>> +- timeout-sec : shall contain the default watchdog timeout in seconds,
>>>> +    if unset, the default timeout is 30 seconds.
>>>> +- alphascale,mode : tree modes are supported
>>>
>>> s/tree/three/
>>>
>>>> +    "hw" - hw reset (defaul).
>>>
>>> s/defaul/default/
>>>
>>>> +    "sw" - sw reset.
>>>> +    "debug" - no action is taken.
>>>
>>> Seems like this should be a generic wdog property.
>>>
>>
>> what do you mean?
>> Using "mode" instead of "alphascale,mode" and implement it in WD
>> framework?
>>
> 
> You should still read the property in the driver, even it if is made
> generic.
> The watchdog framework can't really use the information, most drivers don't
> support selecting the mode. We can consider moving it into the framework
> if/when it benefits other drivers.
> 
> On a side note, I would suggest "software" and "hardware" instead of sw
> / hw,
> but that is just a personal preference.

Ok, then i will leave it for now as is. If "mode" will be introduced, it
can brake compatibility. Using "alphascale,mode" feels save :)

-- 
Regards,
Oleksij


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 213 bytes --]

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

* Re: [PATCH v2] watchdog: add Alphascale asm9260-wdt driver
  2015-11-23  9:51                         ` Oleksij Rempel
@ 2015-11-23 15:47                             ` Guenter Roeck
  -1 siblings, 0 replies; 43+ messages in thread
From: Guenter Roeck @ 2015-11-23 15:47 UTC (permalink / raw)
  To: Oleksij Rempel, Rob Herring
  Cc: linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	wim-IQzOog9fTRqzQB+pC5nmwQ, devicetree-u79uwXL29TY76Z2rM5mHXA

On 11/23/2015 01:51 AM, Oleksij Rempel wrote:

>>>>
>>>> Seems like this should be a generic wdog property.
>>>>
>>>
>>> what do you mean?
>>> Using "mode" instead of "alphascale,mode" and implement it in WD
>>> framework?
>>>
>>
>> You should still read the property in the driver, even it if is made
>> generic.
>> The watchdog framework can't really use the information, most drivers don't
>> support selecting the mode. We can consider moving it into the framework
>> if/when it benefits other drivers.
>>
>> On a side note, I would suggest "software" and "hardware" instead of sw
>> / hw,
>> but that is just a personal preference.
>
> Ok, then i will leave it for now as is. If "mode" will be introduced, it
> can brake compatibility. Using "alphascale,mode" feels save :)
>
Not sure I understand. Using a generic property name doesn't mean anything
about its implementation. Devicetree properties are supposed to be OS and
implementation independent, no ?

Guenter

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v2] watchdog: add Alphascale asm9260-wdt driver
@ 2015-11-23 15:47                             ` Guenter Roeck
  0 siblings, 0 replies; 43+ messages in thread
From: Guenter Roeck @ 2015-11-23 15:47 UTC (permalink / raw)
  To: Oleksij Rempel, Rob Herring; +Cc: linux-watchdog, wim, devicetree

On 11/23/2015 01:51 AM, Oleksij Rempel wrote:

>>>>
>>>> Seems like this should be a generic wdog property.
>>>>
>>>
>>> what do you mean?
>>> Using "mode" instead of "alphascale,mode" and implement it in WD
>>> framework?
>>>
>>
>> You should still read the property in the driver, even it if is made
>> generic.
>> The watchdog framework can't really use the information, most drivers don't
>> support selecting the mode. We can consider moving it into the framework
>> if/when it benefits other drivers.
>>
>> On a side note, I would suggest "software" and "hardware" instead of sw
>> / hw,
>> but that is just a personal preference.
>
> Ok, then i will leave it for now as is. If "mode" will be introduced, it
> can brake compatibility. Using "alphascale,mode" feels save :)
>
Not sure I understand. Using a generic property name doesn't mean anything
about its implementation. Devicetree properties are supposed to be OS and
implementation independent, no ?

Guenter


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

* [PATCH v3 0/2] add Alphascale asm9260-wdt driver
  2015-11-23  7:26                     ` Guenter Roeck
@ 2015-11-24 21:40                         ` Oleksij Rempel
  -1 siblings, 0 replies; 43+ messages in thread
From: Oleksij Rempel @ 2015-11-24 21:40 UTC (permalink / raw)
  To: linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	linux-0h96xk9xTtrk1uMJSBkQmQ, wim-IQzOog9fTRqzQB+pC5nmwQ,
	devicetree-u79uwXL29TY76Z2rM5mHXA, robh-DgEjT+Ai2ygdnm+yROfE0A
  Cc: Oleksij Rempel

v2:
- fix DT types
- remove obsolet comments
- add clk error handling
- reduce log noise
- allow to return an error in asm9260_wdt_get_dt_mode

v3:
- split patch to two parts, binding documentation and driver
- spelling fixes
- make reset a required property. It will make the code easier
- reduce timeout for asm9260_wdt_sys_reset
- rework error handling in asm9260_wdt_get_dt_clks
- asm9260_wdt_get_dt_mode - convert return type from int to void
- don't touch watchdog_set_nowayout
- don't do dev_err() if driver is still working, use dev_warn instead.
- asm9260_wdt_remove - use asm9260_wdt_disable() instead of asm9260_wdt_shutdown()

Oleksij Rempel (2):
  watchdog: add Alphascale asm9260-wdt driver
  DT: watchdog: add Alphascale asm9260 watchdog binding documentation.

 .../bindings/watchdog/alphascale-asm9260.txt       |  35 ++
 drivers/watchdog/Kconfig                           |  10 +
 drivers/watchdog/Makefile                          |   1 +
 drivers/watchdog/asm9260_wdt.c                     | 400 +++++++++++++++++++++
 4 files changed, 446 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
 create mode 100644 drivers/watchdog/asm9260_wdt.c

-- 
2.5.0

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH v3 0/2] add Alphascale asm9260-wdt driver
@ 2015-11-24 21:40                         ` Oleksij Rempel
  0 siblings, 0 replies; 43+ messages in thread
From: Oleksij Rempel @ 2015-11-24 21:40 UTC (permalink / raw)
  To: linux-watchdog, linux, wim, devicetree, robh; +Cc: Oleksij Rempel

v2:
- fix DT types
- remove obsolet comments
- add clk error handling
- reduce log noise
- allow to return an error in asm9260_wdt_get_dt_mode

v3:
- split patch to two parts, binding documentation and driver
- spelling fixes
- make reset a required property. It will make the code easier
- reduce timeout for asm9260_wdt_sys_reset
- rework error handling in asm9260_wdt_get_dt_clks
- asm9260_wdt_get_dt_mode - convert return type from int to void
- don't touch watchdog_set_nowayout
- don't do dev_err() if driver is still working, use dev_warn instead.
- asm9260_wdt_remove - use asm9260_wdt_disable() instead of asm9260_wdt_shutdown()

Oleksij Rempel (2):
  watchdog: add Alphascale asm9260-wdt driver
  DT: watchdog: add Alphascale asm9260 watchdog binding documentation.

 .../bindings/watchdog/alphascale-asm9260.txt       |  35 ++
 drivers/watchdog/Kconfig                           |  10 +
 drivers/watchdog/Makefile                          |   1 +
 drivers/watchdog/asm9260_wdt.c                     | 400 +++++++++++++++++++++
 4 files changed, 446 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
 create mode 100644 drivers/watchdog/asm9260_wdt.c

-- 
2.5.0

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

* [PATCH v3 1/2] watchdog: add Alphascale asm9260-wdt driver
  2015-11-24 21:40                         ` Oleksij Rempel
@ 2015-11-24 21:40                             ` Oleksij Rempel
  -1 siblings, 0 replies; 43+ messages in thread
From: Oleksij Rempel @ 2015-11-24 21:40 UTC (permalink / raw)
  To: linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	linux-0h96xk9xTtrk1uMJSBkQmQ, wim-IQzOog9fTRqzQB+pC5nmwQ,
	devicetree-u79uwXL29TY76Z2rM5mHXA, robh-DgEjT+Ai2ygdnm+yROfE0A
  Cc: Oleksij Rempel

Add WD support for Alphascale asm9260 SoC. This driver
provide support for different function modes:
- HW mode to trigger SoC reset on timeout
- SW mode do soft reset if needed
- DEBUG mode

Optional support for stopping watchdog. If reset binding are not provided
this driver will work in nowayout mode.

Signed-off-by: Oleksij Rempel <linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>
---
 drivers/watchdog/Kconfig       |  10 ++
 drivers/watchdog/Makefile      |   1 +
 drivers/watchdog/asm9260_wdt.c | 400 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 411 insertions(+)
 create mode 100644 drivers/watchdog/asm9260_wdt.c

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index c68edc1..9cd9b75 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -173,6 +173,16 @@ config ARM_SP805_WATCHDOG
 	  ARM Primecell SP805 Watchdog timer. This will reboot your system when
 	  the timeout is reached.
 
+config ASM9260_WATCHDOG
+	tristate "Alphascale ASM9260 watchdog"
+	depends on MACH_ASM9260
+	depends on OF
+	select WATCHDOG_CORE
+	select RESET_CONTROLLER
+	help
+	  Watchdog timer embedded into Alphascale asm9260 chips. This will reboot your
+	  system when the timeout is reached.
+
 config AT91RM9200_WATCHDOG
 	tristate "AT91RM9200 watchdog"
 	depends on SOC_AT91RM9200 && MFD_SYSCON
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 0c616e3..bd7b0cd 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
 
 # ARM Architecture
 obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o
+obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o
 obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
 obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
 obj-$(CONFIG_CADENCE_WATCHDOG) += cadence_wdt.o
diff --git a/drivers/watchdog/asm9260_wdt.c b/drivers/watchdog/asm9260_wdt.c
new file mode 100644
index 0000000..c8daeff
--- /dev/null
+++ b/drivers/watchdog/asm9260_wdt.c
@@ -0,0 +1,400 @@
+/*
+ * Watchdog driver for Alphascale ASM9260.
+ *
+ * Copyright (c) 2014 Oleksij Rempel <linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/reset.h>
+#include <linux/watchdog.h>
+
+#define CLOCK_FREQ	1000000
+
+/* Watchdog Mode register */
+#define HW_WDMOD			0x00
+/* Wake interrupt. Set by HW, can't be cleared. */
+#define BM_MOD_WDINT			BIT(3)
+/* This bit set if timeout reached. Cleared by SW. */
+#define BM_MOD_WDTOF			BIT(2)
+/* HW Reset on timeout */
+#define BM_MOD_WDRESET			BIT(1)
+/* WD enable */
+#define BM_MOD_WDEN			BIT(0)
+
+/*
+ * Watchdog Timer Constant register
+ * Minimal value is 0xff, the meaning of this value
+ * depends on used clock: T = WDCLK * (0xff + 1) * 4
+ */
+#define HW_WDTC				0x04
+#define BM_WDTC_MAX(freq)		(0x7fffffff / (freq))
+
+/* Watchdog Feed register */
+#define HW_WDFEED			0x08
+
+/* Watchdog Timer Value register */
+#define HW_WDTV				0x0c
+
+#define ASM9260_WDT_DEFAULT_TIMEOUT	30
+
+enum asm9260_wdt_mode {
+	HW_RESET,
+	SW_RESET,
+	DEBUG,
+};
+
+struct asm9260_wdt_priv {
+	struct device		*dev;
+	struct watchdog_device	wdd;
+	struct clk		*clk;
+	struct clk		*clk_ahb;
+	struct reset_control	*rst;
+	struct notifier_block	restart_handler;
+
+	void __iomem		*iobase;
+	int			irq;
+	unsigned long		wdt_freq;
+	enum asm9260_wdt_mode	mode;
+};
+
+static int asm9260_wdt_feed(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+
+	iowrite32(0xaa, priv->iobase + HW_WDFEED);
+	iowrite32(0x55, priv->iobase + HW_WDFEED);
+
+	return 0;
+}
+
+static unsigned int asm9260_wdt_gettimeleft(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+	u32 counter;
+
+	counter = ioread32(priv->iobase + HW_WDTV);
+
+	return DIV_ROUND_CLOSEST(counter, priv->wdt_freq);
+}
+
+static int asm9260_wdt_updatetimeout(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+	u32 counter;
+
+	counter = wdd->timeout * priv->wdt_freq;
+
+	iowrite32(counter, priv->iobase + HW_WDTC);
+
+	return 0;
+}
+
+static int asm9260_wdt_enable(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+	u32 mode = 0;
+
+	if (priv->mode == HW_RESET)
+		mode = BM_MOD_WDRESET;
+
+	iowrite32(BM_MOD_WDEN | mode, priv->iobase + HW_WDMOD);
+
+	asm9260_wdt_updatetimeout(wdd);
+
+	asm9260_wdt_feed(wdd);
+
+	return 0;
+}
+
+static int asm9260_wdt_disable(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+
+	/* The only way to disable WD is to reset it. */
+	reset_control_assert(priv->rst);
+	reset_control_deassert(priv->rst);
+
+	return 0;
+}
+
+static int asm9260_wdt_settimeout(struct watchdog_device *wdd, unsigned int to)
+{
+	wdd->timeout = to;
+	asm9260_wdt_updatetimeout(wdd);
+
+	return 0;
+}
+
+static void asm9260_wdt_sys_reset(struct asm9260_wdt_priv *priv)
+{
+	/* init WD if it was not started */
+
+	iowrite32(BM_MOD_WDEN | BM_MOD_WDRESET, priv->iobase + HW_WDMOD);
+
+	iowrite32(0xff, priv->iobase + HW_WDTC);
+	/* first pass correct sequence */
+	asm9260_wdt_feed(&priv->wdd);
+	/*
+	 * Then write wrong pattern to the feed to trigger reset
+	 * ASAP.
+	 */
+	iowrite32(0xff, priv->iobase + HW_WDFEED);
+
+	mdelay(1000);
+}
+
+static irqreturn_t asm9260_wdt_irq(int irq, void *devid)
+{
+	struct asm9260_wdt_priv *priv = devid;
+	u32 stat;
+
+	stat = ioread32(priv->iobase + HW_WDMOD);
+	if (!(stat & BM_MOD_WDINT))
+		return IRQ_NONE;
+
+	if (priv->mode == DEBUG)
+		dev_info(priv->dev, "Watchdog Timeout. Do nothing.\n");
+	else {
+		dev_info(priv->dev, "Watchdog Timeout. Doing SW Reset.\n");
+		asm9260_wdt_sys_reset(priv);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int asm9260_restart_handler(struct notifier_block *this,
+				   unsigned long mode, void *cmd)
+{
+	struct asm9260_wdt_priv *priv =
+		container_of(this, struct asm9260_wdt_priv, restart_handler);
+
+	asm9260_wdt_sys_reset(priv);
+
+	return NOTIFY_DONE;
+}
+
+static const struct watchdog_info asm9260_wdt_ident = {
+	.options          =     WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING
+				| WDIOF_MAGICCLOSE,
+	.identity         =	"Alphascale asm9260 Watchdog",
+};
+
+static struct watchdog_ops asm9260_wdt_ops = {
+	.owner		= THIS_MODULE,
+	.start		= asm9260_wdt_enable,
+	.stop		= asm9260_wdt_disable,
+	.get_timeleft	= asm9260_wdt_gettimeleft,
+	.ping		= asm9260_wdt_feed,
+	.set_timeout	= asm9260_wdt_settimeout,
+};
+
+static int __init asm9260_wdt_get_dt_clks(struct asm9260_wdt_priv *priv)
+{
+	int err;
+	unsigned long clk;
+
+	priv->clk = devm_clk_get(priv->dev, "mod");
+	if (IS_ERR(priv->clk)) {
+		dev_err(priv->dev, "Failed to get \"mod\" clk\n");
+		return PTR_ERR(priv->clk);
+	}
+
+	/* configure AHB clock */
+	priv->clk_ahb = devm_clk_get(priv->dev, "ahb");
+	if (IS_ERR(priv->clk_ahb)) {
+		dev_err(priv->dev, "Failed to get \"ahb\" clk\n");
+		return PTR_ERR(priv->clk_ahb);
+	}
+
+	err = clk_prepare_enable(priv->clk_ahb);
+	if (err) {
+		dev_err(priv->dev, "Failed to enable ahb_clk!\n");
+		return err;
+	}
+
+	err = clk_set_rate(priv->clk, CLOCK_FREQ);
+	if (err) {
+		clk_disable_unprepare(priv->clk_ahb);
+		dev_err(priv->dev, "Failed to set rate!\n");
+		return err;
+	}
+
+	err = clk_prepare_enable(priv->clk);
+	if (err) {
+		clk_disable_unprepare(priv->clk_ahb);
+		dev_err(priv->dev, "Failed to enable clk!\n");
+		return err;
+	}
+
+	/* wdt has internal divider */
+	clk = clk_get_rate(priv->clk);
+	if (!clk) {
+		clk_disable_unprepare(priv->clk);
+		clk_disable_unprepare(priv->clk_ahb);
+		dev_err(priv->dev, "Failed, clk is 0!\n");
+		return -EINVAL;
+	}
+
+	priv->wdt_freq = clk / 2;
+
+	return 0;
+}
+
+static void __init asm9260_wdt_get_dt_mode(struct asm9260_wdt_priv *priv)
+{
+	const char *tmp;
+	int ret;
+
+	/* default mode */
+	priv->mode = HW_RESET;
+
+	ret = of_property_read_string(priv->dev->of_node,
+				      "alphascale,mode", &tmp);
+	if (ret < 0)
+		return;
+
+	if (!strcmp(tmp, "hw"))
+		priv->mode = HW_RESET;
+	else if (!strcmp(tmp, "sw"))
+		priv->mode = SW_RESET;
+	else if (!strcmp(tmp, "debug"))
+		priv->mode = DEBUG;
+	else
+		dev_warn(priv->dev, "unknown reset-type: %s. Using default \"hw\" mode.",
+			 tmp);
+}
+
+static int __init asm9260_wdt_probe(struct platform_device *pdev)
+{
+	struct asm9260_wdt_priv *priv;
+	struct watchdog_device *wdd;
+	struct resource *res;
+	int ret;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(struct asm9260_wdt_priv),
+			    GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->dev = &pdev->dev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	priv->iobase = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(priv->iobase))
+		return PTR_ERR(priv->iobase);
+
+	ret = asm9260_wdt_get_dt_clks(priv);
+	if (ret)
+		return ret;
+
+	priv->rst = devm_reset_control_get(&pdev->dev, "wdt_rst");
+	if (IS_ERR(priv->rst))
+		return PTR_ERR(priv->rst);
+
+	wdd = &priv->wdd;
+	wdd->info = &asm9260_wdt_ident;
+	wdd->ops = &asm9260_wdt_ops;
+	wdd->min_timeout = 1;
+	wdd->max_timeout = BM_WDTC_MAX(priv->wdt_freq);
+	wdd->parent = &pdev->dev;
+
+	watchdog_set_drvdata(wdd, priv);
+
+	/*
+	 * If 'timeout-sec' unspecified in devicetree, assume a 30 second
+	 * default, unless the max timeout is less than 30 seconds, then use
+	 * the max instead.
+	 */
+	wdd->timeout = ASM9260_WDT_DEFAULT_TIMEOUT;
+	watchdog_init_timeout(wdd, 0, &pdev->dev);
+
+	asm9260_wdt_get_dt_mode(priv);
+
+	if (priv->mode != HW_RESET)
+		priv->irq = platform_get_irq(pdev, 0);
+
+	if (priv->irq > 0) {
+		/*
+		 * Not all supported platforms specify an interrupt for the
+		 * watchdog, so let's make it optional.
+		 */
+		ret = devm_request_irq(&pdev->dev, priv->irq,
+				       asm9260_wdt_irq, IRQF_SHARED,
+				       pdev->name, priv);
+		if (ret < 0)
+			dev_warn(&pdev->dev, "failed to request IRQ\n");
+	}
+
+	ret = watchdog_register_device(wdd);
+	if (ret)
+		goto clk_off;
+
+	platform_set_drvdata(pdev, priv);
+
+	priv->restart_handler.notifier_call = asm9260_restart_handler;
+	priv->restart_handler.priority = 128;
+	ret = register_restart_handler(&priv->restart_handler);
+	if (ret)
+		dev_warn(&pdev->dev, "cannot register restart handler\n");
+
+	dev_info(&pdev->dev, "Watchdog enabled (to=%d, mode=%d)\n",
+		 wdd->timeout, priv->mode);
+	return 0;
+
+clk_off:
+	clk_disable_unprepare(priv->clk);
+	clk_disable_unprepare(priv->clk_ahb);
+	return ret;
+}
+
+static void asm9260_wdt_shutdown(struct platform_device *pdev)
+{
+	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
+
+	asm9260_wdt_disable(&priv->wdd);
+}
+
+static int __exit asm9260_wdt_remove(struct platform_device *pdev)
+{
+	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
+
+	asm9260_wdt_disable(&priv->wdd);
+
+	unregister_restart_handler(&priv->restart_handler);
+	clk_disable_unprepare(priv->clk);
+	clk_disable_unprepare(priv->clk_ahb);
+
+	return 0;
+}
+
+static const struct of_device_id asm9260_wdt_of_match[] = {
+	{ .compatible = "alphascale,asm9260-wdt"},
+	{},
+};
+MODULE_DEVICE_TABLE(of, asm9260_wdt_of_match);
+
+static struct platform_driver asm9260_wdt_driver = {
+	.driver = {
+		.name = "asm9260-wdt",
+		.owner = THIS_MODULE,
+		.of_match_table	= asm9260_wdt_of_match,
+	},
+	.probe = asm9260_wdt_probe,
+	.remove = asm9260_wdt_remove,
+	.shutdown = asm9260_wdt_shutdown,
+};
+module_platform_driver(asm9260_wdt_driver);
+
+MODULE_DESCRIPTION("asm9260 WatchDog Timer Driver");
+MODULE_AUTHOR("Oleksij Rempel <linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>");
+MODULE_LICENSE("GPL");
-- 
2.5.0

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH v3 1/2] watchdog: add Alphascale asm9260-wdt driver
@ 2015-11-24 21:40                             ` Oleksij Rempel
  0 siblings, 0 replies; 43+ messages in thread
From: Oleksij Rempel @ 2015-11-24 21:40 UTC (permalink / raw)
  To: linux-watchdog, linux, wim, devicetree, robh; +Cc: Oleksij Rempel

Add WD support for Alphascale asm9260 SoC. This driver
provide support for different function modes:
- HW mode to trigger SoC reset on timeout
- SW mode do soft reset if needed
- DEBUG mode

Optional support for stopping watchdog. If reset binding are not provided
this driver will work in nowayout mode.

Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
---
 drivers/watchdog/Kconfig       |  10 ++
 drivers/watchdog/Makefile      |   1 +
 drivers/watchdog/asm9260_wdt.c | 400 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 411 insertions(+)
 create mode 100644 drivers/watchdog/asm9260_wdt.c

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index c68edc1..9cd9b75 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -173,6 +173,16 @@ config ARM_SP805_WATCHDOG
 	  ARM Primecell SP805 Watchdog timer. This will reboot your system when
 	  the timeout is reached.
 
+config ASM9260_WATCHDOG
+	tristate "Alphascale ASM9260 watchdog"
+	depends on MACH_ASM9260
+	depends on OF
+	select WATCHDOG_CORE
+	select RESET_CONTROLLER
+	help
+	  Watchdog timer embedded into Alphascale asm9260 chips. This will reboot your
+	  system when the timeout is reached.
+
 config AT91RM9200_WATCHDOG
 	tristate "AT91RM9200 watchdog"
 	depends on SOC_AT91RM9200 && MFD_SYSCON
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 0c616e3..bd7b0cd 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
 
 # ARM Architecture
 obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o
+obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o
 obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
 obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
 obj-$(CONFIG_CADENCE_WATCHDOG) += cadence_wdt.o
diff --git a/drivers/watchdog/asm9260_wdt.c b/drivers/watchdog/asm9260_wdt.c
new file mode 100644
index 0000000..c8daeff
--- /dev/null
+++ b/drivers/watchdog/asm9260_wdt.c
@@ -0,0 +1,400 @@
+/*
+ * Watchdog driver for Alphascale ASM9260.
+ *
+ * Copyright (c) 2014 Oleksij Rempel <linux@rempel-privat.de>
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/reset.h>
+#include <linux/watchdog.h>
+
+#define CLOCK_FREQ	1000000
+
+/* Watchdog Mode register */
+#define HW_WDMOD			0x00
+/* Wake interrupt. Set by HW, can't be cleared. */
+#define BM_MOD_WDINT			BIT(3)
+/* This bit set if timeout reached. Cleared by SW. */
+#define BM_MOD_WDTOF			BIT(2)
+/* HW Reset on timeout */
+#define BM_MOD_WDRESET			BIT(1)
+/* WD enable */
+#define BM_MOD_WDEN			BIT(0)
+
+/*
+ * Watchdog Timer Constant register
+ * Minimal value is 0xff, the meaning of this value
+ * depends on used clock: T = WDCLK * (0xff + 1) * 4
+ */
+#define HW_WDTC				0x04
+#define BM_WDTC_MAX(freq)		(0x7fffffff / (freq))
+
+/* Watchdog Feed register */
+#define HW_WDFEED			0x08
+
+/* Watchdog Timer Value register */
+#define HW_WDTV				0x0c
+
+#define ASM9260_WDT_DEFAULT_TIMEOUT	30
+
+enum asm9260_wdt_mode {
+	HW_RESET,
+	SW_RESET,
+	DEBUG,
+};
+
+struct asm9260_wdt_priv {
+	struct device		*dev;
+	struct watchdog_device	wdd;
+	struct clk		*clk;
+	struct clk		*clk_ahb;
+	struct reset_control	*rst;
+	struct notifier_block	restart_handler;
+
+	void __iomem		*iobase;
+	int			irq;
+	unsigned long		wdt_freq;
+	enum asm9260_wdt_mode	mode;
+};
+
+static int asm9260_wdt_feed(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+
+	iowrite32(0xaa, priv->iobase + HW_WDFEED);
+	iowrite32(0x55, priv->iobase + HW_WDFEED);
+
+	return 0;
+}
+
+static unsigned int asm9260_wdt_gettimeleft(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+	u32 counter;
+
+	counter = ioread32(priv->iobase + HW_WDTV);
+
+	return DIV_ROUND_CLOSEST(counter, priv->wdt_freq);
+}
+
+static int asm9260_wdt_updatetimeout(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+	u32 counter;
+
+	counter = wdd->timeout * priv->wdt_freq;
+
+	iowrite32(counter, priv->iobase + HW_WDTC);
+
+	return 0;
+}
+
+static int asm9260_wdt_enable(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+	u32 mode = 0;
+
+	if (priv->mode == HW_RESET)
+		mode = BM_MOD_WDRESET;
+
+	iowrite32(BM_MOD_WDEN | mode, priv->iobase + HW_WDMOD);
+
+	asm9260_wdt_updatetimeout(wdd);
+
+	asm9260_wdt_feed(wdd);
+
+	return 0;
+}
+
+static int asm9260_wdt_disable(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+
+	/* The only way to disable WD is to reset it. */
+	reset_control_assert(priv->rst);
+	reset_control_deassert(priv->rst);
+
+	return 0;
+}
+
+static int asm9260_wdt_settimeout(struct watchdog_device *wdd, unsigned int to)
+{
+	wdd->timeout = to;
+	asm9260_wdt_updatetimeout(wdd);
+
+	return 0;
+}
+
+static void asm9260_wdt_sys_reset(struct asm9260_wdt_priv *priv)
+{
+	/* init WD if it was not started */
+
+	iowrite32(BM_MOD_WDEN | BM_MOD_WDRESET, priv->iobase + HW_WDMOD);
+
+	iowrite32(0xff, priv->iobase + HW_WDTC);
+	/* first pass correct sequence */
+	asm9260_wdt_feed(&priv->wdd);
+	/*
+	 * Then write wrong pattern to the feed to trigger reset
+	 * ASAP.
+	 */
+	iowrite32(0xff, priv->iobase + HW_WDFEED);
+
+	mdelay(1000);
+}
+
+static irqreturn_t asm9260_wdt_irq(int irq, void *devid)
+{
+	struct asm9260_wdt_priv *priv = devid;
+	u32 stat;
+
+	stat = ioread32(priv->iobase + HW_WDMOD);
+	if (!(stat & BM_MOD_WDINT))
+		return IRQ_NONE;
+
+	if (priv->mode == DEBUG)
+		dev_info(priv->dev, "Watchdog Timeout. Do nothing.\n");
+	else {
+		dev_info(priv->dev, "Watchdog Timeout. Doing SW Reset.\n");
+		asm9260_wdt_sys_reset(priv);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int asm9260_restart_handler(struct notifier_block *this,
+				   unsigned long mode, void *cmd)
+{
+	struct asm9260_wdt_priv *priv =
+		container_of(this, struct asm9260_wdt_priv, restart_handler);
+
+	asm9260_wdt_sys_reset(priv);
+
+	return NOTIFY_DONE;
+}
+
+static const struct watchdog_info asm9260_wdt_ident = {
+	.options          =     WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING
+				| WDIOF_MAGICCLOSE,
+	.identity         =	"Alphascale asm9260 Watchdog",
+};
+
+static struct watchdog_ops asm9260_wdt_ops = {
+	.owner		= THIS_MODULE,
+	.start		= asm9260_wdt_enable,
+	.stop		= asm9260_wdt_disable,
+	.get_timeleft	= asm9260_wdt_gettimeleft,
+	.ping		= asm9260_wdt_feed,
+	.set_timeout	= asm9260_wdt_settimeout,
+};
+
+static int __init asm9260_wdt_get_dt_clks(struct asm9260_wdt_priv *priv)
+{
+	int err;
+	unsigned long clk;
+
+	priv->clk = devm_clk_get(priv->dev, "mod");
+	if (IS_ERR(priv->clk)) {
+		dev_err(priv->dev, "Failed to get \"mod\" clk\n");
+		return PTR_ERR(priv->clk);
+	}
+
+	/* configure AHB clock */
+	priv->clk_ahb = devm_clk_get(priv->dev, "ahb");
+	if (IS_ERR(priv->clk_ahb)) {
+		dev_err(priv->dev, "Failed to get \"ahb\" clk\n");
+		return PTR_ERR(priv->clk_ahb);
+	}
+
+	err = clk_prepare_enable(priv->clk_ahb);
+	if (err) {
+		dev_err(priv->dev, "Failed to enable ahb_clk!\n");
+		return err;
+	}
+
+	err = clk_set_rate(priv->clk, CLOCK_FREQ);
+	if (err) {
+		clk_disable_unprepare(priv->clk_ahb);
+		dev_err(priv->dev, "Failed to set rate!\n");
+		return err;
+	}
+
+	err = clk_prepare_enable(priv->clk);
+	if (err) {
+		clk_disable_unprepare(priv->clk_ahb);
+		dev_err(priv->dev, "Failed to enable clk!\n");
+		return err;
+	}
+
+	/* wdt has internal divider */
+	clk = clk_get_rate(priv->clk);
+	if (!clk) {
+		clk_disable_unprepare(priv->clk);
+		clk_disable_unprepare(priv->clk_ahb);
+		dev_err(priv->dev, "Failed, clk is 0!\n");
+		return -EINVAL;
+	}
+
+	priv->wdt_freq = clk / 2;
+
+	return 0;
+}
+
+static void __init asm9260_wdt_get_dt_mode(struct asm9260_wdt_priv *priv)
+{
+	const char *tmp;
+	int ret;
+
+	/* default mode */
+	priv->mode = HW_RESET;
+
+	ret = of_property_read_string(priv->dev->of_node,
+				      "alphascale,mode", &tmp);
+	if (ret < 0)
+		return;
+
+	if (!strcmp(tmp, "hw"))
+		priv->mode = HW_RESET;
+	else if (!strcmp(tmp, "sw"))
+		priv->mode = SW_RESET;
+	else if (!strcmp(tmp, "debug"))
+		priv->mode = DEBUG;
+	else
+		dev_warn(priv->dev, "unknown reset-type: %s. Using default \"hw\" mode.",
+			 tmp);
+}
+
+static int __init asm9260_wdt_probe(struct platform_device *pdev)
+{
+	struct asm9260_wdt_priv *priv;
+	struct watchdog_device *wdd;
+	struct resource *res;
+	int ret;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(struct asm9260_wdt_priv),
+			    GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->dev = &pdev->dev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	priv->iobase = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(priv->iobase))
+		return PTR_ERR(priv->iobase);
+
+	ret = asm9260_wdt_get_dt_clks(priv);
+	if (ret)
+		return ret;
+
+	priv->rst = devm_reset_control_get(&pdev->dev, "wdt_rst");
+	if (IS_ERR(priv->rst))
+		return PTR_ERR(priv->rst);
+
+	wdd = &priv->wdd;
+	wdd->info = &asm9260_wdt_ident;
+	wdd->ops = &asm9260_wdt_ops;
+	wdd->min_timeout = 1;
+	wdd->max_timeout = BM_WDTC_MAX(priv->wdt_freq);
+	wdd->parent = &pdev->dev;
+
+	watchdog_set_drvdata(wdd, priv);
+
+	/*
+	 * If 'timeout-sec' unspecified in devicetree, assume a 30 second
+	 * default, unless the max timeout is less than 30 seconds, then use
+	 * the max instead.
+	 */
+	wdd->timeout = ASM9260_WDT_DEFAULT_TIMEOUT;
+	watchdog_init_timeout(wdd, 0, &pdev->dev);
+
+	asm9260_wdt_get_dt_mode(priv);
+
+	if (priv->mode != HW_RESET)
+		priv->irq = platform_get_irq(pdev, 0);
+
+	if (priv->irq > 0) {
+		/*
+		 * Not all supported platforms specify an interrupt for the
+		 * watchdog, so let's make it optional.
+		 */
+		ret = devm_request_irq(&pdev->dev, priv->irq,
+				       asm9260_wdt_irq, IRQF_SHARED,
+				       pdev->name, priv);
+		if (ret < 0)
+			dev_warn(&pdev->dev, "failed to request IRQ\n");
+	}
+
+	ret = watchdog_register_device(wdd);
+	if (ret)
+		goto clk_off;
+
+	platform_set_drvdata(pdev, priv);
+
+	priv->restart_handler.notifier_call = asm9260_restart_handler;
+	priv->restart_handler.priority = 128;
+	ret = register_restart_handler(&priv->restart_handler);
+	if (ret)
+		dev_warn(&pdev->dev, "cannot register restart handler\n");
+
+	dev_info(&pdev->dev, "Watchdog enabled (to=%d, mode=%d)\n",
+		 wdd->timeout, priv->mode);
+	return 0;
+
+clk_off:
+	clk_disable_unprepare(priv->clk);
+	clk_disable_unprepare(priv->clk_ahb);
+	return ret;
+}
+
+static void asm9260_wdt_shutdown(struct platform_device *pdev)
+{
+	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
+
+	asm9260_wdt_disable(&priv->wdd);
+}
+
+static int __exit asm9260_wdt_remove(struct platform_device *pdev)
+{
+	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
+
+	asm9260_wdt_disable(&priv->wdd);
+
+	unregister_restart_handler(&priv->restart_handler);
+	clk_disable_unprepare(priv->clk);
+	clk_disable_unprepare(priv->clk_ahb);
+
+	return 0;
+}
+
+static const struct of_device_id asm9260_wdt_of_match[] = {
+	{ .compatible = "alphascale,asm9260-wdt"},
+	{},
+};
+MODULE_DEVICE_TABLE(of, asm9260_wdt_of_match);
+
+static struct platform_driver asm9260_wdt_driver = {
+	.driver = {
+		.name = "asm9260-wdt",
+		.owner = THIS_MODULE,
+		.of_match_table	= asm9260_wdt_of_match,
+	},
+	.probe = asm9260_wdt_probe,
+	.remove = asm9260_wdt_remove,
+	.shutdown = asm9260_wdt_shutdown,
+};
+module_platform_driver(asm9260_wdt_driver);
+
+MODULE_DESCRIPTION("asm9260 WatchDog Timer Driver");
+MODULE_AUTHOR("Oleksij Rempel <linux@rempel-privat.de>");
+MODULE_LICENSE("GPL");
-- 
2.5.0


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

* [PATCH v3 2/2] DT: watchdog: add Alphascale asm9260 watchdog binding documentation.
  2015-11-24 21:40                         ` Oleksij Rempel
@ 2015-11-24 21:40                             ` Oleksij Rempel
  -1 siblings, 0 replies; 43+ messages in thread
From: Oleksij Rempel @ 2015-11-24 21:40 UTC (permalink / raw)
  To: linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	linux-0h96xk9xTtrk1uMJSBkQmQ, wim-IQzOog9fTRqzQB+pC5nmwQ,
	devicetree-u79uwXL29TY76Z2rM5mHXA, robh-DgEjT+Ai2ygdnm+yROfE0A
  Cc: Oleksij Rempel

Signed-off-by: Oleksij Rempel <linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>
---
 .../bindings/watchdog/alphascale-asm9260.txt       | 35 ++++++++++++++++++++++
 1 file changed, 35 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt

diff --git a/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
new file mode 100644
index 0000000..75b265a
--- /dev/null
+++ b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
@@ -0,0 +1,35 @@
+Alphascale asm9260 Watchdog timer
+
+Required properties:
+
+- compatible : should be "alphascale,asm9260-wdt".
+- reg : Specifies base physical address and size of the registers.
+- clocks : the clocks feeding the watchdog timer. See clock-bindings.txt
+- clock-names : should be set to
+	"mod" - source for tick counter.
+	"ahb" - ahb gate.
+- resets : phandle pointing to the system reset controller with
+	line index for the watchdog.
+- reset-names : should be set to "wdt_rst".
+
+Optional properties:
+- timeout-sec : shall contain the default watchdog timeout in seconds,
+	if unset, the default timeout is 30 seconds.
+- alphascale,mode : three modes are supported
+	"hw" - hw reset (default).
+	"sw" - sw reset.
+	"debug" - no action is taken.
+
+Example:
+
+watchdog0: watchdog@80048000 {
+	compatible = "alphascale,asm9260-wdt";
+	reg = <0x80048000 0x10>;
+	clocks = <&acc CLKID_SYS_WDT>, <&acc CLKID_AHB_WDT>;
+	clock-names = "mod", "ahb";
+	interrupts = <55>;
+	resets = <&rst WDT_RESET>;
+	reset-names = "wdt_rst";
+	timeout-sec = <30>;
+	alphascale,mode = "hw";
+};
-- 
2.5.0

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH v3 2/2] DT: watchdog: add Alphascale asm9260 watchdog binding documentation.
@ 2015-11-24 21:40                             ` Oleksij Rempel
  0 siblings, 0 replies; 43+ messages in thread
From: Oleksij Rempel @ 2015-11-24 21:40 UTC (permalink / raw)
  To: linux-watchdog, linux, wim, devicetree, robh; +Cc: Oleksij Rempel

Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
---
 .../bindings/watchdog/alphascale-asm9260.txt       | 35 ++++++++++++++++++++++
 1 file changed, 35 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt

diff --git a/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
new file mode 100644
index 0000000..75b265a
--- /dev/null
+++ b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
@@ -0,0 +1,35 @@
+Alphascale asm9260 Watchdog timer
+
+Required properties:
+
+- compatible : should be "alphascale,asm9260-wdt".
+- reg : Specifies base physical address and size of the registers.
+- clocks : the clocks feeding the watchdog timer. See clock-bindings.txt
+- clock-names : should be set to
+	"mod" - source for tick counter.
+	"ahb" - ahb gate.
+- resets : phandle pointing to the system reset controller with
+	line index for the watchdog.
+- reset-names : should be set to "wdt_rst".
+
+Optional properties:
+- timeout-sec : shall contain the default watchdog timeout in seconds,
+	if unset, the default timeout is 30 seconds.
+- alphascale,mode : three modes are supported
+	"hw" - hw reset (default).
+	"sw" - sw reset.
+	"debug" - no action is taken.
+
+Example:
+
+watchdog0: watchdog@80048000 {
+	compatible = "alphascale,asm9260-wdt";
+	reg = <0x80048000 0x10>;
+	clocks = <&acc CLKID_SYS_WDT>, <&acc CLKID_AHB_WDT>;
+	clock-names = "mod", "ahb";
+	interrupts = <55>;
+	resets = <&rst WDT_RESET>;
+	reset-names = "wdt_rst";
+	timeout-sec = <30>;
+	alphascale,mode = "hw";
+};
-- 
2.5.0


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

* Re: [PATCH v3 1/2] watchdog: add Alphascale asm9260-wdt driver
  2015-11-24 21:40                             ` Oleksij Rempel
@ 2015-11-25  3:22                                 ` Guenter Roeck
  -1 siblings, 0 replies; 43+ messages in thread
From: Guenter Roeck @ 2015-11-25  3:22 UTC (permalink / raw)
  To: Oleksij Rempel, linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	wim-IQzOog9fTRqzQB+pC5nmwQ, devicetree-u79uwXL29TY76Z2rM5mHXA,
	robh-DgEjT+Ai2ygdnm+yROfE0A

On 11/24/2015 01:40 PM, Oleksij Rempel wrote:
> Add WD support for Alphascale asm9260 SoC. This driver
> provide support for different function modes:
> - HW mode to trigger SoC reset on timeout
> - SW mode do soft reset if needed
> - DEBUG mode
>
> Optional support for stopping watchdog. If reset binding are not provided
> this driver will work in nowayout mode.

I think this is no longer optional ?

>
> Signed-off-by: Oleksij Rempel <linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>
> ---
>   drivers/watchdog/Kconfig       |  10 ++
>   drivers/watchdog/Makefile      |   1 +
>   drivers/watchdog/asm9260_wdt.c | 400 +++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 411 insertions(+)
>   create mode 100644 drivers/watchdog/asm9260_wdt.c
>
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index c68edc1..9cd9b75 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -173,6 +173,16 @@ config ARM_SP805_WATCHDOG
>   	  ARM Primecell SP805 Watchdog timer. This will reboot your system when
>   	  the timeout is reached.
>
> +config ASM9260_WATCHDOG
> +	tristate "Alphascale ASM9260 watchdog"
> +	depends on MACH_ASM9260
> +	depends on OF
> +	select WATCHDOG_CORE
> +	select RESET_CONTROLLER
> +	help
> +	  Watchdog timer embedded into Alphascale asm9260 chips. This will reboot your
> +	  system when the timeout is reached.
> +
>   config AT91RM9200_WATCHDOG
>   	tristate "AT91RM9200 watchdog"
>   	depends on SOC_AT91RM9200 && MFD_SYSCON
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index 0c616e3..bd7b0cd 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -30,6 +30,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
>
>   # ARM Architecture
>   obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o
> +obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o
>   obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
>   obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
>   obj-$(CONFIG_CADENCE_WATCHDOG) += cadence_wdt.o
> diff --git a/drivers/watchdog/asm9260_wdt.c b/drivers/watchdog/asm9260_wdt.c
> new file mode 100644
> index 0000000..c8daeff
> --- /dev/null
> +++ b/drivers/watchdog/asm9260_wdt.c
> @@ -0,0 +1,400 @@
> +/*
> + * Watchdog driver for Alphascale ASM9260.
> + *
> + * Copyright (c) 2014 Oleksij Rempel <linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>
> + *
> + * Licensed under GPLv2 or later.
> + */
> +
> +#include <linux/bitops.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/reboot.h>
> +#include <linux/reset.h>
> +#include <linux/watchdog.h>
> +
> +#define CLOCK_FREQ	1000000
> +
> +/* Watchdog Mode register */
> +#define HW_WDMOD			0x00
> +/* Wake interrupt. Set by HW, can't be cleared. */
> +#define BM_MOD_WDINT			BIT(3)
> +/* This bit set if timeout reached. Cleared by SW. */
> +#define BM_MOD_WDTOF			BIT(2)
> +/* HW Reset on timeout */
> +#define BM_MOD_WDRESET			BIT(1)
> +/* WD enable */
> +#define BM_MOD_WDEN			BIT(0)
> +
> +/*
> + * Watchdog Timer Constant register
> + * Minimal value is 0xff, the meaning of this value
> + * depends on used clock: T = WDCLK * (0xff + 1) * 4
> + */
> +#define HW_WDTC				0x04
> +#define BM_WDTC_MAX(freq)		(0x7fffffff / (freq))
> +
> +/* Watchdog Feed register */
> +#define HW_WDFEED			0x08
> +
> +/* Watchdog Timer Value register */
> +#define HW_WDTV				0x0c
> +
> +#define ASM9260_WDT_DEFAULT_TIMEOUT	30
> +
> +enum asm9260_wdt_mode {
> +	HW_RESET,
> +	SW_RESET,
> +	DEBUG,
> +};
> +
> +struct asm9260_wdt_priv {
> +	struct device		*dev;
> +	struct watchdog_device	wdd;
> +	struct clk		*clk;
> +	struct clk		*clk_ahb;
> +	struct reset_control	*rst;
> +	struct notifier_block	restart_handler;
> +
> +	void __iomem		*iobase;
> +	int			irq;
> +	unsigned long		wdt_freq;
> +	enum asm9260_wdt_mode	mode;
> +};
> +
> +static int asm9260_wdt_feed(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +
> +	iowrite32(0xaa, priv->iobase + HW_WDFEED);
> +	iowrite32(0x55, priv->iobase + HW_WDFEED);
> +
> +	return 0;
> +}
> +
> +static unsigned int asm9260_wdt_gettimeleft(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +	u32 counter;
> +
> +	counter = ioread32(priv->iobase + HW_WDTV);
> +
> +	return DIV_ROUND_CLOSEST(counter, priv->wdt_freq);
> +}
> +
> +static int asm9260_wdt_updatetimeout(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +	u32 counter;
> +
> +	counter = wdd->timeout * priv->wdt_freq;
> +
> +	iowrite32(counter, priv->iobase + HW_WDTC);
> +
> +	return 0;
> +}
> +
> +static int asm9260_wdt_enable(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +	u32 mode = 0;
> +
> +	if (priv->mode == HW_RESET)
> +		mode = BM_MOD_WDRESET;
> +
> +	iowrite32(BM_MOD_WDEN | mode, priv->iobase + HW_WDMOD);
> +
> +	asm9260_wdt_updatetimeout(wdd);
> +
> +	asm9260_wdt_feed(wdd);
> +
> +	return 0;
> +}
> +
> +static int asm9260_wdt_disable(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +
> +	/* The only way to disable WD is to reset it. */
> +	reset_control_assert(priv->rst);
> +	reset_control_deassert(priv->rst);
> +
> +	return 0;
> +}
> +
> +static int asm9260_wdt_settimeout(struct watchdog_device *wdd, unsigned int to)
> +{
> +	wdd->timeout = to;
> +	asm9260_wdt_updatetimeout(wdd);
> +
> +	return 0;
> +}
> +
> +static void asm9260_wdt_sys_reset(struct asm9260_wdt_priv *priv)
> +{
> +	/* init WD if it was not started */
> +
> +	iowrite32(BM_MOD_WDEN | BM_MOD_WDRESET, priv->iobase + HW_WDMOD);
> +
> +	iowrite32(0xff, priv->iobase + HW_WDTC);
> +	/* first pass correct sequence */
> +	asm9260_wdt_feed(&priv->wdd);
> +	/*
> +	 * Then write wrong pattern to the feed to trigger reset
> +	 * ASAP.
> +	 */
> +	iowrite32(0xff, priv->iobase + HW_WDFEED);
> +
> +	mdelay(1000);
> +}
> +
> +static irqreturn_t asm9260_wdt_irq(int irq, void *devid)
> +{
> +	struct asm9260_wdt_priv *priv = devid;
> +	u32 stat;
> +
> +	stat = ioread32(priv->iobase + HW_WDMOD);
> +	if (!(stat & BM_MOD_WDINT))
> +		return IRQ_NONE;
> +
> +	if (priv->mode == DEBUG)
> +		dev_info(priv->dev, "Watchdog Timeout. Do nothing.\n");
> +	else {
> +		dev_info(priv->dev, "Watchdog Timeout. Doing SW Reset.\n");
> +		asm9260_wdt_sys_reset(priv);
> +	}
> +

coding style asks for { } in both branches of the if statement.

> +	return IRQ_HANDLED;
> +}
> +
> +static int asm9260_restart_handler(struct notifier_block *this,
> +				   unsigned long mode, void *cmd)
> +{
> +	struct asm9260_wdt_priv *priv =
> +		container_of(this, struct asm9260_wdt_priv, restart_handler);
> +
> +	asm9260_wdt_sys_reset(priv);
> +
> +	return NOTIFY_DONE;
> +}
> +
> +static const struct watchdog_info asm9260_wdt_ident = {
> +	.options          =     WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING
> +				| WDIOF_MAGICCLOSE,
> +	.identity         =	"Alphascale asm9260 Watchdog",
> +};
> +
> +static struct watchdog_ops asm9260_wdt_ops = {
> +	.owner		= THIS_MODULE,
> +	.start		= asm9260_wdt_enable,
> +	.stop		= asm9260_wdt_disable,
> +	.get_timeleft	= asm9260_wdt_gettimeleft,
> +	.ping		= asm9260_wdt_feed,
> +	.set_timeout	= asm9260_wdt_settimeout,
> +};
> +
> +static int __init asm9260_wdt_get_dt_clks(struct asm9260_wdt_priv *priv)
> +{
> +	int err;
> +	unsigned long clk;
> +
> +	priv->clk = devm_clk_get(priv->dev, "mod");
> +	if (IS_ERR(priv->clk)) {
> +		dev_err(priv->dev, "Failed to get \"mod\" clk\n");
> +		return PTR_ERR(priv->clk);
> +	}
> +
> +	/* configure AHB clock */
> +	priv->clk_ahb = devm_clk_get(priv->dev, "ahb");
> +	if (IS_ERR(priv->clk_ahb)) {
> +		dev_err(priv->dev, "Failed to get \"ahb\" clk\n");
> +		return PTR_ERR(priv->clk_ahb);
> +	}
> +
> +	err = clk_prepare_enable(priv->clk_ahb);
> +	if (err) {
> +		dev_err(priv->dev, "Failed to enable ahb_clk!\n");
> +		return err;
> +	}
> +
> +	err = clk_set_rate(priv->clk, CLOCK_FREQ);
> +	if (err) {
> +		clk_disable_unprepare(priv->clk_ahb);
> +		dev_err(priv->dev, "Failed to set rate!\n");
> +		return err;
> +	}
> +
> +	err = clk_prepare_enable(priv->clk);
> +	if (err) {
> +		clk_disable_unprepare(priv->clk_ahb);
> +		dev_err(priv->dev, "Failed to enable clk!\n");
> +		return err;
> +	}
> +
> +	/* wdt has internal divider */
> +	clk = clk_get_rate(priv->clk);
> +	if (!clk) {
> +		clk_disable_unprepare(priv->clk);
> +		clk_disable_unprepare(priv->clk_ahb);
> +		dev_err(priv->dev, "Failed, clk is 0!\n");
> +		return -EINVAL;
> +	}
> +
> +	priv->wdt_freq = clk / 2;
> +
> +	return 0;
> +}
> +
> +static void __init asm9260_wdt_get_dt_mode(struct asm9260_wdt_priv *priv)
> +{
> +	const char *tmp;
> +	int ret;
> +
> +	/* default mode */
> +	priv->mode = HW_RESET;
> +
> +	ret = of_property_read_string(priv->dev->of_node,
> +				      "alphascale,mode", &tmp);
> +	if (ret < 0)
> +		return;
> +
> +	if (!strcmp(tmp, "hw"))
> +		priv->mode = HW_RESET;
> +	else if (!strcmp(tmp, "sw"))
> +		priv->mode = SW_RESET;
> +	else if (!strcmp(tmp, "debug"))
> +		priv->mode = DEBUG;
> +	else
> +		dev_warn(priv->dev, "unknown reset-type: %s. Using default \"hw\" mode.",
> +			 tmp);
> +}
> +
> +static int __init asm9260_wdt_probe(struct platform_device *pdev)
> +{
> +	struct asm9260_wdt_priv *priv;
> +	struct watchdog_device *wdd;
> +	struct resource *res;
> +	int ret;
> +
> +	priv = devm_kzalloc(&pdev->dev, sizeof(struct asm9260_wdt_priv),
> +			    GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->dev = &pdev->dev;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	priv->iobase = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(priv->iobase))
> +		return PTR_ERR(priv->iobase);
> +
> +	ret = asm9260_wdt_get_dt_clks(priv);
> +	if (ret)
> +		return ret;
> +
> +	priv->rst = devm_reset_control_get(&pdev->dev, "wdt_rst");
> +	if (IS_ERR(priv->rst))
> +		return PTR_ERR(priv->rst);
> +
> +	wdd = &priv->wdd;
> +	wdd->info = &asm9260_wdt_ident;
> +	wdd->ops = &asm9260_wdt_ops;
> +	wdd->min_timeout = 1;
> +	wdd->max_timeout = BM_WDTC_MAX(priv->wdt_freq);
> +	wdd->parent = &pdev->dev;
> +
> +	watchdog_set_drvdata(wdd, priv);
> +
> +	/*
> +	 * If 'timeout-sec' unspecified in devicetree, assume a 30 second
> +	 * default, unless the max timeout is less than 30 seconds, then use
> +	 * the max instead.
> +	 */
> +	wdd->timeout = ASM9260_WDT_DEFAULT_TIMEOUT;
> +	watchdog_init_timeout(wdd, 0, &pdev->dev);
> +
> +	asm9260_wdt_get_dt_mode(priv);
> +
> +	if (priv->mode != HW_RESET)
> +		priv->irq = platform_get_irq(pdev, 0);
> +
> +	if (priv->irq > 0) {
> +		/*
> +		 * Not all supported platforms specify an interrupt for the
> +		 * watchdog, so let's make it optional.
> +		 */
> +		ret = devm_request_irq(&pdev->dev, priv->irq,
> +				       asm9260_wdt_irq, IRQF_SHARED,
> +				       pdev->name, priv);
> +		if (ret < 0)
> +			dev_warn(&pdev->dev, "failed to request IRQ\n");
> +	}
> +
> +	ret = watchdog_register_device(wdd);
> +	if (ret)
> +		goto clk_off;
> +
> +	platform_set_drvdata(pdev, priv);
> +
> +	priv->restart_handler.notifier_call = asm9260_restart_handler;
> +	priv->restart_handler.priority = 128;
> +	ret = register_restart_handler(&priv->restart_handler);
> +	if (ret)
> +		dev_warn(&pdev->dev, "cannot register restart handler\n");
> +
> +	dev_info(&pdev->dev, "Watchdog enabled (to=%d, mode=%d)\n",

Wonder who will know what "to" means. How about "timeout" ?

Also, would it possibly make more sense to spell out the mode ?
Users won't know what 0/1/2 means.

> +		 wdd->timeout, priv->mode);
> +	return 0;
> +
> +clk_off:
> +	clk_disable_unprepare(priv->clk);
> +	clk_disable_unprepare(priv->clk_ahb);

The interrupt is still enabled here. Is this safe ?

> +	return ret;
> +}
> +
> +static void asm9260_wdt_shutdown(struct platform_device *pdev)
> +{
> +	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
> +
> +	asm9260_wdt_disable(&priv->wdd);
> +}
> +
> +static int __exit asm9260_wdt_remove(struct platform_device *pdev)
> +{
> +	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
> +
> +	asm9260_wdt_disable(&priv->wdd);
> +
> +	unregister_restart_handler(&priv->restart_handler);

	watchdog_unregister_device() ?

> +	clk_disable_unprepare(priv->clk);
> +	clk_disable_unprepare(priv->clk_ahb);

The interrupt is still enabled here, and the handler may be called
since it is a shared interrupt. Is this safe ?

> +
> +	return 0;
> +}
> +
> +static const struct of_device_id asm9260_wdt_of_match[] = {
> +	{ .compatible = "alphascale,asm9260-wdt"},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, asm9260_wdt_of_match);
> +
> +static struct platform_driver asm9260_wdt_driver = {
> +	.driver = {
> +		.name = "asm9260-wdt",
> +		.owner = THIS_MODULE,
> +		.of_match_table	= asm9260_wdt_of_match,
> +	},
> +	.probe = asm9260_wdt_probe,
> +	.remove = asm9260_wdt_remove,
> +	.shutdown = asm9260_wdt_shutdown,
> +};
> +module_platform_driver(asm9260_wdt_driver);
> +
> +MODULE_DESCRIPTION("asm9260 WatchDog Timer Driver");
> +MODULE_AUTHOR("Oleksij Rempel <linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>");
> +MODULE_LICENSE("GPL");
>

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v3 1/2] watchdog: add Alphascale asm9260-wdt driver
@ 2015-11-25  3:22                                 ` Guenter Roeck
  0 siblings, 0 replies; 43+ messages in thread
From: Guenter Roeck @ 2015-11-25  3:22 UTC (permalink / raw)
  To: Oleksij Rempel, linux-watchdog, wim, devicetree, robh

On 11/24/2015 01:40 PM, Oleksij Rempel wrote:
> Add WD support for Alphascale asm9260 SoC. This driver
> provide support for different function modes:
> - HW mode to trigger SoC reset on timeout
> - SW mode do soft reset if needed
> - DEBUG mode
>
> Optional support for stopping watchdog. If reset binding are not provided
> this driver will work in nowayout mode.

I think this is no longer optional ?

>
> Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
> ---
>   drivers/watchdog/Kconfig       |  10 ++
>   drivers/watchdog/Makefile      |   1 +
>   drivers/watchdog/asm9260_wdt.c | 400 +++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 411 insertions(+)
>   create mode 100644 drivers/watchdog/asm9260_wdt.c
>
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index c68edc1..9cd9b75 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -173,6 +173,16 @@ config ARM_SP805_WATCHDOG
>   	  ARM Primecell SP805 Watchdog timer. This will reboot your system when
>   	  the timeout is reached.
>
> +config ASM9260_WATCHDOG
> +	tristate "Alphascale ASM9260 watchdog"
> +	depends on MACH_ASM9260
> +	depends on OF
> +	select WATCHDOG_CORE
> +	select RESET_CONTROLLER
> +	help
> +	  Watchdog timer embedded into Alphascale asm9260 chips. This will reboot your
> +	  system when the timeout is reached.
> +
>   config AT91RM9200_WATCHDOG
>   	tristate "AT91RM9200 watchdog"
>   	depends on SOC_AT91RM9200 && MFD_SYSCON
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index 0c616e3..bd7b0cd 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -30,6 +30,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
>
>   # ARM Architecture
>   obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o
> +obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o
>   obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
>   obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
>   obj-$(CONFIG_CADENCE_WATCHDOG) += cadence_wdt.o
> diff --git a/drivers/watchdog/asm9260_wdt.c b/drivers/watchdog/asm9260_wdt.c
> new file mode 100644
> index 0000000..c8daeff
> --- /dev/null
> +++ b/drivers/watchdog/asm9260_wdt.c
> @@ -0,0 +1,400 @@
> +/*
> + * Watchdog driver for Alphascale ASM9260.
> + *
> + * Copyright (c) 2014 Oleksij Rempel <linux@rempel-privat.de>
> + *
> + * Licensed under GPLv2 or later.
> + */
> +
> +#include <linux/bitops.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/reboot.h>
> +#include <linux/reset.h>
> +#include <linux/watchdog.h>
> +
> +#define CLOCK_FREQ	1000000
> +
> +/* Watchdog Mode register */
> +#define HW_WDMOD			0x00
> +/* Wake interrupt. Set by HW, can't be cleared. */
> +#define BM_MOD_WDINT			BIT(3)
> +/* This bit set if timeout reached. Cleared by SW. */
> +#define BM_MOD_WDTOF			BIT(2)
> +/* HW Reset on timeout */
> +#define BM_MOD_WDRESET			BIT(1)
> +/* WD enable */
> +#define BM_MOD_WDEN			BIT(0)
> +
> +/*
> + * Watchdog Timer Constant register
> + * Minimal value is 0xff, the meaning of this value
> + * depends on used clock: T = WDCLK * (0xff + 1) * 4
> + */
> +#define HW_WDTC				0x04
> +#define BM_WDTC_MAX(freq)		(0x7fffffff / (freq))
> +
> +/* Watchdog Feed register */
> +#define HW_WDFEED			0x08
> +
> +/* Watchdog Timer Value register */
> +#define HW_WDTV				0x0c
> +
> +#define ASM9260_WDT_DEFAULT_TIMEOUT	30
> +
> +enum asm9260_wdt_mode {
> +	HW_RESET,
> +	SW_RESET,
> +	DEBUG,
> +};
> +
> +struct asm9260_wdt_priv {
> +	struct device		*dev;
> +	struct watchdog_device	wdd;
> +	struct clk		*clk;
> +	struct clk		*clk_ahb;
> +	struct reset_control	*rst;
> +	struct notifier_block	restart_handler;
> +
> +	void __iomem		*iobase;
> +	int			irq;
> +	unsigned long		wdt_freq;
> +	enum asm9260_wdt_mode	mode;
> +};
> +
> +static int asm9260_wdt_feed(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +
> +	iowrite32(0xaa, priv->iobase + HW_WDFEED);
> +	iowrite32(0x55, priv->iobase + HW_WDFEED);
> +
> +	return 0;
> +}
> +
> +static unsigned int asm9260_wdt_gettimeleft(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +	u32 counter;
> +
> +	counter = ioread32(priv->iobase + HW_WDTV);
> +
> +	return DIV_ROUND_CLOSEST(counter, priv->wdt_freq);
> +}
> +
> +static int asm9260_wdt_updatetimeout(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +	u32 counter;
> +
> +	counter = wdd->timeout * priv->wdt_freq;
> +
> +	iowrite32(counter, priv->iobase + HW_WDTC);
> +
> +	return 0;
> +}
> +
> +static int asm9260_wdt_enable(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +	u32 mode = 0;
> +
> +	if (priv->mode == HW_RESET)
> +		mode = BM_MOD_WDRESET;
> +
> +	iowrite32(BM_MOD_WDEN | mode, priv->iobase + HW_WDMOD);
> +
> +	asm9260_wdt_updatetimeout(wdd);
> +
> +	asm9260_wdt_feed(wdd);
> +
> +	return 0;
> +}
> +
> +static int asm9260_wdt_disable(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +
> +	/* The only way to disable WD is to reset it. */
> +	reset_control_assert(priv->rst);
> +	reset_control_deassert(priv->rst);
> +
> +	return 0;
> +}
> +
> +static int asm9260_wdt_settimeout(struct watchdog_device *wdd, unsigned int to)
> +{
> +	wdd->timeout = to;
> +	asm9260_wdt_updatetimeout(wdd);
> +
> +	return 0;
> +}
> +
> +static void asm9260_wdt_sys_reset(struct asm9260_wdt_priv *priv)
> +{
> +	/* init WD if it was not started */
> +
> +	iowrite32(BM_MOD_WDEN | BM_MOD_WDRESET, priv->iobase + HW_WDMOD);
> +
> +	iowrite32(0xff, priv->iobase + HW_WDTC);
> +	/* first pass correct sequence */
> +	asm9260_wdt_feed(&priv->wdd);
> +	/*
> +	 * Then write wrong pattern to the feed to trigger reset
> +	 * ASAP.
> +	 */
> +	iowrite32(0xff, priv->iobase + HW_WDFEED);
> +
> +	mdelay(1000);
> +}
> +
> +static irqreturn_t asm9260_wdt_irq(int irq, void *devid)
> +{
> +	struct asm9260_wdt_priv *priv = devid;
> +	u32 stat;
> +
> +	stat = ioread32(priv->iobase + HW_WDMOD);
> +	if (!(stat & BM_MOD_WDINT))
> +		return IRQ_NONE;
> +
> +	if (priv->mode == DEBUG)
> +		dev_info(priv->dev, "Watchdog Timeout. Do nothing.\n");
> +	else {
> +		dev_info(priv->dev, "Watchdog Timeout. Doing SW Reset.\n");
> +		asm9260_wdt_sys_reset(priv);
> +	}
> +

coding style asks for { } in both branches of the if statement.

> +	return IRQ_HANDLED;
> +}
> +
> +static int asm9260_restart_handler(struct notifier_block *this,
> +				   unsigned long mode, void *cmd)
> +{
> +	struct asm9260_wdt_priv *priv =
> +		container_of(this, struct asm9260_wdt_priv, restart_handler);
> +
> +	asm9260_wdt_sys_reset(priv);
> +
> +	return NOTIFY_DONE;
> +}
> +
> +static const struct watchdog_info asm9260_wdt_ident = {
> +	.options          =     WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING
> +				| WDIOF_MAGICCLOSE,
> +	.identity         =	"Alphascale asm9260 Watchdog",
> +};
> +
> +static struct watchdog_ops asm9260_wdt_ops = {
> +	.owner		= THIS_MODULE,
> +	.start		= asm9260_wdt_enable,
> +	.stop		= asm9260_wdt_disable,
> +	.get_timeleft	= asm9260_wdt_gettimeleft,
> +	.ping		= asm9260_wdt_feed,
> +	.set_timeout	= asm9260_wdt_settimeout,
> +};
> +
> +static int __init asm9260_wdt_get_dt_clks(struct asm9260_wdt_priv *priv)
> +{
> +	int err;
> +	unsigned long clk;
> +
> +	priv->clk = devm_clk_get(priv->dev, "mod");
> +	if (IS_ERR(priv->clk)) {
> +		dev_err(priv->dev, "Failed to get \"mod\" clk\n");
> +		return PTR_ERR(priv->clk);
> +	}
> +
> +	/* configure AHB clock */
> +	priv->clk_ahb = devm_clk_get(priv->dev, "ahb");
> +	if (IS_ERR(priv->clk_ahb)) {
> +		dev_err(priv->dev, "Failed to get \"ahb\" clk\n");
> +		return PTR_ERR(priv->clk_ahb);
> +	}
> +
> +	err = clk_prepare_enable(priv->clk_ahb);
> +	if (err) {
> +		dev_err(priv->dev, "Failed to enable ahb_clk!\n");
> +		return err;
> +	}
> +
> +	err = clk_set_rate(priv->clk, CLOCK_FREQ);
> +	if (err) {
> +		clk_disable_unprepare(priv->clk_ahb);
> +		dev_err(priv->dev, "Failed to set rate!\n");
> +		return err;
> +	}
> +
> +	err = clk_prepare_enable(priv->clk);
> +	if (err) {
> +		clk_disable_unprepare(priv->clk_ahb);
> +		dev_err(priv->dev, "Failed to enable clk!\n");
> +		return err;
> +	}
> +
> +	/* wdt has internal divider */
> +	clk = clk_get_rate(priv->clk);
> +	if (!clk) {
> +		clk_disable_unprepare(priv->clk);
> +		clk_disable_unprepare(priv->clk_ahb);
> +		dev_err(priv->dev, "Failed, clk is 0!\n");
> +		return -EINVAL;
> +	}
> +
> +	priv->wdt_freq = clk / 2;
> +
> +	return 0;
> +}
> +
> +static void __init asm9260_wdt_get_dt_mode(struct asm9260_wdt_priv *priv)
> +{
> +	const char *tmp;
> +	int ret;
> +
> +	/* default mode */
> +	priv->mode = HW_RESET;
> +
> +	ret = of_property_read_string(priv->dev->of_node,
> +				      "alphascale,mode", &tmp);
> +	if (ret < 0)
> +		return;
> +
> +	if (!strcmp(tmp, "hw"))
> +		priv->mode = HW_RESET;
> +	else if (!strcmp(tmp, "sw"))
> +		priv->mode = SW_RESET;
> +	else if (!strcmp(tmp, "debug"))
> +		priv->mode = DEBUG;
> +	else
> +		dev_warn(priv->dev, "unknown reset-type: %s. Using default \"hw\" mode.",
> +			 tmp);
> +}
> +
> +static int __init asm9260_wdt_probe(struct platform_device *pdev)
> +{
> +	struct asm9260_wdt_priv *priv;
> +	struct watchdog_device *wdd;
> +	struct resource *res;
> +	int ret;
> +
> +	priv = devm_kzalloc(&pdev->dev, sizeof(struct asm9260_wdt_priv),
> +			    GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->dev = &pdev->dev;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	priv->iobase = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(priv->iobase))
> +		return PTR_ERR(priv->iobase);
> +
> +	ret = asm9260_wdt_get_dt_clks(priv);
> +	if (ret)
> +		return ret;
> +
> +	priv->rst = devm_reset_control_get(&pdev->dev, "wdt_rst");
> +	if (IS_ERR(priv->rst))
> +		return PTR_ERR(priv->rst);
> +
> +	wdd = &priv->wdd;
> +	wdd->info = &asm9260_wdt_ident;
> +	wdd->ops = &asm9260_wdt_ops;
> +	wdd->min_timeout = 1;
> +	wdd->max_timeout = BM_WDTC_MAX(priv->wdt_freq);
> +	wdd->parent = &pdev->dev;
> +
> +	watchdog_set_drvdata(wdd, priv);
> +
> +	/*
> +	 * If 'timeout-sec' unspecified in devicetree, assume a 30 second
> +	 * default, unless the max timeout is less than 30 seconds, then use
> +	 * the max instead.
> +	 */
> +	wdd->timeout = ASM9260_WDT_DEFAULT_TIMEOUT;
> +	watchdog_init_timeout(wdd, 0, &pdev->dev);
> +
> +	asm9260_wdt_get_dt_mode(priv);
> +
> +	if (priv->mode != HW_RESET)
> +		priv->irq = platform_get_irq(pdev, 0);
> +
> +	if (priv->irq > 0) {
> +		/*
> +		 * Not all supported platforms specify an interrupt for the
> +		 * watchdog, so let's make it optional.
> +		 */
> +		ret = devm_request_irq(&pdev->dev, priv->irq,
> +				       asm9260_wdt_irq, IRQF_SHARED,
> +				       pdev->name, priv);
> +		if (ret < 0)
> +			dev_warn(&pdev->dev, "failed to request IRQ\n");
> +	}
> +
> +	ret = watchdog_register_device(wdd);
> +	if (ret)
> +		goto clk_off;
> +
> +	platform_set_drvdata(pdev, priv);
> +
> +	priv->restart_handler.notifier_call = asm9260_restart_handler;
> +	priv->restart_handler.priority = 128;
> +	ret = register_restart_handler(&priv->restart_handler);
> +	if (ret)
> +		dev_warn(&pdev->dev, "cannot register restart handler\n");
> +
> +	dev_info(&pdev->dev, "Watchdog enabled (to=%d, mode=%d)\n",

Wonder who will know what "to" means. How about "timeout" ?

Also, would it possibly make more sense to spell out the mode ?
Users won't know what 0/1/2 means.

> +		 wdd->timeout, priv->mode);
> +	return 0;
> +
> +clk_off:
> +	clk_disable_unprepare(priv->clk);
> +	clk_disable_unprepare(priv->clk_ahb);

The interrupt is still enabled here. Is this safe ?

> +	return ret;
> +}
> +
> +static void asm9260_wdt_shutdown(struct platform_device *pdev)
> +{
> +	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
> +
> +	asm9260_wdt_disable(&priv->wdd);
> +}
> +
> +static int __exit asm9260_wdt_remove(struct platform_device *pdev)
> +{
> +	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
> +
> +	asm9260_wdt_disable(&priv->wdd);
> +
> +	unregister_restart_handler(&priv->restart_handler);

	watchdog_unregister_device() ?

> +	clk_disable_unprepare(priv->clk);
> +	clk_disable_unprepare(priv->clk_ahb);

The interrupt is still enabled here, and the handler may be called
since it is a shared interrupt. Is this safe ?

> +
> +	return 0;
> +}
> +
> +static const struct of_device_id asm9260_wdt_of_match[] = {
> +	{ .compatible = "alphascale,asm9260-wdt"},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, asm9260_wdt_of_match);
> +
> +static struct platform_driver asm9260_wdt_driver = {
> +	.driver = {
> +		.name = "asm9260-wdt",
> +		.owner = THIS_MODULE,
> +		.of_match_table	= asm9260_wdt_of_match,
> +	},
> +	.probe = asm9260_wdt_probe,
> +	.remove = asm9260_wdt_remove,
> +	.shutdown = asm9260_wdt_shutdown,
> +};
> +module_platform_driver(asm9260_wdt_driver);
> +
> +MODULE_DESCRIPTION("asm9260 WatchDog Timer Driver");
> +MODULE_AUTHOR("Oleksij Rempel <linux@rempel-privat.de>");
> +MODULE_LICENSE("GPL");
>


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

* [PATCH v4 0/2] add Alphascale asm9260-wdt driver
  2015-11-23  7:26                     ` Guenter Roeck
@ 2015-11-25 19:33                         ` Oleksij Rempel
  -1 siblings, 0 replies; 43+ messages in thread
From: Oleksij Rempel @ 2015-11-25 19:33 UTC (permalink / raw)
  To: linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	linux-0h96xk9xTtrk1uMJSBkQmQ, wim-IQzOog9fTRqzQB+pC5nmwQ,
	devicetree-u79uwXL29TY76Z2rM5mHXA, robh-DgEjT+Ai2ygdnm+yROfE0A
  Cc: Oleksij Rempel

v2:
- fix DT types
- remove obsolet comments
- add clk error handling
- reduce log noise
- allow to return an error in asm9260_wdt_get_dt_mode

v3:
- split patch to two parts, binding documentation and driver
- spelling fixes
- make reset a required property. It will make the code easier
- reduce timeout for asm9260_wdt_sys_reset
- rework error handling in asm9260_wdt_get_dt_clks
- asm9260_wdt_get_dt_mode - convert return type from int to void
- don't touch watchdog_set_nowayout
- don't do dev_err() if driver is still working, use dev_warn instead.
- asm9260_wdt_remove - use asm9260_wdt_disable() instead of asm9260_wdt_shutdown()

v4:
- update commit message
- remove IRQF_SHARED flag. This SOC don't need it.
- make "Watchdog enabled" more user freandly
- add watchdog_unregister_device to asm9260_wdt_remove

Oleksij Rempel (2):
  watchdog: add Alphascale asm9260-wdt driver
  DT: watchdog: add Alphascale asm9260 watchdog binding documentation.

 .../bindings/watchdog/alphascale-asm9260.txt       |  35 ++
 drivers/watchdog/Kconfig                           |  10 +
 drivers/watchdog/Makefile                          |   1 +
 drivers/watchdog/asm9260_wdt.c                     | 403 +++++++++++++++++++++
 4 files changed, 449 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
 create mode 100644 drivers/watchdog/asm9260_wdt.c

-- 
2.5.0

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH v4 0/2] add Alphascale asm9260-wdt driver
@ 2015-11-25 19:33                         ` Oleksij Rempel
  0 siblings, 0 replies; 43+ messages in thread
From: Oleksij Rempel @ 2015-11-25 19:33 UTC (permalink / raw)
  To: linux-watchdog, linux, wim, devicetree, robh; +Cc: Oleksij Rempel

v2:
- fix DT types
- remove obsolet comments
- add clk error handling
- reduce log noise
- allow to return an error in asm9260_wdt_get_dt_mode

v3:
- split patch to two parts, binding documentation and driver
- spelling fixes
- make reset a required property. It will make the code easier
- reduce timeout for asm9260_wdt_sys_reset
- rework error handling in asm9260_wdt_get_dt_clks
- asm9260_wdt_get_dt_mode - convert return type from int to void
- don't touch watchdog_set_nowayout
- don't do dev_err() if driver is still working, use dev_warn instead.
- asm9260_wdt_remove - use asm9260_wdt_disable() instead of asm9260_wdt_shutdown()

v4:
- update commit message
- remove IRQF_SHARED flag. This SOC don't need it.
- make "Watchdog enabled" more user freandly
- add watchdog_unregister_device to asm9260_wdt_remove

Oleksij Rempel (2):
  watchdog: add Alphascale asm9260-wdt driver
  DT: watchdog: add Alphascale asm9260 watchdog binding documentation.

 .../bindings/watchdog/alphascale-asm9260.txt       |  35 ++
 drivers/watchdog/Kconfig                           |  10 +
 drivers/watchdog/Makefile                          |   1 +
 drivers/watchdog/asm9260_wdt.c                     | 403 +++++++++++++++++++++
 4 files changed, 449 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
 create mode 100644 drivers/watchdog/asm9260_wdt.c

-- 
2.5.0

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

* [PATCH v4 1/2] watchdog: add Alphascale asm9260-wdt driver
  2015-11-25 19:33                         ` Oleksij Rempel
@ 2015-11-25 19:33                             ` Oleksij Rempel
  -1 siblings, 0 replies; 43+ messages in thread
From: Oleksij Rempel @ 2015-11-25 19:33 UTC (permalink / raw)
  To: linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	linux-0h96xk9xTtrk1uMJSBkQmQ, wim-IQzOog9fTRqzQB+pC5nmwQ,
	devicetree-u79uwXL29TY76Z2rM5mHXA, robh-DgEjT+Ai2ygdnm+yROfE0A
  Cc: Oleksij Rempel

Add WD support for Alphascale asm9260 SoC. This driver
provide support for different function modes:
- HW mode to trigger SoC reset on timeout
- SW mode do soft reset if needed
- DEBUG mode

Signed-off-by: Oleksij Rempel <linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>
---
 drivers/watchdog/Kconfig       |  10 +
 drivers/watchdog/Makefile      |   1 +
 drivers/watchdog/asm9260_wdt.c | 403 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 414 insertions(+)
 create mode 100644 drivers/watchdog/asm9260_wdt.c

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index c68edc1..9cd9b75 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -173,6 +173,16 @@ config ARM_SP805_WATCHDOG
 	  ARM Primecell SP805 Watchdog timer. This will reboot your system when
 	  the timeout is reached.
 
+config ASM9260_WATCHDOG
+	tristate "Alphascale ASM9260 watchdog"
+	depends on MACH_ASM9260
+	depends on OF
+	select WATCHDOG_CORE
+	select RESET_CONTROLLER
+	help
+	  Watchdog timer embedded into Alphascale asm9260 chips. This will reboot your
+	  system when the timeout is reached.
+
 config AT91RM9200_WATCHDOG
 	tristate "AT91RM9200 watchdog"
 	depends on SOC_AT91RM9200 && MFD_SYSCON
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 0c616e3..bd7b0cd 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
 
 # ARM Architecture
 obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o
+obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o
 obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
 obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
 obj-$(CONFIG_CADENCE_WATCHDOG) += cadence_wdt.o
diff --git a/drivers/watchdog/asm9260_wdt.c b/drivers/watchdog/asm9260_wdt.c
new file mode 100644
index 0000000..1c22ff4
--- /dev/null
+++ b/drivers/watchdog/asm9260_wdt.c
@@ -0,0 +1,403 @@
+/*
+ * Watchdog driver for Alphascale ASM9260.
+ *
+ * Copyright (c) 2014 Oleksij Rempel <linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/reset.h>
+#include <linux/watchdog.h>
+
+#define CLOCK_FREQ	1000000
+
+/* Watchdog Mode register */
+#define HW_WDMOD			0x00
+/* Wake interrupt. Set by HW, can't be cleared. */
+#define BM_MOD_WDINT			BIT(3)
+/* This bit set if timeout reached. Cleared by SW. */
+#define BM_MOD_WDTOF			BIT(2)
+/* HW Reset on timeout */
+#define BM_MOD_WDRESET			BIT(1)
+/* WD enable */
+#define BM_MOD_WDEN			BIT(0)
+
+/*
+ * Watchdog Timer Constant register
+ * Minimal value is 0xff, the meaning of this value
+ * depends on used clock: T = WDCLK * (0xff + 1) * 4
+ */
+#define HW_WDTC				0x04
+#define BM_WDTC_MAX(freq)		(0x7fffffff / (freq))
+
+/* Watchdog Feed register */
+#define HW_WDFEED			0x08
+
+/* Watchdog Timer Value register */
+#define HW_WDTV				0x0c
+
+#define ASM9260_WDT_DEFAULT_TIMEOUT	30
+
+enum asm9260_wdt_mode {
+	HW_RESET,
+	SW_RESET,
+	DEBUG,
+};
+
+struct asm9260_wdt_priv {
+	struct device		*dev;
+	struct watchdog_device	wdd;
+	struct clk		*clk;
+	struct clk		*clk_ahb;
+	struct reset_control	*rst;
+	struct notifier_block	restart_handler;
+
+	void __iomem		*iobase;
+	int			irq;
+	unsigned long		wdt_freq;
+	enum asm9260_wdt_mode	mode;
+};
+
+static int asm9260_wdt_feed(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+
+	iowrite32(0xaa, priv->iobase + HW_WDFEED);
+	iowrite32(0x55, priv->iobase + HW_WDFEED);
+
+	return 0;
+}
+
+static unsigned int asm9260_wdt_gettimeleft(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+	u32 counter;
+
+	counter = ioread32(priv->iobase + HW_WDTV);
+
+	return DIV_ROUND_CLOSEST(counter, priv->wdt_freq);
+}
+
+static int asm9260_wdt_updatetimeout(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+	u32 counter;
+
+	counter = wdd->timeout * priv->wdt_freq;
+
+	iowrite32(counter, priv->iobase + HW_WDTC);
+
+	return 0;
+}
+
+static int asm9260_wdt_enable(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+	u32 mode = 0;
+
+	if (priv->mode == HW_RESET)
+		mode = BM_MOD_WDRESET;
+
+	iowrite32(BM_MOD_WDEN | mode, priv->iobase + HW_WDMOD);
+
+	asm9260_wdt_updatetimeout(wdd);
+
+	asm9260_wdt_feed(wdd);
+
+	return 0;
+}
+
+static int asm9260_wdt_disable(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+
+	/* The only way to disable WD is to reset it. */
+	reset_control_assert(priv->rst);
+	reset_control_deassert(priv->rst);
+
+	return 0;
+}
+
+static int asm9260_wdt_settimeout(struct watchdog_device *wdd, unsigned int to)
+{
+	wdd->timeout = to;
+	asm9260_wdt_updatetimeout(wdd);
+
+	return 0;
+}
+
+static void asm9260_wdt_sys_reset(struct asm9260_wdt_priv *priv)
+{
+	/* init WD if it was not started */
+
+	iowrite32(BM_MOD_WDEN | BM_MOD_WDRESET, priv->iobase + HW_WDMOD);
+
+	iowrite32(0xff, priv->iobase + HW_WDTC);
+	/* first pass correct sequence */
+	asm9260_wdt_feed(&priv->wdd);
+	/*
+	 * Then write wrong pattern to the feed to trigger reset
+	 * ASAP.
+	 */
+	iowrite32(0xff, priv->iobase + HW_WDFEED);
+
+	mdelay(1000);
+}
+
+static irqreturn_t asm9260_wdt_irq(int irq, void *devid)
+{
+	struct asm9260_wdt_priv *priv = devid;
+	u32 stat;
+
+	stat = ioread32(priv->iobase + HW_WDMOD);
+	if (!(stat & BM_MOD_WDINT))
+		return IRQ_NONE;
+
+	if (priv->mode == DEBUG) {
+		dev_info(priv->dev, "Watchdog Timeout. Do nothing.\n");
+	} else {
+		dev_info(priv->dev, "Watchdog Timeout. Doing SW Reset.\n");
+		asm9260_wdt_sys_reset(priv);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int asm9260_restart_handler(struct notifier_block *this,
+				   unsigned long mode, void *cmd)
+{
+	struct asm9260_wdt_priv *priv =
+		container_of(this, struct asm9260_wdt_priv, restart_handler);
+
+	asm9260_wdt_sys_reset(priv);
+
+	return NOTIFY_DONE;
+}
+
+static const struct watchdog_info asm9260_wdt_ident = {
+	.options          =     WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING
+				| WDIOF_MAGICCLOSE,
+	.identity         =	"Alphascale asm9260 Watchdog",
+};
+
+static struct watchdog_ops asm9260_wdt_ops = {
+	.owner		= THIS_MODULE,
+	.start		= asm9260_wdt_enable,
+	.stop		= asm9260_wdt_disable,
+	.get_timeleft	= asm9260_wdt_gettimeleft,
+	.ping		= asm9260_wdt_feed,
+	.set_timeout	= asm9260_wdt_settimeout,
+};
+
+static int __init asm9260_wdt_get_dt_clks(struct asm9260_wdt_priv *priv)
+{
+	int err;
+	unsigned long clk;
+
+	priv->clk = devm_clk_get(priv->dev, "mod");
+	if (IS_ERR(priv->clk)) {
+		dev_err(priv->dev, "Failed to get \"mod\" clk\n");
+		return PTR_ERR(priv->clk);
+	}
+
+	/* configure AHB clock */
+	priv->clk_ahb = devm_clk_get(priv->dev, "ahb");
+	if (IS_ERR(priv->clk_ahb)) {
+		dev_err(priv->dev, "Failed to get \"ahb\" clk\n");
+		return PTR_ERR(priv->clk_ahb);
+	}
+
+	err = clk_prepare_enable(priv->clk_ahb);
+	if (err) {
+		dev_err(priv->dev, "Failed to enable ahb_clk!\n");
+		return err;
+	}
+
+	err = clk_set_rate(priv->clk, CLOCK_FREQ);
+	if (err) {
+		clk_disable_unprepare(priv->clk_ahb);
+		dev_err(priv->dev, "Failed to set rate!\n");
+		return err;
+	}
+
+	err = clk_prepare_enable(priv->clk);
+	if (err) {
+		clk_disable_unprepare(priv->clk_ahb);
+		dev_err(priv->dev, "Failed to enable clk!\n");
+		return err;
+	}
+
+	/* wdt has internal divider */
+	clk = clk_get_rate(priv->clk);
+	if (!clk) {
+		clk_disable_unprepare(priv->clk);
+		clk_disable_unprepare(priv->clk_ahb);
+		dev_err(priv->dev, "Failed, clk is 0!\n");
+		return -EINVAL;
+	}
+
+	priv->wdt_freq = clk / 2;
+
+	return 0;
+}
+
+static void __init asm9260_wdt_get_dt_mode(struct asm9260_wdt_priv *priv)
+{
+	const char *tmp;
+	int ret;
+
+	/* default mode */
+	priv->mode = HW_RESET;
+
+	ret = of_property_read_string(priv->dev->of_node,
+				      "alphascale,mode", &tmp);
+	if (ret < 0)
+		return;
+
+	if (!strcmp(tmp, "hw"))
+		priv->mode = HW_RESET;
+	else if (!strcmp(tmp, "sw"))
+		priv->mode = SW_RESET;
+	else if (!strcmp(tmp, "debug"))
+		priv->mode = DEBUG;
+	else
+		dev_warn(priv->dev, "unknown reset-type: %s. Using default \"hw\" mode.",
+			 tmp);
+}
+
+static int __init asm9260_wdt_probe(struct platform_device *pdev)
+{
+	struct asm9260_wdt_priv *priv;
+	struct watchdog_device *wdd;
+	struct resource *res;
+	int ret;
+	const char * const mode_name[] = { "hw", "sw", "debug", };
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(struct asm9260_wdt_priv),
+			    GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->dev = &pdev->dev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	priv->iobase = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(priv->iobase))
+		return PTR_ERR(priv->iobase);
+
+	ret = asm9260_wdt_get_dt_clks(priv);
+	if (ret)
+		return ret;
+
+	priv->rst = devm_reset_control_get(&pdev->dev, "wdt_rst");
+	if (IS_ERR(priv->rst))
+		return PTR_ERR(priv->rst);
+
+	wdd = &priv->wdd;
+	wdd->info = &asm9260_wdt_ident;
+	wdd->ops = &asm9260_wdt_ops;
+	wdd->min_timeout = 1;
+	wdd->max_timeout = BM_WDTC_MAX(priv->wdt_freq);
+	wdd->parent = &pdev->dev;
+
+	watchdog_set_drvdata(wdd, priv);
+
+	/*
+	 * If 'timeout-sec' unspecified in devicetree, assume a 30 second
+	 * default, unless the max timeout is less than 30 seconds, then use
+	 * the max instead.
+	 */
+	wdd->timeout = ASM9260_WDT_DEFAULT_TIMEOUT;
+	watchdog_init_timeout(wdd, 0, &pdev->dev);
+
+	asm9260_wdt_get_dt_mode(priv);
+
+	if (priv->mode != HW_RESET)
+		priv->irq = platform_get_irq(pdev, 0);
+
+	if (priv->irq > 0) {
+		/*
+		 * Not all supported platforms specify an interrupt for the
+		 * watchdog, so let's make it optional.
+		 */
+		ret = devm_request_irq(&pdev->dev, priv->irq,
+				       asm9260_wdt_irq, 0, pdev->name, priv);
+		if (ret < 0)
+			dev_warn(&pdev->dev, "failed to request IRQ\n");
+	}
+
+	ret = watchdog_register_device(wdd);
+	if (ret)
+		goto clk_off;
+
+	platform_set_drvdata(pdev, priv);
+
+	priv->restart_handler.notifier_call = asm9260_restart_handler;
+	priv->restart_handler.priority = 128;
+	ret = register_restart_handler(&priv->restart_handler);
+	if (ret)
+		dev_warn(&pdev->dev, "cannot register restart handler\n");
+
+	dev_info(&pdev->dev, "Watchdog enabled (timeout: %d sec, mode: %s)\n",
+		 wdd->timeout, mode_name[priv->mode]);
+	return 0;
+
+clk_off:
+	clk_disable_unprepare(priv->clk);
+	clk_disable_unprepare(priv->clk_ahb);
+	return ret;
+}
+
+static void asm9260_wdt_shutdown(struct platform_device *pdev)
+{
+	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
+
+	asm9260_wdt_disable(&priv->wdd);
+}
+
+static int __exit asm9260_wdt_remove(struct platform_device *pdev)
+{
+	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
+
+	asm9260_wdt_disable(&priv->wdd);
+
+	unregister_restart_handler(&priv->restart_handler);
+
+	watchdog_unregister_device(&priv->wdd);
+
+	clk_disable_unprepare(priv->clk);
+	clk_disable_unprepare(priv->clk_ahb);
+
+	return 0;
+}
+
+static const struct of_device_id asm9260_wdt_of_match[] = {
+	{ .compatible = "alphascale,asm9260-wdt"},
+	{},
+};
+MODULE_DEVICE_TABLE(of, asm9260_wdt_of_match);
+
+static struct platform_driver asm9260_wdt_driver = {
+	.driver = {
+		.name = "asm9260-wdt",
+		.owner = THIS_MODULE,
+		.of_match_table	= asm9260_wdt_of_match,
+	},
+	.probe = asm9260_wdt_probe,
+	.remove = asm9260_wdt_remove,
+	.shutdown = asm9260_wdt_shutdown,
+};
+module_platform_driver(asm9260_wdt_driver);
+
+MODULE_DESCRIPTION("asm9260 WatchDog Timer Driver");
+MODULE_AUTHOR("Oleksij Rempel <linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>");
+MODULE_LICENSE("GPL");
-- 
2.5.0

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH v4 1/2] watchdog: add Alphascale asm9260-wdt driver
@ 2015-11-25 19:33                             ` Oleksij Rempel
  0 siblings, 0 replies; 43+ messages in thread
From: Oleksij Rempel @ 2015-11-25 19:33 UTC (permalink / raw)
  To: linux-watchdog, linux, wim, devicetree, robh; +Cc: Oleksij Rempel

Add WD support for Alphascale asm9260 SoC. This driver
provide support for different function modes:
- HW mode to trigger SoC reset on timeout
- SW mode do soft reset if needed
- DEBUG mode

Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
---
 drivers/watchdog/Kconfig       |  10 +
 drivers/watchdog/Makefile      |   1 +
 drivers/watchdog/asm9260_wdt.c | 403 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 414 insertions(+)
 create mode 100644 drivers/watchdog/asm9260_wdt.c

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index c68edc1..9cd9b75 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -173,6 +173,16 @@ config ARM_SP805_WATCHDOG
 	  ARM Primecell SP805 Watchdog timer. This will reboot your system when
 	  the timeout is reached.
 
+config ASM9260_WATCHDOG
+	tristate "Alphascale ASM9260 watchdog"
+	depends on MACH_ASM9260
+	depends on OF
+	select WATCHDOG_CORE
+	select RESET_CONTROLLER
+	help
+	  Watchdog timer embedded into Alphascale asm9260 chips. This will reboot your
+	  system when the timeout is reached.
+
 config AT91RM9200_WATCHDOG
 	tristate "AT91RM9200 watchdog"
 	depends on SOC_AT91RM9200 && MFD_SYSCON
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 0c616e3..bd7b0cd 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
 
 # ARM Architecture
 obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o
+obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o
 obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
 obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
 obj-$(CONFIG_CADENCE_WATCHDOG) += cadence_wdt.o
diff --git a/drivers/watchdog/asm9260_wdt.c b/drivers/watchdog/asm9260_wdt.c
new file mode 100644
index 0000000..1c22ff4
--- /dev/null
+++ b/drivers/watchdog/asm9260_wdt.c
@@ -0,0 +1,403 @@
+/*
+ * Watchdog driver for Alphascale ASM9260.
+ *
+ * Copyright (c) 2014 Oleksij Rempel <linux@rempel-privat.de>
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/reset.h>
+#include <linux/watchdog.h>
+
+#define CLOCK_FREQ	1000000
+
+/* Watchdog Mode register */
+#define HW_WDMOD			0x00
+/* Wake interrupt. Set by HW, can't be cleared. */
+#define BM_MOD_WDINT			BIT(3)
+/* This bit set if timeout reached. Cleared by SW. */
+#define BM_MOD_WDTOF			BIT(2)
+/* HW Reset on timeout */
+#define BM_MOD_WDRESET			BIT(1)
+/* WD enable */
+#define BM_MOD_WDEN			BIT(0)
+
+/*
+ * Watchdog Timer Constant register
+ * Minimal value is 0xff, the meaning of this value
+ * depends on used clock: T = WDCLK * (0xff + 1) * 4
+ */
+#define HW_WDTC				0x04
+#define BM_WDTC_MAX(freq)		(0x7fffffff / (freq))
+
+/* Watchdog Feed register */
+#define HW_WDFEED			0x08
+
+/* Watchdog Timer Value register */
+#define HW_WDTV				0x0c
+
+#define ASM9260_WDT_DEFAULT_TIMEOUT	30
+
+enum asm9260_wdt_mode {
+	HW_RESET,
+	SW_RESET,
+	DEBUG,
+};
+
+struct asm9260_wdt_priv {
+	struct device		*dev;
+	struct watchdog_device	wdd;
+	struct clk		*clk;
+	struct clk		*clk_ahb;
+	struct reset_control	*rst;
+	struct notifier_block	restart_handler;
+
+	void __iomem		*iobase;
+	int			irq;
+	unsigned long		wdt_freq;
+	enum asm9260_wdt_mode	mode;
+};
+
+static int asm9260_wdt_feed(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+
+	iowrite32(0xaa, priv->iobase + HW_WDFEED);
+	iowrite32(0x55, priv->iobase + HW_WDFEED);
+
+	return 0;
+}
+
+static unsigned int asm9260_wdt_gettimeleft(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+	u32 counter;
+
+	counter = ioread32(priv->iobase + HW_WDTV);
+
+	return DIV_ROUND_CLOSEST(counter, priv->wdt_freq);
+}
+
+static int asm9260_wdt_updatetimeout(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+	u32 counter;
+
+	counter = wdd->timeout * priv->wdt_freq;
+
+	iowrite32(counter, priv->iobase + HW_WDTC);
+
+	return 0;
+}
+
+static int asm9260_wdt_enable(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+	u32 mode = 0;
+
+	if (priv->mode == HW_RESET)
+		mode = BM_MOD_WDRESET;
+
+	iowrite32(BM_MOD_WDEN | mode, priv->iobase + HW_WDMOD);
+
+	asm9260_wdt_updatetimeout(wdd);
+
+	asm9260_wdt_feed(wdd);
+
+	return 0;
+}
+
+static int asm9260_wdt_disable(struct watchdog_device *wdd)
+{
+	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
+
+	/* The only way to disable WD is to reset it. */
+	reset_control_assert(priv->rst);
+	reset_control_deassert(priv->rst);
+
+	return 0;
+}
+
+static int asm9260_wdt_settimeout(struct watchdog_device *wdd, unsigned int to)
+{
+	wdd->timeout = to;
+	asm9260_wdt_updatetimeout(wdd);
+
+	return 0;
+}
+
+static void asm9260_wdt_sys_reset(struct asm9260_wdt_priv *priv)
+{
+	/* init WD if it was not started */
+
+	iowrite32(BM_MOD_WDEN | BM_MOD_WDRESET, priv->iobase + HW_WDMOD);
+
+	iowrite32(0xff, priv->iobase + HW_WDTC);
+	/* first pass correct sequence */
+	asm9260_wdt_feed(&priv->wdd);
+	/*
+	 * Then write wrong pattern to the feed to trigger reset
+	 * ASAP.
+	 */
+	iowrite32(0xff, priv->iobase + HW_WDFEED);
+
+	mdelay(1000);
+}
+
+static irqreturn_t asm9260_wdt_irq(int irq, void *devid)
+{
+	struct asm9260_wdt_priv *priv = devid;
+	u32 stat;
+
+	stat = ioread32(priv->iobase + HW_WDMOD);
+	if (!(stat & BM_MOD_WDINT))
+		return IRQ_NONE;
+
+	if (priv->mode == DEBUG) {
+		dev_info(priv->dev, "Watchdog Timeout. Do nothing.\n");
+	} else {
+		dev_info(priv->dev, "Watchdog Timeout. Doing SW Reset.\n");
+		asm9260_wdt_sys_reset(priv);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int asm9260_restart_handler(struct notifier_block *this,
+				   unsigned long mode, void *cmd)
+{
+	struct asm9260_wdt_priv *priv =
+		container_of(this, struct asm9260_wdt_priv, restart_handler);
+
+	asm9260_wdt_sys_reset(priv);
+
+	return NOTIFY_DONE;
+}
+
+static const struct watchdog_info asm9260_wdt_ident = {
+	.options          =     WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING
+				| WDIOF_MAGICCLOSE,
+	.identity         =	"Alphascale asm9260 Watchdog",
+};
+
+static struct watchdog_ops asm9260_wdt_ops = {
+	.owner		= THIS_MODULE,
+	.start		= asm9260_wdt_enable,
+	.stop		= asm9260_wdt_disable,
+	.get_timeleft	= asm9260_wdt_gettimeleft,
+	.ping		= asm9260_wdt_feed,
+	.set_timeout	= asm9260_wdt_settimeout,
+};
+
+static int __init asm9260_wdt_get_dt_clks(struct asm9260_wdt_priv *priv)
+{
+	int err;
+	unsigned long clk;
+
+	priv->clk = devm_clk_get(priv->dev, "mod");
+	if (IS_ERR(priv->clk)) {
+		dev_err(priv->dev, "Failed to get \"mod\" clk\n");
+		return PTR_ERR(priv->clk);
+	}
+
+	/* configure AHB clock */
+	priv->clk_ahb = devm_clk_get(priv->dev, "ahb");
+	if (IS_ERR(priv->clk_ahb)) {
+		dev_err(priv->dev, "Failed to get \"ahb\" clk\n");
+		return PTR_ERR(priv->clk_ahb);
+	}
+
+	err = clk_prepare_enable(priv->clk_ahb);
+	if (err) {
+		dev_err(priv->dev, "Failed to enable ahb_clk!\n");
+		return err;
+	}
+
+	err = clk_set_rate(priv->clk, CLOCK_FREQ);
+	if (err) {
+		clk_disable_unprepare(priv->clk_ahb);
+		dev_err(priv->dev, "Failed to set rate!\n");
+		return err;
+	}
+
+	err = clk_prepare_enable(priv->clk);
+	if (err) {
+		clk_disable_unprepare(priv->clk_ahb);
+		dev_err(priv->dev, "Failed to enable clk!\n");
+		return err;
+	}
+
+	/* wdt has internal divider */
+	clk = clk_get_rate(priv->clk);
+	if (!clk) {
+		clk_disable_unprepare(priv->clk);
+		clk_disable_unprepare(priv->clk_ahb);
+		dev_err(priv->dev, "Failed, clk is 0!\n");
+		return -EINVAL;
+	}
+
+	priv->wdt_freq = clk / 2;
+
+	return 0;
+}
+
+static void __init asm9260_wdt_get_dt_mode(struct asm9260_wdt_priv *priv)
+{
+	const char *tmp;
+	int ret;
+
+	/* default mode */
+	priv->mode = HW_RESET;
+
+	ret = of_property_read_string(priv->dev->of_node,
+				      "alphascale,mode", &tmp);
+	if (ret < 0)
+		return;
+
+	if (!strcmp(tmp, "hw"))
+		priv->mode = HW_RESET;
+	else if (!strcmp(tmp, "sw"))
+		priv->mode = SW_RESET;
+	else if (!strcmp(tmp, "debug"))
+		priv->mode = DEBUG;
+	else
+		dev_warn(priv->dev, "unknown reset-type: %s. Using default \"hw\" mode.",
+			 tmp);
+}
+
+static int __init asm9260_wdt_probe(struct platform_device *pdev)
+{
+	struct asm9260_wdt_priv *priv;
+	struct watchdog_device *wdd;
+	struct resource *res;
+	int ret;
+	const char * const mode_name[] = { "hw", "sw", "debug", };
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(struct asm9260_wdt_priv),
+			    GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->dev = &pdev->dev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	priv->iobase = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(priv->iobase))
+		return PTR_ERR(priv->iobase);
+
+	ret = asm9260_wdt_get_dt_clks(priv);
+	if (ret)
+		return ret;
+
+	priv->rst = devm_reset_control_get(&pdev->dev, "wdt_rst");
+	if (IS_ERR(priv->rst))
+		return PTR_ERR(priv->rst);
+
+	wdd = &priv->wdd;
+	wdd->info = &asm9260_wdt_ident;
+	wdd->ops = &asm9260_wdt_ops;
+	wdd->min_timeout = 1;
+	wdd->max_timeout = BM_WDTC_MAX(priv->wdt_freq);
+	wdd->parent = &pdev->dev;
+
+	watchdog_set_drvdata(wdd, priv);
+
+	/*
+	 * If 'timeout-sec' unspecified in devicetree, assume a 30 second
+	 * default, unless the max timeout is less than 30 seconds, then use
+	 * the max instead.
+	 */
+	wdd->timeout = ASM9260_WDT_DEFAULT_TIMEOUT;
+	watchdog_init_timeout(wdd, 0, &pdev->dev);
+
+	asm9260_wdt_get_dt_mode(priv);
+
+	if (priv->mode != HW_RESET)
+		priv->irq = platform_get_irq(pdev, 0);
+
+	if (priv->irq > 0) {
+		/*
+		 * Not all supported platforms specify an interrupt for the
+		 * watchdog, so let's make it optional.
+		 */
+		ret = devm_request_irq(&pdev->dev, priv->irq,
+				       asm9260_wdt_irq, 0, pdev->name, priv);
+		if (ret < 0)
+			dev_warn(&pdev->dev, "failed to request IRQ\n");
+	}
+
+	ret = watchdog_register_device(wdd);
+	if (ret)
+		goto clk_off;
+
+	platform_set_drvdata(pdev, priv);
+
+	priv->restart_handler.notifier_call = asm9260_restart_handler;
+	priv->restart_handler.priority = 128;
+	ret = register_restart_handler(&priv->restart_handler);
+	if (ret)
+		dev_warn(&pdev->dev, "cannot register restart handler\n");
+
+	dev_info(&pdev->dev, "Watchdog enabled (timeout: %d sec, mode: %s)\n",
+		 wdd->timeout, mode_name[priv->mode]);
+	return 0;
+
+clk_off:
+	clk_disable_unprepare(priv->clk);
+	clk_disable_unprepare(priv->clk_ahb);
+	return ret;
+}
+
+static void asm9260_wdt_shutdown(struct platform_device *pdev)
+{
+	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
+
+	asm9260_wdt_disable(&priv->wdd);
+}
+
+static int __exit asm9260_wdt_remove(struct platform_device *pdev)
+{
+	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
+
+	asm9260_wdt_disable(&priv->wdd);
+
+	unregister_restart_handler(&priv->restart_handler);
+
+	watchdog_unregister_device(&priv->wdd);
+
+	clk_disable_unprepare(priv->clk);
+	clk_disable_unprepare(priv->clk_ahb);
+
+	return 0;
+}
+
+static const struct of_device_id asm9260_wdt_of_match[] = {
+	{ .compatible = "alphascale,asm9260-wdt"},
+	{},
+};
+MODULE_DEVICE_TABLE(of, asm9260_wdt_of_match);
+
+static struct platform_driver asm9260_wdt_driver = {
+	.driver = {
+		.name = "asm9260-wdt",
+		.owner = THIS_MODULE,
+		.of_match_table	= asm9260_wdt_of_match,
+	},
+	.probe = asm9260_wdt_probe,
+	.remove = asm9260_wdt_remove,
+	.shutdown = asm9260_wdt_shutdown,
+};
+module_platform_driver(asm9260_wdt_driver);
+
+MODULE_DESCRIPTION("asm9260 WatchDog Timer Driver");
+MODULE_AUTHOR("Oleksij Rempel <linux@rempel-privat.de>");
+MODULE_LICENSE("GPL");
-- 
2.5.0


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

* [PATCH v4 2/2] DT: watchdog: add Alphascale asm9260 watchdog binding documentation.
  2015-11-25 19:33                         ` Oleksij Rempel
@ 2015-11-25 19:33                             ` Oleksij Rempel
  -1 siblings, 0 replies; 43+ messages in thread
From: Oleksij Rempel @ 2015-11-25 19:33 UTC (permalink / raw)
  To: linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	linux-0h96xk9xTtrk1uMJSBkQmQ, wim-IQzOog9fTRqzQB+pC5nmwQ,
	devicetree-u79uwXL29TY76Z2rM5mHXA, robh-DgEjT+Ai2ygdnm+yROfE0A
  Cc: Oleksij Rempel

Signed-off-by: Oleksij Rempel <linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>
---
 .../bindings/watchdog/alphascale-asm9260.txt       | 35 ++++++++++++++++++++++
 1 file changed, 35 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt

diff --git a/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
new file mode 100644
index 0000000..75b265a
--- /dev/null
+++ b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
@@ -0,0 +1,35 @@
+Alphascale asm9260 Watchdog timer
+
+Required properties:
+
+- compatible : should be "alphascale,asm9260-wdt".
+- reg : Specifies base physical address and size of the registers.
+- clocks : the clocks feeding the watchdog timer. See clock-bindings.txt
+- clock-names : should be set to
+	"mod" - source for tick counter.
+	"ahb" - ahb gate.
+- resets : phandle pointing to the system reset controller with
+	line index for the watchdog.
+- reset-names : should be set to "wdt_rst".
+
+Optional properties:
+- timeout-sec : shall contain the default watchdog timeout in seconds,
+	if unset, the default timeout is 30 seconds.
+- alphascale,mode : three modes are supported
+	"hw" - hw reset (default).
+	"sw" - sw reset.
+	"debug" - no action is taken.
+
+Example:
+
+watchdog0: watchdog@80048000 {
+	compatible = "alphascale,asm9260-wdt";
+	reg = <0x80048000 0x10>;
+	clocks = <&acc CLKID_SYS_WDT>, <&acc CLKID_AHB_WDT>;
+	clock-names = "mod", "ahb";
+	interrupts = <55>;
+	resets = <&rst WDT_RESET>;
+	reset-names = "wdt_rst";
+	timeout-sec = <30>;
+	alphascale,mode = "hw";
+};
-- 
2.5.0

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH v4 2/2] DT: watchdog: add Alphascale asm9260 watchdog binding documentation.
@ 2015-11-25 19:33                             ` Oleksij Rempel
  0 siblings, 0 replies; 43+ messages in thread
From: Oleksij Rempel @ 2015-11-25 19:33 UTC (permalink / raw)
  To: linux-watchdog, linux, wim, devicetree, robh; +Cc: Oleksij Rempel

Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
---
 .../bindings/watchdog/alphascale-asm9260.txt       | 35 ++++++++++++++++++++++
 1 file changed, 35 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt

diff --git a/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
new file mode 100644
index 0000000..75b265a
--- /dev/null
+++ b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
@@ -0,0 +1,35 @@
+Alphascale asm9260 Watchdog timer
+
+Required properties:
+
+- compatible : should be "alphascale,asm9260-wdt".
+- reg : Specifies base physical address and size of the registers.
+- clocks : the clocks feeding the watchdog timer. See clock-bindings.txt
+- clock-names : should be set to
+	"mod" - source for tick counter.
+	"ahb" - ahb gate.
+- resets : phandle pointing to the system reset controller with
+	line index for the watchdog.
+- reset-names : should be set to "wdt_rst".
+
+Optional properties:
+- timeout-sec : shall contain the default watchdog timeout in seconds,
+	if unset, the default timeout is 30 seconds.
+- alphascale,mode : three modes are supported
+	"hw" - hw reset (default).
+	"sw" - sw reset.
+	"debug" - no action is taken.
+
+Example:
+
+watchdog0: watchdog@80048000 {
+	compatible = "alphascale,asm9260-wdt";
+	reg = <0x80048000 0x10>;
+	clocks = <&acc CLKID_SYS_WDT>, <&acc CLKID_AHB_WDT>;
+	clock-names = "mod", "ahb";
+	interrupts = <55>;
+	resets = <&rst WDT_RESET>;
+	reset-names = "wdt_rst";
+	timeout-sec = <30>;
+	alphascale,mode = "hw";
+};
-- 
2.5.0


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

* Re: [PATCH v3 2/2] DT: watchdog: add Alphascale asm9260 watchdog binding documentation.
  2015-11-24 21:40                             ` Oleksij Rempel
@ 2015-11-25 20:06                                 ` Rob Herring
  -1 siblings, 0 replies; 43+ messages in thread
From: Rob Herring @ 2015-11-25 20:06 UTC (permalink / raw)
  To: Oleksij Rempel
  Cc: linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	linux-0h96xk9xTtrk1uMJSBkQmQ, wim-IQzOog9fTRqzQB+pC5nmwQ,
	devicetree-u79uwXL29TY76Z2rM5mHXA

On Tue, Nov 24, 2015 at 10:40:33PM +0100, Oleksij Rempel wrote:
> Signed-off-by: Oleksij Rempel <linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>

Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>

> ---
>  .../bindings/watchdog/alphascale-asm9260.txt       | 35 ++++++++++++++++++++++
>  1 file changed, 35 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
> 
> diff --git a/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
> new file mode 100644
> index 0000000..75b265a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
> @@ -0,0 +1,35 @@
> +Alphascale asm9260 Watchdog timer
> +
> +Required properties:
> +
> +- compatible : should be "alphascale,asm9260-wdt".
> +- reg : Specifies base physical address and size of the registers.
> +- clocks : the clocks feeding the watchdog timer. See clock-bindings.txt
> +- clock-names : should be set to
> +	"mod" - source for tick counter.
> +	"ahb" - ahb gate.
> +- resets : phandle pointing to the system reset controller with
> +	line index for the watchdog.
> +- reset-names : should be set to "wdt_rst".
> +
> +Optional properties:
> +- timeout-sec : shall contain the default watchdog timeout in seconds,
> +	if unset, the default timeout is 30 seconds.
> +- alphascale,mode : three modes are supported
> +	"hw" - hw reset (default).
> +	"sw" - sw reset.
> +	"debug" - no action is taken.
> +
> +Example:
> +
> +watchdog0: watchdog@80048000 {
> +	compatible = "alphascale,asm9260-wdt";
> +	reg = <0x80048000 0x10>;
> +	clocks = <&acc CLKID_SYS_WDT>, <&acc CLKID_AHB_WDT>;
> +	clock-names = "mod", "ahb";
> +	interrupts = <55>;
> +	resets = <&rst WDT_RESET>;
> +	reset-names = "wdt_rst";
> +	timeout-sec = <30>;
> +	alphascale,mode = "hw";
> +};
> -- 
> 2.5.0
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v3 2/2] DT: watchdog: add Alphascale asm9260 watchdog binding documentation.
@ 2015-11-25 20:06                                 ` Rob Herring
  0 siblings, 0 replies; 43+ messages in thread
From: Rob Herring @ 2015-11-25 20:06 UTC (permalink / raw)
  To: Oleksij Rempel; +Cc: linux-watchdog, linux, wim, devicetree

On Tue, Nov 24, 2015 at 10:40:33PM +0100, Oleksij Rempel wrote:
> Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>

Acked-by: Rob Herring <robh@kernel.org>

> ---
>  .../bindings/watchdog/alphascale-asm9260.txt       | 35 ++++++++++++++++++++++
>  1 file changed, 35 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
> 
> diff --git a/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
> new file mode 100644
> index 0000000..75b265a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
> @@ -0,0 +1,35 @@
> +Alphascale asm9260 Watchdog timer
> +
> +Required properties:
> +
> +- compatible : should be "alphascale,asm9260-wdt".
> +- reg : Specifies base physical address and size of the registers.
> +- clocks : the clocks feeding the watchdog timer. See clock-bindings.txt
> +- clock-names : should be set to
> +	"mod" - source for tick counter.
> +	"ahb" - ahb gate.
> +- resets : phandle pointing to the system reset controller with
> +	line index for the watchdog.
> +- reset-names : should be set to "wdt_rst".
> +
> +Optional properties:
> +- timeout-sec : shall contain the default watchdog timeout in seconds,
> +	if unset, the default timeout is 30 seconds.
> +- alphascale,mode : three modes are supported
> +	"hw" - hw reset (default).
> +	"sw" - sw reset.
> +	"debug" - no action is taken.
> +
> +Example:
> +
> +watchdog0: watchdog@80048000 {
> +	compatible = "alphascale,asm9260-wdt";
> +	reg = <0x80048000 0x10>;
> +	clocks = <&acc CLKID_SYS_WDT>, <&acc CLKID_AHB_WDT>;
> +	clock-names = "mod", "ahb";
> +	interrupts = <55>;
> +	resets = <&rst WDT_RESET>;
> +	reset-names = "wdt_rst";
> +	timeout-sec = <30>;
> +	alphascale,mode = "hw";
> +};
> -- 
> 2.5.0
> 

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

* Re: [PATCH v4 1/2] watchdog: add Alphascale asm9260-wdt driver
  2015-11-25 19:33                             ` Oleksij Rempel
@ 2015-11-30 16:10                                 ` Guenter Roeck
  -1 siblings, 0 replies; 43+ messages in thread
From: Guenter Roeck @ 2015-11-30 16:10 UTC (permalink / raw)
  To: Oleksij Rempel, linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	wim-IQzOog9fTRqzQB+pC5nmwQ, devicetree-u79uwXL29TY76Z2rM5mHXA,
	robh-DgEjT+Ai2ygdnm+yROfE0A

On 11/25/2015 11:33 AM, Oleksij Rempel wrote:
> Add WD support for Alphascale asm9260 SoC. This driver
> provide support for different function modes:
> - HW mode to trigger SoC reset on timeout
> - SW mode do soft reset if needed
> - DEBUG mode
>
> Signed-off-by: Oleksij Rempel <linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>

Reviewed-by: Guenter Roeck <linux-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org>

> ---
>   drivers/watchdog/Kconfig       |  10 +
>   drivers/watchdog/Makefile      |   1 +
>   drivers/watchdog/asm9260_wdt.c | 403 +++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 414 insertions(+)
>   create mode 100644 drivers/watchdog/asm9260_wdt.c
>
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index c68edc1..9cd9b75 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -173,6 +173,16 @@ config ARM_SP805_WATCHDOG
>   	  ARM Primecell SP805 Watchdog timer. This will reboot your system when
>   	  the timeout is reached.
>
> +config ASM9260_WATCHDOG
> +	tristate "Alphascale ASM9260 watchdog"
> +	depends on MACH_ASM9260
> +	depends on OF
> +	select WATCHDOG_CORE
> +	select RESET_CONTROLLER
> +	help
> +	  Watchdog timer embedded into Alphascale asm9260 chips. This will reboot your
> +	  system when the timeout is reached.
> +
>   config AT91RM9200_WATCHDOG
>   	tristate "AT91RM9200 watchdog"
>   	depends on SOC_AT91RM9200 && MFD_SYSCON
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index 0c616e3..bd7b0cd 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -30,6 +30,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
>
>   # ARM Architecture
>   obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o
> +obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o
>   obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
>   obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
>   obj-$(CONFIG_CADENCE_WATCHDOG) += cadence_wdt.o
> diff --git a/drivers/watchdog/asm9260_wdt.c b/drivers/watchdog/asm9260_wdt.c
> new file mode 100644
> index 0000000..1c22ff4
> --- /dev/null
> +++ b/drivers/watchdog/asm9260_wdt.c
> @@ -0,0 +1,403 @@
> +/*
> + * Watchdog driver for Alphascale ASM9260.
> + *
> + * Copyright (c) 2014 Oleksij Rempel <linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>
> + *
> + * Licensed under GPLv2 or later.
> + */
> +
> +#include <linux/bitops.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/reboot.h>
> +#include <linux/reset.h>
> +#include <linux/watchdog.h>
> +
> +#define CLOCK_FREQ	1000000
> +
> +/* Watchdog Mode register */
> +#define HW_WDMOD			0x00
> +/* Wake interrupt. Set by HW, can't be cleared. */
> +#define BM_MOD_WDINT			BIT(3)
> +/* This bit set if timeout reached. Cleared by SW. */
> +#define BM_MOD_WDTOF			BIT(2)
> +/* HW Reset on timeout */
> +#define BM_MOD_WDRESET			BIT(1)
> +/* WD enable */
> +#define BM_MOD_WDEN			BIT(0)
> +
> +/*
> + * Watchdog Timer Constant register
> + * Minimal value is 0xff, the meaning of this value
> + * depends on used clock: T = WDCLK * (0xff + 1) * 4
> + */
> +#define HW_WDTC				0x04
> +#define BM_WDTC_MAX(freq)		(0x7fffffff / (freq))
> +
> +/* Watchdog Feed register */
> +#define HW_WDFEED			0x08
> +
> +/* Watchdog Timer Value register */
> +#define HW_WDTV				0x0c
> +
> +#define ASM9260_WDT_DEFAULT_TIMEOUT	30
> +
> +enum asm9260_wdt_mode {
> +	HW_RESET,
> +	SW_RESET,
> +	DEBUG,
> +};
> +
> +struct asm9260_wdt_priv {
> +	struct device		*dev;
> +	struct watchdog_device	wdd;
> +	struct clk		*clk;
> +	struct clk		*clk_ahb;
> +	struct reset_control	*rst;
> +	struct notifier_block	restart_handler;
> +
> +	void __iomem		*iobase;
> +	int			irq;
> +	unsigned long		wdt_freq;
> +	enum asm9260_wdt_mode	mode;
> +};
> +
> +static int asm9260_wdt_feed(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +
> +	iowrite32(0xaa, priv->iobase + HW_WDFEED);
> +	iowrite32(0x55, priv->iobase + HW_WDFEED);
> +
> +	return 0;
> +}
> +
> +static unsigned int asm9260_wdt_gettimeleft(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +	u32 counter;
> +
> +	counter = ioread32(priv->iobase + HW_WDTV);
> +
> +	return DIV_ROUND_CLOSEST(counter, priv->wdt_freq);
> +}
> +
> +static int asm9260_wdt_updatetimeout(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +	u32 counter;
> +
> +	counter = wdd->timeout * priv->wdt_freq;
> +
> +	iowrite32(counter, priv->iobase + HW_WDTC);
> +
> +	return 0;
> +}
> +
> +static int asm9260_wdt_enable(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +	u32 mode = 0;
> +
> +	if (priv->mode == HW_RESET)
> +		mode = BM_MOD_WDRESET;
> +
> +	iowrite32(BM_MOD_WDEN | mode, priv->iobase + HW_WDMOD);
> +
> +	asm9260_wdt_updatetimeout(wdd);
> +
> +	asm9260_wdt_feed(wdd);
> +
> +	return 0;
> +}
> +
> +static int asm9260_wdt_disable(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +
> +	/* The only way to disable WD is to reset it. */
> +	reset_control_assert(priv->rst);
> +	reset_control_deassert(priv->rst);
> +
> +	return 0;
> +}
> +
> +static int asm9260_wdt_settimeout(struct watchdog_device *wdd, unsigned int to)
> +{
> +	wdd->timeout = to;
> +	asm9260_wdt_updatetimeout(wdd);
> +
> +	return 0;
> +}
> +
> +static void asm9260_wdt_sys_reset(struct asm9260_wdt_priv *priv)
> +{
> +	/* init WD if it was not started */
> +
> +	iowrite32(BM_MOD_WDEN | BM_MOD_WDRESET, priv->iobase + HW_WDMOD);
> +
> +	iowrite32(0xff, priv->iobase + HW_WDTC);
> +	/* first pass correct sequence */
> +	asm9260_wdt_feed(&priv->wdd);
> +	/*
> +	 * Then write wrong pattern to the feed to trigger reset
> +	 * ASAP.
> +	 */
> +	iowrite32(0xff, priv->iobase + HW_WDFEED);
> +
> +	mdelay(1000);
> +}
> +
> +static irqreturn_t asm9260_wdt_irq(int irq, void *devid)
> +{
> +	struct asm9260_wdt_priv *priv = devid;
> +	u32 stat;
> +
> +	stat = ioread32(priv->iobase + HW_WDMOD);
> +	if (!(stat & BM_MOD_WDINT))
> +		return IRQ_NONE;
> +
> +	if (priv->mode == DEBUG) {
> +		dev_info(priv->dev, "Watchdog Timeout. Do nothing.\n");
> +	} else {
> +		dev_info(priv->dev, "Watchdog Timeout. Doing SW Reset.\n");
> +		asm9260_wdt_sys_reset(priv);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int asm9260_restart_handler(struct notifier_block *this,
> +				   unsigned long mode, void *cmd)
> +{
> +	struct asm9260_wdt_priv *priv =
> +		container_of(this, struct asm9260_wdt_priv, restart_handler);
> +
> +	asm9260_wdt_sys_reset(priv);
> +
> +	return NOTIFY_DONE;
> +}
> +
> +static const struct watchdog_info asm9260_wdt_ident = {
> +	.options          =     WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING
> +				| WDIOF_MAGICCLOSE,
> +	.identity         =	"Alphascale asm9260 Watchdog",
> +};
> +
> +static struct watchdog_ops asm9260_wdt_ops = {
> +	.owner		= THIS_MODULE,
> +	.start		= asm9260_wdt_enable,
> +	.stop		= asm9260_wdt_disable,
> +	.get_timeleft	= asm9260_wdt_gettimeleft,
> +	.ping		= asm9260_wdt_feed,
> +	.set_timeout	= asm9260_wdt_settimeout,
> +};
> +
> +static int __init asm9260_wdt_get_dt_clks(struct asm9260_wdt_priv *priv)
> +{
> +	int err;
> +	unsigned long clk;
> +
> +	priv->clk = devm_clk_get(priv->dev, "mod");
> +	if (IS_ERR(priv->clk)) {
> +		dev_err(priv->dev, "Failed to get \"mod\" clk\n");
> +		return PTR_ERR(priv->clk);
> +	}
> +
> +	/* configure AHB clock */
> +	priv->clk_ahb = devm_clk_get(priv->dev, "ahb");
> +	if (IS_ERR(priv->clk_ahb)) {
> +		dev_err(priv->dev, "Failed to get \"ahb\" clk\n");
> +		return PTR_ERR(priv->clk_ahb);
> +	}
> +
> +	err = clk_prepare_enable(priv->clk_ahb);
> +	if (err) {
> +		dev_err(priv->dev, "Failed to enable ahb_clk!\n");
> +		return err;
> +	}
> +
> +	err = clk_set_rate(priv->clk, CLOCK_FREQ);
> +	if (err) {
> +		clk_disable_unprepare(priv->clk_ahb);
> +		dev_err(priv->dev, "Failed to set rate!\n");
> +		return err;
> +	}
> +
> +	err = clk_prepare_enable(priv->clk);
> +	if (err) {
> +		clk_disable_unprepare(priv->clk_ahb);
> +		dev_err(priv->dev, "Failed to enable clk!\n");
> +		return err;
> +	}
> +
> +	/* wdt has internal divider */
> +	clk = clk_get_rate(priv->clk);
> +	if (!clk) {
> +		clk_disable_unprepare(priv->clk);
> +		clk_disable_unprepare(priv->clk_ahb);
> +		dev_err(priv->dev, "Failed, clk is 0!\n");
> +		return -EINVAL;
> +	}
> +
> +	priv->wdt_freq = clk / 2;
> +
> +	return 0;
> +}
> +
> +static void __init asm9260_wdt_get_dt_mode(struct asm9260_wdt_priv *priv)
> +{
> +	const char *tmp;
> +	int ret;
> +
> +	/* default mode */
> +	priv->mode = HW_RESET;
> +
> +	ret = of_property_read_string(priv->dev->of_node,
> +				      "alphascale,mode", &tmp);
> +	if (ret < 0)
> +		return;
> +
> +	if (!strcmp(tmp, "hw"))
> +		priv->mode = HW_RESET;
> +	else if (!strcmp(tmp, "sw"))
> +		priv->mode = SW_RESET;
> +	else if (!strcmp(tmp, "debug"))
> +		priv->mode = DEBUG;
> +	else
> +		dev_warn(priv->dev, "unknown reset-type: %s. Using default \"hw\" mode.",
> +			 tmp);
> +}
> +
> +static int __init asm9260_wdt_probe(struct platform_device *pdev)
> +{
> +	struct asm9260_wdt_priv *priv;
> +	struct watchdog_device *wdd;
> +	struct resource *res;
> +	int ret;
> +	const char * const mode_name[] = { "hw", "sw", "debug", };
> +
> +	priv = devm_kzalloc(&pdev->dev, sizeof(struct asm9260_wdt_priv),
> +			    GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->dev = &pdev->dev;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	priv->iobase = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(priv->iobase))
> +		return PTR_ERR(priv->iobase);
> +
> +	ret = asm9260_wdt_get_dt_clks(priv);
> +	if (ret)
> +		return ret;
> +
> +	priv->rst = devm_reset_control_get(&pdev->dev, "wdt_rst");
> +	if (IS_ERR(priv->rst))
> +		return PTR_ERR(priv->rst);
> +
> +	wdd = &priv->wdd;
> +	wdd->info = &asm9260_wdt_ident;
> +	wdd->ops = &asm9260_wdt_ops;
> +	wdd->min_timeout = 1;
> +	wdd->max_timeout = BM_WDTC_MAX(priv->wdt_freq);
> +	wdd->parent = &pdev->dev;
> +
> +	watchdog_set_drvdata(wdd, priv);
> +
> +	/*
> +	 * If 'timeout-sec' unspecified in devicetree, assume a 30 second
> +	 * default, unless the max timeout is less than 30 seconds, then use
> +	 * the max instead.
> +	 */
> +	wdd->timeout = ASM9260_WDT_DEFAULT_TIMEOUT;
> +	watchdog_init_timeout(wdd, 0, &pdev->dev);
> +
> +	asm9260_wdt_get_dt_mode(priv);
> +
> +	if (priv->mode != HW_RESET)
> +		priv->irq = platform_get_irq(pdev, 0);
> +
> +	if (priv->irq > 0) {
> +		/*
> +		 * Not all supported platforms specify an interrupt for the
> +		 * watchdog, so let's make it optional.
> +		 */
> +		ret = devm_request_irq(&pdev->dev, priv->irq,
> +				       asm9260_wdt_irq, 0, pdev->name, priv);
> +		if (ret < 0)
> +			dev_warn(&pdev->dev, "failed to request IRQ\n");
> +	}
> +
> +	ret = watchdog_register_device(wdd);
> +	if (ret)
> +		goto clk_off;
> +
> +	platform_set_drvdata(pdev, priv);
> +
> +	priv->restart_handler.notifier_call = asm9260_restart_handler;
> +	priv->restart_handler.priority = 128;
> +	ret = register_restart_handler(&priv->restart_handler);
> +	if (ret)
> +		dev_warn(&pdev->dev, "cannot register restart handler\n");
> +
> +	dev_info(&pdev->dev, "Watchdog enabled (timeout: %d sec, mode: %s)\n",
> +		 wdd->timeout, mode_name[priv->mode]);
> +	return 0;
> +
> +clk_off:
> +	clk_disable_unprepare(priv->clk);
> +	clk_disable_unprepare(priv->clk_ahb);
> +	return ret;
> +}
> +
> +static void asm9260_wdt_shutdown(struct platform_device *pdev)
> +{
> +	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
> +
> +	asm9260_wdt_disable(&priv->wdd);
> +}
> +
> +static int __exit asm9260_wdt_remove(struct platform_device *pdev)
> +{
> +	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
> +
> +	asm9260_wdt_disable(&priv->wdd);
> +
> +	unregister_restart_handler(&priv->restart_handler);
> +
> +	watchdog_unregister_device(&priv->wdd);
> +
> +	clk_disable_unprepare(priv->clk);
> +	clk_disable_unprepare(priv->clk_ahb);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id asm9260_wdt_of_match[] = {
> +	{ .compatible = "alphascale,asm9260-wdt"},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, asm9260_wdt_of_match);
> +
> +static struct platform_driver asm9260_wdt_driver = {
> +	.driver = {
> +		.name = "asm9260-wdt",
> +		.owner = THIS_MODULE,
> +		.of_match_table	= asm9260_wdt_of_match,
> +	},
> +	.probe = asm9260_wdt_probe,
> +	.remove = asm9260_wdt_remove,
> +	.shutdown = asm9260_wdt_shutdown,
> +};
> +module_platform_driver(asm9260_wdt_driver);
> +
> +MODULE_DESCRIPTION("asm9260 WatchDog Timer Driver");
> +MODULE_AUTHOR("Oleksij Rempel <linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>");
> +MODULE_LICENSE("GPL");
>

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v4 1/2] watchdog: add Alphascale asm9260-wdt driver
@ 2015-11-30 16:10                                 ` Guenter Roeck
  0 siblings, 0 replies; 43+ messages in thread
From: Guenter Roeck @ 2015-11-30 16:10 UTC (permalink / raw)
  To: Oleksij Rempel, linux-watchdog, wim, devicetree, robh

On 11/25/2015 11:33 AM, Oleksij Rempel wrote:
> Add WD support for Alphascale asm9260 SoC. This driver
> provide support for different function modes:
> - HW mode to trigger SoC reset on timeout
> - SW mode do soft reset if needed
> - DEBUG mode
>
> Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>

Reviewed-by: Guenter Roeck <linux@roeck-us.net>

> ---
>   drivers/watchdog/Kconfig       |  10 +
>   drivers/watchdog/Makefile      |   1 +
>   drivers/watchdog/asm9260_wdt.c | 403 +++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 414 insertions(+)
>   create mode 100644 drivers/watchdog/asm9260_wdt.c
>
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index c68edc1..9cd9b75 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -173,6 +173,16 @@ config ARM_SP805_WATCHDOG
>   	  ARM Primecell SP805 Watchdog timer. This will reboot your system when
>   	  the timeout is reached.
>
> +config ASM9260_WATCHDOG
> +	tristate "Alphascale ASM9260 watchdog"
> +	depends on MACH_ASM9260
> +	depends on OF
> +	select WATCHDOG_CORE
> +	select RESET_CONTROLLER
> +	help
> +	  Watchdog timer embedded into Alphascale asm9260 chips. This will reboot your
> +	  system when the timeout is reached.
> +
>   config AT91RM9200_WATCHDOG
>   	tristate "AT91RM9200 watchdog"
>   	depends on SOC_AT91RM9200 && MFD_SYSCON
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index 0c616e3..bd7b0cd 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -30,6 +30,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
>
>   # ARM Architecture
>   obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o
> +obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o
>   obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
>   obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
>   obj-$(CONFIG_CADENCE_WATCHDOG) += cadence_wdt.o
> diff --git a/drivers/watchdog/asm9260_wdt.c b/drivers/watchdog/asm9260_wdt.c
> new file mode 100644
> index 0000000..1c22ff4
> --- /dev/null
> +++ b/drivers/watchdog/asm9260_wdt.c
> @@ -0,0 +1,403 @@
> +/*
> + * Watchdog driver for Alphascale ASM9260.
> + *
> + * Copyright (c) 2014 Oleksij Rempel <linux@rempel-privat.de>
> + *
> + * Licensed under GPLv2 or later.
> + */
> +
> +#include <linux/bitops.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/reboot.h>
> +#include <linux/reset.h>
> +#include <linux/watchdog.h>
> +
> +#define CLOCK_FREQ	1000000
> +
> +/* Watchdog Mode register */
> +#define HW_WDMOD			0x00
> +/* Wake interrupt. Set by HW, can't be cleared. */
> +#define BM_MOD_WDINT			BIT(3)
> +/* This bit set if timeout reached. Cleared by SW. */
> +#define BM_MOD_WDTOF			BIT(2)
> +/* HW Reset on timeout */
> +#define BM_MOD_WDRESET			BIT(1)
> +/* WD enable */
> +#define BM_MOD_WDEN			BIT(0)
> +
> +/*
> + * Watchdog Timer Constant register
> + * Minimal value is 0xff, the meaning of this value
> + * depends on used clock: T = WDCLK * (0xff + 1) * 4
> + */
> +#define HW_WDTC				0x04
> +#define BM_WDTC_MAX(freq)		(0x7fffffff / (freq))
> +
> +/* Watchdog Feed register */
> +#define HW_WDFEED			0x08
> +
> +/* Watchdog Timer Value register */
> +#define HW_WDTV				0x0c
> +
> +#define ASM9260_WDT_DEFAULT_TIMEOUT	30
> +
> +enum asm9260_wdt_mode {
> +	HW_RESET,
> +	SW_RESET,
> +	DEBUG,
> +};
> +
> +struct asm9260_wdt_priv {
> +	struct device		*dev;
> +	struct watchdog_device	wdd;
> +	struct clk		*clk;
> +	struct clk		*clk_ahb;
> +	struct reset_control	*rst;
> +	struct notifier_block	restart_handler;
> +
> +	void __iomem		*iobase;
> +	int			irq;
> +	unsigned long		wdt_freq;
> +	enum asm9260_wdt_mode	mode;
> +};
> +
> +static int asm9260_wdt_feed(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +
> +	iowrite32(0xaa, priv->iobase + HW_WDFEED);
> +	iowrite32(0x55, priv->iobase + HW_WDFEED);
> +
> +	return 0;
> +}
> +
> +static unsigned int asm9260_wdt_gettimeleft(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +	u32 counter;
> +
> +	counter = ioread32(priv->iobase + HW_WDTV);
> +
> +	return DIV_ROUND_CLOSEST(counter, priv->wdt_freq);
> +}
> +
> +static int asm9260_wdt_updatetimeout(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +	u32 counter;
> +
> +	counter = wdd->timeout * priv->wdt_freq;
> +
> +	iowrite32(counter, priv->iobase + HW_WDTC);
> +
> +	return 0;
> +}
> +
> +static int asm9260_wdt_enable(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +	u32 mode = 0;
> +
> +	if (priv->mode == HW_RESET)
> +		mode = BM_MOD_WDRESET;
> +
> +	iowrite32(BM_MOD_WDEN | mode, priv->iobase + HW_WDMOD);
> +
> +	asm9260_wdt_updatetimeout(wdd);
> +
> +	asm9260_wdt_feed(wdd);
> +
> +	return 0;
> +}
> +
> +static int asm9260_wdt_disable(struct watchdog_device *wdd)
> +{
> +	struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> +
> +	/* The only way to disable WD is to reset it. */
> +	reset_control_assert(priv->rst);
> +	reset_control_deassert(priv->rst);
> +
> +	return 0;
> +}
> +
> +static int asm9260_wdt_settimeout(struct watchdog_device *wdd, unsigned int to)
> +{
> +	wdd->timeout = to;
> +	asm9260_wdt_updatetimeout(wdd);
> +
> +	return 0;
> +}
> +
> +static void asm9260_wdt_sys_reset(struct asm9260_wdt_priv *priv)
> +{
> +	/* init WD if it was not started */
> +
> +	iowrite32(BM_MOD_WDEN | BM_MOD_WDRESET, priv->iobase + HW_WDMOD);
> +
> +	iowrite32(0xff, priv->iobase + HW_WDTC);
> +	/* first pass correct sequence */
> +	asm9260_wdt_feed(&priv->wdd);
> +	/*
> +	 * Then write wrong pattern to the feed to trigger reset
> +	 * ASAP.
> +	 */
> +	iowrite32(0xff, priv->iobase + HW_WDFEED);
> +
> +	mdelay(1000);
> +}
> +
> +static irqreturn_t asm9260_wdt_irq(int irq, void *devid)
> +{
> +	struct asm9260_wdt_priv *priv = devid;
> +	u32 stat;
> +
> +	stat = ioread32(priv->iobase + HW_WDMOD);
> +	if (!(stat & BM_MOD_WDINT))
> +		return IRQ_NONE;
> +
> +	if (priv->mode == DEBUG) {
> +		dev_info(priv->dev, "Watchdog Timeout. Do nothing.\n");
> +	} else {
> +		dev_info(priv->dev, "Watchdog Timeout. Doing SW Reset.\n");
> +		asm9260_wdt_sys_reset(priv);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int asm9260_restart_handler(struct notifier_block *this,
> +				   unsigned long mode, void *cmd)
> +{
> +	struct asm9260_wdt_priv *priv =
> +		container_of(this, struct asm9260_wdt_priv, restart_handler);
> +
> +	asm9260_wdt_sys_reset(priv);
> +
> +	return NOTIFY_DONE;
> +}
> +
> +static const struct watchdog_info asm9260_wdt_ident = {
> +	.options          =     WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING
> +				| WDIOF_MAGICCLOSE,
> +	.identity         =	"Alphascale asm9260 Watchdog",
> +};
> +
> +static struct watchdog_ops asm9260_wdt_ops = {
> +	.owner		= THIS_MODULE,
> +	.start		= asm9260_wdt_enable,
> +	.stop		= asm9260_wdt_disable,
> +	.get_timeleft	= asm9260_wdt_gettimeleft,
> +	.ping		= asm9260_wdt_feed,
> +	.set_timeout	= asm9260_wdt_settimeout,
> +};
> +
> +static int __init asm9260_wdt_get_dt_clks(struct asm9260_wdt_priv *priv)
> +{
> +	int err;
> +	unsigned long clk;
> +
> +	priv->clk = devm_clk_get(priv->dev, "mod");
> +	if (IS_ERR(priv->clk)) {
> +		dev_err(priv->dev, "Failed to get \"mod\" clk\n");
> +		return PTR_ERR(priv->clk);
> +	}
> +
> +	/* configure AHB clock */
> +	priv->clk_ahb = devm_clk_get(priv->dev, "ahb");
> +	if (IS_ERR(priv->clk_ahb)) {
> +		dev_err(priv->dev, "Failed to get \"ahb\" clk\n");
> +		return PTR_ERR(priv->clk_ahb);
> +	}
> +
> +	err = clk_prepare_enable(priv->clk_ahb);
> +	if (err) {
> +		dev_err(priv->dev, "Failed to enable ahb_clk!\n");
> +		return err;
> +	}
> +
> +	err = clk_set_rate(priv->clk, CLOCK_FREQ);
> +	if (err) {
> +		clk_disable_unprepare(priv->clk_ahb);
> +		dev_err(priv->dev, "Failed to set rate!\n");
> +		return err;
> +	}
> +
> +	err = clk_prepare_enable(priv->clk);
> +	if (err) {
> +		clk_disable_unprepare(priv->clk_ahb);
> +		dev_err(priv->dev, "Failed to enable clk!\n");
> +		return err;
> +	}
> +
> +	/* wdt has internal divider */
> +	clk = clk_get_rate(priv->clk);
> +	if (!clk) {
> +		clk_disable_unprepare(priv->clk);
> +		clk_disable_unprepare(priv->clk_ahb);
> +		dev_err(priv->dev, "Failed, clk is 0!\n");
> +		return -EINVAL;
> +	}
> +
> +	priv->wdt_freq = clk / 2;
> +
> +	return 0;
> +}
> +
> +static void __init asm9260_wdt_get_dt_mode(struct asm9260_wdt_priv *priv)
> +{
> +	const char *tmp;
> +	int ret;
> +
> +	/* default mode */
> +	priv->mode = HW_RESET;
> +
> +	ret = of_property_read_string(priv->dev->of_node,
> +				      "alphascale,mode", &tmp);
> +	if (ret < 0)
> +		return;
> +
> +	if (!strcmp(tmp, "hw"))
> +		priv->mode = HW_RESET;
> +	else if (!strcmp(tmp, "sw"))
> +		priv->mode = SW_RESET;
> +	else if (!strcmp(tmp, "debug"))
> +		priv->mode = DEBUG;
> +	else
> +		dev_warn(priv->dev, "unknown reset-type: %s. Using default \"hw\" mode.",
> +			 tmp);
> +}
> +
> +static int __init asm9260_wdt_probe(struct platform_device *pdev)
> +{
> +	struct asm9260_wdt_priv *priv;
> +	struct watchdog_device *wdd;
> +	struct resource *res;
> +	int ret;
> +	const char * const mode_name[] = { "hw", "sw", "debug", };
> +
> +	priv = devm_kzalloc(&pdev->dev, sizeof(struct asm9260_wdt_priv),
> +			    GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->dev = &pdev->dev;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	priv->iobase = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(priv->iobase))
> +		return PTR_ERR(priv->iobase);
> +
> +	ret = asm9260_wdt_get_dt_clks(priv);
> +	if (ret)
> +		return ret;
> +
> +	priv->rst = devm_reset_control_get(&pdev->dev, "wdt_rst");
> +	if (IS_ERR(priv->rst))
> +		return PTR_ERR(priv->rst);
> +
> +	wdd = &priv->wdd;
> +	wdd->info = &asm9260_wdt_ident;
> +	wdd->ops = &asm9260_wdt_ops;
> +	wdd->min_timeout = 1;
> +	wdd->max_timeout = BM_WDTC_MAX(priv->wdt_freq);
> +	wdd->parent = &pdev->dev;
> +
> +	watchdog_set_drvdata(wdd, priv);
> +
> +	/*
> +	 * If 'timeout-sec' unspecified in devicetree, assume a 30 second
> +	 * default, unless the max timeout is less than 30 seconds, then use
> +	 * the max instead.
> +	 */
> +	wdd->timeout = ASM9260_WDT_DEFAULT_TIMEOUT;
> +	watchdog_init_timeout(wdd, 0, &pdev->dev);
> +
> +	asm9260_wdt_get_dt_mode(priv);
> +
> +	if (priv->mode != HW_RESET)
> +		priv->irq = platform_get_irq(pdev, 0);
> +
> +	if (priv->irq > 0) {
> +		/*
> +		 * Not all supported platforms specify an interrupt for the
> +		 * watchdog, so let's make it optional.
> +		 */
> +		ret = devm_request_irq(&pdev->dev, priv->irq,
> +				       asm9260_wdt_irq, 0, pdev->name, priv);
> +		if (ret < 0)
> +			dev_warn(&pdev->dev, "failed to request IRQ\n");
> +	}
> +
> +	ret = watchdog_register_device(wdd);
> +	if (ret)
> +		goto clk_off;
> +
> +	platform_set_drvdata(pdev, priv);
> +
> +	priv->restart_handler.notifier_call = asm9260_restart_handler;
> +	priv->restart_handler.priority = 128;
> +	ret = register_restart_handler(&priv->restart_handler);
> +	if (ret)
> +		dev_warn(&pdev->dev, "cannot register restart handler\n");
> +
> +	dev_info(&pdev->dev, "Watchdog enabled (timeout: %d sec, mode: %s)\n",
> +		 wdd->timeout, mode_name[priv->mode]);
> +	return 0;
> +
> +clk_off:
> +	clk_disable_unprepare(priv->clk);
> +	clk_disable_unprepare(priv->clk_ahb);
> +	return ret;
> +}
> +
> +static void asm9260_wdt_shutdown(struct platform_device *pdev)
> +{
> +	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
> +
> +	asm9260_wdt_disable(&priv->wdd);
> +}
> +
> +static int __exit asm9260_wdt_remove(struct platform_device *pdev)
> +{
> +	struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
> +
> +	asm9260_wdt_disable(&priv->wdd);
> +
> +	unregister_restart_handler(&priv->restart_handler);
> +
> +	watchdog_unregister_device(&priv->wdd);
> +
> +	clk_disable_unprepare(priv->clk);
> +	clk_disable_unprepare(priv->clk_ahb);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id asm9260_wdt_of_match[] = {
> +	{ .compatible = "alphascale,asm9260-wdt"},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, asm9260_wdt_of_match);
> +
> +static struct platform_driver asm9260_wdt_driver = {
> +	.driver = {
> +		.name = "asm9260-wdt",
> +		.owner = THIS_MODULE,
> +		.of_match_table	= asm9260_wdt_of_match,
> +	},
> +	.probe = asm9260_wdt_probe,
> +	.remove = asm9260_wdt_remove,
> +	.shutdown = asm9260_wdt_shutdown,
> +};
> +module_platform_driver(asm9260_wdt_driver);
> +
> +MODULE_DESCRIPTION("asm9260 WatchDog Timer Driver");
> +MODULE_AUTHOR("Oleksij Rempel <linux@rempel-privat.de>");
> +MODULE_LICENSE("GPL");
>


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

* Re: [PATCH v4 1/2] watchdog: add Alphascale asm9260-wdt driver
  2015-11-25 19:33                             ` Oleksij Rempel
@ 2015-11-30 16:10                                 ` Guenter Roeck
  -1 siblings, 0 replies; 43+ messages in thread
From: Guenter Roeck @ 2015-11-30 16:10 UTC (permalink / raw)
  To: Oleksij Rempel, linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	wim-IQzOog9fTRqzQB+pC5nmwQ, devicetree-u79uwXL29TY76Z2rM5mHXA,
	robh-DgEjT+Ai2ygdnm+yROfE0A

On 11/25/2015 11:33 AM, Oleksij Rempel wrote:
> Add WD support for Alphascale asm9260 SoC. This driver
> provide support for different function modes:
> - HW mode to trigger SoC reset on timeout
> - SW mode do soft reset if needed
> - DEBUG mode
>
> Signed-off-by: Oleksij Rempel <linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>

Reviewed-by: Guenter Roeck <linux-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org>

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v4 1/2] watchdog: add Alphascale asm9260-wdt driver
@ 2015-11-30 16:10                                 ` Guenter Roeck
  0 siblings, 0 replies; 43+ messages in thread
From: Guenter Roeck @ 2015-11-30 16:10 UTC (permalink / raw)
  To: Oleksij Rempel, linux-watchdog, wim, devicetree, robh

On 11/25/2015 11:33 AM, Oleksij Rempel wrote:
> Add WD support for Alphascale asm9260 SoC. This driver
> provide support for different function modes:
> - HW mode to trigger SoC reset on timeout
> - SW mode do soft reset if needed
> - DEBUG mode
>
> Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>

Reviewed-by: Guenter Roeck <linux@roeck-us.net>


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

* Re: [PATCH v4 0/2] add Alphascale asm9260-wdt driver
  2015-11-25 19:33                         ` Oleksij Rempel
@ 2015-12-28 21:49                             ` Wim Van Sebroeck
  -1 siblings, 0 replies; 43+ messages in thread
From: Wim Van Sebroeck @ 2015-12-28 21:49 UTC (permalink / raw)
  To: Oleksij Rempel
  Cc: linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	linux-0h96xk9xTtrk1uMJSBkQmQ, devicetree-u79uwXL29TY76Z2rM5mHXA,
	robh-DgEjT+Ai2ygdnm+yROfE0A

Hi Oleksij,

> v2:
> - fix DT types
> - remove obsolet comments
> - add clk error handling
> - reduce log noise
> - allow to return an error in asm9260_wdt_get_dt_mode
> 
> v3:
> - split patch to two parts, binding documentation and driver
> - spelling fixes
> - make reset a required property. It will make the code easier
> - reduce timeout for asm9260_wdt_sys_reset
> - rework error handling in asm9260_wdt_get_dt_clks
> - asm9260_wdt_get_dt_mode - convert return type from int to void
> - don't touch watchdog_set_nowayout
> - don't do dev_err() if driver is still working, use dev_warn instead.
> - asm9260_wdt_remove - use asm9260_wdt_disable() instead of asm9260_wdt_shutdown()
> 
> v4:
> - update commit message
> - remove IRQF_SHARED flag. This SOC don't need it.
> - make "Watchdog enabled" more user freandly
> - add watchdog_unregister_device to asm9260_wdt_remove
> 
> Oleksij Rempel (2):
>   watchdog: add Alphascale asm9260-wdt driver
>   DT: watchdog: add Alphascale asm9260 watchdog binding documentation.
> 
>  .../bindings/watchdog/alphascale-asm9260.txt       |  35 ++
>  drivers/watchdog/Kconfig                           |  10 +
>  drivers/watchdog/Makefile                          |   1 +
>  drivers/watchdog/asm9260_wdt.c                     | 403 +++++++++++++++++++++
>  4 files changed, 449 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
>  create mode 100644 drivers/watchdog/asm9260_wdt.c
> 
> -- 
> 2.5.0
>

This new driver has been added o linux-watchdog-next.

Kind regards,
Wim.

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v4 0/2] add Alphascale asm9260-wdt driver
@ 2015-12-28 21:49                             ` Wim Van Sebroeck
  0 siblings, 0 replies; 43+ messages in thread
From: Wim Van Sebroeck @ 2015-12-28 21:49 UTC (permalink / raw)
  To: Oleksij Rempel; +Cc: linux-watchdog, linux, devicetree, robh

Hi Oleksij,

> v2:
> - fix DT types
> - remove obsolet comments
> - add clk error handling
> - reduce log noise
> - allow to return an error in asm9260_wdt_get_dt_mode
> 
> v3:
> - split patch to two parts, binding documentation and driver
> - spelling fixes
> - make reset a required property. It will make the code easier
> - reduce timeout for asm9260_wdt_sys_reset
> - rework error handling in asm9260_wdt_get_dt_clks
> - asm9260_wdt_get_dt_mode - convert return type from int to void
> - don't touch watchdog_set_nowayout
> - don't do dev_err() if driver is still working, use dev_warn instead.
> - asm9260_wdt_remove - use asm9260_wdt_disable() instead of asm9260_wdt_shutdown()
> 
> v4:
> - update commit message
> - remove IRQF_SHARED flag. This SOC don't need it.
> - make "Watchdog enabled" more user freandly
> - add watchdog_unregister_device to asm9260_wdt_remove
> 
> Oleksij Rempel (2):
>   watchdog: add Alphascale asm9260-wdt driver
>   DT: watchdog: add Alphascale asm9260 watchdog binding documentation.
> 
>  .../bindings/watchdog/alphascale-asm9260.txt       |  35 ++
>  drivers/watchdog/Kconfig                           |  10 +
>  drivers/watchdog/Makefile                          |   1 +
>  drivers/watchdog/asm9260_wdt.c                     | 403 +++++++++++++++++++++
>  4 files changed, 449 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
>  create mode 100644 drivers/watchdog/asm9260_wdt.c
> 
> -- 
> 2.5.0
>

This new driver has been added o linux-watchdog-next.

Kind regards,
Wim.


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

end of thread, other threads:[~2015-12-28 21:49 UTC | newest]

Thread overview: 43+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-09-18  9:23 [PATCH] watchdog: add Alphascale asm9260-wdt driver Oleksij Rempel
2015-10-20  7:49 ` Oleksij Rempel
2015-10-26 12:31   ` Guenter Roeck
2015-10-27  2:31 ` Guenter Roeck
2015-10-29  7:20   ` Oleksij Rempel
     [not found]   ` <20151027023132.GA1270-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org>
2015-11-05  9:06     ` [PATCH v2] " Oleksij Rempel
2015-11-05  9:06       ` Oleksij Rempel
     [not found]       ` <1446714416-22587-1-git-send-email-linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>
2015-11-05  9:06         ` [PATCH v2] watchdog: " Oleksij Rempel
2015-11-05  9:06           ` Oleksij Rempel
     [not found]           ` <1446714416-22587-2-git-send-email-linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>
2015-11-05 16:32             ` Guenter Roeck
2015-11-05 16:32               ` Guenter Roeck
2015-11-05 20:43             ` Rob Herring
2015-11-05 20:43               ` Rob Herring
2015-11-23  7:11               ` Oleksij Rempel
2015-11-23  7:11                 ` Oleksij Rempel
     [not found]                 ` <5652BC30.7060803-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>
2015-11-23  7:26                   ` Guenter Roeck
2015-11-23  7:26                     ` Guenter Roeck
     [not found]                     ` <5652BFA5.9010101-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org>
2015-11-23  9:51                       ` Oleksij Rempel
2015-11-23  9:51                         ` Oleksij Rempel
     [not found]                         ` <5652E1A7.2090108-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>
2015-11-23 15:47                           ` Guenter Roeck
2015-11-23 15:47                             ` Guenter Roeck
2015-11-24 21:40                       ` [PATCH v3 0/2] " Oleksij Rempel
2015-11-24 21:40                         ` Oleksij Rempel
     [not found]                         ` <1448401233-1064-1-git-send-email-linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>
2015-11-24 21:40                           ` [PATCH v3 1/2] watchdog: " Oleksij Rempel
2015-11-24 21:40                             ` Oleksij Rempel
     [not found]                             ` <1448401233-1064-2-git-send-email-linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>
2015-11-25  3:22                               ` Guenter Roeck
2015-11-25  3:22                                 ` Guenter Roeck
2015-11-24 21:40                           ` [PATCH v3 2/2] DT: watchdog: add Alphascale asm9260 watchdog binding documentation Oleksij Rempel
2015-11-24 21:40                             ` Oleksij Rempel
     [not found]                             ` <1448401233-1064-3-git-send-email-linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>
2015-11-25 20:06                               ` Rob Herring
2015-11-25 20:06                                 ` Rob Herring
2015-11-25 19:33                       ` [PATCH v4 0/2] add Alphascale asm9260-wdt driver Oleksij Rempel
2015-11-25 19:33                         ` Oleksij Rempel
     [not found]                         ` <1448480003-8029-1-git-send-email-linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>
2015-11-25 19:33                           ` [PATCH v4 1/2] watchdog: " Oleksij Rempel
2015-11-25 19:33                             ` Oleksij Rempel
     [not found]                             ` <1448480003-8029-2-git-send-email-linux-YEK0n+YFykbzxQdaRaTXBw@public.gmane.org>
2015-11-30 16:10                               ` Guenter Roeck
2015-11-30 16:10                                 ` Guenter Roeck
2015-11-30 16:10                               ` Guenter Roeck
2015-11-30 16:10                                 ` Guenter Roeck
2015-11-25 19:33                           ` [PATCH v4 2/2] DT: watchdog: add Alphascale asm9260 watchdog binding documentation Oleksij Rempel
2015-11-25 19:33                             ` Oleksij Rempel
2015-12-28 21:49                           ` [PATCH v4 0/2] add Alphascale asm9260-wdt driver Wim Van Sebroeck
2015-12-28 21:49                             ` Wim Van Sebroeck

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.