From: Jordan Crouse <jordan.crouse@amd.com>
To: linux-acpi@vger.kernel.org
Subject: [PATCH] WDRT based watchdog timer
Date: Fri, 26 Sep 2008 17:27:52 -0600 [thread overview]
Message-ID: <20080926232752.GA6061@cosmic.amd.com> (raw)
[-- Attachment #1: Type: text/plain, Size: 585 bytes --]
Attached is a patch to support watchdog timers as described
by the WDRT (Watch Dog Resource Table). This has been tested on
the AMD SB600 chipset, but it should work for any chipset / BIOS
that implements the WDRT table.
I'm not in love with the name - but I couldn't think of anything
better, so I'm open to suggestions. I have a limited number of
systems that implement this interface, so I would be very happy
to hear from other users, especially ones not using AMD chipsets.
Thanks,
Jordan
--
Jordan Crouse
Systems Software Development Engineer
Advanced Micro Devices, Inc.
[-- Attachment #2: acpi-watchdog.patch --]
[-- Type: text/x-diff, Size: 9760 bytes --]
[PATCH]: Add a watchdog driver for the ACPI WDRT table
From: Jordan Crouse <jordan.crouse@amd.com>
A watchdog timer driver for systems with that implement hardware
timers described by the ACPI Watchdog Resource Table (WRDT).
Signed-off-by: Jordan Crouse <jordan.crouse@amd.com>
---
MAINTAINERS | 7 +
drivers/watchdog/Kconfig | 10 +
drivers/watchdog/Makefile | 1
drivers/watchdog/acpiwdt.c | 338 ++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 356 insertions(+), 0 deletions(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index 3596d17..8500456 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -277,6 +277,13 @@ L: linux-acpi@vger.kernel.org
W: http://www.lesswatts.org/projects/acpi/
S: Maintained
+ACPI WATCHDOG DRIVER
+P: Jordan Crouse
+M: jordan.crouse@amd.com
+L: linux-acpi@vger.kernel.org
+W: http://www.lesswatts.org/projects/acpi/
+S: Maintained
+
AD1889 ALSA SOUND DRIVER
P: Kyle McMartin
M: kyle@mcmartin.ca
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index c510367..bf8ce27 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -249,6 +249,16 @@ config BFIN_WDT
# X86 (i386 + ia64 + x86_64) Architecture
+config ACPI_WDT
+ tristate "ACPI WDRT Watchdog Timer"
+ depends on X86 && ACPI
+ ---help---
+ This driver supports hardware watchdog timer implementations
+ described by the Watchdog Resource Table (WDRT) ACPI table.
+
+ To compile this driver as a module, choose M here: the
+ module will be called acpiwdt.
+
config ACQUIRE_WDT
tristate "Acquire SBC Watchdog Timer"
depends on X86
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index e0ef123..d03167c 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_BFIN_WDT) += bfin_wdt.o
# H8300 Architecture
# X86 (i386 + ia64 + x86_64) Architecture
+obj-$(CONFIG_ACPI_WDT) += acpiwdt.o
obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o
obj-$(CONFIG_ADVANTECH_WDT) += advantechwdt.o
obj-$(CONFIG_ALIM1535_WDT) += alim1535_wdt.o
diff --git a/drivers/watchdog/acpiwdt.c b/drivers/watchdog/acpiwdt.c
new file mode 100644
index 0000000..b6bcd52
--- /dev/null
+++ b/drivers/watchdog/acpiwdt.c
@@ -0,0 +1,338 @@
+/* Driver for systems that implement hardware watchdog
+ * timers described by the ACPI Watchdog Resource Table (WDRT)
+ *
+ * Copyright (C) 2008, Advanced Micro Devices, Inc.
+ *
+ * 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; version
+ * 2 of the License
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/acpi.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <acpi/actbl.h>
+
+#define PFX "acpiwdt: "
+
+#define WATCHDOG_TIMEOUT 60
+#define WDT_FLAGS_OPEN 1
+#define WDT_FLAGS_ORPHAN 2
+
+static int timeout = WATCHDOG_TIMEOUT;
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds, default="
+ __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static u32 wdt_flags;
+static int expect_close;
+
+/* ACPI WDRT table */
+
+struct wdrt_table {
+ struct acpi_table_header header;
+ struct acpi_generic_address ctrl_addr;
+ struct acpi_generic_address count_addr;
+ u16 pci_devid;
+ u16 pci_venid;
+ u8 pci_bus;
+ u8 pci_device;
+ u8 pci_function;
+ u8 pci_segment;
+ u16 max_count;
+ u8 units;
+} __attribute__ ((packed));
+
+/* Bit definitions for the control register */
+
+#define WDT_CTRL_TRIGGER (1 << 7)
+#define WDT_CTRL_DISABLE (1 << 3)
+#define WDT_CTRL_ACTION (1 << 2)
+#define WDT_CTRL_FIRED (1 << 1)
+#define WDT_CTRL_RUN (1 << 0)
+
+static u64 *wdt_ctrl_reg; /* Address of the control register */
+static u64 *wdt_count_reg; /* Address of the count register */
+
+static u32 wdt_units; /* Number of milliseconds per tick */
+static u32 wdt_max_count; /* Maximum number of ticks */
+
+static void acpiwdt_ping(void)
+{
+ u32 count = (timeout * 1000) / wdt_units;
+ u32 val;
+
+ /* Write the timeout to the counter */
+ writel(count, wdt_count_reg);
+
+ /* Hit the trigger bit */
+ val = readl(wdt_ctrl_reg);
+ writel(val | WDT_CTRL_TRIGGER, wdt_ctrl_reg);
+}
+
+static void acpiwdt_disable(void)
+{
+ u32 val = readl(wdt_ctrl_reg);
+ writel(val & ~WDT_CTRL_RUN, wdt_ctrl_reg);
+}
+
+static int acpiwdt_set_heartbeat(unsigned int interval)
+{
+ u32 val;
+
+ /* Make sure the interval is sane */
+
+ if (((interval * 1000) / wdt_units) > wdt_max_count)
+ return -EINVAL;
+
+ /* Enable the timer */
+
+ val = readl(wdt_ctrl_reg);
+ writel(val | WDT_CTRL_RUN, wdt_ctrl_reg);
+
+ timeout = interval;
+
+ return 0;
+}
+
+static int acpiwdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(WDT_FLAGS_OPEN, &wdt_flags))
+ return -EBUSY;
+ if (!test_and_clear_bit(WDT_FLAGS_ORPHAN, &wdt_flags))
+ __module_get(THIS_MODULE);
+
+ acpiwdt_ping();
+ return nonseekable_open(inode, file);
+}
+
+static int acpiwdt_release(struct inode *inode, struct file *file)
+{
+ if (expect_close) {
+ acpiwdt_disable();
+ module_put(THIS_MODULE);
+ } else {
+ printk(KERN_CRIT PFX
+ "Unexpected close - watchdog is not stopping\n");
+ acpiwdt_ping();
+ set_bit(WDT_FLAGS_ORPHAN, &wdt_flags);
+ }
+
+ clear_bit(WDT_FLAGS_OPEN, &wdt_flags);
+ expect_close = 0;
+ return 0;
+}
+
+static ssize_t acpiwdt_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+ expect_close = 0;
+
+ for (i = 0; i != len; i++) {
+ char c;
+ if (get_user(c, buf + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 42;
+ }
+ }
+
+ acpiwdt_ping();
+ }
+
+ return len;
+}
+
+static struct watchdog_info ident = {
+ .options = WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE,
+ .identity = "ACPI Watchdog Timer",
+};
+
+static long acpiwdt_ioctl(struct file *file,
+ unsigned cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *) arg;
+ int __user *p = argp;
+ int new_timeout;
+ int options;
+ int ret = 0;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user(argp, &ident, sizeof(ident)))
+ ret = -EFAULT;
+ break;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ ret = put_user(0, p);
+ break;
+
+ case WDIOC_SETOPTIONS:
+ ret = -EINVAL;
+
+ if (get_user(options, p)) {
+ ret = -EFAULT;
+ break;
+ }
+ if (options & WDIOS_DISABLECARD) {
+ acpiwdt_disable();
+ ret = 0;
+ }
+
+ if (options & WDIOS_ENABLECARD) {
+ acpiwdt_ping();
+ ret = 0;
+ }
+ break;
+
+ case WDIOC_KEEPALIVE:
+ acpiwdt_ping();
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ ret = get_user(new_timeout, p);
+ if (ret)
+ break;
+ ret = acpiwdt_set_heartbeat(new_timeout);
+ if (ret)
+ break;
+
+ acpiwdt_ping();
+ /* fall through */
+ case WDIOC_GETTIMEOUT:
+ ret = put_user(timeout, p);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct file_operations acpiwdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = acpiwdt_write,
+ .unlocked_ioctl = acpiwdt_ioctl,
+ .open = acpiwdt_open,
+ .release = acpiwdt_release,
+};
+
+static struct miscdevice acpiwdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &acpiwdt_fops,
+};
+
+static int __init acpiwdt_init(void)
+{
+ struct wdrt_table *wdrt;
+ acpi_status status;
+ u32 val;
+ int ret = -ENODEV;
+
+ status = acpi_get_table(ACPI_SIG_WDRT, 0,
+ (struct acpi_table_header **) &wdrt);
+
+ /* Silently return if there is no WDRT table */
+
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ wdt_ctrl_reg = ioremap(wdrt->ctrl_addr.address, sizeof(u64));
+ wdt_count_reg = ioremap(wdrt->count_addr.address, sizeof(u64));
+
+ if (wdt_ctrl_reg == NULL || wdt_count_reg == NULL)
+ goto err;
+
+ val = readl(wdt_ctrl_reg);
+
+ if (val & WDT_CTRL_DISABLE) {
+ /* Try to enable the watchdog timer if we can - some
+ * implementations may not allow this
+ */
+
+ writel(val & ~WDT_CTRL_DISABLE, wdt_ctrl_reg);
+
+ val = readl(wdt_ctrl_reg);
+
+ if (val & WDT_CTRL_DISABLE) {
+ printk(KERN_ERR PFX
+ "Timer is disabled by the firmware\n");
+ goto err;
+ }
+ }
+
+ wdt_max_count = wdrt->max_count;
+
+ switch (wdrt->units) {
+ case 0: /* 1 sec/count */
+ wdt_units = 1000;
+ break;
+ case 1: /* 100 ms/count */
+ wdt_units = 100;
+ break;
+ case 2: /* 10 ms/count */
+ wdt_units = 10;
+ break;
+ }
+
+ if (acpiwdt_set_heartbeat(timeout)) {
+ acpiwdt_set_heartbeat(WATCHDOG_TIMEOUT);
+ printk(KERN_INFO PFX
+ "The timeout must be > 0 and < %d seconds. Using %d\n",
+ (wdt_units * 0xFFFF) / 1000, WATCHDOG_TIMEOUT);
+ }
+
+ ret = misc_register(&acpiwdt_miscdev);
+
+ if (ret) {
+ printk(KERN_ERR PFX "Could not register misc device\n");
+ goto err;
+ }
+
+ printk(KERN_INFO PFX "ACPI WRDT Watchdog Timer initialized\n");
+ return 0;
+
+err:
+ if (wdt_ctrl_reg != NULL)
+ iounmap(wdt_ctrl_reg);
+
+ if (wdt_count_reg != NULL)
+ iounmap(wdt_count_reg);
+
+ return ret;
+}
+
+static void __exit acpiwdt_exit(void)
+{
+ iounmap(wdt_ctrl_reg);
+ iounmap(wdt_count_reg);
+
+ misc_deregister(&acpiwdt_miscdev);
+}
+
+module_init(acpiwdt_init);
+module_exit(acpiwdt_exit);
+
+MODULE_AUTHOR("Advanced Micro Devices, Inc");
+MODULE_DESCRIPTION("Driver for watchdog timers described by the ACPI WDRT");
+MODULE_LICENSE("GPL");
next reply other threads:[~2008-09-26 23:25 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-09-26 23:27 Jordan Crouse [this message]
2008-10-07 7:44 ` [PATCH] WDRT based watchdog timer Andi Kleen
2008-10-07 15:39 ` Jordan Crouse
2008-10-08 20:25 ` [PATCH] " Len Brown
2008-10-08 20:36 ` Wim Van Sebroeck
2008-10-08 20:44 ` Jordan Crouse
2008-10-14 20:40 ` Wim Van Sebroeck
2008-10-15 13:33 ` Wim Van Sebroeck
2008-10-16 22:23 ` Len Brown
2008-10-16 22:40 ` Jordan Crouse
2009-01-12 20:27 ` Wim Van Sebroeck
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20080926232752.GA6061@cosmic.amd.com \
--to=jordan.crouse@amd.com \
--cc=linux-acpi@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).