Skip to contentSkip to conversation

watchdog: New watchdog driver for MEN A21 watchdogs

PATCH Superseded Johannes Thumshirn 4 participants 38 messages May 31, 2013 – Jul 8, 2013

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

PATCH v5 1/2 watchdog: New watchdog driver for MEN A21 watchdogs

This patch adds the driver for the watchdog devices found on MEN Mikro Elektronik A21 VMEbus CPU Carrier Boards. It has DT-support and uses the watchdog framework.

Revision 2:
* Removed unneeded open flag in struct a21_wdt_drv
* Corrected 3bit reason code from gpio
* Additional sysfs files are now part of watchdog sysfs
* Changed OFF/ON delay in ping from 400ms to 10ns
* Reworked timeout setting
* Removed a21_wdt_ioctl(...)

Revision 3:
* Changed pr_{err,info} to dev_{err,info}
* Removed out of memory error print
* Transition from "fast" to "slow" mode not allowed by chip

Revision 4:
* Remove reboot_notifier and place disable code into platform_device's shutdown function
* Removed sysfs interface

Revision 5:

* Added setting of .bootstatus on driver init
* Added initial timeout on driver init

---
 MAINTAINERS                   |    6 ++
 drivers/watchdog/Kconfig      |    8 ++
 drivers/watchdog/Makefile     |    1 +
 drivers/watchdog/mena21_wdt.c |  234 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 249 insertions(+)
 create mode 100644 drivers/watchdog/mena21_wdt.c
MAINTAINERS +6 -0
index 7714c3c..023945a 100644
@@ -5307,6 +5307,12 @@ F:	drivers/mtd/
 F:	include/linux/mtd/
 F:	include/uapi/mtd/
 
+MEN A21 WATCHDOG DRIVER
+M:  	Johannes Thumshirn <[email protected]>
+L:	[email protected]
+S:	Supported
+F:	drivers/watchdog/mena21_wdt.c
+
 METAG ARCHITECTURE
 M:	James Hogan <[email protected]>
 S:	Supported
drivers/watchdog/Kconfig +8 -0
index e89fc31..192b84d 100644
@@ -1172,6 +1172,14 @@ config BOOKE_WDT_DEFAULT_TIMEOUT
 
 	  The value can be overridden by the wdt_period command-line parameter.
 
+config MEN_A21_WDT
+       tristate "MEN A21 VME CPU Carrier Board Watchdog Timer"
+       select WATCHDOG_CORE
+       help
+        Watchdog driver for MEN A21 VMEbus CPU Carrier Boards.
+
+	If unsure select N here.
+
 # PPC64 Architecture
 
 config WATCHDOG_RTAS
drivers/watchdog/Makefile +1 -0
index a300b94..bffdcb1 100644
@@ -143,6 +143,7 @@ obj-$(CONFIG_8xxx_WDT) += mpc8xxx_wdt.o
 obj-$(CONFIG_MV64X60_WDT) += mv64x60_wdt.o
 obj-$(CONFIG_PIKA_WDT) += pika_wdt.o
 obj-$(CONFIG_BOOKE_WDT) += booke_wdt.o
+obj-$(CONFIG_MEN_A21_WDT) += mena21_wdt.o
 
 # PPC64 Architecture
 obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o
drivers/watchdog/mena21_wdt.c new file +234 -0

234 lines not on this page · raw message

--
1.7.9.5

#1 lore raw

PATCH v2 2/2 This patch adds a sysfs interface for the watchdog device found on MEN A21 Boards.

The newly generated files are:
* rebootcause:
Can be one of:
Power on Reset,
CPU Reset Request,
Push Button,
FPGA Reset Request,
Watchdog,
Local Power Bad,
Invalid or
BDI
and shows the reason of the boards last reboot.

* active:
Shows if the watchdog CPLD is actually running

* allow_disable:
Shows if the watchdog is allowed to be disabled (NOWAYOUT disabled)

* fastmode:
Shows if the CPLD is running in fast mode (1s timeout), once it is in fastmode it can't be switched back to slow mode (30s timeout) until the next reboot.

Revision 2:
* Re-worked sysfs patch to apply on re-worked base

---
 drivers/watchdog/mena21_wdt.c |   88 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 88 insertions(+)
drivers/watchdog/mena21_wdt.c +88 -0
index e586c2e..e11d869 100644
@@ -19,6 +19,17 @@
 #include <linux/delay.h>
 #include <linux/bitops.h>
 
+static char *reset_causes[] = {
+	"Power On Reset",
+	"CPU Reset Request",
+	"Push Button",
+	"FPGA Reset Request",
+	"Watchdog",
+	"Local Power Bad",
+	"Invalid",
+	"BDI",
+};
+
 #define GPIO_WD_ENAB	169
 #define GPIO_WD_FAST	170
 #define GPIO_WD_TRIG	171
@@ -49,6 +60,74 @@ static int a21_wdt_get_bootstatus(unsigned int *reset)
 	return 0;
 }
 
+static ssize_t rebootcause_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	unsigned int reset = 0;
+	int i;
+
+	for (i = 0; i < 3; i++)
+		reset |= gpio_get_value(GPIO_RST_CAUSE_BASE + i)
+			 ? (1 << i) : 0;
+
+	if (reset >= 8)
+		return -EIO;
+
+	return sprintf(buf, "%s\n", reset_causes[reset]);
+}
+static DEVICE_ATTR(rebootcause, S_IRUGO, rebootcause_show, NULL);
+
+static ssize_t active_show(struct device *dev, struct device_attribute *attr,
+			   char *buf)
+{
+	return sprintf(buf, "%d\n", !!gpio_get_value(GPIO_WD_ENAB));
+}
+static DEVICE_ATTR(active, S_IRUGO, active_show, NULL);
+
+static ssize_t allow_disable_show(struct device *dev,
+				  struct device_attribute *attr,
+				  char *buf)
+{
+	return sprintf(buf, "%d\n", !nowayout);
+}
+static DEVICE_ATTR(allow_disable, S_IRUGO, allow_disable_show, NULL);
+
+static ssize_t fastmode_show(struct device *dev, struct device_attribute *attr,
+			     char *buf)
+{
+	return sprintf(buf, "%d\n", !!gpio_get_value(GPIO_WD_FAST));
+}
+static DEVICE_ATTR(fastmode, S_IRUGO, fastmode_show, NULL);
+
+static int a21_wdt_create_files(struct watchdog_device *wdev)
+{
+	int ret;
+
+	ret = device_create_file(wdev->dev, &dev_attr_rebootcause);
+	if (ret)
+		return ret;
+	ret = device_create_file(wdev->dev, &dev_attr_active);
+	if (ret)
+		return ret;
+	ret = device_create_file(wdev->dev, &dev_attr_allow_disable);
+	if (ret)
+		return ret;
+	ret = device_create_file(wdev->dev, &dev_attr_fastmode);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static void a21_wdt_remove_files(struct watchdog_device *wdev)
+{
+	device_remove_file(wdev->dev, &dev_attr_rebootcause);
+	device_remove_file(wdev->dev, &dev_attr_active);
+	device_remove_file(wdev->dev, &dev_attr_allow_disable);
+	device_remove_file(wdev->dev, &dev_attr_fastmode);
+}
+
 static int a21_wdt_start(struct watchdog_device *wdt)
 {
 	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
@@ -181,10 +260,18 @@ static int a21_wdt_probe(struct platform_device *pdev)
 			a21_wdt.bootstatus |= WDIOF_EXTERN2;
 	}
 
+	ret = a21_wdt_create_files(&a21_wdt);
+	if (ret) {
+		dev_err(&pdev->dev, "Cannot create sysfs entries\n");
+		goto err_create_sysfs;
+	}
+
 	dev_set_drvdata(&pdev->dev, drv);
 
 	return 0;
 
+err_create_sysfs:
+	watchdog_unregister_device(&drv->wdt);
 err_register_wd:
 	mutex_destroy(&drv->lock);
 
@@ -198,6 +285,7 @@ static int a21_wdt_remove(struct platform_device *pdev)
 	dev_warn(&pdev->dev,
 		"Unregistering A21 watchdog driver, board may reboot\n");
 
+	a21_wdt_remove_files(&drv->wdt);
 
 	watchdog_unregister_device(&drv->wdt);
 

--
1.7.9.5

#2 lore raw
In reply to Johannes Thumshirn
Quoted 3 lines · Johannes Thumshirn

This patch adds the driver for the watchdog devices found on MEN Mikro Elektronik A21 VMEbus CPU Carrier Boards. It has DT-support and uses the watchdog framework.

[...]

Quoted 6 lines
drivers/watchdog/mena21_wdt.c new file +3 -1
+MODULE_DESCRIPTION("MEN A21 Watchdog");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:a21-watchdog");
--

1.7.9.5

Args, sorry messed up the thread :-(.

I'm not so firm with using mutt for patches, but git send-email doesn't seem to like Exchange here...

Johannes

maintainer
#3 lore raw
In reply to Johannes Thumshirn
Quoted 248 lines · Johannes Thumshirn

This patch adds the driver for the watchdog devices found on MEN Mikro Elektronik A21 VMEbus CPU Carrier Boards. It has DT-support and uses the watchdog framework.

Revision 2:
* Removed unneeded open flag in struct a21_wdt_drv
* Corrected 3bit reason code from gpio
* Additional sysfs files are now part of watchdog sysfs
* Changed OFF/ON delay in ping from 400ms to 10ns
* Reworked timeout setting
* Removed a21_wdt_ioctl(...)

Revision 3:
* Changed pr_{err,info} to dev_{err,info}
* Removed out of memory error print
* Transition from "fast" to "slow" mode not allowed by chip

Revision 4:
* Remove reboot_notifier and place disable code into platform_device's shutdown function
* Removed sysfs interface

Revision 5:

* Added setting of .bootstatus on driver init
* Added initial timeout on driver init

---
 MAINTAINERS                   |    6 ++
 drivers/watchdog/Kconfig      |    8 ++
 drivers/watchdog/Makefile     |    1 +
 drivers/watchdog/mena21_wdt.c |  234 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 249 insertions(+)
 create mode 100644 drivers/watchdog/mena21_wdt.c
MAINTAINERS +6 -0
index 7714c3c..023945a 100644
@@ -5307,6 +5307,12 @@ F:	drivers/mtd/
 F:	include/linux/mtd/
 F:	include/uapi/mtd/
 
+MEN A21 WATCHDOG DRIVER
+M:  	Johannes Thumshirn <[email protected]>
+L:	[email protected]
+S:	Supported
+F:	drivers/watchdog/mena21_wdt.c
+
 METAG ARCHITECTURE
 M:	James Hogan <[email protected]>
 S:	Supported
drivers/watchdog/Kconfig +8 -0
index e89fc31..192b84d 100644
@@ -1172,6 +1172,14 @@ config BOOKE_WDT_DEFAULT_TIMEOUT
 
 	  The value can be overridden by the wdt_period command-line parameter.
 
+config MEN_A21_WDT
+       tristate "MEN A21 VME CPU Carrier Board Watchdog Timer"
+       select WATCHDOG_CORE
+       help
+        Watchdog driver for MEN A21 VMEbus CPU Carrier Boards.
+
+	If unsure select N here.
+
 # PPC64 Architecture
 
 config WATCHDOG_RTAS
drivers/watchdog/Makefile +1 -0
index a300b94..bffdcb1 100644
@@ -143,6 +143,7 @@ obj-$(CONFIG_8xxx_WDT) += mpc8xxx_wdt.o
 obj-$(CONFIG_MV64X60_WDT) += mv64x60_wdt.o
 obj-$(CONFIG_PIKA_WDT) += pika_wdt.o
 obj-$(CONFIG_BOOKE_WDT) += booke_wdt.o
+obj-$(CONFIG_MEN_A21_WDT) += mena21_wdt.o
 
 # PPC64 Architecture
 obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o
drivers/watchdog/mena21_wdt.c new file +159 -0
new file mode 100644
index 0000000..e586c2e
@@ -0,0 +1,234 @@
+/*
+ * Watchdog driver for the A21 VME CPU Boards
+ *
+ * Copyright (C) 2013 MEN Mikro Elektronik Nuernberg GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+#include <linux/uaccess.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+
+#define GPIO_WD_ENAB	169
+#define GPIO_WD_FAST	170
+#define GPIO_WD_TRIG	171
+
+#define GPIO_RST_CAUSE_BASE 166
+
+struct a21_wdt_drv {
+	struct watchdog_device wdt;
+	struct mutex lock;
+};
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+			    __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static int a21_wdt_get_bootstatus(unsigned int *reset)
+{
+	int i;
+
+	for (i = 0; i < 3; i++)
+		*reset |= gpio_get_value(GPIO_RST_CAUSE_BASE + i)
+			? (1 << i) : 0;
+
+	if (*reset >= 8)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int a21_wdt_start(struct watchdog_device *wdt)
+{
+	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+	mutex_lock(&drv->lock);
+
+	gpio_set_value(GPIO_WD_ENAB, 1);
+
+	mutex_unlock(&drv->lock);
+
+	return 0;
+}
+
+static int a21_wdt_stop(struct watchdog_device *wdt)
+{
+	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+	/* We don't stop if WDOG_NO_WAY_OUT is set */
+	if (test_bit(WDOG_NO_WAY_OUT, &wdt->status))
+		return -EINVAL;
+
+	mutex_lock(&drv->lock);
+
+	gpio_set_value(GPIO_WD_ENAB, 0);
+
+	mutex_unlock(&drv->lock);
+
+	return 0;
+}
+
+static int a21_wdt_ping(struct watchdog_device *wdt)
+{
+	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+	mutex_lock(&drv->lock);
+
+	gpio_set_value(GPIO_WD_TRIG, 0);
+	ndelay(10);
+	gpio_set_value(GPIO_WD_TRIG, 1);
+
+	mutex_unlock(&drv->lock);
+
+	return 0;
+}
+
+static int a21_wdt_set_timeout(struct watchdog_device *wdt,
+			       unsigned int timeout)
+{
+	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+	if (timeout != 1 && timeout != 30) {
+		dev_err(wdt->dev, "Only 1 and 30 allowed as timeout\n");
+		return -EINVAL;
+	}
+
+	if (timeout == 30 && wdt->timeout == 1) {
+		dev_err(wdt->dev,
+			"Transition from fast to slow mode not allowed\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&drv->lock);
+
+	if (timeout == 1)
+		gpio_set_value(GPIO_WD_FAST, 1);
+	else
+		gpio_set_value(GPIO_WD_FAST, 0);
+
+	wdt->timeout = timeout;
+
+	mutex_unlock(&drv->lock);
+
+	return 0;
+}
+
+static const struct watchdog_info a21_wdt_info = {
+	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+	.identity = "MEN A21 Watchdog",
+};
+
+static const struct watchdog_ops a21_wdt_ops = {
+	.owner = THIS_MODULE,
+	.start = a21_wdt_start,
+	.stop = a21_wdt_stop,
+	.ping = a21_wdt_ping,
+	.set_timeout = a21_wdt_set_timeout,
+};
+
+static struct watchdog_device a21_wdt = {
+	.info = &a21_wdt_info,
+	.ops = &a21_wdt_ops,
+	.min_timeout = 1,
+	.max_timeout = 30,
+};
+
+static int a21_wdt_probe(struct platform_device *pdev)
+{
+	struct a21_wdt_drv *drv;
+	unsigned int reset = 0;
+	int ret;
+
+	dev_info(&pdev->dev, "MEN A21 watchdog timer driver enabled\n");
+
+	drv = devm_kzalloc(&pdev->dev, sizeof(struct a21_wdt_drv), GFP_KERNEL);
+	if (!drv)
+		return -ENOMEM;
+
+	mutex_init(&drv->lock);

drivers/watchdog/mena21_wdt.c line 169

Quoted 10 lines
drivers/watchdog/mena21_wdt.c new file +10 -0
+	watchdog_set_nowayout(&a21_wdt, nowayout);
+	watchdog_set_drvdata(&a21_wdt, drv);
+
+	ret = watchdog_register_device(&a21_wdt);
+	if (ret) {
+		dev_err(&pdev->dev, "Cannot register watchdog device\n");
+		goto err_register_wd;
+	}
+
+	a21_wdt.timeout = 30;

It would be better to use watchdog_init_timeout(), as it automatically supports setting the timeout through an fdt property. It should be set before registering the device with the watchdog subsystem.

Thanks,
Guenter

Quoted 73 lines
drivers/watchdog/mena21_wdt.c new file +65 -1
+	ret = a21_wdt_get_bootstatus(&reset);
+	if (ret)
+		dev_warn(&pdev->dev, "Reset Cause contains invalid data\n");
+	else {
+		if (reset == 2)
+			a21_wdt.bootstatus |= WDIOF_EXTERN1;
+		else if (reset == 4)
+			a21_wdt.bootstatus |= WDIOF_CARDRESET;
+		else if (reset == 5)
+			a21_wdt.bootstatus |= WDIOF_POWERUNDER;
+		else if (reset == 7)
+			a21_wdt.bootstatus |= WDIOF_EXTERN2;
+	}
+
+	dev_set_drvdata(&pdev->dev, drv);
+
+	return 0;
+
+err_register_wd:
+	mutex_destroy(&drv->lock);
+
+	return ret;
+}
+
+static int a21_wdt_remove(struct platform_device *pdev)
+{
+	struct a21_wdt_drv *drv = dev_get_drvdata(&pdev->dev);
+
+	dev_warn(&pdev->dev,
+		"Unregistering A21 watchdog driver, board may reboot\n");
+
+
+	watchdog_unregister_device(&drv->wdt);
+
+	mutex_destroy(&drv->lock);
+
+	return 0;
+}
+
+static void a21_wdt_shutdown(struct platform_device *pdev)
+{
+	gpio_set_value(GPIO_WD_ENAB, 0);
+}
+
+static const struct of_device_id a21_wdt_ids[] = {
+	{ .compatible = "men,a021-wdt" },
+	{ },
+};
+
+static struct platform_driver a21_wdt_driver = {
+	.probe = a21_wdt_probe,
+	.remove = a21_wdt_remove,
+	.shutdown = a21_wdt_shutdown,
+	.driver = {
+		.name = "a21-watchdog",
+		.of_match_table = a21_wdt_ids,
+	},
+};
+
+module_platform_driver(a21_wdt_driver);
+
+MODULE_AUTHOR("MEN Mikro Elektronik");
+MODULE_DESCRIPTION("MEN A21 Watchdog");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:a21-watchdog");
--

1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#4 lore raw
Quoted 255 lines · Guenter Roeck
Quoted 254 lines · Johannes Thumshirn

This patch adds the driver for the watchdog devices found on MEN Mikro Elektronik A21 VMEbus CPU Carrier Boards. It has DT-support and uses the watchdog framework.

Revision 2:
* Removed unneeded open flag in struct a21_wdt_drv
* Corrected 3bit reason code from gpio
* Additional sysfs files are now part of watchdog sysfs
* Changed OFF/ON delay in ping from 400ms to 10ns
* Reworked timeout setting
* Removed a21_wdt_ioctl(...)

Revision 3:
* Changed pr_{err,info} to dev_{err,info}
* Removed out of memory error print
* Transition from "fast" to "slow" mode not allowed by chip

Revision 4:
* Remove reboot_notifier and place disable code into platform_device's shutdown function
* Removed sysfs interface

Revision 5:

* Added setting of .bootstatus on driver init
* Added initial timeout on driver init

---
 MAINTAINERS                   |    6 ++
 drivers/watchdog/Kconfig      |    8 ++
 drivers/watchdog/Makefile     |    1 +
 drivers/watchdog/mena21_wdt.c |  234 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 249 insertions(+)
 create mode 100644 drivers/watchdog/mena21_wdt.c
MAINTAINERS +6 -0
index 7714c3c..023945a 100644
@@ -5307,6 +5307,12 @@ F:	drivers/mtd/
 F:	include/linux/mtd/
 F:	include/uapi/mtd/
 
+MEN A21 WATCHDOG DRIVER
+M:  	Johannes Thumshirn <[email protected]>
+L:	[email protected]
+S:	Supported
+F:	drivers/watchdog/mena21_wdt.c
+
 METAG ARCHITECTURE
 M:	James Hogan <[email protected]>
 S:	Supported
drivers/watchdog/Kconfig +8 -0
index e89fc31..192b84d 100644
@@ -1172,6 +1172,14 @@ config BOOKE_WDT_DEFAULT_TIMEOUT
 
 	  The value can be overridden by the wdt_period command-line parameter.
 
+config MEN_A21_WDT
+       tristate "MEN A21 VME CPU Carrier Board Watchdog Timer"
+       select WATCHDOG_CORE
+       help
+        Watchdog driver for MEN A21 VMEbus CPU Carrier Boards.
+
+	If unsure select N here.
+
 # PPC64 Architecture
 
 config WATCHDOG_RTAS
drivers/watchdog/Makefile +1 -0
index a300b94..bffdcb1 100644
@@ -143,6 +143,7 @@ obj-$(CONFIG_8xxx_WDT) += mpc8xxx_wdt.o
 obj-$(CONFIG_MV64X60_WDT) += mv64x60_wdt.o
 obj-$(CONFIG_PIKA_WDT) += pika_wdt.o
 obj-$(CONFIG_BOOKE_WDT) += booke_wdt.o
+obj-$(CONFIG_MEN_A21_WDT) += mena21_wdt.o
 
 # PPC64 Architecture
 obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o
drivers/watchdog/mena21_wdt.c new file +165 -0
new file mode 100644
index 0000000..e586c2e
@@ -0,0 +1,234 @@
+/*
+ * Watchdog driver for the A21 VME CPU Boards
+ *
+ * Copyright (C) 2013 MEN Mikro Elektronik Nuernberg GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+#include <linux/uaccess.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+
+#define GPIO_WD_ENAB	169
+#define GPIO_WD_FAST	170
+#define GPIO_WD_TRIG	171
+
+#define GPIO_RST_CAUSE_BASE 166
+
+struct a21_wdt_drv {
+	struct watchdog_device wdt;
+	struct mutex lock;
+};
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+			    __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static int a21_wdt_get_bootstatus(unsigned int *reset)
+{
+	int i;
+
+	for (i = 0; i < 3; i++)
+		*reset |= gpio_get_value(GPIO_RST_CAUSE_BASE + i)
+			? (1 << i) : 0;
+
+	if (*reset >= 8)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int a21_wdt_start(struct watchdog_device *wdt)
+{
+	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+	mutex_lock(&drv->lock);
+
+	gpio_set_value(GPIO_WD_ENAB, 1);
+
+	mutex_unlock(&drv->lock);
+
+	return 0;
+}
+
+static int a21_wdt_stop(struct watchdog_device *wdt)
+{
+	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+	/* We don't stop if WDOG_NO_WAY_OUT is set */
+	if (test_bit(WDOG_NO_WAY_OUT, &wdt->status))
+		return -EINVAL;
+
+	mutex_lock(&drv->lock);
+
+	gpio_set_value(GPIO_WD_ENAB, 0);
+
+	mutex_unlock(&drv->lock);
+
+	return 0;
+}
+
+static int a21_wdt_ping(struct watchdog_device *wdt)
+{
+	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+	mutex_lock(&drv->lock);
+
+	gpio_set_value(GPIO_WD_TRIG, 0);
+	ndelay(10);
+	gpio_set_value(GPIO_WD_TRIG, 1);
+
+	mutex_unlock(&drv->lock);
+
+	return 0;
+}
+
+static int a21_wdt_set_timeout(struct watchdog_device *wdt,
+			       unsigned int timeout)
+{
+	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+	if (timeout != 1 && timeout != 30) {
+		dev_err(wdt->dev, "Only 1 and 30 allowed as timeout\n");
+		return -EINVAL;
+	}
+
+	if (timeout == 30 && wdt->timeout == 1) {
+		dev_err(wdt->dev,
+			"Transition from fast to slow mode not allowed\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&drv->lock);
+
+	if (timeout == 1)
+		gpio_set_value(GPIO_WD_FAST, 1);
+	else
+		gpio_set_value(GPIO_WD_FAST, 0);
+
+	wdt->timeout = timeout;
+
+	mutex_unlock(&drv->lock);
+
+	return 0;
+}
+
+static const struct watchdog_info a21_wdt_info = {
+	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+	.identity = "MEN A21 Watchdog",
+};
+
+static const struct watchdog_ops a21_wdt_ops = {
+	.owner = THIS_MODULE,
+	.start = a21_wdt_start,
+	.stop = a21_wdt_stop,
+	.ping = a21_wdt_ping,
+	.set_timeout = a21_wdt_set_timeout,
+};
+
+static struct watchdog_device a21_wdt = {
+	.info = &a21_wdt_info,
+	.ops = &a21_wdt_ops,
+	.min_timeout = 1,
+	.max_timeout = 30,
+};
+
+static int a21_wdt_probe(struct platform_device *pdev)
+{
+	struct a21_wdt_drv *drv;
+	unsigned int reset = 0;
+	int ret;
+
+	dev_info(&pdev->dev, "MEN A21 watchdog timer driver enabled\n");
+
+	drv = devm_kzalloc(&pdev->dev, sizeof(struct a21_wdt_drv), GFP_KERNEL);
+	if (!drv)
+		return -ENOMEM;
+
+	mutex_init(&drv->lock);
+	watchdog_set_nowayout(&a21_wdt, nowayout);
+	watchdog_set_drvdata(&a21_wdt, drv);
+
+	ret = watchdog_register_device(&a21_wdt);
+	if (ret) {
+		dev_err(&pdev->dev, "Cannot register watchdog device\n");
Quoted 8 lines

drivers/watchdog/mena21_wdt.c line 169

Quoted 4 lines
drivers/watchdog/mena21_wdt.c new file +4 -0
+		goto err_register_wd;
+	}
+
+	a21_wdt.timeout = 30;

It would be better to use watchdog_init_timeout(), as it automatically supports setting the timeout through an fdt property. It should be set before registering the device with the watchdog subsystem.

I thought there would be something like watchdog_init_timeout() but didn't find it actually. Only found watchdog_set_timeout() which isn't exported...

Up for the next update..

#5 lore raw

PATCH v6 1/1 watchdog: New watchdog driver for MEN A21 watchdogs

In reply to Guenter Roeck

This patch adds the driver for the watchdog devices found on MEN Mikro Elektronik A21 VMEbus CPU Carrier Boards. It has DT-support and uses the watchdog framework.

Revision 2:
* Removed unneeded open flag in struct a21_wdt_drv
* Corrected 3bit reason code from gpio
* Additional sysfs files are now part of watchdog sysfs
* Changed OFF/ON delay in ping from 400ms to 10ns
* Reworked timeout setting
* Removed a21_wdt_ioctl(...)

Revision 3:
* Changed pr_{err,info} to dev_{err,info}
* Removed out of memory error print
* Transition from "fast" to "slow" mode not allowed by chip

Revision 4:
* Remove reboot_notifier and place disable code into platform_device's shutdown function
* Removed sysfs interface

Revision 5:

* Added setting of .bootstatus on driver init
* Added initial timeout on driver init

Revision 6:
* Use watchdog_init_timeout() to initialize timeout

---
 MAINTAINERS                   |    6 ++
 drivers/watchdog/Kconfig      |    8 ++
 drivers/watchdog/Makefile     |    1 +
 drivers/watchdog/mena21_wdt.c |  234 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 249 insertions(+)
 create mode 100644 drivers/watchdog/mena21_wdt.c
MAINTAINERS +6 -0
index 7714c3c..023945a 100644
@@ -5307,6 +5307,12 @@ F:	drivers/mtd/
 F:	include/linux/mtd/
 F:	include/uapi/mtd/
 
+MEN A21 WATCHDOG DRIVER
+M:  	Johannes Thumshirn <[email protected]>
+L:	[email protected]
+S:	Supported
+F:	drivers/watchdog/mena21_wdt.c
+
 METAG ARCHITECTURE
 M:	James Hogan <[email protected]>
 S:	Supported
drivers/watchdog/Kconfig +8 -0
index e89fc31..192b84d 100644
@@ -1172,6 +1172,14 @@ config BOOKE_WDT_DEFAULT_TIMEOUT
 
 	  The value can be overridden by the wdt_period command-line parameter.
 
+config MEN_A21_WDT
+       tristate "MEN A21 VME CPU Carrier Board Watchdog Timer"
+       select WATCHDOG_CORE
+       help
+        Watchdog driver for MEN A21 VMEbus CPU Carrier Boards.
+
+	If unsure select N here.
+
 # PPC64 Architecture
 
 config WATCHDOG_RTAS
drivers/watchdog/Makefile +1 -0
index a300b94..bffdcb1 100644
@@ -143,6 +143,7 @@ obj-$(CONFIG_8xxx_WDT) += mpc8xxx_wdt.o
 obj-$(CONFIG_MV64X60_WDT) += mv64x60_wdt.o
 obj-$(CONFIG_PIKA_WDT) += pika_wdt.o
 obj-$(CONFIG_BOOKE_WDT) += booke_wdt.o
+obj-$(CONFIG_MEN_A21_WDT) += mena21_wdt.o
 
 # PPC64 Architecture
 obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o
drivers/watchdog/mena21_wdt.c new file +234 -0

234 lines not on this page · raw message

--
1.7.9.5

maintainer
#6 lore raw
Quoted 23 lines · Johannes Thumshirn

This patch adds the driver for the watchdog devices found on MEN Mikro Elektronik A21 VMEbus CPU Carrier Boards. It has DT-support and uses the watchdog framework.

Revision 2:
* Removed unneeded open flag in struct a21_wdt_drv
* Corrected 3bit reason code from gpio
* Additional sysfs files are now part of watchdog sysfs
* Changed OFF/ON delay in ping from 400ms to 10ns
* Reworked timeout setting
* Removed a21_wdt_ioctl(...)

Revision 3:
* Changed pr_{err,info} to dev_{err,info}
* Removed out of memory error print
* Transition from "fast" to "slow" mode not allowed by chip

Revision 4:
* Remove reboot_notifier and place disable code into platform_device's shutdown function
* Removed sysfs interface

Revision 5:

Quoted 7 lines

* Added setting of .bootstatus on driver init
* Added initial timeout on driver init

Revision 6:
* Use watchdog_init_timeout() to initialize timeout

Couple of additional comments ... sorry, middle of night here and my thinking is not at its best.

Quoted 79 lines
---
 MAINTAINERS                   |    6 ++
 drivers/watchdog/Kconfig      |    8 ++
 drivers/watchdog/Makefile     |    1 +
 drivers/watchdog/mena21_wdt.c |  234 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 249 insertions(+)
 create mode 100644 drivers/watchdog/mena21_wdt.c
MAINTAINERS +6 -0
index 7714c3c..023945a 100644
@@ -5307,6 +5307,12 @@ F:	drivers/mtd/
 F:	include/linux/mtd/
 F:	include/uapi/mtd/
 
+MEN A21 WATCHDOG DRIVER
+M:  	Johannes Thumshirn <[email protected]>
+L:	[email protected]
+S:	Supported
+F:	drivers/watchdog/mena21_wdt.c
+
 METAG ARCHITECTURE
 M:	James Hogan <[email protected]>
 S:	Supported
drivers/watchdog/Kconfig +8 -0
index e89fc31..192b84d 100644
@@ -1172,6 +1172,14 @@ config BOOKE_WDT_DEFAULT_TIMEOUT
 
 	  The value can be overridden by the wdt_period command-line parameter.
 
+config MEN_A21_WDT
+       tristate "MEN A21 VME CPU Carrier Board Watchdog Timer"
+       select WATCHDOG_CORE
+       help
+        Watchdog driver for MEN A21 VMEbus CPU Carrier Boards.
+
+	If unsure select N here.
+
 # PPC64 Architecture
 
 config WATCHDOG_RTAS
drivers/watchdog/Makefile +1 -0
index a300b94..bffdcb1 100644
@@ -143,6 +143,7 @@ obj-$(CONFIG_8xxx_WDT) += mpc8xxx_wdt.o
 obj-$(CONFIG_MV64X60_WDT) += mv64x60_wdt.o
 obj-$(CONFIG_PIKA_WDT) += pika_wdt.o
 obj-$(CONFIG_BOOKE_WDT) += booke_wdt.o
+obj-$(CONFIG_MEN_A21_WDT) += mena21_wdt.o
 
 # PPC64 Architecture
 obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o
drivers/watchdog/mena21_wdt.c new file +17 -0
new file mode 100644
index 0000000..9874c63
@@ -0,0 +1,234 @@
+/*
+ * Watchdog driver for the A21 VME CPU Boards
+ *
+ * Copyright (C) 2013 MEN Mikro Elektronik Nuernberg GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+#include <linux/uaccess.h>

drivers/watchdog/mena21_wdt.c line 27

Quoted 10 lines
drivers/watchdog/mena21_wdt.c new file +10 -0
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+
+#define GPIO_WD_ENAB	169
+#define GPIO_WD_FAST	170
+#define GPIO_WD_TRIG	171
+
+#define GPIO_RST_CAUSE_BASE 166
+

I think I asked that before ... as you are supporting devicetree, gpio pins should really be provided through devicetree properties and not be hardcoded.

Quoted 75 lines
drivers/watchdog/mena21_wdt.c new file +75 -0
+struct a21_wdt_drv {
+	struct watchdog_device wdt;
+	struct mutex lock;
+};
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+			    __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static int a21_wdt_get_bootstatus(unsigned int *reset)
+{
+	int i;
+
+	for (i = 0; i < 3; i++)
+		*reset |= gpio_get_value(GPIO_RST_CAUSE_BASE + i)
+			? (1 << i) : 0;
+
+	if (*reset >= 8)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int a21_wdt_start(struct watchdog_device *wdt)
+{
+	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+	mutex_lock(&drv->lock);
+
+	gpio_set_value(GPIO_WD_ENAB, 1);
+
+	mutex_unlock(&drv->lock);
+
+	return 0;
+}
+
+static int a21_wdt_stop(struct watchdog_device *wdt)
+{
+	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+	/* We don't stop if WDOG_NO_WAY_OUT is set */
+	if (test_bit(WDOG_NO_WAY_OUT, &wdt->status))
+		return -EINVAL;
+
+	mutex_lock(&drv->lock);
+
+	gpio_set_value(GPIO_WD_ENAB, 0);
+
+	mutex_unlock(&drv->lock);
+
+	return 0;
+}
+
+static int a21_wdt_ping(struct watchdog_device *wdt)
+{
+	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+	mutex_lock(&drv->lock);
+
+	gpio_set_value(GPIO_WD_TRIG, 0);
+	ndelay(10);
+	gpio_set_value(GPIO_WD_TRIG, 1);
+
+	mutex_unlock(&drv->lock);
+
+	return 0;
+}
+
+static int a21_wdt_set_timeout(struct watchdog_device *wdt,
+			       unsigned int timeout)
+{
+	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+	if (timeout != 1 && timeout != 30) {

drivers/watchdog/mena21_wdt.c line 112

Quoted 10 lines
drivers/watchdog/mena21_wdt.c new file +10 -0
+		dev_err(wdt->dev, "Only 1 and 30 allowed as timeout\n");
+		return -EINVAL;
+	}
+
+	if (timeout == 30 && wdt->timeout == 1) {
+		dev_err(wdt->dev,
+			"Transition from fast to slow mode not allowed\n");
+		return -EINVAL;
+	}
+

I dislike that [2 .. 29] timeout values are not supported, and would rather have you accept that range and set the timeout to 30. Also, it is somewhat undesirable that the timeout can not be changed back to 30 after being set to 1.

As Wim recommended, a softdog on top of the hardware watchdog would be the best solution. I'll leave it up to him to decide if he wants to accept the code as-is.

Quoted 59 lines
drivers/watchdog/mena21_wdt.c new file +59 -0
+	mutex_lock(&drv->lock);
+
+	if (timeout == 1)
+		gpio_set_value(GPIO_WD_FAST, 1);
+	else
+		gpio_set_value(GPIO_WD_FAST, 0);
+
+	wdt->timeout = timeout;
+
+	mutex_unlock(&drv->lock);
+
+	return 0;
+}
+
+static const struct watchdog_info a21_wdt_info = {
+	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+	.identity = "MEN A21 Watchdog",
+};
+
+static const struct watchdog_ops a21_wdt_ops = {
+	.owner = THIS_MODULE,
+	.start = a21_wdt_start,
+	.stop = a21_wdt_stop,
+	.ping = a21_wdt_ping,
+	.set_timeout = a21_wdt_set_timeout,
+};
+
+static struct watchdog_device a21_wdt = {
+	.info = &a21_wdt_info,
+	.ops = &a21_wdt_ops,
+	.min_timeout = 1,
+	.max_timeout = 30,
+};
+
+static int a21_wdt_probe(struct platform_device *pdev)
+{
+	struct a21_wdt_drv *drv;
+	unsigned int reset = 0;
+	int ret;
+
+	dev_info(&pdev->dev, "MEN A21 watchdog timer driver enabled\n");
+
+	drv = devm_kzalloc(&pdev->dev, sizeof(struct a21_wdt_drv), GFP_KERNEL);
+	if (!drv)
+		return -ENOMEM;
+
+	mutex_init(&drv->lock);
+	watchdog_init_timeout(&a21_wdt, 30, &pdev->dev);
+	watchdog_set_nowayout(&a21_wdt, nowayout);
+	watchdog_set_drvdata(&a21_wdt, drv);
+
+	ret = watchdog_register_device(&a21_wdt);
+	if (ret) {
+		dev_err(&pdev->dev, "Cannot register watchdog device\n");
+		goto err_register_wd;
+	}
+
+	ret = a21_wdt_get_bootstatus(&reset);
+	if (ret)

drivers/watchdog/mena21_wdt.c line 181

Quoted 10 lines
drivers/watchdog/mena21_wdt.c new file +10 -0
+		dev_warn(&pdev->dev, "Reset Cause contains invalid data\n");
+	else {
+		if (reset == 2)
+			a21_wdt.bootstatus |= WDIOF_EXTERN1;
+		else if (reset == 4)
+			a21_wdt.bootstatus |= WDIOF_CARDRESET;
+		else if (reset == 5)
+			a21_wdt.bootstatus |= WDIOF_POWERUNDER;
+		else if (reset == 7)
+			a21_wdt.bootstatus |= WDIOF_EXTERN2;

What about other causes ? No useful match ?

I think bootstatus should be set prior to registering the watchdog device to avoid race conditions where an application reads it prior to being set.

Quoted 61 lines
drivers/watchdog/mena21_wdt.c new file +53 -1
+	}
+
+	dev_set_drvdata(&pdev->dev, drv);
+
+	return 0;
+
+err_register_wd:
+	mutex_destroy(&drv->lock);
+
+	return ret;
+}
+
+static int a21_wdt_remove(struct platform_device *pdev)
+{
+	struct a21_wdt_drv *drv = dev_get_drvdata(&pdev->dev);
+
+	dev_warn(&pdev->dev,
+		"Unregistering A21 watchdog driver, board may reboot\n");
+
+
+	watchdog_unregister_device(&drv->wdt);
+
+	mutex_destroy(&drv->lock);
+
+	return 0;
+}
+
+static void a21_wdt_shutdown(struct platform_device *pdev)
+{
+	gpio_set_value(GPIO_WD_ENAB, 0);
+}
+
+static const struct of_device_id a21_wdt_ids[] = {
+	{ .compatible = "men,a021-wdt" },
+	{ },
+};
+
+static struct platform_driver a21_wdt_driver = {
+	.probe = a21_wdt_probe,
+	.remove = a21_wdt_remove,
+	.shutdown = a21_wdt_shutdown,
+	.driver = {
+		.name = "a21-watchdog",
+		.of_match_table = a21_wdt_ids,
+	},
+};
+
+module_platform_driver(a21_wdt_driver);
+
+MODULE_AUTHOR("MEN Mikro Elektronik");
+MODULE_DESCRIPTION("MEN A21 Watchdog");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:a21-watchdog");
--

1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#7 lore raw

Hi Guenther,

Quoted 10 lines · Guenter Roeck

drivers/watchdog/mena21_wdt.c line 27

Quoted 6 lines
drivers/watchdog/mena21_wdt.c new file +6 -0
+#define GPIO_WD_ENAB	169
+#define GPIO_WD_FAST	170
+#define GPIO_WD_TRIG	171
+
+#define GPIO_RST_CAUSE_BASE 166
+

I think I asked that before ... as you are supporting devicetree, gpio pins should really be provided through devicetree properties and not be hardcoded.

Yes you did and I didn't come up with a solution to this problem yet. I understand and agree to your concerns but I'm lacking example code/documentation for it, maybe you can point me to an example on that and then I'll update my code accordingly.

Quoted 6 lines
Quoted 6 lines
drivers/watchdog/mena21_wdt.c new file +6 -0
+struct a21_wdt_drv {
+	struct watchdog_device wdt;
+	struct mutex lock;
+	}
+
+	if (timeout == 30 && wdt->timeout == 1) {
Quoted 8 lines

drivers/watchdog/mena21_wdt.c line 112

Quoted 5 lines
drivers/watchdog/mena21_wdt.c new file +5 -0
+		dev_err(wdt->dev,
+			"Transition from fast to slow mode not allowed\n");
+		return -EINVAL;
+	}
+

I dislike that [2 .. 29] timeout values are not supported, and would rather have you accept that range and set the timeout to 30. Also, it is somewhat undesirable that the timeout can not be changed back to 30 after being set to 1.

The not changing back issue was a hardware design criterion and I have no influence on that one. I've already checked with the IC developer and he said, this is intended. I don't quite understand why I should accept the timeout and set it to 30? In my opinion this only introduces some magic the user won't understand. Instead failing and giving an error message is something a user can take as a hint for investigating.

Quoted 3 lines

As Wim recommended, a softdog on top of the hardware watchdog would be the best solution. I'll leave it up to him to decide if he wants to accept the code as-is.

Yes let's see what he's saying.

Quoted 15 lines
Quoted 15 lines
drivers/watchdog/mena21_wdt.c new file +14 -0
+	mutex_lock(&drv->lock);
+
+	ret = watchdog_register_device(&a21_wdt);
+	if (ret) {
+		dev_err(&pdev->dev, "Cannot register watchdog device\n");
+		goto err_register_wd;
+	}
+
+	ret = a21_wdt_get_bootstatus(&reset);
+	if (ret)
+		dev_warn(&pdev->dev, "Reset Cause contains invalid data\n");
+	else {
+		if (reset == 2)
+			a21_wdt.bootstatus |= WDIOF_EXTERN1;
Quoted 9 lines

drivers/watchdog/mena21_wdt.c line 181

Quoted 6 lines
drivers/watchdog/mena21_wdt.c new file +6 -0
+		else if (reset == 4)
+			a21_wdt.bootstatus |= WDIOF_CARDRESET;
+		else if (reset == 5)
+			a21_wdt.bootstatus |= WDIOF_POWERUNDER;
+		else if (reset == 7)
+			a21_wdt.bootstatus |= WDIOF_EXTERN2;

What about other causes ? No useful match ?

None I could find. Actually WDIOF_EXTERN[12] already are a pretty creative mapping in my opinion. EXTERN1 is a "Push Button" event, EXTERN2 is a reset caused by a JTAG/BDM adapter...

Quoted 3 lines

I think bootstatus should be set prior to registering the watchdog device to avoid race conditions where an application reads it prior to being set.

Agreed, didn't see that one *doh*, must be fixed before inclusion.

Quoted 1 line
Quoted 1 line
(quoted diff) +1 -0
+	}
maintainer
#8 lore raw
Quoted 12 lines · Johannes Thumshirn

Hi Guenther,

Quoted 10 lines · Guenter Roeck

drivers/watchdog/mena21_wdt.c line 27

Quoted 6 lines
drivers/watchdog/mena21_wdt.c new file +6 -0
+#define GPIO_WD_ENAB	169
+#define GPIO_WD_FAST	170
+#define GPIO_WD_TRIG	171
+
+#define GPIO_RST_CAUSE_BASE 166
+

I think I asked that before ... as you are supporting devicetree, gpio pins should really be provided through devicetree properties and not be hardcoded.

Quoted 4 lines

Yes you did and I didn't come up with a solution to this problem yet. I understand and agree to your concerns but I'm lacking example code/documentation for it, maybe you can point me to an example on that and then I'll update my code accordingly.

Have a look at Documentation/devicetree/bindings/gpio/gpio-fan.txt and drivers/hwmon/gpio-fan.c.

Quoted 15 lines
Quoted 6 lines
Quoted 6 lines
drivers/watchdog/mena21_wdt.c new file +6 -0
+struct a21_wdt_drv {
+	struct watchdog_device wdt;
+	struct mutex lock;
+	}
+
+	if (timeout == 30 && wdt->timeout == 1) {
Quoted 8 lines

drivers/watchdog/mena21_wdt.c line 112

Quoted 5 lines
drivers/watchdog/mena21_wdt.c new file +5 -0
+		dev_err(wdt->dev,
+			"Transition from fast to slow mode not allowed\n");
+		return -EINVAL;
+	}
+

I dislike that [2 .. 29] timeout values are not supported, and would rather have you accept that range and set the timeout to 30. Also, it is somewhat undesirable that the timeout can not be changed back to 30 after being set to 1.

Quoted 7 lines

The not changing back issue was a hardware design criterion and I have no influence on that one. I've already checked with the IC developer and he said, this is intended. I don't quite understand why I should accept the timeout and set it to 30? In my opinion this only introduces some magic the user won't understand. Instead failing and giving an error message is something a user can take as a hint for investigating.

On the other side you expect the user to know about the 1 / 30 limitation, which I think is worse. The value actually set is returned by the ioctl per API, so the application can check the actually configured value. As such, it is not entirely true that the behavior would be unexpected.

Quoted 6 lines
Quoted 3 lines

As Wim recommended, a softdog on top of the hardware watchdog would be the best solution. I'll leave it up to him to decide if he wants to accept the code as-is.

Yes let's see what he's saying.

Agreed.

Quoted 25 lines
Quoted 15 lines
Quoted 15 lines
drivers/watchdog/mena21_wdt.c new file +14 -0
+	mutex_lock(&drv->lock);
+
+	ret = watchdog_register_device(&a21_wdt);
+	if (ret) {
+		dev_err(&pdev->dev, "Cannot register watchdog device\n");
+		goto err_register_wd;
+	}
+
+	ret = a21_wdt_get_bootstatus(&reset);
+	if (ret)
+		dev_warn(&pdev->dev, "Reset Cause contains invalid data\n");
+	else {
+		if (reset == 2)
+			a21_wdt.bootstatus |= WDIOF_EXTERN1;
Quoted 9 lines

drivers/watchdog/mena21_wdt.c line 181

Quoted 6 lines
drivers/watchdog/mena21_wdt.c new file +6 -0
+		else if (reset == 4)
+			a21_wdt.bootstatus |= WDIOF_CARDRESET;
+		else if (reset == 5)
+			a21_wdt.bootstatus |= WDIOF_POWERUNDER;
+		else if (reset == 7)
+			a21_wdt.bootstatus |= WDIOF_EXTERN2;

What about other causes ? No useful match ?

Quoted 4 lines

None I could find. Actually WDIOF_EXTERN[12] already are a pretty creative mapping in my opinion. EXTERN1 is a "Push Button" event, EXTERN2 is a reset caused by a JTAG/BDM adapter...

ok.

Quoted 4 lines
Quoted 3 lines

I think bootstatus should be set prior to registering the watchdog device to avoid race conditions where an application reads it prior to being set.

Agreed, didn't see that one *doh*, must be fixed before inclusion.

Happens to me all the time :)

Guenter

#9 lore raw
Quoted 13 lines · Guenter Roeck
Quoted 12 lines · Johannes Thumshirn

Hi Guenther,

Quoted 10 lines · Guenter Roeck

drivers/watchdog/mena21_wdt.c line 27

Quoted 6 lines
drivers/watchdog/mena21_wdt.c new file +6 -0
+#define GPIO_WD_ENAB	169
+#define GPIO_WD_FAST	170
+#define GPIO_WD_TRIG	171
+
+#define GPIO_RST_CAUSE_BASE 166
+

I think I asked that before ... as you are supporting devicetree, gpio pins should really be provided through devicetree properties and not be hardcoded.

Quoted 7 lines
Quoted 4 lines

Yes you did and I didn't come up with a solution to this problem yet. I understand and agree to your concerns but I'm lacking example code/documentation for it, maybe you can point me to an example on that and then I'll update my code accordingly.

Have a look at Documentation/devicetree/bindings/gpio/gpio-fan.txt and drivers/hwmon/gpio-fan.c.

Thanks a lot, this really helped me out. Updated patch is comming today including the bindings document Arnd Bergmann requested. I only need to rebase the sysfs patch on top of that changes.

Regards,
Johannes

#10 lore raw

Some problems with sysfs patch (was Re: [PATCH v6] watchdog: New watchdog driver for MEN A21 watchdogs)

Quoted 22 lines · Johannes Thumshirn
Quoted 13 lines · Guenter Roeck
Quoted 12 lines · Johannes Thumshirn

Hi Guenther,

Quoted 10 lines · Guenter Roeck

drivers/watchdog/mena21_wdt.c line 27

Quoted 6 lines
drivers/watchdog/mena21_wdt.c new file +6 -0
+#define GPIO_WD_ENAB	169
+#define GPIO_WD_FAST	170
+#define GPIO_WD_TRIG	171
+
+#define GPIO_RST_CAUSE_BASE 166
+

I think I asked that before ... as you are supporting devicetree, gpio pins should really be provided through devicetree properties and not be hardcoded.

Quoted 7 lines
Quoted 4 lines

Yes you did and I didn't come up with a solution to this problem yet. I understand and agree to your concerns but I'm lacking example code/documentation for it, maybe you can point me to an example on that and then I'll update my code accordingly.

Have a look at Documentation/devicetree/bindings/gpio/gpio-fan.txt and drivers/hwmon/gpio-fan.c.

Quoted 6 lines

Thanks a lot, this really helped me out. Updated patch is comming today including the bindings document Arnd Bergmann requested. I only need to rebase the sysfs patch on top of that changes.

Regards,
Johannes

Regarding the sysfs patch, I have a funny effect in my (rebased) sysfs code.

Given the following code:

+static ssize_t rebootcause_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct a21_wdt_drv *drv = dev_get_drvdata(dev);
+ unsigned int reset = 0;
+
+ if (!drv)
+ return -EIO;
+
+ reset = a21_wdt_get_bootstatus(drv);
+
+ return sprintf(buf, "%s\n", reset_causes[reset]);
+}
+static DEVICE_ATTR(rebootcause, S_IRUGO, rebootcause_show, NULL);

I actually need the check for if (!drv) to prevent an OOPS, as dev_get_drvdata(dev) returns NULL., though it is set at the end of my probe function via:

[...]
+ ret = watchdog_register_device(&a21_wdt);
+ if (ret) {
+ dev_err(&pdev->dev, "Cannot register watchdog device\n");
+ goto err_register_wd;
+ }
+
+ dev_set_drvdata(&pdev->dev, drv);
+
+ return 0;
[...]

The watchdog driver itself is working without any troubles.

Some advice here would be worth its weight in gold.

Thanks,
Johannes

maintainer
#11 lore raw

Re: Some problems with sysfs patch (was Re: [PATCH v6] watchdog: New watchdog driver for MEN A21 watchdogs)

Quoted 66 lines · Johannes Thumshirn
Quoted 22 lines · Johannes Thumshirn
Quoted 13 lines · Guenter Roeck
Quoted 12 lines · Johannes Thumshirn

Hi Guenther,

Quoted 10 lines · Guenter Roeck

drivers/watchdog/mena21_wdt.c line 27

Quoted 6 lines
drivers/watchdog/mena21_wdt.c new file +6 -0
+#define GPIO_WD_ENAB	169
+#define GPIO_WD_FAST	170
+#define GPIO_WD_TRIG	171
+
+#define GPIO_RST_CAUSE_BASE 166
+

I think I asked that before ... as you are supporting devicetree, gpio pins should really be provided through devicetree properties and not be hardcoded.

Quoted 7 lines
Quoted 4 lines

Yes you did and I didn't come up with a solution to this problem yet. I understand and agree to your concerns but I'm lacking example code/documentation for it, maybe you can point me to an example on that and then I'll update my code accordingly.

Have a look at Documentation/devicetree/bindings/gpio/gpio-fan.txt and drivers/hwmon/gpio-fan.c.

Quoted 6 lines

Thanks a lot, this really helped me out. Updated patch is comming today including the bindings document Arnd Bergmann requested. I only need to rebase the sysfs patch on top of that changes.

Regards,
Johannes

Regarding the sysfs patch, I have a funny effect in my (rebased) sysfs code.

Given the following code:

(quoted diff) +15 -0
+static ssize_t rebootcause_show(struct device *dev,
+                               struct device_attribute *attr,
+                               char *buf)
+{
+       struct a21_wdt_drv *drv = dev_get_drvdata(dev);
+       unsigned int reset = 0;
+
+       if (!drv)
+               return -EIO;
+
+       reset = a21_wdt_get_bootstatus(drv);
+
+       return sprintf(buf, "%s\n", reset_causes[reset]);
+}
+static DEVICE_ATTR(rebootcause, S_IRUGO, rebootcause_show, NULL);

I actually need the check for if (!drv) to prevent an OOPS, as dev_get_drvdata(dev) returns NULL., though it is set at the end of my probe function via:

[...]

drivers/watchdog/mena21_wdt.c new file +9 -0
+       ret = watchdog_register_device(&a21_wdt);
+       if (ret) {
+               dev_err(&pdev->dev, "Cannot register watchdog device\n");
+               goto err_register_wd;
+       }
+
+       dev_set_drvdata(&pdev->dev, drv);
+
+       return 0;
[...]
Quoted 4 lines

The watchdog driver itself is working without any troubles.

Some advice here would be worth its weight in gold.

It has to be set before the attribute is created. Are you doing that ? Also, is 'dev' the same device pointer (ie is the 'dev' in your function the same as &pdev->dev) ?

Guenter

Quoted 3 lines

Thanks,
Johannes

#12 lore raw

Re: Some problems with sysfs patch (was Re: [PATCH v6] watchdog: New watchdog driver for MEN A21 watchdogs)

Quoted 69 lines · Guenter Roeck
Quoted 68 lines · Johannes Thumshirn
Quoted 22 lines · Johannes Thumshirn
Quoted 13 lines · Guenter Roeck
Quoted 12 lines · Johannes Thumshirn

Hi Guenther,

Quoted 10 lines · Guenter Roeck

Review

Quoted 6 lines
(quoted diff) +6 -0
+#define GPIO_WD_ENAB	169
+#define GPIO_WD_FAST	170
+#define GPIO_WD_TRIG	171
+
+#define GPIO_RST_CAUSE_BASE 166
+

I think I asked that before ... as you are supporting devicetree, gpio pins should really be provided through devicetree properties and not be hardcoded.

Quoted 7 lines
Quoted 4 lines

Yes you did and I didn't come up with a solution to this problem yet. I understand and agree to your concerns but I'm lacking example code/documentation for it, maybe you can point me to an example on that and then I'll update my code accordingly.

Have a look at Documentation/devicetree/bindings/gpio/gpio-fan.txt and drivers/hwmon/gpio-fan.c.

Quoted 6 lines

Thanks a lot, this really helped me out. Updated patch is comming today including the bindings document Arnd Bergmann requested. I only need to rebase the sysfs patch on top of that changes.

Regards,
Johannes

Regarding the sysfs patch, I have a funny effect in my (rebased) sysfs code.

Given the following code:

(quoted diff) +15 -0
+static ssize_t rebootcause_show(struct device *dev,
+                               struct device_attribute *attr,
+                               char *buf)
+{
+       struct a21_wdt_drv *drv = dev_get_drvdata(dev);
+       unsigned int reset = 0;
+
+       if (!drv)
+               return -EIO;
+
+       reset = a21_wdt_get_bootstatus(drv);
+
+       return sprintf(buf, "%s\n", reset_causes[reset]);
+}
+static DEVICE_ATTR(rebootcause, S_IRUGO, rebootcause_show, NULL);

I actually need the check for if (!drv) to prevent an OOPS, as dev_get_drvdata(dev) returns NULL., though it is set at the end of my probe function via:

[...]

(quoted diff) +9 -0
+       ret = watchdog_register_device(&a21_wdt);
+       if (ret) {
+               dev_err(&pdev->dev, "Cannot register watchdog device\n");
+               goto err_register_wd;
+       }
+
+       dev_set_drvdata(&pdev->dev, drv);
+
+       return 0;
[...]

The watchdog driver itself is working without any troubles.

Quoted 7 lines
Quoted 2 lines

Some advice here would be worth its weight in gold.

It has to be set before the attribute is created. Are you doing that ? Also, is 'dev' the same device pointer (ie is the 'dev' in your function the same as &pdev->dev) ?

Guenter

Hi Guenter,

Thanks for the quick reply. Indeed 'dev' in my function is a struct watchdog_device's dev (for device_create_file) and not &pdev->dev. Which in turn are not the same.

A call to dev_set_drvdata(a21_wdt.dev, drv); solved the NULL pointer access. So last question, is it save to set the drvdata of a dev inside the struct watchdog_device or do I break something vital that way?

Thanks a lot,
Johannes

maintainer
#13 lore raw

Re: Some problems with sysfs patch (was Re: [PATCH v6] watchdog: New watchdog driver for MEN A21 watchdogs)

Quoted 80 lines · Johannes Thumshirn
Quoted 69 lines · Guenter Roeck
Quoted 68 lines · Johannes Thumshirn
Quoted 22 lines · Johannes Thumshirn
Quoted 13 lines · Guenter Roeck
Quoted 12 lines · Johannes Thumshirn

Hi Guenther,

Quoted 10 lines · Guenter Roeck

Review

Quoted 6 lines
(quoted diff) +6 -0
+#define GPIO_WD_ENAB	169
+#define GPIO_WD_FAST	170
+#define GPIO_WD_TRIG	171
+
+#define GPIO_RST_CAUSE_BASE 166
+

I think I asked that before ... as you are supporting devicetree, gpio pins should really be provided through devicetree properties and not be hardcoded.

Quoted 7 lines
Quoted 4 lines

Yes you did and I didn't come up with a solution to this problem yet. I understand and agree to your concerns but I'm lacking example code/documentation for it, maybe you can point me to an example on that and then I'll update my code accordingly.

Have a look at Documentation/devicetree/bindings/gpio/gpio-fan.txt and drivers/hwmon/gpio-fan.c.

Quoted 6 lines

Thanks a lot, this really helped me out. Updated patch is comming today including the bindings document Arnd Bergmann requested. I only need to rebase the sysfs patch on top of that changes.

Regards,
Johannes

Regarding the sysfs patch, I have a funny effect in my (rebased) sysfs code.

Given the following code:

(quoted diff) +15 -0
+static ssize_t rebootcause_show(struct device *dev,
+                               struct device_attribute *attr,
+                               char *buf)
+{
+       struct a21_wdt_drv *drv = dev_get_drvdata(dev);
+       unsigned int reset = 0;
+
+       if (!drv)
+               return -EIO;
+
+       reset = a21_wdt_get_bootstatus(drv);
+
+       return sprintf(buf, "%s\n", reset_causes[reset]);
+}
+static DEVICE_ATTR(rebootcause, S_IRUGO, rebootcause_show, NULL);

I actually need the check for if (!drv) to prevent an OOPS, as dev_get_drvdata(dev) returns NULL., though it is set at the end of my probe function via:

[...]

(quoted diff) +9 -0
+       ret = watchdog_register_device(&a21_wdt);
+       if (ret) {
+               dev_err(&pdev->dev, "Cannot register watchdog device\n");
+               goto err_register_wd;
+       }
+
+       dev_set_drvdata(&pdev->dev, drv);
+
+       return 0;
[...]

The watchdog driver itself is working without any troubles.

Quoted 7 lines
Quoted 2 lines

Some advice here would be worth its weight in gold.

It has to be set before the attribute is created. Are you doing that ? Also, is 'dev' the same device pointer (ie is the 'dev' in your function the same as &pdev->dev) ?

Guenter

Hi Guenter,

Quoted 8 lines

Thanks for the quick reply. Indeed 'dev' in my function is a struct watchdog_device's dev (for device_create_file) and not &pdev->dev. Which in turn are not the same.

A call to dev_set_drvdata(a21_wdt.dev, drv); solved the NULL pointer access. So last question, is it save to set the drvdata of a dev inside the struct watchdog_device or do I break something vital that way?

Good question. At issue is if your driver 'owns' struct watchdog_device, or if the watchdog core owns it.

However, since you know that a21_wdt.dev is the device, you can use container_of() to get a reference to a21_wdt, and watchdog_get_drvdata to get access to drv from a21_wdt. So you should not really need it.

Thanks,
Guenter

#14 lore raw

Re: Some problems with sysfs patch (was Re: [PATCH v6] watchdog: New watchdog driver for MEN A21 watchdogs)

Quoted 92 lines · Guenter Roeck
Quoted 80 lines · Johannes Thumshirn
Quoted 69 lines · Guenter Roeck
Quoted 68 lines · Johannes Thumshirn
Quoted 22 lines · Johannes Thumshirn
Quoted 13 lines · Guenter Roeck
Quoted 12 lines · Johannes Thumshirn

Hi Guenther,

Quoted 10 lines · Guenter Roeck

Review

Quoted 6 lines
(quoted diff) +6 -0
+#define GPIO_WD_ENAB	169
+#define GPIO_WD_FAST	170
+#define GPIO_WD_TRIG	171
+
+#define GPIO_RST_CAUSE_BASE 166
+

I think I asked that before ... as you are supporting devicetree, gpio pins should really be provided through devicetree properties and not be hardcoded.

Quoted 7 lines
Quoted 4 lines

Yes you did and I didn't come up with a solution to this problem yet. I understand and agree to your concerns but I'm lacking example code/documentation for it, maybe you can point me to an example on that and then I'll update my code accordingly.

Have a look at Documentation/devicetree/bindings/gpio/gpio-fan.txt and drivers/hwmon/gpio-fan.c.

Quoted 6 lines

Thanks a lot, this really helped me out. Updated patch is comming today including the bindings document Arnd Bergmann requested. I only need to rebase the sysfs patch on top of that changes.

Regards,
Johannes

Regarding the sysfs patch, I have a funny effect in my (rebased) sysfs code.

Given the following code:

(quoted diff) +15 -0
+static ssize_t rebootcause_show(struct device *dev,
+                               struct device_attribute *attr,
+                               char *buf)
+{
+       struct a21_wdt_drv *drv = dev_get_drvdata(dev);
+       unsigned int reset = 0;
+
+       if (!drv)
+               return -EIO;
+
+       reset = a21_wdt_get_bootstatus(drv);
+
+       return sprintf(buf, "%s\n", reset_causes[reset]);
+}
+static DEVICE_ATTR(rebootcause, S_IRUGO, rebootcause_show, NULL);

I actually need the check for if (!drv) to prevent an OOPS, as dev_get_drvdata(dev) returns NULL., though it is set at the end of my probe function via:

[...]

(quoted diff) +9 -0
+       ret = watchdog_register_device(&a21_wdt);
+       if (ret) {
+               dev_err(&pdev->dev, "Cannot register watchdog device\n");
+               goto err_register_wd;
+       }
+
+       dev_set_drvdata(&pdev->dev, drv);
+
+       return 0;
[...]

The watchdog driver itself is working without any troubles.

Quoted 7 lines
Quoted 2 lines

Some advice here would be worth its weight in gold.

It has to be set before the attribute is created. Are you doing that ? Also, is 'dev' the same device pointer (ie is the 'dev' in your function the same as &pdev->dev) ?

Guenter

Hi Guenter,

Quoted 8 lines

Thanks for the quick reply. Indeed 'dev' in my function is a struct watchdog_device's dev (for device_create_file) and not &pdev->dev. Which in turn are not the same.

A call to dev_set_drvdata(a21_wdt.dev, drv); solved the NULL pointer access. So last question, is it save to set the drvdata of a dev inside the struct watchdog_device or do I break something vital that way?

Good question. At issue is if your driver 'owns' struct watchdog_device, or if the watchdog core owns it.

Quoted 6 lines

However, since you know that a21_wdt.dev is the device, you can use container_of() to get a reference to a21_wdt, and watchdog_get_drvdata to get access to drv from a21_wdt. So you should not really need it.

Thanks,
Guenter

OK, but doesn't the container_of() macro only work if the parent struct embeds the child struct and not a pointer to it? But struct watchdog_device only has a pointer to a struct device (checked my 3.10-rc4 as well as watchdog-next/master, just in case I missed something).

This would also explain my compiler warnings about "initialization from incompatible pointer type" when compiling this pice code:

static inline struct a21_wdt_drv *to_a21_wdt_drv(struct device *dev)

{
        struct watchdog_device *wdt = container_of(dev, struct watchdog_device, dev);
        struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);

        return drv;
}

Regards,
Johannes

maintainer
#15 lore raw

Re: Some problems with sysfs patch (was Re: [PATCH v6] watchdog: New watchdog driver for MEN A21 watchdogs)

Quoted 97 lines · Johannes Thumshirn
Quoted 96 lines · Guenter Roeck
Quoted 80 lines · Johannes Thumshirn
Quoted 69 lines · Guenter Roeck
Quoted 68 lines · Johannes Thumshirn
Quoted 22 lines · Johannes Thumshirn
Quoted 13 lines · Guenter Roeck
Quoted 12 lines · Johannes Thumshirn

Hi Guenther,

Quoted 10 lines · Guenter Roeck

Review

Quoted 6 lines
(quoted diff) +6 -0
+#define GPIO_WD_ENAB	169
+#define GPIO_WD_FAST	170
+#define GPIO_WD_TRIG	171
+
+#define GPIO_RST_CAUSE_BASE 166
+

I think I asked that before ... as you are supporting devicetree, gpio pins should really be provided through devicetree properties and not be hardcoded.

Quoted 7 lines
Quoted 4 lines

Yes you did and I didn't come up with a solution to this problem yet. I understand and agree to your concerns but I'm lacking example code/documentation for it, maybe you can point me to an example on that and then I'll update my code accordingly.

Have a look at Documentation/devicetree/bindings/gpio/gpio-fan.txt and drivers/hwmon/gpio-fan.c.

Quoted 6 lines

Thanks a lot, this really helped me out. Updated patch is comming today including the bindings document Arnd Bergmann requested. I only need to rebase the sysfs patch on top of that changes.

Regards,
Johannes

Regarding the sysfs patch, I have a funny effect in my (rebased) sysfs code.

Given the following code:

(quoted diff) +15 -0
+static ssize_t rebootcause_show(struct device *dev,
+                               struct device_attribute *attr,
+                               char *buf)
+{
+       struct a21_wdt_drv *drv = dev_get_drvdata(dev);
+       unsigned int reset = 0;
+
+       if (!drv)
+               return -EIO;
+
+       reset = a21_wdt_get_bootstatus(drv);
+
+       return sprintf(buf, "%s\n", reset_causes[reset]);
+}
+static DEVICE_ATTR(rebootcause, S_IRUGO, rebootcause_show, NULL);

I actually need the check for if (!drv) to prevent an OOPS, as dev_get_drvdata(dev) returns NULL., though it is set at the end of my probe function via:

[...]

(quoted diff) +9 -0
+       ret = watchdog_register_device(&a21_wdt);
+       if (ret) {
+               dev_err(&pdev->dev, "Cannot register watchdog device\n");
+               goto err_register_wd;
+       }
+
+       dev_set_drvdata(&pdev->dev, drv);
+
+       return 0;
[...]

The watchdog driver itself is working without any troubles.

Quoted 7 lines
Quoted 2 lines

Some advice here would be worth its weight in gold.

It has to be set before the attribute is created. Are you doing that ? Also, is 'dev' the same device pointer (ie is the 'dev' in your function the same as &pdev->dev) ?

Guenter

Hi Guenter,

Quoted 8 lines

Thanks for the quick reply. Indeed 'dev' in my function is a struct watchdog_device's dev (for device_create_file) and not &pdev->dev. Which in turn are not the same.

A call to dev_set_drvdata(a21_wdt.dev, drv); solved the NULL pointer access. So last question, is it save to set the drvdata of a dev inside the struct watchdog_device or do I break something vital that way?

Good question. At issue is if your driver 'owns' struct watchdog_device, or if the watchdog core owns it.

However, since you know that a21_wdt.dev is the device, you can use container_of() to get a reference to a21_wdt, and watchdog_get_drvdata to get access to drv from a21_wdt. So you should not really need it.

Quoted 8 lines
Quoted 2 lines

Thanks,
Guenter

OK, but doesn't the container_of() macro only work if the parent struct embeds the child struct and not a pointer to it? But struct watchdog_device only has a pointer to a struct device (checked my 3.10-rc4 as well as watchdog-next/master, just in case I missed something).

Yes, you are right, sorry for the noise.

Guenter

#16 lore raw

Re: Some problems with sysfs patch (was Re: [PATCH v6] watchdog: New watchdog driver for MEN A21 watchdogs)

Quoted 101 lines · Guenter Roeck
Quoted 100 lines · Johannes Thumshirn
Quoted 98 lines · Guenter Roeck
Quoted 80 lines · Johannes Thumshirn
Quoted 69 lines · Guenter Roeck
Quoted 68 lines · Johannes Thumshirn
Quoted 22 lines · Johannes Thumshirn
Quoted 13 lines · Guenter Roeck
Quoted 12 lines · Johannes Thumshirn

Hi Guenther,

Quoted 10 lines · Guenter Roeck

Review

Quoted 6 lines
(quoted diff) +6 -0
+#define GPIO_WD_ENAB	169
+#define GPIO_WD_FAST	170
+#define GPIO_WD_TRIG	171
+
+#define GPIO_RST_CAUSE_BASE 166
+

I think I asked that before ... as you are supporting devicetree, gpio pins should really be provided through devicetree properties and not be hardcoded.

Quoted 7 lines
Quoted 4 lines

Yes you did and I didn't come up with a solution to this problem yet. I understand and agree to your concerns but I'm lacking example code/documentation for it, maybe you can point me to an example on that and then I'll update my code accordingly.

Have a look at Documentation/devicetree/bindings/gpio/gpio-fan.txt and drivers/hwmon/gpio-fan.c.

Quoted 6 lines

Thanks a lot, this really helped me out. Updated patch is comming today including the bindings document Arnd Bergmann requested. I only need to rebase the sysfs patch on top of that changes.

Regards,
Johannes

Regarding the sysfs patch, I have a funny effect in my (rebased) sysfs code.

Given the following code:

(quoted diff) +15 -0
+static ssize_t rebootcause_show(struct device *dev,
+                               struct device_attribute *attr,
+                               char *buf)
+{
+       struct a21_wdt_drv *drv = dev_get_drvdata(dev);
+       unsigned int reset = 0;
+
+       if (!drv)
+               return -EIO;
+
+       reset = a21_wdt_get_bootstatus(drv);
+
+       return sprintf(buf, "%s\n", reset_causes[reset]);
+}
+static DEVICE_ATTR(rebootcause, S_IRUGO, rebootcause_show, NULL);

I actually need the check for if (!drv) to prevent an OOPS, as dev_get_drvdata(dev) returns NULL., though it is set at the end of my probe function via:

[...]

(quoted diff) +9 -0
+       ret = watchdog_register_device(&a21_wdt);
+       if (ret) {
+               dev_err(&pdev->dev, "Cannot register watchdog device\n");
+               goto err_register_wd;
+       }
+
+       dev_set_drvdata(&pdev->dev, drv);
+
+       return 0;
[...]

The watchdog driver itself is working without any troubles.

Quoted 7 lines
Quoted 2 lines

Some advice here would be worth its weight in gold.

It has to be set before the attribute is created. Are you doing that ? Also, is 'dev' the same device pointer (ie is the 'dev' in your function the same as &pdev->dev) ?

Guenter

Hi Guenter,

Quoted 8 lines

Thanks for the quick reply. Indeed 'dev' in my function is a struct watchdog_device's dev (for device_create_file) and not &pdev->dev. Which in turn are not the same.

A call to dev_set_drvdata(a21_wdt.dev, drv); solved the NULL pointer access. So last question, is it save to set the drvdata of a dev inside the struct watchdog_device or do I break something vital that way?

Good question. At issue is if your driver 'owns' struct watchdog_device, or if the watchdog core owns it.

However, since you know that a21_wdt.dev is the device, you can use container_of() to get a reference to a21_wdt, and watchdog_get_drvdata to get access to drv from a21_wdt. So you should not really need it.

Thanks,
Guenter

Quoted 8 lines
Quoted 5 lines

OK, but doesn't the container_of() macro only work if the parent struct embeds the child struct and not a pointer to it? But struct watchdog_device only has a pointer to a struct device (checked my 3.10-rc4 as well as watchdog-next/master, just in case I missed something).

Yes, you are right, sorry for the noise.

Guenter

No problem. Actually I hoped you tell me I was wrong and point me to the right practice for this.

Anyway I'll send the patch as it is and hope it's clean for inclusion.

Thanks for your help.

Johannes

#17 lore raw

PATCH v7 1/1 watchdog: New watchdog driver for MEN A21 watchdogs

In reply to Guenter Roeck

This patch adds the driver for the watchdog devices found on MEN Mikro Elektronik A21 VMEbus CPU Carrier Boards. It has DT-support and uses the watchdog framework.

Revision 2:
* Removed unneeded open flag in struct a21_wdt_drv
* Corrected 3bit reason code from gpio
* Additional sysfs files are now part of watchdog sysfs
* Changed OFF/ON delay in ping from 400ms to 10ns
* Reworked timeout setting
* Removed a21_wdt_ioctl(...)

Revision 3:
* Changed pr_{err,info} to dev_{err,info}
* Removed out of memory error print
* Transition from "fast" to "slow" mode not allowed by chip

Revision 4:
* Remove reboot_notifier and place disable code into platform_device's shutdown function
* Removed sysfs interface

Revision 5:

* Added setting of .bootstatus on driver init
* Added initial timeout on driver init

Revision 6:
* Use watchdog_init_timeout() to initialize timeout

Revision 7:
* Fix possible get_bootstatus race condition

---
 MAINTAINERS                   |    6 ++
 drivers/watchdog/Kconfig      |    8 ++
 drivers/watchdog/Makefile     |    1 +
 drivers/watchdog/mena21_wdt.c |  234 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 249 insertions(+)
 create mode 100644 drivers/watchdog/mena21_wdt.c
MAINTAINERS +6 -0
index 7714c3c..023945a 100644
@@ -5307,6 +5307,12 @@ F:	drivers/mtd/
 F:	include/linux/mtd/
 F:	include/uapi/mtd/
 
+MEN A21 WATCHDOG DRIVER
+M:  	Johannes Thumshirn <[email protected]>
+L:	[email protected]
+S:	Supported
+F:	drivers/watchdog/mena21_wdt.c
+
 METAG ARCHITECTURE
 M:	James Hogan <[email protected]>
 S:	Supported
drivers/watchdog/Kconfig +8 -0
index e89fc31..192b84d 100644
@@ -1172,6 +1172,14 @@ config BOOKE_WDT_DEFAULT_TIMEOUT
 
 	  The value can be overridden by the wdt_period command-line parameter.
 
+config MEN_A21_WDT
+       tristate "MEN A21 VME CPU Carrier Board Watchdog Timer"
+       select WATCHDOG_CORE
+       help
+        Watchdog driver for MEN A21 VMEbus CPU Carrier Boards.
+
+	If unsure select N here.
+
 # PPC64 Architecture
 
 config WATCHDOG_RTAS
drivers/watchdog/Makefile +1 -0
index a300b94..bffdcb1 100644
@@ -143,6 +143,7 @@ obj-$(CONFIG_8xxx_WDT) += mpc8xxx_wdt.o
 obj-$(CONFIG_MV64X60_WDT) += mv64x60_wdt.o
 obj-$(CONFIG_PIKA_WDT) += pika_wdt.o
 obj-$(CONFIG_BOOKE_WDT) += booke_wdt.o
+obj-$(CONFIG_MEN_A21_WDT) += mena21_wdt.o
 
 # PPC64 Architecture
 obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o
drivers/watchdog/mena21_wdt.c new file +234 -0

234 lines not on this page · raw message

--
1.7.9.5

maintainer
#18 lore raw
Quoted 133 lines · Johannes Thumshirn

This patch adds the driver for the watchdog devices found on MEN Mikro Elektronik A21 VMEbus CPU Carrier Boards. It has DT-support and uses the watchdog framework.

Revision 2:
* Removed unneeded open flag in struct a21_wdt_drv
* Corrected 3bit reason code from gpio
* Additional sysfs files are now part of watchdog sysfs
* Changed OFF/ON delay in ping from 400ms to 10ns
* Reworked timeout setting
* Removed a21_wdt_ioctl(...)

Revision 3:
* Changed pr_{err,info} to dev_{err,info}
* Removed out of memory error print
* Transition from "fast" to "slow" mode not allowed by chip

Revision 4:
* Remove reboot_notifier and place disable code into platform_device's shutdown function
* Removed sysfs interface

Revision 5:

* Added setting of .bootstatus on driver init
* Added initial timeout on driver init

Revision 6:
* Use watchdog_init_timeout() to initialize timeout

Revision 7:
* Fix possible get_bootstatus race condition

---
 MAINTAINERS                   |    6 ++
 drivers/watchdog/Kconfig      |    8 ++
 drivers/watchdog/Makefile     |    1 +
 drivers/watchdog/mena21_wdt.c |  234 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 249 insertions(+)
 create mode 100644 drivers/watchdog/mena21_wdt.c
MAINTAINERS +6 -0
index 7714c3c..023945a 100644
@@ -5307,6 +5307,12 @@ F:	drivers/mtd/
 F:	include/linux/mtd/
 F:	include/uapi/mtd/
 
+MEN A21 WATCHDOG DRIVER
+M:  	Johannes Thumshirn <[email protected]>
+L:	[email protected]
+S:	Supported
+F:	drivers/watchdog/mena21_wdt.c
+
 METAG ARCHITECTURE
 M:	James Hogan <[email protected]>
 S:	Supported
drivers/watchdog/Kconfig +8 -0
index e89fc31..192b84d 100644
@@ -1172,6 +1172,14 @@ config BOOKE_WDT_DEFAULT_TIMEOUT
 
 	  The value can be overridden by the wdt_period command-line parameter.
 
+config MEN_A21_WDT
+       tristate "MEN A21 VME CPU Carrier Board Watchdog Timer"
+       select WATCHDOG_CORE
+       help
+        Watchdog driver for MEN A21 VMEbus CPU Carrier Boards.
+
+	If unsure select N here.
+
 # PPC64 Architecture
 
 config WATCHDOG_RTAS
drivers/watchdog/Makefile +1 -0
index a300b94..bffdcb1 100644
@@ -143,6 +143,7 @@ obj-$(CONFIG_8xxx_WDT) += mpc8xxx_wdt.o
 obj-$(CONFIG_MV64X60_WDT) += mv64x60_wdt.o
 obj-$(CONFIG_PIKA_WDT) += pika_wdt.o
 obj-$(CONFIG_BOOKE_WDT) += booke_wdt.o
+obj-$(CONFIG_MEN_A21_WDT) += mena21_wdt.o
 
 # PPC64 Architecture
 obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o
drivers/watchdog/mena21_wdt.c new file +38 -0
new file mode 100644
index 0000000..dec35ec
@@ -0,0 +1,234 @@
+/*
+ * Watchdog driver for the A21 VME CPU Boards
+ *
+ * Copyright (C) 2013 MEN Mikro Elektronik Nuernberg GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+#include <linux/uaccess.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+
+#define GPIO_WD_ENAB	169
+#define GPIO_WD_FAST	170
+#define GPIO_WD_TRIG	171
+
+#define GPIO_RST_CAUSE_BASE 166
+
+struct a21_wdt_drv {
+	struct watchdog_device wdt;
+	struct mutex lock;
+};
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+			    __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static int a21_wdt_get_bootstatus(unsigned int *reset)

drivers/watchdog/mena21_wdt.c line 48

Quoted 10 lines
drivers/watchdog/mena21_wdt.c new file +10 -0
+{
+	int i;
+
+	for (i = 0; i < 3; i++)
+		*reset |= gpio_get_value(GPIO_RST_CAUSE_BASE + i)
+			? (1 << i) : 0;
+
+	if (*reset >= 8)
+		return -EINVAL;
+

This can never happen. Even if it does, -EINVAL would not be appropriate.

Also, I don't see why you pass a pointer to the reset code (which is pre-set to 0) in the first place. Just return the reset cause. If you really need to return an error, you can pass it as negative return value.

Thanks,
Guenter

Quoted 194 lines
drivers/watchdog/mena21_wdt.c new file +186 -1
+	return 0;
+}
+
+static int a21_wdt_start(struct watchdog_device *wdt)
+{
+	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+	mutex_lock(&drv->lock);
+
+	gpio_set_value(GPIO_WD_ENAB, 1);
+
+	mutex_unlock(&drv->lock);
+
+	return 0;
+}
+
+static int a21_wdt_stop(struct watchdog_device *wdt)
+{
+	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+	/* We don't stop if WDOG_NO_WAY_OUT is set */
+	if (test_bit(WDOG_NO_WAY_OUT, &wdt->status))
+		return -EINVAL;
+
+	mutex_lock(&drv->lock);
+
+	gpio_set_value(GPIO_WD_ENAB, 0);
+
+	mutex_unlock(&drv->lock);
+
+	return 0;
+}
+
+static int a21_wdt_ping(struct watchdog_device *wdt)
+{
+	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+	mutex_lock(&drv->lock);
+
+	gpio_set_value(GPIO_WD_TRIG, 0);
+	ndelay(10);
+	gpio_set_value(GPIO_WD_TRIG, 1);
+
+	mutex_unlock(&drv->lock);
+
+	return 0;
+}
+
+static int a21_wdt_set_timeout(struct watchdog_device *wdt,
+			       unsigned int timeout)
+{
+	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+	if (timeout != 1 && timeout != 30) {
+		dev_err(wdt->dev, "Only 1 and 30 allowed as timeout\n");
+		return -EINVAL;
+	}
+
+	if (timeout == 30 && wdt->timeout == 1) {
+		dev_err(wdt->dev,
+			"Transition from fast to slow mode not allowed\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&drv->lock);
+
+	if (timeout == 1)
+		gpio_set_value(GPIO_WD_FAST, 1);
+	else
+		gpio_set_value(GPIO_WD_FAST, 0);
+
+	wdt->timeout = timeout;
+
+	mutex_unlock(&drv->lock);
+
+	return 0;
+}
+
+static const struct watchdog_info a21_wdt_info = {
+	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+	.identity = "MEN A21 Watchdog",
+};
+
+static const struct watchdog_ops a21_wdt_ops = {
+	.owner = THIS_MODULE,
+	.start = a21_wdt_start,
+	.stop = a21_wdt_stop,
+	.ping = a21_wdt_ping,
+	.set_timeout = a21_wdt_set_timeout,
+};
+
+static struct watchdog_device a21_wdt = {
+	.info = &a21_wdt_info,
+	.ops = &a21_wdt_ops,
+	.min_timeout = 1,
+	.max_timeout = 30,
+};
+
+static int a21_wdt_probe(struct platform_device *pdev)
+{
+	struct a21_wdt_drv *drv;
+	unsigned int reset = 0;
+	int ret;
+
+	dev_info(&pdev->dev, "MEN A21 watchdog timer driver enabled\n");
+
+	drv = devm_kzalloc(&pdev->dev, sizeof(struct a21_wdt_drv), GFP_KERNEL);
+	if (!drv)
+		return -ENOMEM;
+
+	mutex_init(&drv->lock);
+	watchdog_init_timeout(&a21_wdt, 30, &pdev->dev);
+	watchdog_set_nowayout(&a21_wdt, nowayout);
+	watchdog_set_drvdata(&a21_wdt, drv);
+
+	ret = a21_wdt_get_bootstatus(&reset);
+	if (ret)
+		dev_warn(&pdev->dev, "Reset Cause contains invalid data\n");
+	else {
+		if (reset == 2)
+			a21_wdt.bootstatus |= WDIOF_EXTERN1;
+		else if (reset == 4)
+			a21_wdt.bootstatus |= WDIOF_CARDRESET;
+		else if (reset == 5)
+			a21_wdt.bootstatus |= WDIOF_POWERUNDER;
+		else if (reset == 7)
+			a21_wdt.bootstatus |= WDIOF_EXTERN2;
+	}
+
+	ret = watchdog_register_device(&a21_wdt);
+	if (ret) {
+		dev_err(&pdev->dev, "Cannot register watchdog device\n");
+		goto err_register_wd;
+	}
+
+	dev_set_drvdata(&pdev->dev, drv);
+
+	return 0;
+
+err_register_wd:
+	mutex_destroy(&drv->lock);
+
+	return ret;
+}
+
+static int a21_wdt_remove(struct platform_device *pdev)
+{
+	struct a21_wdt_drv *drv = dev_get_drvdata(&pdev->dev);
+
+	dev_warn(&pdev->dev,
+		"Unregistering A21 watchdog driver, board may reboot\n");
+
+
+	watchdog_unregister_device(&drv->wdt);
+
+	mutex_destroy(&drv->lock);
+
+	return 0;
+}
+
+static void a21_wdt_shutdown(struct platform_device *pdev)
+{
+	gpio_set_value(GPIO_WD_ENAB, 0);
+}
+
+static const struct of_device_id a21_wdt_ids[] = {
+	{ .compatible = "men,a021-wdt" },
+	{ },
+};
+
+static struct platform_driver a21_wdt_driver = {
+	.probe = a21_wdt_probe,
+	.remove = a21_wdt_remove,
+	.shutdown = a21_wdt_shutdown,
+	.driver = {
+		.name = "a21-watchdog",
+		.of_match_table = a21_wdt_ids,
+	},
+};
+
+module_platform_driver(a21_wdt_driver);
+
+MODULE_AUTHOR("MEN Mikro Elektronik");
+MODULE_DESCRIPTION("MEN A21 Watchdog");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:a21-watchdog");
--

1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#19 lore raw

PATCH v8 1/1 watchdog: New watchdog driver for MEN A21 watchdogs

In reply to Guenter Roeck

This patch adds the driver for the watchdog devices found on MEN Mikro Elektronik A21 VMEbus CPU Carrier Boards. It has DT-support and uses the watchdog framework.

Revision 2:
* Removed unneeded open flag in struct a21_wdt_drv
* Corrected 3bit reason code from gpio
* Additional sysfs files are now part of watchdog sysfs
* Changed OFF/ON delay in ping from 400ms to 10ns
* Reworked timeout setting
* Removed a21_wdt_ioctl(...)

Revision 3:
* Changed pr_{err,info} to dev_{err,info}
* Removed out of memory error print
* Transition from "fast" to "slow" mode not allowed by chip

Revision 4:
* Remove reboot_notifier and place disable code into platform_device's shutdown function
* Removed sysfs interface

Revision 5:
* Added setting of .bootstatus on driver init
* Added initial timeout on driver init

Revision 6:
* Use watchdog_init_timeout() to initialize timeout

Revision 7:
* Fix possible get_bootstatus race condition

Revision 8:
* a21_wdt_get_bootstatus() should return reset code
* GPIOs are supplied via DT instead of being hardcoded. Code derived from (drivers/hwmon/gpio-fan.c)
* Added Devicetree binding document
* Driver now depends on GPIOLIB

---
 .../devicetree/bindings/gpio/men-a021-wdt.txt      |   25 ++
 MAINTAINERS                                        |    6 +
 drivers/watchdog/Kconfig                           |   12 +
 drivers/watchdog/Makefile                          |    1 +
 drivers/watchdog/mena21_wdt.c                      |  277 ++++++++++++++++++++
 5 files changed, 321 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/men-a021-wdt.txt
 create mode 100644 drivers/watchdog/mena21_wdt.c
Documentation/devicetree/bindings/gpio/men-a021-wdt.txt new file +25 -0
new file mode 100644
index 0000000..370dee3
@@ -0,0 +1,25 @@
+Bindings for MEN A21 Watchdog device connected to GPIO lines
+
+Required properties:
+- compatible: "men,a021-wdt"
+- gpios: Specifies the pins that control the Watchdog, order:
+  1: Watchdog enable
+  2: Watchdog fast-mode
+  3: Watchdog trigger
+  4: Watchdog reset cause bit 0
+  5: Watchdog reset cause bit 1
+  6: Watchdog reset cause bit 2
+
+Optional properties:
+- None
+
+Example:
+	watchdog {
+		compatible ="men,a021-wdt";
+		gpios = <&gpio3 9  1	/* WD_EN */
+			 &gpio3 10 1	/* WD_FAST */
+			 &gpio3 11 1	/* WD_TRIG */
+			 &gpio3 6  1	/* RST_CAUSE[0] */
+			 &gpio3 7  1	/* RST_CAUSE[1] */
+			 &gpio3 8  1>;	/* RST_CAUSE[2] */
+	};
MAINTAINERS +6 -0
index f35a259..bd86661 100644
@@ -5307,6 +5307,12 @@ F:	drivers/mtd/
 F:	include/linux/mtd/
 F:	include/uapi/mtd/
 
+MEN A21 WATCHDOG DRIVER
+M:  	Johannes Thumshirn <[email protected]>
+L:	[email protected]
+S:	Supported
+F:	drivers/watchdog/mena21_wdt.c
+
 METAG ARCHITECTURE
 M:	James Hogan <[email protected]>
 S:	Supported
drivers/watchdog/Kconfig +12 -0
index e89fc31..8b143ee 100644
@@ -1172,6 +1172,18 @@ config BOOKE_WDT_DEFAULT_TIMEOUT
 
 	  The value can be overridden by the wdt_period command-line parameter.
 
+config MEN_A21_WDT
+       tristate "MEN A21 VME CPU Carrier Board Watchdog Timer"
+       select WATCHDOG_CORE
+       depends on GPIOLIB
+       help
+        Watchdog driver for MEN A21 VMEbus CPU Carrier Boards.
+
+	The driver can also be built as a module. If so, the module will be
+	called mena21_wdt.
+
+	If unsure select N here.
+
 # PPC64 Architecture
 
 config WATCHDOG_RTAS
drivers/watchdog/Makefile +1 -0
index a300b94..bffdcb1 100644
@@ -143,6 +143,7 @@ obj-$(CONFIG_8xxx_WDT) += mpc8xxx_wdt.o
 obj-$(CONFIG_MV64X60_WDT) += mv64x60_wdt.o
 obj-$(CONFIG_PIKA_WDT) += pika_wdt.o
 obj-$(CONFIG_BOOKE_WDT) += booke_wdt.o
+obj-$(CONFIG_MEN_A21_WDT) += mena21_wdt.o
 
 # PPC64 Architecture
 obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o
drivers/watchdog/mena21_wdt.c new file +277 -0

277 lines not on this page · raw message

--
1.7.9.5

#20 lore raw

PATCH v8 2/2 watchdog: Sysfs interface for MEN A21 watchdog

This patch adds a sysfs interface for the watchdog device found on MEN A21 Boards.

The newly generated files are:
* rebootcause:
Can be one of:
Power on Reset,
CPU Reset Request,
Push Button,
FPGA Reset Request,
Watchdog,
Local Power Bad,
Invalid or
BDI
and shows the reason of the boards last reboot.

* active:
Shows if the watchdog CPLD is actually running

* allow_disable:
Shows if the watchdog is allowed to be disabled (NOWAYOUT disabled)

* fastmode:
Shows if the CPLD is running in fast mode (1s timeout), once it is in fastmode it can't be switched back to slow mode (30s timeout) until the next reboot.

Revision 2:
* Re-worked sysfs patch to apply on re-worked base

Revision 3:
* Re-worked sysfs patch to apply on re-worked base

---
 drivers/watchdog/mena21_wdt.c |   88 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 88 insertions(+)
drivers/watchdog/mena21_wdt.c +88 -0
index 29f9c0d..cae5462 100644
@@ -20,6 +20,17 @@
 #include <linux/delay.h>
 #include <linux/bitops.h>
 
+static char *reset_causes[] = {
+	"Power On Reset",
+	"CPU Reset Request",
+	"Push Button",
+	"FPGA Reset Request",
+	"Watchdog",
+	"Local Power Bad",
+	"Invalid",
+	"BDI",
+};
+
 enum a21_wdt_gpios {
 	GPIO_WD_ENAB,
 	GPIO_WD_FAST,
@@ -52,6 +63,73 @@ static unsigned int a21_wdt_get_bootstatus(struct a21_wdt_drv *drv)
 	return reset;
 }
 
+static ssize_t rebootcause_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct a21_wdt_drv *drv = dev_get_drvdata(dev);
+	unsigned int reset = 0;
+
+	reset = a21_wdt_get_bootstatus(drv);
+
+	return sprintf(buf, "%s\n", reset_causes[reset]);
+}
+static DEVICE_ATTR(rebootcause, S_IRUGO, rebootcause_show, NULL);
+
+static ssize_t active_show(struct device *dev, struct device_attribute *attr,
+			   char *buf)
+{
+	struct a21_wdt_drv *drv = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%d\n", !!gpio_get_value(drv->gpios[GPIO_WD_ENAB]));
+}
+static DEVICE_ATTR(active, S_IRUGO, active_show, NULL);
+
+static ssize_t allow_disable_show(struct device *dev,
+				  struct device_attribute *attr,
+				  char *buf)
+{
+	return sprintf(buf, "%d\n", !nowayout);
+}
+static DEVICE_ATTR(allow_disable, S_IRUGO, allow_disable_show, NULL);
+
+static ssize_t fastmode_show(struct device *dev, struct device_attribute *attr,
+			     char *buf)
+{
+	struct a21_wdt_drv *drv = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%d\n", !!gpio_get_value(drv->gpios[GPIO_WD_FAST]));
+}
+static DEVICE_ATTR(fastmode, S_IRUGO, fastmode_show, NULL);
+
+static int a21_wdt_create_files(struct watchdog_device *wdev)
+{
+	int ret;
+
+	ret = device_create_file(wdev->dev, &dev_attr_rebootcause);
+	if (ret)
+		return ret;
+	ret = device_create_file(wdev->dev, &dev_attr_active);
+	if (ret)
+		return ret;
+	ret = device_create_file(wdev->dev, &dev_attr_allow_disable);
+	if (ret)
+		return ret;
+	ret = device_create_file(wdev->dev, &dev_attr_fastmode);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static void a21_wdt_remove_files(struct watchdog_device *wdev)
+{
+	device_remove_file(wdev->dev, &dev_attr_rebootcause);
+	device_remove_file(wdev->dev, &dev_attr_active);
+	device_remove_file(wdev->dev, &dev_attr_allow_disable);
+	device_remove_file(wdev->dev, &dev_attr_fastmode);
+}
+
 static int a21_wdt_start(struct watchdog_device *wdt)
 {
 	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
@@ -223,9 +301,18 @@ static int a21_wdt_probe(struct platform_device *pdev)
 	}
 
 	dev_set_drvdata(&pdev->dev, drv);
+	dev_set_drvdata(a21_wdt.dev, drv);
+
+	ret = a21_wdt_create_files(&a21_wdt);
+	if (ret) {
+		dev_err(&pdev->dev, "Cannot create sysfs entries\n");
+		goto err_create_sysfs;
+	}
 
 	return 0;
 
+err_create_sysfs:
+	watchdog_unregister_device(&drv->wdt);
 err_register_wd:
 	mutex_destroy(&drv->lock);
 
@@ -239,6 +326,7 @@ static int a21_wdt_remove(struct platform_device *pdev)
 	dev_warn(&pdev->dev,
 		"Unregistering A21 watchdog driver, board may reboot\n");
 
+	a21_wdt_remove_files(&drv->wdt);
 
 	watchdog_unregister_device(&drv->wdt);
 

--
1.7.9.5

maintainer
#21 lore raw
In reply to Johannes Thumshirn
Quoted 31 lines · Johannes Thumshirn

This patch adds the driver for the watchdog devices found on MEN Mikro Elektronik A21 VMEbus CPU Carrier Boards. It has DT-support and uses the watchdog framework.

Revision 2:
* Removed unneeded open flag in struct a21_wdt_drv
* Corrected 3bit reason code from gpio
* Additional sysfs files are now part of watchdog sysfs
* Changed OFF/ON delay in ping from 400ms to 10ns
* Reworked timeout setting
* Removed a21_wdt_ioctl(...)

Revision 3:
* Changed pr_{err,info} to dev_{err,info}
* Removed out of memory error print
* Transition from "fast" to "slow" mode not allowed by chip

Revision 4:
* Remove reboot_notifier and place disable code into platform_device's shutdown function
* Removed sysfs interface

Revision 5:
* Added setting of .bootstatus on driver init
* Added initial timeout on driver init

Revision 6:
* Use watchdog_init_timeout() to initialize timeout

Revision 7:
* Fix possible get_bootstatus race condition

Quoted 8 lines

Revision 8:
* a21_wdt_get_bootstatus() should return reset code
* GPIOs are supplied via DT instead of being hardcoded. Code derived from (drivers/hwmon/gpio-fan.c)
* Added Devicetree binding document
* Driver now depends on GPIOLIB

Almost good. One comment below.

Thanks,
Guenter

Quoted 261 lines
---
 .../devicetree/bindings/gpio/men-a021-wdt.txt      |   25 ++
 MAINTAINERS                                        |    6 +
 drivers/watchdog/Kconfig                           |   12 +
 drivers/watchdog/Makefile                          |    1 +
 drivers/watchdog/mena21_wdt.c                      |  277 ++++++++++++++++++++
 5 files changed, 321 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/men-a021-wdt.txt
 create mode 100644 drivers/watchdog/mena21_wdt.c
Documentation/devicetree/bindings/gpio/men-a021-wdt.txt new file +25 -0
new file mode 100644
index 0000000..370dee3
@@ -0,0 +1,25 @@
+Bindings for MEN A21 Watchdog device connected to GPIO lines
+
+Required properties:
+- compatible: "men,a021-wdt"
+- gpios: Specifies the pins that control the Watchdog, order:
+  1: Watchdog enable
+  2: Watchdog fast-mode
+  3: Watchdog trigger
+  4: Watchdog reset cause bit 0
+  5: Watchdog reset cause bit 1
+  6: Watchdog reset cause bit 2
+
+Optional properties:
+- None
+
+Example:
+	watchdog {
+		compatible ="men,a021-wdt";
+		gpios = <&gpio3 9  1	/* WD_EN */
+			 &gpio3 10 1	/* WD_FAST */
+			 &gpio3 11 1	/* WD_TRIG */
+			 &gpio3 6  1	/* RST_CAUSE[0] */
+			 &gpio3 7  1	/* RST_CAUSE[1] */
+			 &gpio3 8  1>;	/* RST_CAUSE[2] */
+	};
MAINTAINERS +6 -0
index f35a259..bd86661 100644
@@ -5307,6 +5307,12 @@ F:	drivers/mtd/
 F:	include/linux/mtd/
 F:	include/uapi/mtd/
 
+MEN A21 WATCHDOG DRIVER
+M:  	Johannes Thumshirn <[email protected]>
+L:	[email protected]
+S:	Supported
+F:	drivers/watchdog/mena21_wdt.c
+
 METAG ARCHITECTURE
 M:	James Hogan <[email protected]>
 S:	Supported
drivers/watchdog/Kconfig +12 -0
index e89fc31..8b143ee 100644
@@ -1172,6 +1172,18 @@ config BOOKE_WDT_DEFAULT_TIMEOUT
 
 	  The value can be overridden by the wdt_period command-line parameter.
 
+config MEN_A21_WDT
+       tristate "MEN A21 VME CPU Carrier Board Watchdog Timer"
+       select WATCHDOG_CORE
+       depends on GPIOLIB
+       help
+        Watchdog driver for MEN A21 VMEbus CPU Carrier Boards.
+
+	The driver can also be built as a module. If so, the module will be
+	called mena21_wdt.
+
+	If unsure select N here.
+
 # PPC64 Architecture
 
 config WATCHDOG_RTAS
drivers/watchdog/Makefile +1 -0
index a300b94..bffdcb1 100644
@@ -143,6 +143,7 @@ obj-$(CONFIG_8xxx_WDT) += mpc8xxx_wdt.o
 obj-$(CONFIG_MV64X60_WDT) += mv64x60_wdt.o
 obj-$(CONFIG_PIKA_WDT) += pika_wdt.o
 obj-$(CONFIG_BOOKE_WDT) += booke_wdt.o
+obj-$(CONFIG_MEN_A21_WDT) += mena21_wdt.o
 
 # PPC64 Architecture
 obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o
drivers/watchdog/mena21_wdt.c new file +162 -0
new file mode 100644
index 0000000..29f9c0d
@@ -0,0 +1,277 @@
+/*
+ * Watchdog driver for the A21 VME CPU Boards
+ *
+ * Copyright (C) 2013 MEN Mikro Elektronik Nuernberg GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+#include <linux/uaccess.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+
+enum a21_wdt_gpios {
+	GPIO_WD_ENAB,
+	GPIO_WD_FAST,
+	GPIO_WD_TRIG,
+	GPIO_WD_RST0,
+	GPIO_WD_RST1,
+	GPIO_WD_RST2,
+};
+
+struct a21_wdt_drv {
+	struct watchdog_device wdt;
+	struct mutex lock;
+	int num_gpios;
+	unsigned *gpios;
+};
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+			    __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static unsigned int a21_wdt_get_bootstatus(struct a21_wdt_drv *drv)
+{
+	int reset = 0;
+
+	reset |= gpio_get_value(drv->gpios[GPIO_WD_RST0]) ? (1 << 0) : 0;
+	reset |= gpio_get_value(drv->gpios[GPIO_WD_RST1]) ? (1 << 1) : 0;
+	reset |= gpio_get_value(drv->gpios[GPIO_WD_RST2]) ? (1 << 2) : 0;
+
+	return reset;
+}
+
+static int a21_wdt_start(struct watchdog_device *wdt)
+{
+	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+	mutex_lock(&drv->lock);
+
+	gpio_set_value(drv->gpios[GPIO_WD_ENAB], 1);
+
+	mutex_unlock(&drv->lock);
+
+	return 0;
+}
+
+static int a21_wdt_stop(struct watchdog_device *wdt)
+{
+	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+	/* We don't stop if WDOG_NO_WAY_OUT is set */
+	if (test_bit(WDOG_NO_WAY_OUT, &wdt->status))
+		return -EINVAL;
+
+	mutex_lock(&drv->lock);
+
+	gpio_set_value(drv->gpios[GPIO_WD_ENAB], 0);
+
+	mutex_unlock(&drv->lock);
+
+	return 0;
+}
+
+static int a21_wdt_ping(struct watchdog_device *wdt)
+{
+	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+	mutex_lock(&drv->lock);
+
+	gpio_set_value(drv->gpios[GPIO_WD_TRIG], 0);
+	ndelay(10);
+	gpio_set_value(drv->gpios[GPIO_WD_TRIG], 1);
+
+	mutex_unlock(&drv->lock);
+
+	return 0;
+}
+
+static int a21_wdt_set_timeout(struct watchdog_device *wdt,
+			       unsigned int timeout)
+{
+	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+	if (timeout != 1 && timeout != 30) {
+		dev_err(wdt->dev, "Only 1 and 30 allowed as timeout\n");
+		return -EINVAL;
+	}
+
+	if (timeout == 30 && wdt->timeout == 1) {
+		dev_err(wdt->dev,
+			"Transition from fast to slow mode not allowed\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&drv->lock);
+
+	if (timeout == 1)
+		gpio_set_value(drv->gpios[GPIO_WD_FAST], 1);
+	else
+		gpio_set_value(drv->gpios[GPIO_WD_FAST], 0);
+
+	wdt->timeout = timeout;
+
+	mutex_unlock(&drv->lock);
+
+	return 0;
+}
+
+static const struct watchdog_info a21_wdt_info = {
+	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+	.identity = "MEN A21 Watchdog",
+};
+
+static const struct watchdog_ops a21_wdt_ops = {
+	.owner = THIS_MODULE,
+	.start = a21_wdt_start,
+	.stop = a21_wdt_stop,
+	.ping = a21_wdt_ping,
+	.set_timeout = a21_wdt_set_timeout,
+};
+
+static struct watchdog_device a21_wdt = {
+	.info = &a21_wdt_info,
+	.ops = &a21_wdt_ops,
+	.min_timeout = 1,
+	.max_timeout = 30,
+};
+
+static int a21_wdt_probe(struct platform_device *pdev)
+{
+	struct device_node *node;
+	struct a21_wdt_drv *drv;
+	unsigned int reset = 0;
+	unsigned *gpios;
+	int ret;
+	int i;
+
+	dev_info(&pdev->dev, "MEN A21 watchdog timer driver enabled\n");
+
+	drv = devm_kzalloc(&pdev->dev, sizeof(struct a21_wdt_drv), GFP_KERNEL);
+	if (!drv)

drivers/watchdog/mena21_wdt.c line 172

Quoted 10 lines
drivers/watchdog/mena21_wdt.c new file +10 -0
+		return -ENOMEM;
+
+	/* Fill GPIO pin array */
+	node = pdev->dev.of_node;
+
+	drv->num_gpios = of_gpio_count(node);
+	if (drv->num_gpios <= 0) {
+		dev_err(&pdev->dev, "gpios DT property empty / missing");
+		return -ENODEV;
+	}

Not happy with this. Code accepts one gpio pin, but then assumes throughout the driver that all pins are defined. I am quite sure I can get it to crash if I specify only a single gpio pin. num_gpios will be 1, you allocate a single entry, but then access all 6 of them.

I think it would be easier if you expect a fixed number of gpio pins (6) and abort if it is not correct. You can then make drv->gpios an array and don't have to allocate it separately, and you don't need drv->num_gpios (which you don't need anyway as it is not used outside this function).

Quoted 113 lines
drivers/watchdog/mena21_wdt.c new file +105 -1
+
+	gpios = devm_kzalloc(&pdev->dev, drv->num_gpios * sizeof(unsigned),
+			     GFP_KERNEL);
+	if (!gpios)
+		return -ENOMEM;
+
+	for (i = 0; i < drv->num_gpios; i++) {
+		int val;
+
+		val = of_get_gpio(node, i);
+		if (val < 0)
+			return val;
+
+		gpios[i] = val;
+	}
+	drv->gpios = gpios;
+
+	/* Request the used GPIOs */
+	for (i = 0; i < drv->num_gpios; i++) {
+		ret = devm_gpio_request(&pdev->dev, drv->gpios[i],
+					"MEN A21 Watchdog");
+		if (ret)
+			return ret;
+
+		ret = gpio_direction_output(drv->gpios[i],
+					    gpio_get_value(drv->gpios[i]));
+		if (ret)
+			return ret;
+	}
+
+	mutex_init(&drv->lock);
+	watchdog_init_timeout(&a21_wdt, 30, &pdev->dev);
+	watchdog_set_nowayout(&a21_wdt, nowayout);
+	watchdog_set_drvdata(&a21_wdt, drv);
+
+	reset = a21_wdt_get_bootstatus(drv);
+	if (reset == 2)
+		a21_wdt.bootstatus |= WDIOF_EXTERN1;
+	else if (reset == 4)
+		a21_wdt.bootstatus |= WDIOF_CARDRESET;
+	else if (reset == 5)
+		a21_wdt.bootstatus |= WDIOF_POWERUNDER;
+	else if (reset == 7)
+		a21_wdt.bootstatus |= WDIOF_EXTERN2;
+
+
+	ret = watchdog_register_device(&a21_wdt);
+	if (ret) {
+		dev_err(&pdev->dev, "Cannot register watchdog device\n");
+		goto err_register_wd;
+	}
+
+	dev_set_drvdata(&pdev->dev, drv);
+
+	return 0;
+
+err_register_wd:
+	mutex_destroy(&drv->lock);
+
+	return ret;
+}
+
+static int a21_wdt_remove(struct platform_device *pdev)
+{
+	struct a21_wdt_drv *drv = dev_get_drvdata(&pdev->dev);
+
+	dev_warn(&pdev->dev,
+		"Unregistering A21 watchdog driver, board may reboot\n");
+
+
+	watchdog_unregister_device(&drv->wdt);
+
+	mutex_destroy(&drv->lock);
+
+	return 0;
+}
+
+static void a21_wdt_shutdown(struct platform_device *pdev)
+{
+	struct a21_wdt_drv *drv = dev_get_drvdata(&pdev->dev);
+
+	gpio_set_value(drv->gpios[GPIO_WD_ENAB], 0);
+}
+
+static const struct of_device_id a21_wdt_ids[] = {
+	{ .compatible = "men,a021-wdt" },
+	{ },
+};
+
+static struct platform_driver a21_wdt_driver = {
+	.probe = a21_wdt_probe,
+	.remove = a21_wdt_remove,
+	.shutdown = a21_wdt_shutdown,
+	.driver = {
+		.name = "a21-watchdog",
+		.of_match_table = a21_wdt_ids,
+	},
+};
+
+module_platform_driver(a21_wdt_driver);
+
+MODULE_AUTHOR("MEN Mikro Elektronik");
+MODULE_DESCRIPTION("MEN A21 Watchdog");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:a21-watchdog");
--

1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#22 lore raw

On Thu, Jun 13, 2013 at 08:55:33PM -0700, Guenter Roeck wrote:
[...]

Quoted 7 lines
Quoted 1 line

Almost good. One comment below.

Thanks,
Guenter

Nice to hear. Thanks for the review by the way.

[...]

Quoted 12 lines

drivers/watchdog/mena21_wdt.c line 172

Quoted 6 lines
drivers/watchdog/mena21_wdt.c new file +6 -0
+
+	drv->num_gpios = of_gpio_count(node);
+	if (drv->num_gpios <= 0) {
+		dev_err(&pdev->dev, "gpios DT property empty / missing");
+		return -ENODEV;
+	}

Not happy with this. Code accepts one gpio pin, but then assumes throughout the driver that all pins are defined. I am quite sure I can get it to crash if I specify only a single gpio pin. num_gpios will be 1, you allocate a single entry, but then access all 6 of them.

Quoted 5 lines

I think it would be easier if you expect a fixed number of gpio pins (6) and abort if it is not correct. You can then make drv->gpios an array and don't have to allocate it separately, and you don't need drv->num_gpios (which you don't need anyway as it is not used outside this function).

Yup, you're right. Now that you're saying it I see it as well. This is probably a copy'n'paste error from drivers/hwmon/gpio-fan.c.

@Wim are there any objections from your side?

Byte
Johannes

#23 lore raw

PATCH v9 1/1 watchdog: New watchdog driver for MEN A21 watchdogs

In reply to Johannes Thumshirn

This patch adds the driver for the watchdog devices found on MEN Mikro Elektronik A21 VMEbus CPU Carrier Boards. It has DT-support and uses the watchdog framework.

---

Revision 2:
* Removed unneeded open flag in struct a21_wdt_drv
* Corrected 3bit reason code from gpio
* Additional sysfs files are now part of watchdog sysfs
* Changed OFF/ON delay in ping from 400ms to 10ns
* Reworked timeout setting
* Removed a21_wdt_ioctl(...)

Revision 3:
* Changed pr_{err,info} to dev_{err,info}
* Removed out of memory error print
* Transition from "fast" to "slow" mode not allowed by chip

Revision 4:
* Remove reboot_notifier and place disable code into platform_device's shutdown function
* Removed sysfs interface

Revision 5:
* Added setting of .bootstatus on driver init
* Added initial timeout on driver init

Revision 6:
* Use watchdog_init_timeout() to initialize timeout

Revision 7:
* Fix possible get_bootstatus race condition

Revision 8:
* a21_wdt_get_bootstatus() should return reset code
* GPIOs are supplied via DT instead of being hardcoded. Code derived from (drivers/hwmon/gpio-fan.c)
* Added Devicetree binding document
* Driver now depends on GPIOLIB

Revision 9:
* We need 6 GPIOs not only 1 to work
* GPIO_WD_RST[0..2] are inputs

 .../devicetree/bindings/gpio/men-a021-wdt.txt      |   25 ++
 MAINTAINERS                                        |    6 +
 drivers/watchdog/Kconfig                           |   12 +
 drivers/watchdog/Makefile                          |    1 +
 drivers/watchdog/mena21_wdt.c                      |  276 ++++++++++++++++++++
 5 files changed, 320 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/men-a021-wdt.txt
 create mode 100644 drivers/watchdog/mena21_wdt.c
Documentation/devicetree/bindings/gpio/men-a021-wdt.txt new file +25 -0
new file mode 100644
index 0000000..370dee3
@@ -0,0 +1,25 @@
+Bindings for MEN A21 Watchdog device connected to GPIO lines
+
+Required properties:
+- compatible: "men,a021-wdt"
+- gpios: Specifies the pins that control the Watchdog, order:
+  1: Watchdog enable
+  2: Watchdog fast-mode
+  3: Watchdog trigger
+  4: Watchdog reset cause bit 0
+  5: Watchdog reset cause bit 1
+  6: Watchdog reset cause bit 2
+
+Optional properties:
+- None
+
+Example:
+	watchdog {
+		compatible ="men,a021-wdt";
+		gpios = <&gpio3 9  1	/* WD_EN */
+			 &gpio3 10 1	/* WD_FAST */
+			 &gpio3 11 1	/* WD_TRIG */
+			 &gpio3 6  1	/* RST_CAUSE[0] */
+			 &gpio3 7  1	/* RST_CAUSE[1] */
+			 &gpio3 8  1>;	/* RST_CAUSE[2] */
+	};
MAINTAINERS +6 -0
index 5be702c..824261e 100644
@@ -5317,6 +5317,12 @@ F:	drivers/mtd/
 F:	include/linux/mtd/
 F:	include/uapi/mtd/
 
+MEN A21 WATCHDOG DRIVER
+M:  	Johannes Thumshirn <[email protected]>
+L:	[email protected]
+S:	Supported
+F:	drivers/watchdog/mena21_wdt.c
+
 METAG ARCHITECTURE
 M:	James Hogan <[email protected]>
 S:	Supported
drivers/watchdog/Kconfig +12 -0
index e89fc31..8b143ee 100644
@@ -1172,6 +1172,18 @@ config BOOKE_WDT_DEFAULT_TIMEOUT
 
 	  The value can be overridden by the wdt_period command-line parameter.
 
+config MEN_A21_WDT
+       tristate "MEN A21 VME CPU Carrier Board Watchdog Timer"
+       select WATCHDOG_CORE
+       depends on GPIOLIB
+       help
+        Watchdog driver for MEN A21 VMEbus CPU Carrier Boards.
+
+	The driver can also be built as a module. If so, the module will be
+	called mena21_wdt.
+
+	If unsure select N here.
+
 # PPC64 Architecture
 
 config WATCHDOG_RTAS
drivers/watchdog/Makefile +1 -0
index a300b94..bffdcb1 100644
@@ -143,6 +143,7 @@ obj-$(CONFIG_8xxx_WDT) += mpc8xxx_wdt.o
 obj-$(CONFIG_MV64X60_WDT) += mv64x60_wdt.o
 obj-$(CONFIG_PIKA_WDT) += pika_wdt.o
 obj-$(CONFIG_BOOKE_WDT) += booke_wdt.o
+obj-$(CONFIG_MEN_A21_WDT) += mena21_wdt.o
 
 # PPC64 Architecture
 obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o
drivers/watchdog/mena21_wdt.c new file +276 -0

276 lines not on this page · raw message

--
1.7.9.5

#24 lore raw

PATCH v9 2/2 watchdog: Sysfs interface for MEN A21 watchdog

This patch adds a sysfs interface for the watchdog device found on MEN A21 Boards.

The newly generated files are:
* rebootcause:
Can be one of:
Power on Reset,
CPU Reset Request,
Push Button,
FPGA Reset Request,
Watchdog,
Local Power Bad,
Invalid or
BDI
and shows the reason of the boards last reboot.

* active:
Shows if the watchdog CPLD is actually running

* allow_disable:
Shows if the watchdog is allowed to be disabled (NOWAYOUT disabled)

* fastmode:
Shows if the CPLD is running in fast mode (1s timeout), once it is in fastmode it can't be switched back to slow mode (30s timeout) until the next reboot.

---

Revision 2:
* Re-worked sysfs patch to apply on re-worked base

Revision 3:
* Re-worked sysfs patch to apply on re-worked base

Revision 4:
* Re-worked sysfs patch to apply on re-worked base

 drivers/watchdog/mena21_wdt.c |   88 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 88 insertions(+)
drivers/watchdog/mena21_wdt.c +88 -0
index 0d3409a..03165be 100644
@@ -22,6 +22,17 @@
 
 #define NUM_GPIOS 6
 
+static char *reset_causes[] = {
+	"Power On Reset",
+	"CPU Reset Request",
+	"Push Button",
+	"FPGA Reset Request",
+	"Watchdog",
+	"Local Power Bad",
+	"Invalid",
+	"BDI",
+};
+
 enum a21_wdt_gpios {
 	GPIO_WD_ENAB,
 	GPIO_WD_FAST,
@@ -53,6 +64,73 @@ static unsigned int a21_wdt_get_bootstatus(struct a21_wdt_drv *drv)
 	return reset;
 }
 
+static ssize_t rebootcause_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct a21_wdt_drv *drv = dev_get_drvdata(dev);
+	unsigned int reset = 0;
+
+	reset = a21_wdt_get_bootstatus(drv);
+
+	return sprintf(buf, "%s\n", reset_causes[reset]);
+}
+static DEVICE_ATTR(rebootcause, S_IRUGO, rebootcause_show, NULL);
+
+static ssize_t active_show(struct device *dev, struct device_attribute *attr,
+			   char *buf)
+{
+	struct a21_wdt_drv *drv = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%d\n", !!gpio_get_value(drv->gpios[GPIO_WD_ENAB]));
+}
+static DEVICE_ATTR(active, S_IRUGO, active_show, NULL);
+
+static ssize_t allow_disable_show(struct device *dev,
+				  struct device_attribute *attr,
+				  char *buf)
+{
+	return sprintf(buf, "%d\n", !nowayout);
+}
+static DEVICE_ATTR(allow_disable, S_IRUGO, allow_disable_show, NULL);
+
+static ssize_t fastmode_show(struct device *dev, struct device_attribute *attr,
+			     char *buf)
+{
+	struct a21_wdt_drv *drv = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%d\n", !!gpio_get_value(drv->gpios[GPIO_WD_FAST]));
+}
+static DEVICE_ATTR(fastmode, S_IRUGO, fastmode_show, NULL);
+
+static int a21_wdt_create_files(struct watchdog_device *wdev)
+{
+	int ret;
+
+	ret = device_create_file(wdev->dev, &dev_attr_rebootcause);
+	if (ret)
+		return ret;
+	ret = device_create_file(wdev->dev, &dev_attr_active);
+	if (ret)
+		return ret;
+	ret = device_create_file(wdev->dev, &dev_attr_allow_disable);
+	if (ret)
+		return ret;
+	ret = device_create_file(wdev->dev, &dev_attr_fastmode);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static void a21_wdt_remove_files(struct watchdog_device *wdev)
+{
+	device_remove_file(wdev->dev, &dev_attr_rebootcause);
+	device_remove_file(wdev->dev, &dev_attr_active);
+	device_remove_file(wdev->dev, &dev_attr_allow_disable);
+	device_remove_file(wdev->dev, &dev_attr_fastmode);
+}
+
 static int a21_wdt_start(struct watchdog_device *wdt)
 {
 	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
@@ -222,9 +300,18 @@ static int a21_wdt_probe(struct platform_device *pdev)
 	}
 
 	dev_set_drvdata(&pdev->dev, drv);
+	dev_set_drvdata(a21_wdt.dev, drv);
+
+	ret = a21_wdt_create_files(&a21_wdt);
+	if (ret) {
+		dev_err(&pdev->dev, "Cannot create sysfs entries\n");
+		goto err_create_sysfs;
+	}
 
 	return 0;
 
+err_create_sysfs:
+	watchdog_unregister_device(&drv->wdt);
 err_register_wd:
 	mutex_destroy(&drv->lock);
 
@@ -238,6 +325,7 @@ static int a21_wdt_remove(struct platform_device *pdev)
 	dev_warn(&pdev->dev,
 		"Unregistering A21 watchdog driver, board may reboot\n");
 
+	a21_wdt_remove_files(&drv->wdt);
 
 	watchdog_unregister_device(&drv->wdt);
 

--
1.7.9.5

maintainer
#25 lore raw
In reply to Johannes Thumshirn
Quoted 5 lines · Johannes Thumshirn

This patch adds the driver for the watchdog devices found on MEN Mikro Elektronik A21 VMEbus CPU Carrier Boards. It has DT-support and uses the watchdog framework.

Only nitpicks this time.

Quoted 204 lines

---

Revision 2:
* Removed unneeded open flag in struct a21_wdt_drv
* Corrected 3bit reason code from gpio
* Additional sysfs files are now part of watchdog sysfs
* Changed OFF/ON delay in ping from 400ms to 10ns
* Reworked timeout setting
* Removed a21_wdt_ioctl(...)

Revision 3:
* Changed pr_{err,info} to dev_{err,info}
* Removed out of memory error print
* Transition from "fast" to "slow" mode not allowed by chip

Revision 4:
* Remove reboot_notifier and place disable code into platform_device's shutdown function
* Removed sysfs interface

Revision 5:
* Added setting of .bootstatus on driver init
* Added initial timeout on driver init

Revision 6:
* Use watchdog_init_timeout() to initialize timeout

Revision 7:
* Fix possible get_bootstatus race condition

Revision 8:
* a21_wdt_get_bootstatus() should return reset code
* GPIOs are supplied via DT instead of being hardcoded. Code derived from (drivers/hwmon/gpio-fan.c)
* Added Devicetree binding document
* Driver now depends on GPIOLIB

Revision 9:
* We need 6 GPIOs not only 1 to work
* GPIO_WD_RST[0..2] are inputs

 .../devicetree/bindings/gpio/men-a021-wdt.txt      |   25 ++
 MAINTAINERS                                        |    6 +
 drivers/watchdog/Kconfig                           |   12 +
 drivers/watchdog/Makefile                          |    1 +
 drivers/watchdog/mena21_wdt.c                      |  276 ++++++++++++++++++++
 5 files changed, 320 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/men-a021-wdt.txt
 create mode 100644 drivers/watchdog/mena21_wdt.c
Documentation/devicetree/bindings/gpio/men-a021-wdt.txt new file +25 -0
new file mode 100644
index 0000000..370dee3
@@ -0,0 +1,25 @@
+Bindings for MEN A21 Watchdog device connected to GPIO lines
+
+Required properties:
+- compatible: "men,a021-wdt"
+- gpios: Specifies the pins that control the Watchdog, order:
+  1: Watchdog enable
+  2: Watchdog fast-mode
+  3: Watchdog trigger
+  4: Watchdog reset cause bit 0
+  5: Watchdog reset cause bit 1
+  6: Watchdog reset cause bit 2
+
+Optional properties:
+- None
+
+Example:
+	watchdog {
+		compatible ="men,a021-wdt";
+		gpios = <&gpio3 9  1	/* WD_EN */
+			 &gpio3 10 1	/* WD_FAST */
+			 &gpio3 11 1	/* WD_TRIG */
+			 &gpio3 6  1	/* RST_CAUSE[0] */
+			 &gpio3 7  1	/* RST_CAUSE[1] */
+			 &gpio3 8  1>;	/* RST_CAUSE[2] */
+	};
MAINTAINERS +6 -0
index 5be702c..824261e 100644
@@ -5317,6 +5317,12 @@ F:	drivers/mtd/
 F:	include/linux/mtd/
 F:	include/uapi/mtd/
 
+MEN A21 WATCHDOG DRIVER
+M:  	Johannes Thumshirn <[email protected]>
+L:	[email protected]
+S:	Supported
+F:	drivers/watchdog/mena21_wdt.c
+
 METAG ARCHITECTURE
 M:	James Hogan <[email protected]>
 S:	Supported
drivers/watchdog/Kconfig +12 -0
index e89fc31..8b143ee 100644
@@ -1172,6 +1172,18 @@ config BOOKE_WDT_DEFAULT_TIMEOUT
 
 	  The value can be overridden by the wdt_period command-line parameter.
 
+config MEN_A21_WDT
+       tristate "MEN A21 VME CPU Carrier Board Watchdog Timer"
+       select WATCHDOG_CORE
+       depends on GPIOLIB
+       help
+        Watchdog driver for MEN A21 VMEbus CPU Carrier Boards.
+
+	The driver can also be built as a module. If so, the module will be
+	called mena21_wdt.
+
+	If unsure select N here.
+
 # PPC64 Architecture
 
 config WATCHDOG_RTAS
drivers/watchdog/Makefile +1 -0
index a300b94..bffdcb1 100644
@@ -143,6 +143,7 @@ obj-$(CONFIG_8xxx_WDT) += mpc8xxx_wdt.o
 obj-$(CONFIG_MV64X60_WDT) += mv64x60_wdt.o
 obj-$(CONFIG_PIKA_WDT) += pika_wdt.o
 obj-$(CONFIG_BOOKE_WDT) += booke_wdt.o
+obj-$(CONFIG_MEN_A21_WDT) += mena21_wdt.o
 
 # PPC64 Architecture
 obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o
drivers/watchdog/mena21_wdt.c new file +66 -0
new file mode 100644
index 0000000..0d3409a
@@ -0,0 +1,276 @@
+/*
+ * Watchdog driver for the A21 VME CPU Boards
+ *
+ * Copyright (C) 2013 MEN Mikro Elektronik Nuernberg GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+#include <linux/uaccess.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+
+#define NUM_GPIOS 6
+
+enum a21_wdt_gpios {
+	GPIO_WD_ENAB,
+	GPIO_WD_FAST,
+	GPIO_WD_TRIG,
+	GPIO_WD_RST0,
+	GPIO_WD_RST1,
+	GPIO_WD_RST2,
+};
+
+struct a21_wdt_drv {
+	struct watchdog_device wdt;
+	struct mutex lock;
+	unsigned gpios[NUM_GPIOS];
+};
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+			    __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static unsigned int a21_wdt_get_bootstatus(struct a21_wdt_drv *drv)
+{
+	int reset = 0;
+
+	reset |= gpio_get_value(drv->gpios[GPIO_WD_RST0]) ? (1 << 0) : 0;
+	reset |= gpio_get_value(drv->gpios[GPIO_WD_RST1]) ? (1 << 1) : 0;
+	reset |= gpio_get_value(drv->gpios[GPIO_WD_RST2]) ? (1 << 2) : 0;
+
+	return reset;
+}
+
+static int a21_wdt_start(struct watchdog_device *wdt)
+{
+	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+	mutex_lock(&drv->lock);
+
+	gpio_set_value(drv->gpios[GPIO_WD_ENAB], 1);
+
+	mutex_unlock(&drv->lock);
+
+	return 0;

drivers/watchdog/mena21_wdt.c line 76

Quoted 10 lines
drivers/watchdog/mena21_wdt.c new file +10 -0
+}
+
+static int a21_wdt_stop(struct watchdog_device *wdt)
+{
+	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+	/* We don't stop if WDOG_NO_WAY_OUT is set */
+	if (test_bit(WDOG_NO_WAY_OUT, &wdt->status))
+		return -EINVAL;
+

Already handled by watchdog core; no need to check again here.

Quoted 7 lines
drivers/watchdog/mena21_wdt.c new file +7 -0
+	mutex_lock(&drv->lock);
+
+	gpio_set_value(drv->gpios[GPIO_WD_ENAB], 0);
+
+	mutex_unlock(&drv->lock);
+
+	return 0;

drivers/watchdog/mena21_wdt.c line 93

Quoted 10 lines
drivers/watchdog/mena21_wdt.c new file +10 -0
+}
+
+static int a21_wdt_ping(struct watchdog_device *wdt)
+{
+	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+	mutex_lock(&drv->lock);
+
+	gpio_set_value(drv->gpios[GPIO_WD_TRIG], 0);
+	ndelay(10);

Is the delay specified somewehere or just to be sure ?

Quoted 58 lines
drivers/watchdog/mena21_wdt.c new file +58 -0
+	gpio_set_value(drv->gpios[GPIO_WD_TRIG], 1);
+
+	mutex_unlock(&drv->lock);
+
+	return 0;
+}
+
+static int a21_wdt_set_timeout(struct watchdog_device *wdt,
+			       unsigned int timeout)
+{
+	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+	if (timeout != 1 && timeout != 30) {
+		dev_err(wdt->dev, "Only 1 and 30 allowed as timeout\n");
+		return -EINVAL;
+	}
+
+	if (timeout == 30 && wdt->timeout == 1) {
+		dev_err(wdt->dev,
+			"Transition from fast to slow mode not allowed\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&drv->lock);
+
+	if (timeout == 1)
+		gpio_set_value(drv->gpios[GPIO_WD_FAST], 1);
+	else
+		gpio_set_value(drv->gpios[GPIO_WD_FAST], 0);
+
+	wdt->timeout = timeout;
+
+	mutex_unlock(&drv->lock);
+
+	return 0;
+}
+
+static const struct watchdog_info a21_wdt_info = {
+	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+	.identity = "MEN A21 Watchdog",
+};
+
+static const struct watchdog_ops a21_wdt_ops = {
+	.owner = THIS_MODULE,
+	.start = a21_wdt_start,
+	.stop = a21_wdt_stop,
+	.ping = a21_wdt_ping,
+	.set_timeout = a21_wdt_set_timeout,
+};
+
+static struct watchdog_device a21_wdt = {
+	.info = &a21_wdt_info,
+	.ops = &a21_wdt_ops,
+	.min_timeout = 1,
+	.max_timeout = 30,
+};
+
+static int a21_wdt_probe(struct platform_device *pdev)

drivers/watchdog/mena21_wdt.c line 161

Quoted 10 lines
drivers/watchdog/mena21_wdt.c new file +10 -0
+{
+	struct device_node *node;
+	struct a21_wdt_drv *drv;
+	unsigned int reset = 0;
+	int num_gpios;
+	int ret;
+	int i;
+
+	dev_info(&pdev->dev, "MEN A21 watchdog timer driver enabled\n");
+

Move to the very end of this function; otherwise it is misleading if lading the driver fails.

Quoted 46 lines
drivers/watchdog/mena21_wdt.c new file +46 -0
+	drv = devm_kzalloc(&pdev->dev, sizeof(struct a21_wdt_drv), GFP_KERNEL);
+	if (!drv)
+		return -ENOMEM;
+
+	/* Fill GPIO pin array */
+	node = pdev->dev.of_node;
+
+	num_gpios = of_gpio_count(node);
+	if (num_gpios != NUM_GPIOS) {
+		dev_err(&pdev->dev, "gpios DT property wrong, got %d want %d",
+			num_gpios, NUM_GPIOS);
+		return -ENODEV;
+	}
+
+	for (i = 0; i < num_gpios; i++) {
+		int val;
+
+		val = of_get_gpio(node, i);
+		if (val < 0)
+			return val;
+
+		drv->gpios[i] = val;
+	}
+
+	/* Request the used GPIOs */
+	for (i = 0; i < num_gpios; i++) {
+		ret = devm_gpio_request(&pdev->dev, drv->gpios[i],
+					"MEN A21 Watchdog");
+		if (ret)
+			return ret;
+
+		if (i < GPIO_WD_RST0)
+			ret = gpio_direction_output(drv->gpios[i],
+						gpio_get_value(drv->gpios[i]));
+		else		/* GPIO_WD_RST[0..2] are inputs */
+			ret = gpio_direction_input(drv->gpios[i]);
+		if (ret)
+			return ret;
+	}
+
+	mutex_init(&drv->lock);
+	watchdog_init_timeout(&a21_wdt, 30, &pdev->dev);
+	watchdog_set_nowayout(&a21_wdt, nowayout);
+	watchdog_set_drvdata(&a21_wdt, drv);
+
+	reset = a21_wdt_get_bootstatus(drv);

drivers/watchdog/mena21_wdt.c line 217

Quoted 10 lines
drivers/watchdog/mena21_wdt.c new file +10 -0
+	if (reset == 2)
+		a21_wdt.bootstatus |= WDIOF_EXTERN1;
+	else if (reset == 4)
+		a21_wdt.bootstatus |= WDIOF_CARDRESET;
+	else if (reset == 5)
+		a21_wdt.bootstatus |= WDIOF_POWERUNDER;
+	else if (reset == 7)
+		a21_wdt.bootstatus |= WDIOF_EXTERN2;
+
+

One empty line is sufficient.

Quoted 14 lines
drivers/watchdog/mena21_wdt.c new file +14 -0
+	ret = watchdog_register_device(&a21_wdt);
+	if (ret) {
+		dev_err(&pdev->dev, "Cannot register watchdog device\n");
+		goto err_register_wd;
+	}
+
+	dev_set_drvdata(&pdev->dev, drv);
+
+	return 0;
+
+err_register_wd:
+	mutex_destroy(&drv->lock);
+
+	return ret;

drivers/watchdog/mena21_wdt.c line 241

Quoted 10 lines
drivers/watchdog/mena21_wdt.c new file +10 -0
+}
+
+static int a21_wdt_remove(struct platform_device *pdev)
+{
+	struct a21_wdt_drv *drv = dev_get_drvdata(&pdev->dev);
+
+	dev_warn(&pdev->dev,
+		"Unregistering A21 watchdog driver, board may reboot\n");
+
+

One empty line

Quoted 43 lines
drivers/watchdog/mena21_wdt.c new file +35 -1
+	watchdog_unregister_device(&drv->wdt);
+
+	mutex_destroy(&drv->lock);
+
+	return 0;
+}
+
+static void a21_wdt_shutdown(struct platform_device *pdev)
+{
+	struct a21_wdt_drv *drv = dev_get_drvdata(&pdev->dev);
+
+	gpio_set_value(drv->gpios[GPIO_WD_ENAB], 0);
+}
+
+static const struct of_device_id a21_wdt_ids[] = {
+	{ .compatible = "men,a021-wdt" },
+	{ },
+};
+
+static struct platform_driver a21_wdt_driver = {
+	.probe = a21_wdt_probe,
+	.remove = a21_wdt_remove,
+	.shutdown = a21_wdt_shutdown,
+	.driver = {
+		.name = "a21-watchdog",
+		.of_match_table = a21_wdt_ids,
+	},
+};
+
+module_platform_driver(a21_wdt_driver);
+
+MODULE_AUTHOR("MEN Mikro Elektronik");
+MODULE_DESCRIPTION("MEN A21 Watchdog");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:a21-watchdog");
--

1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#26 lore raw
Quoted 9 lines · Guenter Roeck
Quoted 5 lines · Johannes Thumshirn

This patch adds the driver for the watchdog devices found on MEN Mikro Elektronik A21 VMEbus CPU Carrier Boards. It has DT-support and uses the watchdog framework.

Only nitpicks this time.

*g*

Quoted 207 lines
Quoted 207 lines

---

Revision 2:
* Removed unneeded open flag in struct a21_wdt_drv
* Corrected 3bit reason code from gpio
* Additional sysfs files are now part of watchdog sysfs
* Changed OFF/ON delay in ping from 400ms to 10ns
* Reworked timeout setting
* Removed a21_wdt_ioctl(...)

Revision 3:
* Changed pr_{err,info} to dev_{err,info}
* Removed out of memory error print
* Transition from "fast" to "slow" mode not allowed by chip

Revision 4:
* Remove reboot_notifier and place disable code into platform_device's shutdown function
* Removed sysfs interface

Revision 5:
* Added setting of .bootstatus on driver init
* Added initial timeout on driver init

Revision 6:
* Use watchdog_init_timeout() to initialize timeout

Revision 7:
* Fix possible get_bootstatus race condition

Revision 8:
* a21_wdt_get_bootstatus() should return reset code
* GPIOs are supplied via DT instead of being hardcoded. Code derived from (drivers/hwmon/gpio-fan.c)
* Added Devicetree binding document
* Driver now depends on GPIOLIB

Revision 9:
* We need 6 GPIOs not only 1 to work
* GPIO_WD_RST[0..2] are inputs

 .../devicetree/bindings/gpio/men-a021-wdt.txt      |   25 ++
 MAINTAINERS                                        |    6 +
 drivers/watchdog/Kconfig                           |   12 +
 drivers/watchdog/Makefile                          |    1 +
 drivers/watchdog/mena21_wdt.c                      |  276 ++++++++++++++++++++
 5 files changed, 320 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/men-a021-wdt.txt
 create mode 100644 drivers/watchdog/mena21_wdt.c
Documentation/devicetree/bindings/gpio/men-a021-wdt.txt new file +25 -0
new file mode 100644
index 0000000..370dee3
@@ -0,0 +1,25 @@
+Bindings for MEN A21 Watchdog device connected to GPIO lines
+
+Required properties:
+- compatible: "men,a021-wdt"
+- gpios: Specifies the pins that control the Watchdog, order:
+  1: Watchdog enable
+  2: Watchdog fast-mode
+  3: Watchdog trigger
+  4: Watchdog reset cause bit 0
+  5: Watchdog reset cause bit 1
+  6: Watchdog reset cause bit 2
+
+Optional properties:
+- None
+
+Example:
+	watchdog {
+		compatible ="men,a021-wdt";
+		gpios = <&gpio3 9  1	/* WD_EN */
+			 &gpio3 10 1	/* WD_FAST */
+			 &gpio3 11 1	/* WD_TRIG */
+			 &gpio3 6  1	/* RST_CAUSE[0] */
+			 &gpio3 7  1	/* RST_CAUSE[1] */
+			 &gpio3 8  1>;	/* RST_CAUSE[2] */
+	};
MAINTAINERS +6 -0
index 5be702c..824261e 100644
@@ -5317,6 +5317,12 @@ F:	drivers/mtd/
 F:	include/linux/mtd/
 F:	include/uapi/mtd/
 
+MEN A21 WATCHDOG DRIVER
+M:  	Johannes Thumshirn <[email protected]>
+L:	[email protected]
+S:	Supported
+F:	drivers/watchdog/mena21_wdt.c
+
 METAG ARCHITECTURE
 M:	James Hogan <[email protected]>
 S:	Supported
drivers/watchdog/Kconfig +12 -0
index e89fc31..8b143ee 100644
@@ -1172,6 +1172,18 @@ config BOOKE_WDT_DEFAULT_TIMEOUT
 
 	  The value can be overridden by the wdt_period command-line parameter.
 
+config MEN_A21_WDT
+       tristate "MEN A21 VME CPU Carrier Board Watchdog Timer"
+       select WATCHDOG_CORE
+       depends on GPIOLIB
+       help
+        Watchdog driver for MEN A21 VMEbus CPU Carrier Boards.
+
+	The driver can also be built as a module. If so, the module will be
+	called mena21_wdt.
+
+	If unsure select N here.
+
 # PPC64 Architecture
 
 config WATCHDOG_RTAS
drivers/watchdog/Makefile +1 -0
index a300b94..bffdcb1 100644
@@ -143,6 +143,7 @@ obj-$(CONFIG_8xxx_WDT) += mpc8xxx_wdt.o
 obj-$(CONFIG_MV64X60_WDT) += mv64x60_wdt.o
 obj-$(CONFIG_PIKA_WDT) += pika_wdt.o
 obj-$(CONFIG_BOOKE_WDT) += booke_wdt.o
+obj-$(CONFIG_MEN_A21_WDT) += mena21_wdt.o
 
 # PPC64 Architecture
 obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o
drivers/watchdog/mena21_wdt.c new file +69 -0
new file mode 100644
index 0000000..0d3409a
@@ -0,0 +1,276 @@
+/*
+ * Watchdog driver for the A21 VME CPU Boards
+ *
+ * Copyright (C) 2013 MEN Mikro Elektronik Nuernberg GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+#include <linux/uaccess.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+
+#define NUM_GPIOS 6
+
+enum a21_wdt_gpios {
+	GPIO_WD_ENAB,
+	GPIO_WD_FAST,
+	GPIO_WD_TRIG,
+	GPIO_WD_RST0,
+	GPIO_WD_RST1,
+	GPIO_WD_RST2,
+};
+
+struct a21_wdt_drv {
+	struct watchdog_device wdt;
+	struct mutex lock;
+	unsigned gpios[NUM_GPIOS];
+};
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+			    __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static unsigned int a21_wdt_get_bootstatus(struct a21_wdt_drv *drv)
+{
+	int reset = 0;
+
+	reset |= gpio_get_value(drv->gpios[GPIO_WD_RST0]) ? (1 << 0) : 0;
+	reset |= gpio_get_value(drv->gpios[GPIO_WD_RST1]) ? (1 << 1) : 0;
+	reset |= gpio_get_value(drv->gpios[GPIO_WD_RST2]) ? (1 << 2) : 0;
+
+	return reset;
+}
+
+static int a21_wdt_start(struct watchdog_device *wdt)
+{
+	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+	mutex_lock(&drv->lock);
+
+	gpio_set_value(drv->gpios[GPIO_WD_ENAB], 1);
+
+	mutex_unlock(&drv->lock);
+
+	return 0;
+}
+
+static int a21_wdt_stop(struct watchdog_device *wdt)
Quoted 9 lines

drivers/watchdog/mena21_wdt.c line 76

Quoted 7 lines
drivers/watchdog/mena21_wdt.c new file +7 -0
+{
+	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+	/* We don't stop if WDOG_NO_WAY_OUT is set */
+	if (test_bit(WDOG_NO_WAY_OUT, &wdt->status))
+		return -EINVAL;
+

Already handled by watchdog core; no need to check again here.

ACK

Quoted 11 lines
Quoted 11 lines
drivers/watchdog/mena21_wdt.c new file +11 -0
+	mutex_lock(&drv->lock);
+
+	gpio_set_value(drv->gpios[GPIO_WD_ENAB], 0);
+
+	mutex_unlock(&drv->lock);
+
+	return 0;
+}
+
+static int a21_wdt_ping(struct watchdog_device *wdt)
+{
Quoted 9 lines

drivers/watchdog/mena21_wdt.c line 93

Quoted 6 lines
drivers/watchdog/mena21_wdt.c new file +6 -0
+	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+	mutex_lock(&drv->lock);
+
+	gpio_set_value(drv->gpios[GPIO_WD_TRIG], 0);
+	ndelay(10);

Is the delay specified somewehere or just to be sure ?

I tried to meter it out. 10ns seemed to be a good value where the reset is still reliable. Making it smaller can make it a bit unreliable. Also it is roughly 10*CPLD clock, which seems like a good value to me.

Quoted 62 lines
Quoted 62 lines
drivers/watchdog/mena21_wdt.c new file +62 -0
+	gpio_set_value(drv->gpios[GPIO_WD_TRIG], 1);
+
+	mutex_unlock(&drv->lock);
+
+	return 0;
+}
+
+static int a21_wdt_set_timeout(struct watchdog_device *wdt,
+			       unsigned int timeout)
+{
+	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+	if (timeout != 1 && timeout != 30) {
+		dev_err(wdt->dev, "Only 1 and 30 allowed as timeout\n");
+		return -EINVAL;
+	}
+
+	if (timeout == 30 && wdt->timeout == 1) {
+		dev_err(wdt->dev,
+			"Transition from fast to slow mode not allowed\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&drv->lock);
+
+	if (timeout == 1)
+		gpio_set_value(drv->gpios[GPIO_WD_FAST], 1);
+	else
+		gpio_set_value(drv->gpios[GPIO_WD_FAST], 0);
+
+	wdt->timeout = timeout;
+
+	mutex_unlock(&drv->lock);
+
+	return 0;
+}
+
+static const struct watchdog_info a21_wdt_info = {
+	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+	.identity = "MEN A21 Watchdog",
+};
+
+static const struct watchdog_ops a21_wdt_ops = {
+	.owner = THIS_MODULE,
+	.start = a21_wdt_start,
+	.stop = a21_wdt_stop,
+	.ping = a21_wdt_ping,
+	.set_timeout = a21_wdt_set_timeout,
+};
+
+static struct watchdog_device a21_wdt = {
+	.info = &a21_wdt_info,
+	.ops = &a21_wdt_ops,
+	.min_timeout = 1,
+	.max_timeout = 30,
+};
+
+static int a21_wdt_probe(struct platform_device *pdev)
+{
+	struct device_node *node;
+	struct a21_wdt_drv *drv;
+	unsigned int reset = 0;
Quoted 9 lines

drivers/watchdog/mena21_wdt.c line 161

Quoted 6 lines
drivers/watchdog/mena21_wdt.c new file +6 -0
+	int num_gpios;
+	int ret;
+	int i;
+
+	dev_info(&pdev->dev, "MEN A21 watchdog timer driver enabled\n");
+

Move to the very end of this function; otherwise it is misleading if lading the driver fails.

OK.

Quoted 49 lines
Quoted 49 lines
drivers/watchdog/mena21_wdt.c new file +49 -0
+	drv = devm_kzalloc(&pdev->dev, sizeof(struct a21_wdt_drv), GFP_KERNEL);
+	if (!drv)
+		return -ENOMEM;
+
+	/* Fill GPIO pin array */
+	node = pdev->dev.of_node;
+
+	num_gpios = of_gpio_count(node);
+	if (num_gpios != NUM_GPIOS) {
+		dev_err(&pdev->dev, "gpios DT property wrong, got %d want %d",
+			num_gpios, NUM_GPIOS);
+		return -ENODEV;
+	}
+
+	for (i = 0; i < num_gpios; i++) {
+		int val;
+
+		val = of_get_gpio(node, i);
+		if (val < 0)
+			return val;
+
+		drv->gpios[i] = val;
+	}
+
+	/* Request the used GPIOs */
+	for (i = 0; i < num_gpios; i++) {
+		ret = devm_gpio_request(&pdev->dev, drv->gpios[i],
+					"MEN A21 Watchdog");
+		if (ret)
+			return ret;
+
+		if (i < GPIO_WD_RST0)
+			ret = gpio_direction_output(drv->gpios[i],
+						gpio_get_value(drv->gpios[i]));
+		else		/* GPIO_WD_RST[0..2] are inputs */
+			ret = gpio_direction_input(drv->gpios[i]);
+		if (ret)
+			return ret;
+	}
+
+	mutex_init(&drv->lock);
+	watchdog_init_timeout(&a21_wdt, 30, &pdev->dev);
+	watchdog_set_nowayout(&a21_wdt, nowayout);
+	watchdog_set_drvdata(&a21_wdt, drv);
+
+	reset = a21_wdt_get_bootstatus(drv);
+	if (reset == 2)
+		a21_wdt.bootstatus |= WDIOF_EXTERN1;
+	else if (reset == 4)
Quoted 9 lines

drivers/watchdog/mena21_wdt.c line 217

Quoted 7 lines
drivers/watchdog/mena21_wdt.c new file +7 -0
+		a21_wdt.bootstatus |= WDIOF_CARDRESET;
+	else if (reset == 5)
+		a21_wdt.bootstatus |= WDIOF_POWERUNDER;
+	else if (reset == 7)
+		a21_wdt.bootstatus |= WDIOF_EXTERN2;
+
+

One empty line is sufficient.

Args. Yep.

Quoted 17 lines
Quoted 17 lines
drivers/watchdog/mena21_wdt.c new file +17 -0
+	ret = watchdog_register_device(&a21_wdt);
+	if (ret) {
+		dev_err(&pdev->dev, "Cannot register watchdog device\n");
+		goto err_register_wd;
+	}
+
+	dev_set_drvdata(&pdev->dev, drv);
+
+	return 0;
+
+err_register_wd:
+	mutex_destroy(&drv->lock);
+
+	return ret;
+}
+
+static int a21_wdt_remove(struct platform_device *pdev)
Quoted 9 lines

drivers/watchdog/mena21_wdt.c line 241

Quoted 7 lines
drivers/watchdog/mena21_wdt.c new file +7 -0
+{
+	struct a21_wdt_drv *drv = dev_get_drvdata(&pdev->dev);
+
+	dev_warn(&pdev->dev,
+		"Unregistering A21 watchdog driver, board may reboot\n");
+
+

One empty line

^^

Quoted 43 lines
Quoted 43 lines
drivers/watchdog/mena21_wdt.c new file +35 -1
+	watchdog_unregister_device(&drv->wdt);
+
+	mutex_destroy(&drv->lock);
+
+	return 0;
+}
+
+static void a21_wdt_shutdown(struct platform_device *pdev)
+{
+	struct a21_wdt_drv *drv = dev_get_drvdata(&pdev->dev);
+
+	gpio_set_value(drv->gpios[GPIO_WD_ENAB], 0);
+}
+
+static const struct of_device_id a21_wdt_ids[] = {
+	{ .compatible = "men,a021-wdt" },
+	{ },
+};
+
+static struct platform_driver a21_wdt_driver = {
+	.probe = a21_wdt_probe,
+	.remove = a21_wdt_remove,
+	.shutdown = a21_wdt_shutdown,
+	.driver = {
+		.name = "a21-watchdog",
+		.of_match_table = a21_wdt_ids,
+	},
+};
+
+module_platform_driver(a21_wdt_driver);
+
+MODULE_AUTHOR("MEN Mikro Elektronik");
+MODULE_DESCRIPTION("MEN A21 Watchdog");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:a21-watchdog");
--

1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#27 lore raw

PATCH v10 1/2 watchdog: New watchdog driver for MEN A21 watchdogs

In reply to Guenter Roeck

This patch adds the driver for the watchdog devices found on MEN Mikro Elektronik A21 VMEbus CPU Carrier Boards. It has DT-support and uses the watchdog framework.

---

Revision 2:
* Removed unneeded open flag in struct a21_wdt_drv
* Corrected 3bit reason code from gpio
* Additional sysfs files are now part of watchdog sysfs
* Changed OFF/ON delay in ping from 400ms to 10ns
* Reworked timeout setting
* Removed a21_wdt_ioctl(...)

Revision 3:
* Changed pr_{err,info} to dev_{err,info}
* Removed out of memory error print
* Transition from "fast" to "slow" mode not allowed by chip

Revision 4:
* Remove reboot_notifier and place disable code into platform_device's shutdown function
* Removed sysfs interface

Revision 5:
* Added setting of .bootstatus on driver init
* Added initial timeout on driver init

Revision 6:
* Use watchdog_init_timeout() to initialize timeout

Revision 7:
* Fix possible get_bootstatus race condition

Revision 8:
* a21_wdt_get_bootstatus() should return reset code
* GPIOs are supplied via DT instead of being hardcoded. Code derived from (drivers/hwmon/gpio-fan.c)
* Added Devicetree binding document
* Driver now depends on GPIOLIB

Revision 9:
* We need 6 GPIOs not only 1 to work
* GPIO_WD_RST[0..2] are inputs

Revision 10:
* Don't perform checks of Watchdog core again.
* Some style fixes

 .../devicetree/bindings/gpio/men-a021-wdt.txt      |   25 ++
 MAINTAINERS                                        |    6 +
 drivers/watchdog/Kconfig                           |   12 +
 drivers/watchdog/Makefile                          |    1 +
 drivers/watchdog/mena21_wdt.c                      |  270 ++++++++++++++++++++
 5 files changed, 314 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/men-a021-wdt.txt
 create mode 100644 drivers/watchdog/mena21_wdt.c
Documentation/devicetree/bindings/gpio/men-a021-wdt.txt new file +25 -0
new file mode 100644
index 0000000..370dee3
@@ -0,0 +1,25 @@
+Bindings for MEN A21 Watchdog device connected to GPIO lines
+
+Required properties:
+- compatible: "men,a021-wdt"
+- gpios: Specifies the pins that control the Watchdog, order:
+  1: Watchdog enable
+  2: Watchdog fast-mode
+  3: Watchdog trigger
+  4: Watchdog reset cause bit 0
+  5: Watchdog reset cause bit 1
+  6: Watchdog reset cause bit 2
+
+Optional properties:
+- None
+
+Example:
+	watchdog {
+		compatible ="men,a021-wdt";
+		gpios = <&gpio3 9  1	/* WD_EN */
+			 &gpio3 10 1	/* WD_FAST */
+			 &gpio3 11 1	/* WD_TRIG */
+			 &gpio3 6  1	/* RST_CAUSE[0] */
+			 &gpio3 7  1	/* RST_CAUSE[1] */
+			 &gpio3 8  1>;	/* RST_CAUSE[2] */
+	};
MAINTAINERS +6 -0
index 5be702c..824261e 100644
@@ -5317,6 +5317,12 @@ F:	drivers/mtd/
 F:	include/linux/mtd/
 F:	include/uapi/mtd/
 
+MEN A21 WATCHDOG DRIVER
+M:  	Johannes Thumshirn <[email protected]>
+L:	[email protected]
+S:	Supported
+F:	drivers/watchdog/mena21_wdt.c
+
 METAG ARCHITECTURE
 M:	James Hogan <[email protected]>
 S:	Supported
drivers/watchdog/Kconfig +12 -0
index e89fc31..8b143ee 100644
@@ -1172,6 +1172,18 @@ config BOOKE_WDT_DEFAULT_TIMEOUT
 
 	  The value can be overridden by the wdt_period command-line parameter.
 
+config MEN_A21_WDT
+       tristate "MEN A21 VME CPU Carrier Board Watchdog Timer"
+       select WATCHDOG_CORE
+       depends on GPIOLIB
+       help
+        Watchdog driver for MEN A21 VMEbus CPU Carrier Boards.
+
+	The driver can also be built as a module. If so, the module will be
+	called mena21_wdt.
+
+	If unsure select N here.
+
 # PPC64 Architecture
 
 config WATCHDOG_RTAS
drivers/watchdog/Makefile +1 -0
index a300b94..bffdcb1 100644
@@ -143,6 +143,7 @@ obj-$(CONFIG_8xxx_WDT) += mpc8xxx_wdt.o
 obj-$(CONFIG_MV64X60_WDT) += mv64x60_wdt.o
 obj-$(CONFIG_PIKA_WDT) += pika_wdt.o
 obj-$(CONFIG_BOOKE_WDT) += booke_wdt.o
+obj-$(CONFIG_MEN_A21_WDT) += mena21_wdt.o
 
 # PPC64 Architecture
 obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o
drivers/watchdog/mena21_wdt.c new file +270 -0

270 lines not on this page · raw message

--
1.7.9.5

#28 lore raw

PATCH v10 2/2 watchdog: Sysfs interface for MEN A21 watchdog

This patch adds a sysfs interface for the watchdog device found on MEN A21 Boards.

The newly generated files are:
* rebootcause:
Can be one of:
Power on Reset,
CPU Reset Request,
Push Button,
FPGA Reset Request,
Watchdog,
Local Power Bad,
Invalid or
BDI
and shows the reason of the boards last reboot.

* active:
Shows if the watchdog CPLD is actually running

* allow_disable:
Shows if the watchdog is allowed to be disabled (NOWAYOUT disabled)

* fastmode:
Shows if the CPLD is running in fast mode (1s timeout), once it is in fastmode it can't be switched back to slow mode (30s timeout) until the next reboot.

Revision 2:
* Re-worked sysfs patch to apply on re-worked base

Revision 3:
* Re-worked sysfs patch to apply on re-worked base

---
 drivers/watchdog/mena21_wdt.c |   89 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 89 insertions(+)
drivers/watchdog/mena21_wdt.c +89 -0
index 96dbba9..91cc90b 100644
@@ -22,6 +22,17 @@
 
 #define NUM_GPIOS 6
 
+static char *reset_causes[] = {
+	"Power On Reset",
+	"CPU Reset Request",
+	"Push Button",
+	"FPGA Reset Request",
+	"Watchdog",
+	"Local Power Bad",
+	"Invalid",
+	"BDI",
+};
+
 enum a21_wdt_gpios {
 	GPIO_WD_ENAB,
 	GPIO_WD_FAST,
@@ -53,6 +64,73 @@ static unsigned int a21_wdt_get_bootstatus(struct a21_wdt_drv *drv)
 	return reset;
 }
 
+static ssize_t rebootcause_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct a21_wdt_drv *drv = dev_get_drvdata(dev);
+	unsigned int reset = 0;
+
+	reset = a21_wdt_get_bootstatus(drv);
+
+	return sprintf(buf, "%s\n", reset_causes[reset]);
+}
+static DEVICE_ATTR(rebootcause, S_IRUGO, rebootcause_show, NULL);
+
+static ssize_t active_show(struct device *dev, struct device_attribute *attr,
+			   char *buf)
+{
+	struct a21_wdt_drv *drv = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%d\n", !!gpio_get_value(drv->gpios[GPIO_WD_ENAB]));
+}
+static DEVICE_ATTR(active, S_IRUGO, active_show, NULL);
+
+static ssize_t allow_disable_show(struct device *dev,
+				  struct device_attribute *attr,
+				  char *buf)
+{
+	return sprintf(buf, "%d\n", !nowayout);
+}
+static DEVICE_ATTR(allow_disable, S_IRUGO, allow_disable_show, NULL);
+
+static ssize_t fastmode_show(struct device *dev, struct device_attribute *attr,
+			     char *buf)
+{
+	struct a21_wdt_drv *drv = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%d\n", !!gpio_get_value(drv->gpios[GPIO_WD_FAST]));
+}
+static DEVICE_ATTR(fastmode, S_IRUGO, fastmode_show, NULL);
+
+static int a21_wdt_create_files(struct watchdog_device *wdev)
+{
+	int ret;
+
+	ret = device_create_file(wdev->dev, &dev_attr_rebootcause);
+	if (ret)
+		return ret;
+	ret = device_create_file(wdev->dev, &dev_attr_active);
+	if (ret)
+		return ret;
+	ret = device_create_file(wdev->dev, &dev_attr_allow_disable);
+	if (ret)
+		return ret;
+	ret = device_create_file(wdev->dev, &dev_attr_fastmode);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static void a21_wdt_remove_files(struct watchdog_device *wdev)
+{
+	device_remove_file(wdev->dev, &dev_attr_rebootcause);
+	device_remove_file(wdev->dev, &dev_attr_active);
+	device_remove_file(wdev->dev, &dev_attr_allow_disable);
+	device_remove_file(wdev->dev, &dev_attr_fastmode);
+}
+
 static int a21_wdt_start(struct watchdog_device *wdt)
 {
 	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
@@ -215,11 +293,20 @@ static int a21_wdt_probe(struct platform_device *pdev)
 	}
 
 	dev_set_drvdata(&pdev->dev, drv);
+	dev_set_drvdata(a21_wdt.dev, drv);
+
+	ret = a21_wdt_create_files(&a21_wdt);
+	if (ret) {
+		dev_err(&pdev->dev, "Cannot create sysfs entries\n");
+		goto err_create_sysfs;
+	}
 
 	dev_info(&pdev->dev, "MEN A21 watchdog timer driver enabled\n");
 
 	return 0;
 
+err_create_sysfs:
+	watchdog_unregister_device(&drv->wdt);
 err_register_wd:
 	mutex_destroy(&drv->lock);
 
@@ -233,6 +320,8 @@ static int a21_wdt_remove(struct platform_device *pdev)
 	dev_warn(&pdev->dev,
 		"Unregistering A21 watchdog driver, board may reboot\n");
 
+	a21_wdt_remove_files(&drv->wdt);
+
 	watchdog_unregister_device(&drv->wdt);
 
 	mutex_destroy(&drv->lock);

--
1.7.9.5

maintainer Reviewed-by on v10 1/2
#29 lore raw
In reply to Johannes Thumshirn
Quoted 5 lines · Johannes Thumshirn

This patch adds the driver for the watchdog devices found on MEN Mikro Elektronik A21 VMEbus CPU Carrier Boards. It has DT-support and uses the watchdog framework.

Quoted 420 lines

---

Revision 2:
* Removed unneeded open flag in struct a21_wdt_drv
* Corrected 3bit reason code from gpio
* Additional sysfs files are now part of watchdog sysfs
* Changed OFF/ON delay in ping from 400ms to 10ns
* Reworked timeout setting
* Removed a21_wdt_ioctl(...)

Revision 3:
* Changed pr_{err,info} to dev_{err,info}
* Removed out of memory error print
* Transition from "fast" to "slow" mode not allowed by chip

Revision 4:
* Remove reboot_notifier and place disable code into platform_device's shutdown function
* Removed sysfs interface

Revision 5:
* Added setting of .bootstatus on driver init
* Added initial timeout on driver init

Revision 6:
* Use watchdog_init_timeout() to initialize timeout

Revision 7:
* Fix possible get_bootstatus race condition

Revision 8:
* a21_wdt_get_bootstatus() should return reset code
* GPIOs are supplied via DT instead of being hardcoded. Code derived from (drivers/hwmon/gpio-fan.c)
* Added Devicetree binding document
* Driver now depends on GPIOLIB

Revision 9:
* We need 6 GPIOs not only 1 to work
* GPIO_WD_RST[0..2] are inputs

Revision 10:
* Don't perform checks of Watchdog core again.
* Some style fixes

 .../devicetree/bindings/gpio/men-a021-wdt.txt      |   25 ++
 MAINTAINERS                                        |    6 +
 drivers/watchdog/Kconfig                           |   12 +
 drivers/watchdog/Makefile                          |    1 +
 drivers/watchdog/mena21_wdt.c                      |  270 ++++++++++++++++++++
 5 files changed, 314 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/men-a021-wdt.txt
 create mode 100644 drivers/watchdog/mena21_wdt.c
Documentation/devicetree/bindings/gpio/men-a021-wdt.txt new file +25 -0
new file mode 100644
index 0000000..370dee3
@@ -0,0 +1,25 @@
+Bindings for MEN A21 Watchdog device connected to GPIO lines
+
+Required properties:
+- compatible: "men,a021-wdt"
+- gpios: Specifies the pins that control the Watchdog, order:
+  1: Watchdog enable
+  2: Watchdog fast-mode
+  3: Watchdog trigger
+  4: Watchdog reset cause bit 0
+  5: Watchdog reset cause bit 1
+  6: Watchdog reset cause bit 2
+
+Optional properties:
+- None
+
+Example:
+	watchdog {
+		compatible ="men,a021-wdt";
+		gpios = <&gpio3 9  1	/* WD_EN */
+			 &gpio3 10 1	/* WD_FAST */
+			 &gpio3 11 1	/* WD_TRIG */
+			 &gpio3 6  1	/* RST_CAUSE[0] */
+			 &gpio3 7  1	/* RST_CAUSE[1] */
+			 &gpio3 8  1>;	/* RST_CAUSE[2] */
+	};
MAINTAINERS +6 -0
index 5be702c..824261e 100644
@@ -5317,6 +5317,12 @@ F:	drivers/mtd/
 F:	include/linux/mtd/
 F:	include/uapi/mtd/
 
+MEN A21 WATCHDOG DRIVER
+M:  	Johannes Thumshirn <[email protected]>
+L:	[email protected]
+S:	Supported
+F:	drivers/watchdog/mena21_wdt.c
+
 METAG ARCHITECTURE
 M:	James Hogan <[email protected]>
 S:	Supported
drivers/watchdog/Kconfig +12 -0
index e89fc31..8b143ee 100644
@@ -1172,6 +1172,18 @@ config BOOKE_WDT_DEFAULT_TIMEOUT
 
 	  The value can be overridden by the wdt_period command-line parameter.
 
+config MEN_A21_WDT
+       tristate "MEN A21 VME CPU Carrier Board Watchdog Timer"
+       select WATCHDOG_CORE
+       depends on GPIOLIB
+       help
+        Watchdog driver for MEN A21 VMEbus CPU Carrier Boards.
+
+	The driver can also be built as a module. If so, the module will be
+	called mena21_wdt.
+
+	If unsure select N here.
+
 # PPC64 Architecture
 
 config WATCHDOG_RTAS
drivers/watchdog/Makefile +1 -0
index a300b94..bffdcb1 100644
@@ -143,6 +143,7 @@ obj-$(CONFIG_8xxx_WDT) += mpc8xxx_wdt.o
 obj-$(CONFIG_MV64X60_WDT) += mv64x60_wdt.o
 obj-$(CONFIG_PIKA_WDT) += pika_wdt.o
 obj-$(CONFIG_BOOKE_WDT) += booke_wdt.o
+obj-$(CONFIG_MEN_A21_WDT) += mena21_wdt.o
 
 # PPC64 Architecture
 obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o
drivers/watchdog/mena21_wdt.c new file +270 -1
new file mode 100644
index 0000000..96dbba9
@@ -0,0 +1,270 @@
+/*
+ * Watchdog driver for the A21 VME CPU Boards
+ *
+ * Copyright (C) 2013 MEN Mikro Elektronik Nuernberg GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+#include <linux/uaccess.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+
+#define NUM_GPIOS 6
+
+enum a21_wdt_gpios {
+	GPIO_WD_ENAB,
+	GPIO_WD_FAST,
+	GPIO_WD_TRIG,
+	GPIO_WD_RST0,
+	GPIO_WD_RST1,
+	GPIO_WD_RST2,
+};
+
+struct a21_wdt_drv {
+	struct watchdog_device wdt;
+	struct mutex lock;
+	unsigned gpios[NUM_GPIOS];
+};
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+			    __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static unsigned int a21_wdt_get_bootstatus(struct a21_wdt_drv *drv)
+{
+	int reset = 0;
+
+	reset |= gpio_get_value(drv->gpios[GPIO_WD_RST0]) ? (1 << 0) : 0;
+	reset |= gpio_get_value(drv->gpios[GPIO_WD_RST1]) ? (1 << 1) : 0;
+	reset |= gpio_get_value(drv->gpios[GPIO_WD_RST2]) ? (1 << 2) : 0;
+
+	return reset;
+}
+
+static int a21_wdt_start(struct watchdog_device *wdt)
+{
+	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+	mutex_lock(&drv->lock);
+
+	gpio_set_value(drv->gpios[GPIO_WD_ENAB], 1);
+
+	mutex_unlock(&drv->lock);
+
+	return 0;
+}
+
+static int a21_wdt_stop(struct watchdog_device *wdt)
+{
+	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+	mutex_lock(&drv->lock);
+
+	gpio_set_value(drv->gpios[GPIO_WD_ENAB], 0);
+
+	mutex_unlock(&drv->lock);
+
+	return 0;
+}
+
+static int a21_wdt_ping(struct watchdog_device *wdt)
+{
+	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+	mutex_lock(&drv->lock);
+
+	gpio_set_value(drv->gpios[GPIO_WD_TRIG], 0);
+	ndelay(10);
+	gpio_set_value(drv->gpios[GPIO_WD_TRIG], 1);
+
+	mutex_unlock(&drv->lock);
+
+	return 0;
+}
+
+static int a21_wdt_set_timeout(struct watchdog_device *wdt,
+			       unsigned int timeout)
+{
+	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+	if (timeout != 1 && timeout != 30) {
+		dev_err(wdt->dev, "Only 1 and 30 allowed as timeout\n");
+		return -EINVAL;
+	}
+
+	if (timeout == 30 && wdt->timeout == 1) {
+		dev_err(wdt->dev,
+			"Transition from fast to slow mode not allowed\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&drv->lock);
+
+	if (timeout == 1)
+		gpio_set_value(drv->gpios[GPIO_WD_FAST], 1);
+	else
+		gpio_set_value(drv->gpios[GPIO_WD_FAST], 0);
+
+	wdt->timeout = timeout;
+
+	mutex_unlock(&drv->lock);
+
+	return 0;
+}
+
+static const struct watchdog_info a21_wdt_info = {
+	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+	.identity = "MEN A21 Watchdog",
+};
+
+static const struct watchdog_ops a21_wdt_ops = {
+	.owner = THIS_MODULE,
+	.start = a21_wdt_start,
+	.stop = a21_wdt_stop,
+	.ping = a21_wdt_ping,
+	.set_timeout = a21_wdt_set_timeout,
+};
+
+static struct watchdog_device a21_wdt = {
+	.info = &a21_wdt_info,
+	.ops = &a21_wdt_ops,
+	.min_timeout = 1,
+	.max_timeout = 30,
+};
+
+static int a21_wdt_probe(struct platform_device *pdev)
+{
+	struct device_node *node;
+	struct a21_wdt_drv *drv;
+	unsigned int reset = 0;
+	int num_gpios;
+	int ret;
+	int i;
+
+	drv = devm_kzalloc(&pdev->dev, sizeof(struct a21_wdt_drv), GFP_KERNEL);
+	if (!drv)
+		return -ENOMEM;
+
+	/* Fill GPIO pin array */
+	node = pdev->dev.of_node;
+
+	num_gpios = of_gpio_count(node);
+	if (num_gpios != NUM_GPIOS) {
+		dev_err(&pdev->dev, "gpios DT property wrong, got %d want %d",
+			num_gpios, NUM_GPIOS);
+		return -ENODEV;
+	}
+
+	for (i = 0; i < num_gpios; i++) {
+		int val;
+
+		val = of_get_gpio(node, i);
+		if (val < 0)
+			return val;
+
+		drv->gpios[i] = val;
+	}
+
+	/* Request the used GPIOs */
+	for (i = 0; i < num_gpios; i++) {
+		ret = devm_gpio_request(&pdev->dev, drv->gpios[i],
+					"MEN A21 Watchdog");
+		if (ret)
+			return ret;
+
+		if (i < GPIO_WD_RST0)
+			ret = gpio_direction_output(drv->gpios[i],
+						gpio_get_value(drv->gpios[i]));
+		else		/* GPIO_WD_RST[0..2] are inputs */
+			ret = gpio_direction_input(drv->gpios[i]);
+		if (ret)
+			return ret;
+	}
+
+	mutex_init(&drv->lock);
+	watchdog_init_timeout(&a21_wdt, 30, &pdev->dev);
+	watchdog_set_nowayout(&a21_wdt, nowayout);
+	watchdog_set_drvdata(&a21_wdt, drv);
+
+	reset = a21_wdt_get_bootstatus(drv);
+	if (reset == 2)
+		a21_wdt.bootstatus |= WDIOF_EXTERN1;
+	else if (reset == 4)
+		a21_wdt.bootstatus |= WDIOF_CARDRESET;
+	else if (reset == 5)
+		a21_wdt.bootstatus |= WDIOF_POWERUNDER;
+	else if (reset == 7)
+		a21_wdt.bootstatus |= WDIOF_EXTERN2;
+
+	ret = watchdog_register_device(&a21_wdt);
+	if (ret) {
+		dev_err(&pdev->dev, "Cannot register watchdog device\n");
+		goto err_register_wd;
+	}
+
+	dev_set_drvdata(&pdev->dev, drv);
+
+	dev_info(&pdev->dev, "MEN A21 watchdog timer driver enabled\n");
+
+	return 0;
+
+err_register_wd:
+	mutex_destroy(&drv->lock);
+
+	return ret;
+}
+
+static int a21_wdt_remove(struct platform_device *pdev)
+{
+	struct a21_wdt_drv *drv = dev_get_drvdata(&pdev->dev);
+
+	dev_warn(&pdev->dev,
+		"Unregistering A21 watchdog driver, board may reboot\n");
+
+	watchdog_unregister_device(&drv->wdt);
+
+	mutex_destroy(&drv->lock);
+
+	return 0;
+}
+
+static void a21_wdt_shutdown(struct platform_device *pdev)
+{
+	struct a21_wdt_drv *drv = dev_get_drvdata(&pdev->dev);
+
+	gpio_set_value(drv->gpios[GPIO_WD_ENAB], 0);
+}
+
+static const struct of_device_id a21_wdt_ids[] = {
+	{ .compatible = "men,a021-wdt" },
+	{ },
+};
+
+static struct platform_driver a21_wdt_driver = {
+	.probe = a21_wdt_probe,
+	.remove = a21_wdt_remove,
+	.shutdown = a21_wdt_shutdown,
+	.driver = {
+		.name = "a21-watchdog",
+		.of_match_table = a21_wdt_ids,
+	},
+};
+
+module_platform_driver(a21_wdt_driver);
+
+MODULE_AUTHOR("MEN Mikro Elektronik");
+MODULE_DESCRIPTION("MEN A21 Watchdog");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:a21-watchdog");
--

1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#30 lore raw

PATCH RESEND v10 1/2 watchdog: New watchdog driver for MEN A21 watchdogs

This patch adds the driver for the watchdog devices found on MEN Mikro Elektronik A21 VMEbus CPU Carrier Boards. It has DT-support and uses the watchdog framework.

---

Revision 2:
* Removed unneeded open flag in struct a21_wdt_drv
* Corrected 3bit reason code from gpio
* Additional sysfs files are now part of watchdog sysfs
* Changed OFF/ON delay in ping from 400ms to 10ns
* Reworked timeout setting
* Removed a21_wdt_ioctl(...)

Revision 3:
* Changed pr_{err,info} to dev_{err,info}
* Removed out of memory error print
* Transition from "fast" to "slow" mode not allowed by chip

Revision 4:
* Remove reboot_notifier and place disable code into platform_device's shutdown function
* Removed sysfs interface

Revision 5:
* Added setting of .bootstatus on driver init
* Added initial timeout on driver init

Revision 6:
* Use watchdog_init_timeout() to initialize timeout

Revision 7:
* Fix possible get_bootstatus race condition

Revision 8:
* a21_wdt_get_bootstatus() should return reset code
* GPIOs are supplied via DT instead of being hardcoded. Code derived from (drivers/hwmon/gpio-fan.c)
* Added Devicetree binding document
* Driver now depends on GPIOLIB

Revision 9:
* We need 6 GPIOs not only 1 to work
* GPIO_WD_RST[0..2] are inputs

Revision 10:
* Don't perform checks of Watchdog core again.
* Some style fixes

 .../devicetree/bindings/gpio/men-a021-wdt.txt      |   25 ++
 MAINTAINERS                                        |    6 +
 drivers/watchdog/Kconfig                           |   12 +
 drivers/watchdog/Makefile                          |    1 +
 drivers/watchdog/mena21_wdt.c                      |  270 ++++++++++++++++++++
 5 files changed, 314 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/men-a021-wdt.txt
 create mode 100644 drivers/watchdog/mena21_wdt.c
Documentation/devicetree/bindings/gpio/men-a021-wdt.txt new file +25 -0
new file mode 100644
index 0000000..370dee3
@@ -0,0 +1,25 @@
+Bindings for MEN A21 Watchdog device connected to GPIO lines
+
+Required properties:
+- compatible: "men,a021-wdt"
+- gpios: Specifies the pins that control the Watchdog, order:
+  1: Watchdog enable
+  2: Watchdog fast-mode
+  3: Watchdog trigger
+  4: Watchdog reset cause bit 0
+  5: Watchdog reset cause bit 1
+  6: Watchdog reset cause bit 2
+
+Optional properties:
+- None
+
+Example:
+	watchdog {
+		compatible ="men,a021-wdt";
+		gpios = <&gpio3 9  1	/* WD_EN */
+			 &gpio3 10 1	/* WD_FAST */
+			 &gpio3 11 1	/* WD_TRIG */
+			 &gpio3 6  1	/* RST_CAUSE[0] */
+			 &gpio3 7  1	/* RST_CAUSE[1] */
+			 &gpio3 8  1>;	/* RST_CAUSE[2] */
+	};
MAINTAINERS +6 -0
index 5be702c..824261e 100644
@@ -5317,6 +5317,12 @@ F:	drivers/mtd/
 F:	include/linux/mtd/
 F:	include/uapi/mtd/
 
+MEN A21 WATCHDOG DRIVER
+M:  	Johannes Thumshirn <[email protected]>
+L:	[email protected]
+S:	Supported
+F:	drivers/watchdog/mena21_wdt.c
+
 METAG ARCHITECTURE
 M:	James Hogan <[email protected]>
 S:	Supported
drivers/watchdog/Kconfig +12 -0
index e89fc31..8b143ee 100644
@@ -1172,6 +1172,18 @@ config BOOKE_WDT_DEFAULT_TIMEOUT
 
 	  The value can be overridden by the wdt_period command-line parameter.
 
+config MEN_A21_WDT
+       tristate "MEN A21 VME CPU Carrier Board Watchdog Timer"
+       select WATCHDOG_CORE
+       depends on GPIOLIB
+       help
+        Watchdog driver for MEN A21 VMEbus CPU Carrier Boards.
+
+	The driver can also be built as a module. If so, the module will be
+	called mena21_wdt.
+
+	If unsure select N here.
+
 # PPC64 Architecture
 
 config WATCHDOG_RTAS
drivers/watchdog/Makefile +1 -0
index a300b94..bffdcb1 100644
@@ -143,6 +143,7 @@ obj-$(CONFIG_8xxx_WDT) += mpc8xxx_wdt.o
 obj-$(CONFIG_MV64X60_WDT) += mv64x60_wdt.o
 obj-$(CONFIG_PIKA_WDT) += pika_wdt.o
 obj-$(CONFIG_BOOKE_WDT) += booke_wdt.o
+obj-$(CONFIG_MEN_A21_WDT) += mena21_wdt.o
 
 # PPC64 Architecture
 obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o
drivers/watchdog/mena21_wdt.c new file +270 -0

270 lines not on this page · raw message

--
1.7.9.5

#31 lore raw

PATCH RESEND v10 2/2 watchdog: Sysfs interface for MEN A21 watchdog

This patch adds a sysfs interface for the watchdog device found on MEN A21 Boards.

The newly generated files are:
* rebootcause:
Can be one of:
Power on Reset,
CPU Reset Request,
Push Button,
FPGA Reset Request,
Watchdog,
Local Power Bad,
Invalid or
BDI
and shows the reason of the boards last reboot.

* active:
Shows if the watchdog CPLD is actually running

* allow_disable:
Shows if the watchdog is allowed to be disabled (NOWAYOUT disabled)

* fastmode:
Shows if the CPLD is running in fast mode (1s timeout), once it is in fastmode it can't be switched back to slow mode (30s timeout) until the next reboot.

---

Revision 2:
* Re-worked sysfs patch to apply on re-worked base

Revision 3:
* Re-worked sysfs patch to apply on re-worked base

 drivers/watchdog/mena21_wdt.c |   89 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 89 insertions(+)
drivers/watchdog/mena21_wdt.c +89 -0
index 96dbba9..91cc90b 100644
@@ -22,6 +22,17 @@
 
 #define NUM_GPIOS 6
 
+static char *reset_causes[] = {
+	"Power On Reset",
+	"CPU Reset Request",
+	"Push Button",
+	"FPGA Reset Request",
+	"Watchdog",
+	"Local Power Bad",
+	"Invalid",
+	"BDI",
+};
+
 enum a21_wdt_gpios {
 	GPIO_WD_ENAB,
 	GPIO_WD_FAST,
@@ -53,6 +64,73 @@ static unsigned int a21_wdt_get_bootstatus(struct a21_wdt_drv *drv)
 	return reset;
 }
 
+static ssize_t rebootcause_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct a21_wdt_drv *drv = dev_get_drvdata(dev);
+	unsigned int reset = 0;
+
+	reset = a21_wdt_get_bootstatus(drv);
+
+	return sprintf(buf, "%s\n", reset_causes[reset]);
+}
+static DEVICE_ATTR(rebootcause, S_IRUGO, rebootcause_show, NULL);
+
+static ssize_t active_show(struct device *dev, struct device_attribute *attr,
+			   char *buf)
+{
+	struct a21_wdt_drv *drv = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%d\n", !!gpio_get_value(drv->gpios[GPIO_WD_ENAB]));
+}
+static DEVICE_ATTR(active, S_IRUGO, active_show, NULL);
+
+static ssize_t allow_disable_show(struct device *dev,
+				  struct device_attribute *attr,
+				  char *buf)
+{
+	return sprintf(buf, "%d\n", !nowayout);
+}
+static DEVICE_ATTR(allow_disable, S_IRUGO, allow_disable_show, NULL);
+
+static ssize_t fastmode_show(struct device *dev, struct device_attribute *attr,
+			     char *buf)
+{
+	struct a21_wdt_drv *drv = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%d\n", !!gpio_get_value(drv->gpios[GPIO_WD_FAST]));
+}
+static DEVICE_ATTR(fastmode, S_IRUGO, fastmode_show, NULL);
+
+static int a21_wdt_create_files(struct watchdog_device *wdev)
+{
+	int ret;
+
+	ret = device_create_file(wdev->dev, &dev_attr_rebootcause);
+	if (ret)
+		return ret;
+	ret = device_create_file(wdev->dev, &dev_attr_active);
+	if (ret)
+		return ret;
+	ret = device_create_file(wdev->dev, &dev_attr_allow_disable);
+	if (ret)
+		return ret;
+	ret = device_create_file(wdev->dev, &dev_attr_fastmode);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static void a21_wdt_remove_files(struct watchdog_device *wdev)
+{
+	device_remove_file(wdev->dev, &dev_attr_rebootcause);
+	device_remove_file(wdev->dev, &dev_attr_active);
+	device_remove_file(wdev->dev, &dev_attr_allow_disable);
+	device_remove_file(wdev->dev, &dev_attr_fastmode);
+}
+
 static int a21_wdt_start(struct watchdog_device *wdt)
 {
 	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
@@ -215,11 +293,20 @@ static int a21_wdt_probe(struct platform_device *pdev)
 	}
 
 	dev_set_drvdata(&pdev->dev, drv);
+	dev_set_drvdata(a21_wdt.dev, drv);
+
+	ret = a21_wdt_create_files(&a21_wdt);
+	if (ret) {
+		dev_err(&pdev->dev, "Cannot create sysfs entries\n");
+		goto err_create_sysfs;
+	}
 
 	dev_info(&pdev->dev, "MEN A21 watchdog timer driver enabled\n");
 
 	return 0;
 
+err_create_sysfs:
+	watchdog_unregister_device(&drv->wdt);
 err_register_wd:
 	mutex_destroy(&drv->lock);
 
@@ -233,6 +320,8 @@ static int a21_wdt_remove(struct platform_device *pdev)
 	dev_warn(&pdev->dev,
 		"Unregistering A21 watchdog driver, board may reboot\n");
 
+	a21_wdt_remove_files(&drv->wdt);
+
 	watchdog_unregister_device(&drv->wdt);
 
 	mutex_destroy(&drv->lock);

--
1.7.9.5

#32 lore raw

Re: [PATCH RESEND v10 2/2] watchdog: Sysfs interface for MEN A21 watchdog

Hi Wim,

I just wanted to know if you already had time to review the driver? With Guenter's help I got rid of some of the quirks still in and he issued his Reviewed-by. I also imporoved devicetree support and wrote the dts binding document as Arnd Bergmann suggested. I know you're probably quite busy and 3.10 just came out, but it would be cool to know the current status.

Thanks,
Johannes

#33 lore raw

Re: [PATCH RESEND v10 2/2] watchdog: Sysfs interface for MEN A21 watchdog

In reply to Johannes Thumshirn

Hi Johannes,

Quoted 16 lines

This patch adds a sysfs interface for the watchdog device found on MEN A21 Boards.

The newly generated files are:
* rebootcause:
Can be one of:
Power on Reset,
CPU Reset Request,
Push Button,
FPGA Reset Request,
Watchdog,
Local Power Bad,
Invalid or
BDI
and shows the reason of the boards last reboot.

Quoted 5 lines

* active:
Shows if the watchdog CPLD is actually running

* allow_disable:
Shows if the watchdog is allowed to be disabled (NOWAYOUT disabled)

allow_disable should be nowayout because that is the general watchdog parameter we will have for all watchdog drivers.

Quoted 6 lines

* fastmode:
Shows if the CPLD is running in fast mode (1s timeout), once it is in fastmode it can't be switched back to slow mode (30s timeout) until the next reboot.

I will not add this patch and put it in my waiting queue.
Reason: we should first do the sysfs stuff for the wwatchdog_core (since active and nowayout are parameters that will be in the sysfs watchdog core).

Kind regards,
Wim.

#34 lore raw

Re: [PATCH RESEND v10 2/2] watchdog: Sysfs interface for MEN A21 watchdog

Quoted 32 lines · Wim Van Sebroeck

Hi Johannes,

Quoted 16 lines

This patch adds a sysfs interface for the watchdog device found on MEN A21 Boards.

The newly generated files are:
* rebootcause:
Can be one of:
Power on Reset,
CPU Reset Request,
Push Button,
FPGA Reset Request,
Watchdog,
Local Power Bad,
Invalid or
BDI
and shows the reason of the boards last reboot.

Quoted 5 lines

* active:
Shows if the watchdog CPLD is actually running

* allow_disable:
Shows if the watchdog is allowed to be disabled (NOWAYOUT disabled)

allow_disable should be nowayout because that is the general watchdog parameter we will have for all watchdog drivers.

Quoted 5 lines

* fastmode:
Shows if the CPLD is running in fast mode (1s timeout), once it is in fastmode it can't be switched back to slow mode (30s timeout) until the next reboot.

Quoted 9 lines
Quoted 1 line

I will not add this patch and put it in my waiting queue.
Reason: we should first do the sysfs stuff for the wwatchdog_core (since active and nowayout are parameters that will be in the sysfs watchdog core).

Kind regards,
Wim.

Ok. If you want to offload it, I think I could spend some time on it the next weeks, as I'll probably have to write a driver for a Ti TPS3813K33 watchdog anyway, I can have a look into watchdog core as well.

Byte,
Johannes

#35 lore raw
In reply to Johannes Thumshirn

Hi Johannes,

Quoted 6 lines

This patch adds the driver for the watchdog devices found on MEN Mikro Elektronik A21 VMEbus CPU Carrier Boards. It has DT-support and uses the watchdog framework.

I added this patch to linux-watchdog-next.
I am still thinking about wether or not we shouldn't add a timer to this watchdog device driver. But that's something we can change later on.

Kind regards,
Wim.

#36 lore raw
Quoted 6 lines · Wim Van Sebroeck

Hi Johannes,

Quoted 4 lines

This patch adds the driver for the watchdog devices found on MEN Mikro Elektronik A21 VMEbus CPU Carrier Boards. It has DT-support and uses the watchdog framework.

Quoted 6 lines
Quoted 2 lines

I added this patch to linux-watchdog-next.
I am still thinking about wether or not we shouldn't add a timer to this watchdog device driver. But that's something we can change later on.

Probably yes, but that really would be some work for watchdog core imho.

Anyway, tanks for adding the patch.

Thanks,
Johannes

Quoted 4 lines

Kind regards,
Wim.

#37 lore raw
In reply to Johannes Thumshirn
Quoted 4 lines · On Friday 31 May 2013 10:58:48 Johannes Thumshirn wrote:

This patch adds the driver for the watchdog devices found on MEN Mikro Elektronik A21 VMEbus CPU Carrier Boards. It has DT-support and uses the watchdog framework.

It seems the DT support is rather incomplete

Quoted 5 lines
---
 MAINTAINERS                   |    6 ++
 drivers/watchdog/Kconfig      |    8 ++
 drivers/watchdog/Makefile     |    1 +

Please add a binding document to Documentation/devicetree/bindings/

drivers/watchdog/mena21_wdt.c line 26

Quoted 6 lines
drivers/watchdog/mena21_wdt.c new file +6 -0
+
+#define GPIO_WD_ENAB	169
+#define GPIO_WD_FAST	170
+#define GPIO_WD_TRIG	171
+
+#define GPIO_RST_CAUSE_BASE 166

You should never hardwire GPIO numbers like this. There is a DT binding for GPIO, so use that to get the number of the tree.

Arnd