lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20260107145058.213334-3-cl634@andestech.com>
Date: Wed, 7 Jan 2026 22:50:56 +0800
From: CL Wang <cl634@...estech.com>
To: <cl634@...estech.com>, <wim@...ux-watchdog.org>, <linux@...ck-us.net>,
        <robh@...nel.org>, <krzk+dt@...nel.org>, <conor+dt@...nel.org>
CC: <devicetree@...r.kernel.org>, <linux-kernel@...r.kernel.org>,
        <linux-watchdog@...r.kernel.org>, <tim609@...estech.com>
Subject: [PATCH 2/3] watchdog: atcwdt200: Add driver for Andes ATCWDT200

Add support for the Andes ATCWDT200 watchdog timer. The driver implements
programmable reset and interrupt timers, and includes automatic detection
of the supported IntTime bit-width.

Integrated with the Linux watchdog framework, it supports basic operations
including start, stop, ping, timeout configuration, and system reset via the
restart handler.

Signed-off-by: CL Wang <cl634@...estech.com>
---
 drivers/watchdog/Kconfig         |   9 +
 drivers/watchdog/Makefile        |   1 +
 drivers/watchdog/atcwdt200_wdt.c | 596 +++++++++++++++++++++++++++++++
 3 files changed, 606 insertions(+)
 create mode 100644 drivers/watchdog/atcwdt200_wdt.c

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index d3b9df7d466b..f5d3c1227385 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -2140,6 +2140,15 @@ config WATCHDOG_RTAS
 
 # RISC-V Architecture
 
+config ATCWDT200_WATCHDOG
+	tristate "Andes ATCWDT200 Watchdog support"
+	depends on ARCH_ANDES || COMPILE_TEST
+	help
+	  Driver for the Andes ATCWDT200 watchdog timer. It provides access to
+	  programmable reset and interrupt counters with clock-source dependent
+	  timing. The driver automatically detects the supported IntTime bit-width
+	  and is fully integrated with the Linux Watchdog Framework.
+
 config STARFIVE_WATCHDOG
 	tristate "StarFive Watchdog support"
 	depends on ARCH_STARFIVE || COMPILE_TEST
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index ba52099b1253..aaecf7899380 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -201,6 +201,7 @@ obj-$(CONFIG_PSERIES_WDT) += pseries-wdt.o
 obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o
 
 # RISC-V Architecture
+obj-$(CONFIG_ATCWDT200_WATCHDOG) += atcwdt200_wdt.o
 obj-$(CONFIG_STARFIVE_WATCHDOG) += starfive-wdt.o
 
 # S390 Architecture
diff --git a/drivers/watchdog/atcwdt200_wdt.c b/drivers/watchdog/atcwdt200_wdt.c
new file mode 100644
index 000000000000..d5aa56d165cf
--- /dev/null
+++ b/drivers/watchdog/atcwdt200_wdt.c
@@ -0,0 +1,596 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Andes ATCWDT200 watchdog timer driver.
+ *
+ * Copyright (C) 2025 Andes Technology Corporation
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/dev_printk.h>
+#include <linux/math64.h>
+#include <linux/minmax.h>
+#include <linux/moduleparam.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/watchdog.h>
+
+/* Register definitions */
+#define REG_CTRL		0x10
+#define REG_RESTART		0x14
+#define REG_WRITE_EN		0x18
+#define REG_STATUS		0x1C
+
+/* Control Register */
+#define CTRL_RST_TIME_MSK	GENMASK(10, 8)
+#define CTRL_RST_TIME_SET(x)	FIELD_PREP(CTRL_RST_TIME_MSK, x)
+#define CTRL_INT_TIME_MSK	GENMASK(7, 4)
+#define CTRL_INT_TIME_SET(x)	FIELD_PREP(CTRL_INT_TIME_MSK, x)
+#define CTRL_INT_TIME_GET(x)	FIELD_GET(CTRL_INT_TIME_MSK, x)
+#define CTRL_RST_EN		BIT(3)
+#define CTRL_CLK_SEL		BIT(1)
+#define CTRL_CLK_SEL_PCLK	1
+#define CTRL_CLK_SEL_SET(x)	FIELD_PREP(CTRL_CLK_SEL, x)
+#define CTRL_WDT_EN		BIT(0)
+
+/* Restart Register */
+#define RESTART_MAGIC		0xCAFE
+
+/* Write Enable Register */
+#define WRITE_EN_MAGIC		0x5AA5
+
+/* Status Register */
+#define STATUS_INT_EXPIRED	BIT(1)
+
+/* The default timeout value in seconds */
+#define ATCWDT_TIMEOUT		4
+
+/* Define the array size for each timer type */
+#define TMR_SZ_RST		8
+#define TMR_SZ_INT_16		8
+#define TMR_SZ_INT_32		16
+
+#define DRV_NAME		"atcwdt200"
+/**
+ * enum timer_type - Supported timer types for ATCWDT200 watchdog driver
+ * @TMR_RST:      Reset timer (non-interrupt).
+ * @TMR_INT_16:   16-bit interrupt timer supported by hardware.
+ * @TMR_INT_32:   32-bit interrupt timer supported by hardware.
+ * @TMR_UNKNOWN:  Timer type cannot be determined.
+ */
+enum timer_type {
+	TMR_RST,
+	TMR_INT_16,
+	TMR_INT_32,
+	TMR_UNKNOWN
+};
+
+static unsigned int timeout = ATCWDT_TIMEOUT;
+static bool nowayout = WATCHDOG_NOWAYOUT;
+
+/**
+ * struct atcwdt_drv - ATCWDT200 watchdog driver private data
+ * @wdt_dev:        Watchdog device used by the watchdog framework.
+ * @regmap:         Register map for accessing hardware registers.
+ * @clk:            Hardware clock used by the watchdog timer.
+ * @lock:           Spinlock protecting register accesses and driver state.
+ * @clk_freq:       Input clock frequency of the ATCWDT200.
+ * @clk_src:        Selected clock source for the watchdog timer.
+ * @int_timer_type: Detected interrupt timer type (16-bit, 32-bit, or unknown).
+ */
+struct atcwdt_drv {
+	struct watchdog_device	wdt_dev;
+	struct regmap		*regmap;
+	struct clk		*clk;
+	spinlock_t		lock;
+	unsigned int		clk_freq;
+	unsigned char		clk_src;
+	unsigned char		int_timer_type;
+};
+
+static const struct watchdog_info atcwdt_info = {
+	.identity = DRV_NAME,
+	.options = WDIOF_SETTIMEOUT |
+		   WDIOF_KEEPALIVEPING |
+		   WDIOF_MAGICCLOSE,
+};
+
+/**
+ * atcwdt_get_index - Get the interval value for the specified timer type
+ * @index: The index of the interval in the array
+ * @timer_type: The type of timer, which can be TMR_RST, TMR_INT_16, or
+ *              TMR_INT_32.
+ *
+ * This function retrieves the interval value based on the timer type and
+ * ensures the index stays within the valid range for the given timer type.
+ * For TMR_RST:
+ *  - The maximum array size is 8 (index range: 0-7).
+ * For TMR_INT_16:
+ *  - The maximum array size is 8 (index range: 0-7).
+ * For TMR_INT_32:
+ *  - The maximum array size is 16 (index range: 0-15).
+ *
+ * If the index exceeds the maximum array size, the function will return
+ * the last element of the respective array.
+ */
+static inline unsigned char atcwdt_get_index(unsigned char index,
+					     enum timer_type timer_type)
+{
+	static const unsigned char rst_timer_interval[TMR_SZ_RST] = {
+		7, 8, 9, 10, 11, 12, 13, 14};
+	static const unsigned char int_timer_interval[TMR_SZ_INT_32] = {
+		6, 8, 10, 11, 12, 13, 14, 15, 17, 19, 21, 23, 25, 27, 29, 31};
+	unsigned char array_index;
+
+	if (timer_type == TMR_RST) {
+		array_index = min(index, TMR_SZ_RST - 1);
+		return rst_timer_interval[array_index];
+	}
+
+	if (timer_type == TMR_INT_32)
+		array_index = min(index, TMR_SZ_INT_32 - 1);
+	else
+		array_index = min(index, TMR_SZ_INT_16 - 1);
+
+	return int_timer_interval[array_index];
+}
+
+/**
+ * atcwdt_get_clock_period - Calculate the closest clock period based on a
+ *                           given tick count
+ * @tick: The target tick count to match
+ * @timer_type: The type of timer, which can be TMR_RST, TMR_INT_16, or
+ *              TMR_INT_32.
+ * @index: Pointer to store the index of the selected parameter
+ *
+ * This function calculates the closest clock period to the given tick count
+ * by iterating through the timer parameters and selecting the one that
+ * minimizes the difference between the target tick count and the calculated
+ * clock period. The function determines the index of the closest parameter
+ * and returns the difference between the target tick count and the selected
+ * clock period.
+ *
+ * Return: The difference between the target tick count and the selected
+ * clock period.
+ */
+static long long atcwdt_get_clock_period(long long tick,
+					 enum timer_type timer_type,
+					 unsigned char *index)
+{
+	long long result;
+	unsigned char size;
+	char i;
+
+	if (timer_type == TMR_RST)
+		size = TMR_SZ_RST;
+	else if (timer_type == TMR_INT_32)
+		size = TMR_SZ_INT_32;
+	else
+		size = TMR_SZ_INT_16;
+
+	*index = size - 1;
+	for (i = 0; i < size; i++) {
+		result = tick - (1LL << atcwdt_get_index(i, timer_type));
+
+		if (result <= 1) {
+			*index = i;
+			break;
+		}
+	}
+
+	return result;
+}
+
+/**
+ * atcwdt_get_timeout_params - Calculate optimal parameters for Watchdog Timer
+ * @drv_data: Pointer to the Watchdog driver data structure
+ * @timeout: Desired timeout value (in seconds)
+ * @int_timer_params: Pointer to store the calculated interrupt timer
+ *                    parameter index
+ * @rst_timer_params: Pointer to store the calculated reset timer parameter
+ *                    index
+ *
+ * This function calculates the optimal parameter combination for the
+ * interrupt timer and reset timer of the Watchdog Timer to achieve a
+ * timeout value closest to, but not less than the specified timeout.
+ *
+ * Algorithm:
+ * 1. The parameters for both the interrupt timer and reset timer are
+ *    predefined as a series of options represented as powers of 2.
+ * 2. The function first determines the interrupt timer's parameter index
+ *    that provides a time closest to and not exceeding the desired timeout.
+ * 3. Based on the selected interrupt timer, it calculates the required
+ *    reset timer parameter to ensure the total timeout matches the target.
+ *
+ * Return: The calculated parameter indices are stored in the provided
+ *         pointers.
+ */
+static void atcwdt_get_timeout_params(struct atcwdt_drv *drv_data,
+				      unsigned int timeout,
+				      unsigned char *int_timer_params,
+				      unsigned char *rst_timer_params)
+{
+	long long rest_time_ms;
+	long long result;
+	long long tick;
+	unsigned char rst_index;
+	unsigned char int_index;
+	unsigned char above;
+	unsigned char below;
+
+	tick = (long long)timeout * drv_data->clk_freq;
+	result = atcwdt_get_clock_period(tick,
+					 drv_data->int_timer_type,
+					 &above);
+	if (result == 0 || above == 0) {
+		*int_timer_params = above;
+		*rst_timer_params = 0;
+		return;
+	}
+	below = above - 1;
+
+	int_index = atcwdt_get_index(below, drv_data->int_timer_type);
+	rest_time_ms = timeout * 1000LL
+		       - div64_s64(1000LL << int_index, drv_data->clk_freq);
+
+	result = atcwdt_get_clock_period(rest_time_ms * drv_data->clk_freq,
+					 TMR_RST,
+					 &rst_index);
+
+	if (result > 1) {
+		*int_timer_params = above;
+		*rst_timer_params = 0;
+	} else {
+		*int_timer_params = below;
+		*rst_timer_params = rst_index;
+	}
+}
+
+/**
+ * atcwdt_get_int_timer_type - Get the supported interrupt timer type.
+ * @drv_data: Pointer to the watchdog driver data structure.
+ *
+ * This function tests the writable bits in the IntTime field of the control
+ * register to determine the interrupt timer type supported by the hardware.
+ *
+ * Note: This function must only be called when the ATCWDT200 watchdog is
+ * disabled. If the watchdog is enabled, this function returns TMR_UNKNOWN.
+ *
+ * Returns: The interrupt timer type supported by the hardware.
+ */
+static int atcwdt_get_int_timer_type(struct atcwdt_drv *drv_data)
+{
+	struct device *dev = drv_data->wdt_dev.parent;
+	unsigned int val;
+	int ret  = 0;
+
+	spin_lock(&drv_data->lock);
+	regmap_read(drv_data->regmap, REG_CTRL, &val);
+	if (val & CTRL_WDT_EN) {
+		spin_unlock(&drv_data->lock);
+		return TMR_UNKNOWN;
+	}
+
+	/*
+	 * Configures the IntTime field with the maximum mask value
+	 * (CTRL_INT_TIME_MSK), reads its value from the control register
+	 * to identify the maximum writable bits.
+	 */
+	regmap_write(drv_data->regmap, REG_WRITE_EN, WRITE_EN_MAGIC);
+	regmap_write(drv_data->regmap, REG_CTRL, CTRL_INT_TIME_MSK);
+	regmap_read(drv_data->regmap, REG_CTRL, &val);
+	spin_unlock(&drv_data->lock);
+
+	val = CTRL_INT_TIME_GET(val);
+	switch (val) {
+	case 7:
+		drv_data->int_timer_type = TMR_INT_16;
+		break;
+	case 15:
+		drv_data->int_timer_type = TMR_INT_32;
+		break;
+	default:
+		drv_data->int_timer_type = TMR_UNKNOWN;
+		ret = dev_err_probe(dev, -ENODEV,
+				    "Failed to detect interrupt timer type\n");
+	}
+
+	return ret;
+}
+
+static int atcwdt_ping(struct watchdog_device *wdt_dev)
+{
+	struct atcwdt_drv *drv_data = watchdog_get_drvdata(wdt_dev);
+
+	spin_lock(&drv_data->lock);
+	regmap_write(drv_data->regmap, REG_WRITE_EN, WRITE_EN_MAGIC);
+	regmap_write(drv_data->regmap, REG_RESTART, RESTART_MAGIC);
+	regmap_update_bits(drv_data->regmap, REG_STATUS, STATUS_INT_EXPIRED,
+			   STATUS_INT_EXPIRED);
+	spin_unlock(&drv_data->lock);
+
+	return 0;
+}
+
+static int atcwdt_set_timeout(struct watchdog_device *wdt_dev,
+			      unsigned int timeout)
+{
+	struct atcwdt_drv *drv_data = watchdog_get_drvdata(wdt_dev);
+	unsigned int value;
+	unsigned char  rst_val;
+	unsigned char  int_val;
+
+	wdt_dev->timeout = timeout;
+	atcwdt_get_timeout_params(drv_data, timeout, &int_val, &rst_val);
+
+	spin_lock(&drv_data->lock);
+	regmap_write(drv_data->regmap, REG_WRITE_EN, WRITE_EN_MAGIC);
+
+	value = CTRL_RST_TIME_SET(rst_val) |
+		CTRL_INT_TIME_SET(int_val) |
+		CTRL_CLK_SEL_SET(drv_data->clk_src);
+	regmap_update_bits(drv_data->regmap,
+			   REG_CTRL,
+			   CTRL_RST_TIME_MSK |
+			   CTRL_INT_TIME_MSK |
+			   CTRL_CLK_SEL,
+			   value);
+
+	spin_unlock(&drv_data->lock);
+	atcwdt_ping(wdt_dev);
+
+	return 0;
+}
+
+static int atcwdt_start(struct watchdog_device *wdt_dev)
+{
+	struct atcwdt_drv *drv_data = watchdog_get_drvdata(wdt_dev);
+
+	atcwdt_set_timeout(wdt_dev, wdt_dev->timeout);
+
+	spin_lock(&drv_data->lock);
+	regmap_write(drv_data->regmap, REG_WRITE_EN, WRITE_EN_MAGIC);
+	regmap_update_bits(drv_data->regmap,
+			   REG_CTRL,
+			   CTRL_RST_EN | CTRL_WDT_EN,
+			   CTRL_RST_EN | CTRL_WDT_EN);
+
+	spin_unlock(&drv_data->lock);
+
+	return 0;
+}
+
+static int atcwdt_stop(struct watchdog_device *wdt_dev)
+{
+	struct atcwdt_drv *drv_data = watchdog_get_drvdata(wdt_dev);
+
+	spin_lock(&drv_data->lock);
+	regmap_write(drv_data->regmap, REG_WRITE_EN, WRITE_EN_MAGIC);
+	regmap_update_bits(drv_data->regmap,
+			   REG_CTRL,
+			   CTRL_RST_EN | CTRL_WDT_EN,
+			   0);
+	spin_unlock(&drv_data->lock);
+
+	return 0;
+}
+
+static int atcwdt_restart(struct watchdog_device *wdt_dev,
+			  unsigned long action, void *data)
+{
+	struct atcwdt_drv *drv_data = watchdog_get_drvdata(wdt_dev);
+
+	atcwdt_set_timeout(wdt_dev, 0);
+
+	spin_lock(&drv_data->lock);
+	regmap_write(drv_data->regmap, REG_WRITE_EN, WRITE_EN_MAGIC);
+	regmap_update_bits(drv_data->regmap,
+			   REG_CTRL,
+			   CTRL_RST_EN | CTRL_WDT_EN,
+			   CTRL_RST_EN | CTRL_WDT_EN);
+	spin_unlock(&drv_data->lock);
+
+	return 0;
+}
+
+static const struct watchdog_ops atcwdt_ops = {
+	.owner = THIS_MODULE,
+	.start = atcwdt_start,
+	.stop = atcwdt_stop,
+	.ping = atcwdt_ping,
+	.set_timeout = atcwdt_set_timeout,
+	.restart = atcwdt_restart,
+};
+
+static int atcwdt_init_resource(struct platform_device *pdev,
+				struct atcwdt_drv *drv_data)
+{
+	struct device *dev = &pdev->dev;
+	void __iomem *base;
+	const struct regmap_config cfg = {
+		.name = "atcwdt",
+		.reg_bits = 32,
+		.val_bits = 32,
+		.cache_type = REGCACHE_NONE,
+		.reg_stride = 4,
+		.max_register = REG_STATUS,
+	};
+
+	base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(base))
+		return dev_err_probe(dev, PTR_ERR(base),
+				     "Failed to ioremap I/O resource\n");
+
+	drv_data->regmap = devm_regmap_init_mmio(dev, base, &cfg);
+	if (IS_ERR(drv_data->regmap))
+		return dev_err_probe(dev, PTR_ERR(drv_data->regmap),
+				     "Failed to create regmap\n");
+
+	return 0;
+}
+
+static int atcwdt_enable_clk(struct atcwdt_drv *drv_data)
+{
+	struct device *dev = drv_data->wdt_dev.parent;
+	unsigned int val;
+	int ret;
+
+	drv_data->clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(drv_data->clk))
+		return dev_err_probe(dev, PTR_ERR(drv_data->clk),
+				     "Failed to get watchdog clock\n");
+
+	ret = clk_prepare_enable(drv_data->clk);
+	if (ret)
+		return dev_err_probe(dev, ret,
+				     "Failed to enable clock\n");
+
+	drv_data->clk_freq = clk_get_rate(drv_data->clk);
+	if (!drv_data->clk_freq)
+		return dev_err_probe(dev, -EINVAL,
+				     "Failed to get clock rate\n");
+
+	ret = device_property_read_u32(dev, "andestech,clock-source", &val);
+	drv_data->clk_src = (!ret && val != 0) ? CTRL_CLK_SEL_PCLK : 0;
+
+	return 0;
+}
+
+static int atcwdt_init_wdt_device(struct device *dev,
+				  struct atcwdt_drv *drv_data)
+{
+	struct watchdog_device *wdd = &drv_data->wdt_dev;
+
+	wdd->parent = dev;
+	wdd->info = &atcwdt_info;
+	wdd->ops = &atcwdt_ops;
+	wdd->timeout = ATCWDT_TIMEOUT;
+	wdd->min_timeout = 1;
+
+	watchdog_set_nowayout(wdd, nowayout);
+	watchdog_set_drvdata(wdd, drv_data);
+
+	return 0;
+}
+
+static void atcwdt_calc_max_timeout(struct atcwdt_drv *drv_data)
+{
+	unsigned char rst_idx = atcwdt_get_index(0xFF, TMR_RST);
+	unsigned char int_idx = atcwdt_get_index(0xFF,
+						 drv_data->int_timer_type);
+
+	drv_data->wdt_dev.max_timeout =
+		((1U << rst_idx) + (1U << int_idx)) / drv_data->clk_freq;
+}
+
+static int atcwdt_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct atcwdt_drv *drv_data;
+	int ret;
+
+	drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL);
+	if (!drv_data)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, drv_data);
+	spin_lock_init(&drv_data->lock);
+
+	ret = atcwdt_init_wdt_device(dev, drv_data);
+	if (ret)
+		return ret;
+
+	ret = atcwdt_init_resource(pdev, drv_data);
+	if (ret)
+		return ret;
+
+	ret = atcwdt_enable_clk(drv_data);
+	if (ret)
+		return ret;
+
+	ret = atcwdt_get_int_timer_type(drv_data);
+	if (ret)
+		goto disable_clk;
+
+	atcwdt_calc_max_timeout(drv_data);
+
+	ret = devm_watchdog_register_device(dev, &drv_data->wdt_dev);
+	if (ret)
+		goto disable_clk;
+
+	return 0;
+
+disable_clk:
+	clk_disable_unprepare(drv_data->clk);
+
+	return ret;
+}
+
+static int atcwdt_suspend(struct device *dev)
+{
+	struct atcwdt_drv *drv_data = dev_get_drvdata(dev);
+
+	if (watchdog_active(&drv_data->wdt_dev)) {
+		atcwdt_stop(&drv_data->wdt_dev);
+		clk_disable_unprepare(drv_data->clk);
+	}
+
+	return 0;
+}
+
+static int atcwdt_resume(struct device *dev)
+{
+	struct atcwdt_drv *drv_data = dev_get_drvdata(dev);
+	int ret = 0;
+
+	if (watchdog_active(&drv_data->wdt_dev)) {
+		ret = clk_prepare_enable(drv_data->clk);
+		if (ret)
+			goto clk_prepare_err;
+
+		atcwdt_start(&drv_data->wdt_dev);
+		atcwdt_ping(&drv_data->wdt_dev);
+	}
+
+clk_prepare_err:
+	return ret;
+}
+
+static const struct of_device_id atcwdt_match[] = {
+	{ .compatible = "andestech,qilai-wdt" },
+	{ .compatible = "andestech,ae350-wdt" },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, atcwdt_match);
+
+static DEFINE_SIMPLE_DEV_PM_OPS(atcwdt_pm_ops, atcwdt_suspend, atcwdt_resume);
+
+static struct platform_driver atcwdt_driver = {
+	.probe		= atcwdt_probe,
+	.driver		= {
+		.name	= DRV_NAME,
+		.owner	= THIS_MODULE,
+		.of_match_table = atcwdt_match,
+		.pm = pm_sleep_ptr(&atcwdt_pm_ops),
+	},
+};
+
+module_platform_driver(atcwdt_driver);
+
+module_param(timeout, uint, 0);
+MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (default="
+		 __MODULE_STRING(ATCWDT_TIMEOUT) ")");
+
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+		 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("CL Wang <cl634@...estech.com>");
+MODULE_DESCRIPTION("Andes ATCWDT200 Watchdog timer driver");
-- 
2.34.1


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ