Add support for the watchdog timer on PXI Embedded Controller.
Signed-off-by: Hui Chun Ong
---
v4: Remove nic7018_stop() from nic7018_remove().
Lock WDT registers when watchdog_register_device() call failed.
v3: Update timeout calculation from miliseconds to seconds.
Reorder watchdog_regiser_device() call to prevent potential race condition.
v2: Remove mutex lock and platform_device *pdev fields from struct nic7018_wdt.
Update config NIC7018_WDT description.
Update nic7018_get_config() to never return error.
Remove checking for IO resource size in nic7018_probe().
v1: Remove non-standard attributes.
Change from acpi_driver to platform_driver.
Rename driver from ni7018_wdt to nic7018_wdt.
---
Documentation/watchdog/watchdog-parameters.txt | 5 +
drivers/watchdog/Kconfig | 10 +
drivers/watchdog/Makefile | 1 +
drivers/watchdog/nic7018_wdt.c | 265 +
4 files changed, 281 insertions(+)
create mode 100644 drivers/watchdog/nic7018_wdt.c
diff --git a/Documentation/watchdog/watchdog-parameters.txt
b/Documentation/watchdog/watchdog-parameters.txt
index e21850e..4f7d86d 100644
--- a/Documentation/watchdog/watchdog-parameters.txt
+++ b/Documentation/watchdog/watchdog-parameters.txt
@@ -209,6 +209,11 @@ timeout: Initial watchdog timeout in seconds
(0
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define LOCK 0xA5
+#define UNLOCK 0x5A
+
+#define WDT_CTRL_RESET_EN BIT(7)
+#define WDT_RELOAD_PORT_EN BIT(7)
+
+#define WDT_CTRL 1
+#define WDT_RELOAD_CTRL2
+#define WDT_PRESET_PRESCALE4
+#define WDT_REG_LOCK 5
+#define WDT_COUNT 6
+#define WDT_RELOAD_PORT7
+
+#define WDT_MIN_TIMEOUT1
+#define WDT_MAX_TIMEOUT464
+#define WDT_DEFAULT_TIMEOUT80
+
+#define WDT_MAX_COUNTER15
+
+static unsigned int timeout;
+module_param(timeout, uint, 0);
+MODULE_PARM_DESC(timeout,
+"Watchdog timeout in seconds. (default="
+__MODULE_STRING(WDT_DEFAULT_TIMEOUT) ")");
+
+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) ")");
+
+struct nic7018_wdt {
+ u16 io_base;
+ u32 period;
+ struct watchdog_device wdd;
+};
+
+struct nic7018_config {
+ u32 period;
+ u8 divider;
+};
+
+static const struct nic7018_config nic7018_configs[] = {
+ { 2, 4 },
+ { 32, 5 },
+};
+
+static inline u32 nic7018_timeout(u32 period, u8 counter)
+{
+ return period * counter - period / 2;
+}
+
+static const struct nic7018_config *nic7018_get_config(u32 timeout,
+ u8 *counter)
+{
+ const struct nic7018_config *config;
+ u8 count;
+
+ if (timeout < 30 && timeout != 16) {
+ config = &nic7018_configs[0];
+ count = timeout / 2 + 1;
+ } else {
+ config = &nic7018_configs[1];
+ count = DIV_ROUND_UP(timeout + 16, 32);
+
+ if (count > WDT_MAX_COUNTER)
+ count = WDT_MAX_COUNTER;
+ }
+ *counter = count;
+ return config;
+}
+
+static int nic7018_set_timeout(struct watchdog_device *wdd,
+ unsigned int timeout)
+{
+ struct nic7018_wdt *wdt = watchdog_get_drvdata(wdd);
+ const struct nic7018_config *config;
+ u8 counter;
+
+ config = nic7018_get_config(timeout, &counter);
+
+ outb(counter << 4 | config->divider,
+wdt->io_base + WDT_PRESET_PRESCALE);
+
+ wdd->timeout = nic7018_timeout(config->period, counter);
+ wdt->period = config->period;
+
+ return 0;
+}
+
+static int nic7018_start(struct watchdog_device *wdd)
+{
+ struct nic7018_wdt *wdt = watchdog_get_drvdata(wdd);
+ u8 control;
+
+ nic7018_set_timeout(wdd, wdd->timeout);
+
+ control = inb(wdt->io_base + WDT_RELOAD_CTRL);
+ outb(control | WDT_RELOAD_PORT_EN, wdt->io_base + WDT_RELOAD_CTRL);
+
+ outb(1, wdt->io_base + WDT_RELOAD_PORT);
+
+ control = inb(wdt->io_base + WDT_CTRL);
+ outb(control | WDT_CTRL_RESET_EN, wdt->io_base + WDT_CTRL);
+
+ return 0;
+}
+
+static int nic7018_stop(struct watchdog_device *wdd)
+{
+ struct nic7018_wdt *wdt = watchdog_get_drvdata(wdd);
+
+ outb(0, wdt->io_base + WDT_CTRL);
+ outb(0, wdt->io_base + WDT_RELOAD_CTRL);
+ outb(0xF0, wdt->io_base + WDT_PRESET_PRESCALE);
+
+ return 0;
+}
+
+static int nic7018_ping(struct watchdog_device *wdd)
+{
+ struct nic7018_wdt *wdt = watchdog_get_drvdata(wdd);
+
+ outb(1, wdt->io_base + WDT_RELOAD_PORT);
+
+ return 0;
+}
+
+static unsigned