From nobody Sun Feb 8 05:29:35 2026 Received: from Atcsqr.andestech.com (60-248-80-70.hinet-ip.hinet.net [60.248.80.70]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 471032877D4 for ; Thu, 15 Jan 2026 08:15:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=60.248.80.70 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768464958; cv=none; b=IW6Q5ArFUJaFSC5KBsl9oUl+8vOBslhTDZCeDSpwYmk51nKpSm/HjP3Pb1RciOB5rJwQODBvJfWx10ILD+bHhnyI1tcKj5lr1/DpRG28eJE6lED0hrn/3udcEGFB6HZCgqoTcu4eX28Zp75g3KOgx4WmFJEw/mHV2gWevnRfg/0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768464958; c=relaxed/simple; bh=tC0HZpV4U8DbjEwVPwyUVaNS8zCyAsPHCwq4O6BnFiU=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=b9siHMrM7RSM0v5f/361OiWNa/H75SpGHT+uNjbpZQ7WzMGJaNNpYvTkGvozrQq1uEoJ6IiCVWGFRYaBT40J0jIQCfQG5ET4HyUPzmyd/d8YdFZSRf4QPqK6u1rFSyf9xnfbtLTMS43213UYkqbFjq/olBekKxw/VArxZo+2xS4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=permerror header.from=andestech.com; spf=pass smtp.mailfrom=andestech.com; arc=none smtp.client-ip=60.248.80.70 Authentication-Results: smtp.subspace.kernel.org; dmarc=permerror header.from=andestech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=andestech.com Received: from mail.andestech.com (ATCPCS34.andestech.com [10.0.1.134]) by Atcsqr.andestech.com with ESMTPS id 60F8FNEV082800 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=OK); Thu, 15 Jan 2026 16:15:23 +0800 (+08) (envelope-from cl634@andestech.com) Received: from swlinux02.andestech.com (10.0.15.183) by ATCPCS34.andestech.com (10.0.1.134) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.39; Thu, 15 Jan 2026 16:15:23 +0800 From: CL Wang To: , , , , , CC: , , , Subject: [PATCH V2 1/3] dt-bindings: watchdog: Add support for Andes ATCWDT200 Date: Thu, 15 Jan 2026 16:14:42 +0800 Message-ID: <20260115081444.2452357-2-cl634@andestech.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260115081444.2452357-1-cl634@andestech.com> References: <20260115081444.2452357-1-cl634@andestech.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: ATCPCS33.andestech.com (10.0.1.100) To ATCPCS34.andestech.com (10.0.1.134) X-DKIM-Results: atcpcs34.andestech.com; dkim=none; X-DNSRBL: X-SPAM-SOURCE-CHECK: pass X-MAIL: Atcsqr.andestech.com 60F8FNEV082800 Content-Type: text/plain; charset="utf-8" Add the devicetree binding documentation for the Andes ATCWDT200 watchdog timer. ATCWDT200 is the IP name, which is embedded in AndesCore-based platforms or SoCs such as AE350 and Qilai. Signed-off-by: CL Wang Reviewed-by: Rob Herring (Arm) --- Changes in v2: - Drop redundant text "including supported properties..." from the commit message. - Clarify the relationship between ATCWDT200 IP and SoCs (AE350/Qilai) in the commit message. - Add missing type definition ($ref: uint32), enum constraint, and description for 'andestech,clock-source' property. --- .../watchdog/andestech,ae350-wdt.yaml | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 Documentation/devicetree/bindings/watchdog/andestech,ae= 350-wdt.yaml diff --git a/Documentation/devicetree/bindings/watchdog/andestech,ae350-wdt= .yaml b/Documentation/devicetree/bindings/watchdog/andestech,ae350-wdt.yaml new file mode 100644 index 000000000000..f1107c552788 --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/andestech,ae350-wdt.yaml @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/watchdog/andestech,ae350-wdt.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Andes ATCWDT200 Watchdog Timer + +maintainers: + - CL Wang + +allOf: + - $ref: watchdog.yaml# + +properties: + compatible: + oneOf: + - items: + - enum: + - andestech,qilai-wdt + - const: andestech,ae350-wdt + - const: andestech,ae350-wdt + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + andestech,clock-source: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1] + description: | + Select the clock source for the watchdog timer. + 0 - External clock + 1 - P clock + +required: + - compatible + - reg + - clocks + - andestech,clock-source + +unevaluatedProperties: false + +examples: + - | + watchdog@f0500000 { + compatible =3D "andestech,ae350-wdt"; + reg =3D <0xf0500000 0x20>; + clocks =3D <&clk_wdt>; + andestech,clock-source =3D <0>; + }; --=20 2.34.1 From nobody Sun Feb 8 05:29:35 2026 Received: from Atcsqr.andestech.com (60-248-80-70.hinet-ip.hinet.net [60.248.80.70]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 169D73218B2 for ; Thu, 15 Jan 2026 08:15:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=60.248.80.70 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768464969; cv=none; b=XrneVz8GG0A/pi/KyDIzaMqPlAcYeGJa1d3WnrfTMxzq1s1CIvViL0pnSA9G0SWIpyI0bxmXmOG94MC+U45H3CSd+djSyo1IMnCifb/MTWny3FGC5gh6cGDzLKz75pL+oHulBafqYhoBgroGY6M72oZHb8lL77ZD/AzJn4NeDJY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768464969; c=relaxed/simple; bh=1Ye0Psfo5BQ74k0xpYEhJBm+fo09sft/p51DnWJo+aE=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=IYwMoeaKqzoe1U5OCJse/7gZNrrCmM0NWre7sZjape1SunxASTY+UhAo3Nrd1gY9LsU3AWlAOsHsrEsljQ8Qvw5J9heq2CW4eedsC9PlXVSCF8eGxA2kh4wQj8SgGs3teHbcsqCvntOMDWASfuhaAbZUwK3upq0V8n0TXyOKYWA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=permerror header.from=andestech.com; spf=pass smtp.mailfrom=andestech.com; arc=none smtp.client-ip=60.248.80.70 Authentication-Results: smtp.subspace.kernel.org; dmarc=permerror header.from=andestech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=andestech.com Received: from mail.andestech.com (ATCPCS34.andestech.com [10.0.1.134]) by Atcsqr.andestech.com with ESMTPS id 60F8FYY3082872 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=OK); Thu, 15 Jan 2026 16:15:34 +0800 (+08) (envelope-from cl634@andestech.com) Received: from swlinux02.andestech.com (10.0.15.183) by ATCPCS34.andestech.com (10.0.1.134) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.39; Thu, 15 Jan 2026 16:15:33 +0800 From: CL Wang To: , , , , , CC: , , , Subject: [PATCH V2 2/3] watchdog: atcwdt200: Add driver for Andes ATCWDT200 Date: Thu, 15 Jan 2026 16:14:43 +0800 Message-ID: <20260115081444.2452357-3-cl634@andestech.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260115081444.2452357-1-cl634@andestech.com> References: <20260115081444.2452357-1-cl634@andestech.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: ATCPCS33.andestech.com (10.0.1.100) To ATCPCS34.andestech.com (10.0.1.134) X-DKIM-Results: atcpcs34.andestech.com; dkim=none; X-DNSRBL: X-SPAM-SOURCE-CHECK: pass X-MAIL: Atcsqr.andestech.com 60F8FYY3082872 Content-Type: text/plain; charset="utf-8" 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 --- Changes in v2: - Use devm_clk_get_enabled() instead of devm_clk_get() and clk_prepare_enable() - Drop unnecessary "andestech,qilai-wdt" compatible - Remove .owner assignment from platform_driver - Simplify resume error handling --- drivers/watchdog/Kconfig | 9 + drivers/watchdog/Makefile | 1 + drivers/watchdog/atcwdt200_wdt.c | 580 +++++++++++++++++++++++++++++++ 3 files changed, 590 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 =20 # RISC-V Architecture =20 +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) +=3D pseries-wdt.o obj-$(CONFIG_WATCHDOG_RTAS) +=3D wdrtas.o =20 # RISC-V Architecture +obj-$(CONFIG_ATCWDT200_WATCHDOG) +=3D atcwdt200_wdt.o obj-$(CONFIG_STARFIVE_WATCHDOG) +=3D starfive-wdt.o =20 # S390 Architecture diff --git a/drivers/watchdog/atcwdt200_wdt.c b/drivers/watchdog/atcwdt200_= wdt.c new file mode 100644 index 000000000000..8e3b18aea368 --- /dev/null +++ b/drivers/watchdog/atcwdt200_wdt.c @@ -0,0 +1,580 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Andes ATCWDT200 watchdog timer driver. + * + * Copyright (C) 2025 Andes Technology Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 =3D ATCWDT_TIMEOUT; +static bool nowayout =3D 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 unkn= own). + */ +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 =3D { + .identity =3D DRV_NAME, + .options =3D 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] =3D { + 7, 8, 9, 10, 11, 12, 13, 14}; + static const unsigned char int_timer_interval[TMR_SZ_INT_32] =3D { + 6, 8, 10, 11, 12, 13, 14, 15, 17, 19, 21, 23, 25, 27, 29, 31}; + unsigned char array_index; + + if (timer_type =3D=3D TMR_RST) { + array_index =3D min(index, TMR_SZ_RST - 1); + return rst_timer_interval[array_index]; + } + + if (timer_type =3D=3D TMR_INT_32) + array_index =3D min(index, TMR_SZ_INT_32 - 1); + else + array_index =3D 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 cou= nt + * by iterating through the timer parameters and selecting the one that + * minimizes the difference between the target tick count and the calculat= ed + * clock period. The function determines the index of the closest parameter + * and returns the difference between the target tick count and the select= ed + * 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 =3D=3D TMR_RST) + size =3D TMR_SZ_RST; + else if (timer_type =3D=3D TMR_INT_32) + size =3D TMR_SZ_INT_32; + else + size =3D TMR_SZ_INT_16; + + *index =3D size - 1; + for (i =3D 0; i < size; i++) { + result =3D tick - (1LL << atcwdt_get_index(i, timer_type)); + + if (result <=3D 1) { + *index =3D i; + break; + } + } + + return result; +} + +/** + * atcwdt_get_timeout_params - Calculate optimal parameters for Watchdog T= imer + * @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 timeou= t. + * 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 =3D (long long)timeout * drv_data->clk_freq; + result =3D atcwdt_get_clock_period(tick, + drv_data->int_timer_type, + &above); + if (result =3D=3D 0 || above =3D=3D 0) { + *int_timer_params =3D above; + *rst_timer_params =3D 0; + return; + } + below =3D above - 1; + + int_index =3D atcwdt_get_index(below, drv_data->int_timer_type); + rest_time_ms =3D timeout * 1000LL + - div64_s64(1000LL << int_index, drv_data->clk_freq); + + result =3D atcwdt_get_clock_period(rest_time_ms * drv_data->clk_freq, + TMR_RST, + &rst_index); + + if (result > 1) { + *int_timer_params =3D above; + *rst_timer_params =3D 0; + } else { + *int_timer_params =3D below; + *rst_timer_params =3D 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 contr= ol + * register to determine the interrupt timer type supported by the hardwar= e. + * + * 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 =3D drv_data->wdt_dev.parent; + unsigned int val; + int ret =3D 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 =3D CTRL_INT_TIME_GET(val); + switch (val) { + case 7: + drv_data->int_timer_type =3D TMR_INT_16; + break; + case 15: + drv_data->int_timer_type =3D TMR_INT_32; + break; + default: + drv_data->int_timer_type =3D TMR_UNKNOWN; + ret =3D 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 =3D 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 =3D watchdog_get_drvdata(wdt_dev); + unsigned int value; + unsigned char rst_val; + unsigned char int_val; + + wdt_dev->timeout =3D 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 =3D 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 =3D 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 =3D 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 =3D 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 =3D { + .owner =3D THIS_MODULE, + .start =3D atcwdt_start, + .stop =3D atcwdt_stop, + .ping =3D atcwdt_ping, + .set_timeout =3D atcwdt_set_timeout, + .restart =3D atcwdt_restart, +}; + +static int atcwdt_init_resource(struct platform_device *pdev, + struct atcwdt_drv *drv_data) +{ + struct device *dev =3D &pdev->dev; + void __iomem *base; + const struct regmap_config cfg =3D { + .name =3D "atcwdt", + .reg_bits =3D 32, + .val_bits =3D 32, + .cache_type =3D REGCACHE_NONE, + .reg_stride =3D 4, + .max_register =3D REG_STATUS, + }; + + base =3D 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 =3D 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 =3D drv_data->wdt_dev.parent; + unsigned int val; + int clk_src; + + drv_data->clk =3D devm_clk_get_enabled(dev, NULL); + if (IS_ERR(drv_data->clk)) + return dev_err_probe(dev, PTR_ERR(drv_data->clk), + "Failed to get watchdog clock\n"); + + drv_data->clk_freq =3D clk_get_rate(drv_data->clk); + if (!drv_data->clk_freq) + return dev_err_probe(dev, -EINVAL, + "Failed to get clock rate\n"); + + clk_src =3D device_property_read_u32(dev, "andestech,clock-source", &val); + drv_data->clk_src =3D (!clk_src && val !=3D 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 =3D &drv_data->wdt_dev; + + wdd->parent =3D dev; + wdd->info =3D &atcwdt_info; + wdd->ops =3D &atcwdt_ops; + wdd->timeout =3D ATCWDT_TIMEOUT; + wdd->min_timeout =3D 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 =3D atcwdt_get_index(0xFF, TMR_RST); + unsigned char int_idx =3D atcwdt_get_index(0xFF, + drv_data->int_timer_type); + + drv_data->wdt_dev.max_timeout =3D + ((1U << rst_idx) + (1U << int_idx)) / drv_data->clk_freq; +} + +static int atcwdt_probe(struct platform_device *pdev) +{ + struct device *dev =3D &pdev->dev; + struct atcwdt_drv *drv_data; + int ret; + + drv_data =3D 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 =3D atcwdt_init_wdt_device(dev, drv_data); + if (ret) + return ret; + + ret =3D atcwdt_init_resource(pdev, drv_data); + if (ret) + return ret; + + ret =3D atcwdt_enable_clk(drv_data); + if (ret) + return ret; + + ret =3D atcwdt_get_int_timer_type(drv_data); + if (ret) + return ret; + + atcwdt_calc_max_timeout(drv_data); + + ret =3D devm_watchdog_register_device(dev, &drv_data->wdt_dev); + + return ret; +} + +static int atcwdt_suspend(struct device *dev) +{ + struct atcwdt_drv *drv_data =3D 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 =3D dev_get_drvdata(dev); + int ret =3D 0; + + if (watchdog_active(&drv_data->wdt_dev)) { + ret =3D clk_prepare_enable(drv_data->clk); + if (ret) + return ret; + atcwdt_start(&drv_data->wdt_dev); + atcwdt_ping(&drv_data->wdt_dev); + } + + return ret; +} + +static const struct of_device_id atcwdt_match[] =3D { + { .compatible =3D "andestech,ae350-wdt" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, atcwdt_match); + +static DEFINE_SIMPLE_DEV_PM_OPS(atcwdt_pm_ops, atcwdt_suspend, atcwdt_resu= me); + +static struct platform_driver atcwdt_driver =3D { + .probe =3D atcwdt_probe, + .driver =3D { + .name =3D DRV_NAME, + .of_match_table =3D atcwdt_match, + .pm =3D 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=3D" + __MODULE_STRING(ATCWDT_TIMEOUT) ")"); + +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (defau= lt=3D" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("CL Wang "); +MODULE_DESCRIPTION("Andes ATCWDT200 Watchdog timer driver"); --=20 2.34.1 From nobody Sun Feb 8 05:29:35 2026 Received: from Atcsqr.andestech.com (60-248-80-70.hinet-ip.hinet.net [60.248.80.70]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3177F22B8B0 for ; Thu, 15 Jan 2026 08:15:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=60.248.80.70 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768464969; cv=none; b=B7aG0lq1Zwv5V0bYDunDV6TxEVF0bFtP7YgVQfO7YFfAC6kPKV34pSRLvRG9zM0CpPTUgyPFWWQi1+k+LMmRU0tzYuBgaqCi8ORht3nuRpE+RFfZpXmRAuUF9LM7tGwqARwkY08+T8GKXHh+tjVJJy7Ha/x0HcL0zsbQgIEgi10= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768464969; c=relaxed/simple; bh=N0wFbXydq7vOdxwnTrUXHL3SEarqIdXmD9lvrxzhs9Y=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=Cfyhvk9WcLPDJxM9jyjdpC1lN9PR4EpvV5YBc8BlF4H2yGCEL0Sn0GyhMBOFfIffOz4zNJFT1JRm8uyo1tZkubK2tQ/kiY5tMYoq3I5kM/Bfu5vkXzJNmunIWCdlBq0KqALyuZVFv1VvFOjQDDLcfuR23xFy20YwwqVH9r0qUcA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=permerror header.from=andestech.com; spf=pass smtp.mailfrom=andestech.com; arc=none smtp.client-ip=60.248.80.70 Authentication-Results: smtp.subspace.kernel.org; dmarc=permerror header.from=andestech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=andestech.com Received: from mail.andestech.com (ATCPCS34.andestech.com [10.0.1.134]) by Atcsqr.andestech.com with ESMTPS id 60F8Ffqm082945 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=OK); Thu, 15 Jan 2026 16:15:41 +0800 (+08) (envelope-from cl634@andestech.com) Received: from swlinux02.andestech.com (10.0.15.183) by ATCPCS34.andestech.com (10.0.1.134) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.39; Thu, 15 Jan 2026 16:15:41 +0800 From: CL Wang To: , , , , , CC: , , , Subject: [PATCH V2 3/3] MAINTAINERS: Add entry for Andes ATCWDT200 Date: Thu, 15 Jan 2026 16:14:44 +0800 Message-ID: <20260115081444.2452357-4-cl634@andestech.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260115081444.2452357-1-cl634@andestech.com> References: <20260115081444.2452357-1-cl634@andestech.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: ATCPCS33.andestech.com (10.0.1.100) To ATCPCS34.andestech.com (10.0.1.134) X-DKIM-Results: atcpcs34.andestech.com; dkim=none; X-DNSRBL: X-SPAM-SOURCE-CHECK: pass X-MAIL: Atcsqr.andestech.com 60F8Ffqm082945 Add a MAINTAINERS entry for the Andes ATCWDT200 watchdog driver and its associated Device Tree bindings. Signed-off-by: CL Wang --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 12f49de7fe03..1a1c2b68252a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1817,6 +1817,12 @@ S: Supported F: drivers/clk/analogbits/* F: include/linux/clk/analogbits* =20 +ANDES ATCWDT200 WATCHDOG DRIVER +M: CL Wang +S: Supported +F: Documentation/devicetree/bindings/watchdog/andestech,ae350-wdt.yaml +F: drivers/watchdog/atcwdt200_wdt.c + ANDROID DRIVERS M: Greg Kroah-Hartman M: Arve Hj=C3=B8nnev=C3=A5g --=20 2.34.1