From patchwork Fri Mar 6 13:27:41 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serge Semin X-Patchwork-Id: 11424033 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 2481292A for ; Fri, 6 Mar 2020 13:38:03 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 0F5FF20848 for ; Fri, 6 Mar 2020 13:38:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726368AbgCFNiC (ORCPT ); Fri, 6 Mar 2020 08:38:02 -0500 Received: from mail.baikalelectronics.com ([87.245.175.226]:37298 "EHLO mail.baikalelectronics.ru" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726167AbgCFNiC (ORCPT ); Fri, 6 Mar 2020 08:38:02 -0500 Received: from localhost (unknown [127.0.0.1]) by mail.baikalelectronics.ru (Postfix) with ESMTP id 770DC8030792; Fri, 6 Mar 2020 13:28:03 +0000 (UTC) X-Virus-Scanned: amavisd-new at baikalelectronics.ru Received: from mail.baikalelectronics.ru ([127.0.0.1]) by localhost (mail.baikalelectronics.ru [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id bEO0VVTOMh71; Fri, 6 Mar 2020 16:28:02 +0300 (MSK) From: To: Wim Van Sebroeck , Guenter Roeck , Rob Herring , Mark Rutland CC: Serge Semin , Serge Semin , Alexey Malahov , Thomas Bogendoerfer , Paul Burton , Ralf Baechle , , , Subject: [PATCH 1/7] dt-bindings: watchdog: dw-wdt: Replace legacy bindings file with YAML-based one Date: Fri, 6 Mar 2020 16:27:41 +0300 In-Reply-To: <20200306132747.14701-1-Sergey.Semin@baikalelectronics.ru> References: <20200306132747.14701-1-Sergey.Semin@baikalelectronics.ru> MIME-Version: 1.0 X-ClientProxiedBy: MAIL.baikal.int (192.168.51.25) To mail (192.168.51.25) Message-Id: <20200306132803.770DC8030792@mail.baikalelectronics.ru> Sender: linux-watchdog-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-watchdog@vger.kernel.org From: Serge Semin Modern device tree bindings are supposed to be created as YAML-files in accordane with dt-schema. This commit replaces the DW Watchdog legacy bare text bindings with YAML file. As before the the bindings states that the corresponding dts node is supposed to have a registers range reference, at least one clocks phandle reference, optional reset lines. Seeing all the platforms with DW Watchdog provide the watchdog interrupt property and since in further commit we'll alter the driver to use it for pre-timeout functionality implementation, lets declare the IRQ property to be required. Signed-off-by: Serge Semin Signed-off-by: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Paul Burton Cc: Ralf Baechle --- .../devicetree/bindings/watchdog/dw_wdt.txt | 24 ------- .../bindings/watchdog/snps,dw-wdt.yaml | 66 +++++++++++++++++++ 2 files changed, 66 insertions(+), 24 deletions(-) delete mode 100644 Documentation/devicetree/bindings/watchdog/dw_wdt.txt create mode 100644 Documentation/devicetree/bindings/watchdog/snps,dw-wdt.yaml diff --git a/Documentation/devicetree/bindings/watchdog/dw_wdt.txt b/Documentation/devicetree/bindings/watchdog/dw_wdt.txt deleted file mode 100644 index eb0914420c7c..000000000000 --- a/Documentation/devicetree/bindings/watchdog/dw_wdt.txt +++ /dev/null @@ -1,24 +0,0 @@ -Synopsys Designware Watchdog Timer - -Required Properties: - -- compatible : Should contain "snps,dw-wdt" -- reg : Base address and size of the watchdog timer registers. -- clocks : phandle + clock-specifier for the clock that drives the - watchdog timer. - -Optional Properties: - -- interrupts : The interrupt used for the watchdog timeout warning. -- resets : phandle pointing to the system reset controller with - line index for the watchdog. - -Example: - - watchdog0: wd@ffd02000 { - compatible = "snps,dw-wdt"; - reg = <0xffd02000 0x1000>; - interrupts = <0 171 4>; - clocks = <&per_base_clk>; - resets = <&rst WDT0_RESET>; - }; diff --git a/Documentation/devicetree/bindings/watchdog/snps,dw-wdt.yaml b/Documentation/devicetree/bindings/watchdog/snps,dw-wdt.yaml new file mode 100644 index 000000000000..8b30f9601c38 --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/snps,dw-wdt.yaml @@ -0,0 +1,66 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (C) 2019 BAIKAL ELECTRONICS, JSC +# +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/watchdog/snps,dw-wdt.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Synopsys Designware Watchdog Timer + +allOf: + - $ref: "watchdog.yaml#" + +maintainers: + - Jamie Iles + +properties: + compatible: + const: snps,dw-wdt + + reg: + maxItems: 1 + + interrupts: + description: DW Watchdog pre-timeout interrupts. + maxItems: 1 + + clocks: + minItems: 1 + items: + - description: Watchdog timer reference clock. + - description: APB3 interface clock. + + clock-names: + minItems: 1 + items: + - const: tclk + - const: pclk + + assigned-clocks: true + + assigned-clock-rates: true + + resets: + description: Phandle to the DW Watchdog reset lane. + maxItems: 1 + +additionalProperties: false + +required: + - compatible + - reg + - interrupts + - clocks + +examples: + - | + watchdog0: watchdog@ffd02000 { + compatible = "snps,dw-wdt"; + reg = <0xffd02000 0x1000>; + interrupts = <0 171 4>; + clocks = <&per_base_clk>; + resets = <&wdt_rst>; + }; +... From patchwork Fri Mar 6 13:27:42 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serge Semin X-Patchwork-Id: 11424031 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id D12B014BC for ; Fri, 6 Mar 2020 13:38:02 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id BD29820848 for ; Fri, 6 Mar 2020 13:38:02 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726090AbgCFNiC (ORCPT ); Fri, 6 Mar 2020 08:38:02 -0500 Received: from mail.baikalelectronics.com ([87.245.175.226]:37304 "EHLO mail.baikalelectronics.ru" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726307AbgCFNiC (ORCPT ); Fri, 6 Mar 2020 08:38:02 -0500 Received: from localhost (unknown [127.0.0.1]) by mail.baikalelectronics.ru (Postfix) with ESMTP id 98D7F80307C2; Fri, 6 Mar 2020 13:28:18 +0000 (UTC) X-Virus-Scanned: amavisd-new at baikalelectronics.ru Received: from mail.baikalelectronics.ru ([127.0.0.1]) by localhost (mail.baikalelectronics.ru [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 00CjeEVMtjHR; Fri, 6 Mar 2020 16:28:18 +0300 (MSK) From: To: Wim Van Sebroeck , Guenter Roeck , Rob Herring , Mark Rutland CC: Serge Semin , Serge Semin , Alexey Malahov , Thomas Bogendoerfer , Paul Burton , Ralf Baechle , , , Subject: [PATCH 2/7] dt-bindings: watchdog: dw-wdt: Add watchdog TOPs array property Date: Fri, 6 Mar 2020 16:27:42 +0300 In-Reply-To: <20200306132747.14701-1-Sergey.Semin@baikalelectronics.ru> References: <20200306132747.14701-1-Sergey.Semin@baikalelectronics.ru> MIME-Version: 1.0 X-ClientProxiedBy: MAIL.baikal.int (192.168.51.25) To mail (192.168.51.25) Message-Id: <20200306132818.98D7F80307C2@mail.baikalelectronics.ru> Sender: linux-watchdog-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-watchdog@vger.kernel.org From: Serge Semin In case if DW Watchdog IP core is built with WDT_USE_FIX_TOP == false, a custom timeout periods are used to preset the timer counter. In this case that periods should be specified in a new "snps,watchdog-tops" property of the DW watchdog dts node. Signed-off-by: Serge Semin Signed-off-by: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Paul Burton Cc: Ralf Baechle Reviewed-by: Rob Herring --- .../bindings/watchdog/snps,dw-wdt.yaml | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/Documentation/devicetree/bindings/watchdog/snps,dw-wdt.yaml b/Documentation/devicetree/bindings/watchdog/snps,dw-wdt.yaml index 8b30f9601c38..1b3b71351628 100644 --- a/Documentation/devicetree/bindings/watchdog/snps,dw-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/snps,dw-wdt.yaml @@ -46,6 +46,21 @@ properties: description: Phandle to the DW Watchdog reset lane. maxItems: 1 + snps,watchdog-tops: + description: | + DW APB Watchdog custom timer intervals - Timeout Period ranges (TOPs). + Each TOP is a number loaded into the watchdog counter at the moment of + the timer restart. The counter decrementing happens each tick of the + reference clock. Therefore the TOPs array is equivalent to an array of + the timer expiration intervals supported by the DW APB Watchdog. Note + DW APB Watchdog IP-cores might be synthesized with fixed TOP values, + in which case this property is unnecessary. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32-array + - items: + minItems: 16 + maxItems: 16 + additionalProperties: false required: @@ -63,4 +78,19 @@ examples: clocks = <&per_base_clk>; resets = <&wdt_rst>; }; + + - | + watchdog1: watchdog@ffd02000 { + compatible = "snps,dw-wdt"; + reg = <0xffd02000 0x1000>; + interrupts = <0 171 4>; + clocks = <&per_base_clk>; + clock-names = "tclk"; + snps,watchdog-tops = <0x000000FF 0x000001FF 0x000003FF + 0x000007FF 0x0000FFFF 0x0001FFFF + 0x0003FFFF 0x0007FFFF 0x000FFFFF + 0x001FFFFF 0x003FFFFF 0x007FFFFF + 0x00FFFFFF 0x01FFFFFF 0x03FFFFFF + 0x07FFFFFF>; + }; ... From patchwork Fri Mar 6 13:27:43 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serge Semin X-Patchwork-Id: 11424043 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id DC98B138D for ; Fri, 6 Mar 2020 13:38:06 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id BC63C2072D for ; Fri, 6 Mar 2020 13:38:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726300AbgCFNiG (ORCPT ); Fri, 6 Mar 2020 08:38:06 -0500 Received: from mail.baikalelectronics.com ([87.245.175.226]:37308 "EHLO mail.baikalelectronics.ru" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726565AbgCFNiD (ORCPT ); Fri, 6 Mar 2020 08:38:03 -0500 Received: from localhost (unknown [127.0.0.1]) by mail.baikalelectronics.ru (Postfix) with ESMTP id D5C5B803087C; Fri, 6 Mar 2020 13:28:23 +0000 (UTC) X-Virus-Scanned: amavisd-new at baikalelectronics.ru Received: from mail.baikalelectronics.ru ([127.0.0.1]) by localhost (mail.baikalelectronics.ru [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id yO8_Hpnu8yKT; Fri, 6 Mar 2020 16:28:23 +0300 (MSK) From: To: Wim Van Sebroeck , Guenter Roeck CC: Serge Semin , Serge Semin , Alexey Malahov , Thomas Bogendoerfer , Paul Burton , Ralf Baechle , , Subject: [PATCH 3/7] watchdog: watchdog_dev: Use generic msec-per-sec macro Date: Fri, 6 Mar 2020 16:27:43 +0300 In-Reply-To: <20200306132747.14701-1-Sergey.Semin@baikalelectronics.ru> References: <20200306132747.14701-1-Sergey.Semin@baikalelectronics.ru> MIME-Version: 1.0 X-ClientProxiedBy: MAIL.baikal.int (192.168.51.25) To mail (192.168.51.25) Message-Id: <20200306132823.D5C5B803087C@mail.baikalelectronics.ru> Sender: linux-watchdog-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-watchdog@vger.kernel.org From: Serge Semin For better readability replace the numeric literals with globally available xSEC_PER_SEC macro. Signed-off-by: Serge Semin Signed-off-by: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Paul Burton Cc: Ralf Baechle --- drivers/watchdog/watchdog_dev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 8b5c742f24e8..a1a3bbe21653 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -99,7 +99,7 @@ static inline bool watchdog_need_worker(struct watchdog_device *wdd) { /* All variables in milli-seconds */ unsigned int hm = wdd->max_hw_heartbeat_ms; - unsigned int t = wdd->timeout * 1000; + unsigned int t = wdd->timeout * MSEC_PER_SEC; /* * A worker to generate heartbeat requests is needed if all of the @@ -121,7 +121,7 @@ static inline bool watchdog_need_worker(struct watchdog_device *wdd) static ktime_t watchdog_next_keepalive(struct watchdog_device *wdd) { struct watchdog_core_data *wd_data = wdd->wd_data; - unsigned int timeout_ms = wdd->timeout * 1000; + unsigned int timeout_ms = wdd->timeout * MSEC_PER_SEC; ktime_t keepalive_interval; ktime_t last_heartbeat, latest_heartbeat; ktime_t virt_timeout; From patchwork Fri Mar 6 13:27:44 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serge Semin X-Patchwork-Id: 11424041 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 0DBD0138D for ; Fri, 6 Mar 2020 13:38:06 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id EDDAE2146E for ; Fri, 6 Mar 2020 13:38:05 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726579AbgCFNiF (ORCPT ); Fri, 6 Mar 2020 08:38:05 -0500 Received: from mail.baikalelectronics.com ([87.245.175.226]:37306 "EHLO mail.baikalelectronics.ru" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726490AbgCFNiF (ORCPT ); Fri, 6 Mar 2020 08:38:05 -0500 Received: from localhost (unknown [127.0.0.1]) by mail.baikalelectronics.ru (Postfix) with ESMTP id E508B8030705; Fri, 6 Mar 2020 13:28:29 +0000 (UTC) X-Virus-Scanned: amavisd-new at baikalelectronics.ru Received: from mail.baikalelectronics.ru ([127.0.0.1]) by localhost (mail.baikalelectronics.ru [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id SB3i3KTZAyDK; Fri, 6 Mar 2020 16:28:29 +0300 (MSK) From: To: Wim Van Sebroeck , Guenter Roeck CC: Serge Semin , Serge Semin , Alexey Malahov , Thomas Bogendoerfer , Paul Burton , Ralf Baechle , , Subject: [PATCH 4/7] watchdog: dw_wdt: Support devices with non-fixed TOP values Date: Fri, 6 Mar 2020 16:27:44 +0300 In-Reply-To: <20200306132747.14701-1-Sergey.Semin@baikalelectronics.ru> References: <20200306132747.14701-1-Sergey.Semin@baikalelectronics.ru> MIME-Version: 1.0 X-ClientProxiedBy: MAIL.baikal.int (192.168.51.25) To mail (192.168.51.25) Message-Id: <20200306132829.E508B8030705@mail.baikalelectronics.ru> Sender: linux-watchdog-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-watchdog@vger.kernel.org From: Serge Semin In case if the DW Watchdog IP core is synthesised with WDT_USE_FIX_TOP == false, the TOP interval indexes make the device to load a custom periods to the counter. These periods are hardwired at the synthesis stage and can be within [2^8, 2^(WDT_CNT_WIDTH - 1)]. Alas their values can't be detected at runtime and must be somehow supplied to the driver so one could properly determine the watchdog timeout intervals. For this purpose we suggest to have a vendor- specific dts property "snps,watchdog-tops" utilized, which would provide an array of sixteen counter values. At device probe stage they will be used to initialize the watchdog device timeouts determined from the array values and current clocks source rate. In order to have custom TOP values supported the driver must be altered in the following way. First of all the fixed-top values ready-to-use array must be determined for compatibility with currently supported devices, which were synthesised with WDT_USE_FIX_TOP == true. Secondly we must redefine the timer period search functions. For generality they are redesigned in a way to support the TOP array with no limitations on the items order or value. Finally an array with pre-defined timeouts must be calculated at probe stage from either the custom or fixed TOP values depending on the DW watchdog component parameter WDT_USE_FIX_TOP value. Signed-off-by: Serge Semin Signed-off-by: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Paul Burton Cc: Ralf Baechle --- drivers/watchdog/dw_wdt.c | 145 +++++++++++++++++++++++++++++++------- 1 file changed, 119 insertions(+), 26 deletions(-) diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index fba21de2bbad..4a57b7d777dc 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -13,6 +13,7 @@ */ #include +#include #include #include #include @@ -34,12 +35,24 @@ #define WDOG_CURRENT_COUNT_REG_OFFSET 0x08 #define WDOG_COUNTER_RESTART_REG_OFFSET 0x0c #define WDOG_COUNTER_RESTART_KICK_VALUE 0x76 +#define WDOG_COMP_PARAMS_1_REG_OFFSET 0xf4 +#define WDOG_COMP_PARAMS_1_USE_FIX_TOP BIT(6) -/* The maximum TOP (timeout period) value that can be set in the watchdog. */ -#define DW_WDT_MAX_TOP 15 +/* There are sixteen TOPs (timeout periods) that can be set in the watchdog. */ +#define DW_WDT_NUM_TOPS 16 +#define DW_WDT_FIX_TOP(_idx) (1U << (16 + _idx)) #define DW_WDT_DEFAULT_SECONDS 30 +static const u32 dw_wdt_fix_tops[DW_WDT_NUM_TOPS] = { + DW_WDT_FIX_TOP(0), DW_WDT_FIX_TOP(1), DW_WDT_FIX_TOP(2), + DW_WDT_FIX_TOP(3), DW_WDT_FIX_TOP(4), DW_WDT_FIX_TOP(5), + DW_WDT_FIX_TOP(6), DW_WDT_FIX_TOP(7), DW_WDT_FIX_TOP(8), + DW_WDT_FIX_TOP(9), DW_WDT_FIX_TOP(10), DW_WDT_FIX_TOP(11), + DW_WDT_FIX_TOP(12), DW_WDT_FIX_TOP(13), DW_WDT_FIX_TOP(14), + DW_WDT_FIX_TOP(15) +}; + static bool nowayout = WATCHDOG_NOWAYOUT; module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " @@ -49,6 +62,8 @@ struct dw_wdt { void __iomem *regs; struct clk *clk; unsigned long rate; + unsigned int max_top; + unsigned int timeouts[DW_WDT_NUM_TOPS]; struct watchdog_device wdd; struct reset_control *rst; /* Save/restore */ @@ -64,20 +79,68 @@ static inline int dw_wdt_is_enabled(struct dw_wdt *dw_wdt) WDOG_CONTROL_REG_WDT_EN_MASK; } -static inline int dw_wdt_top_in_seconds(struct dw_wdt *dw_wdt, unsigned top) +static unsigned int dw_wdt_find_best_top(struct dw_wdt *dw_wdt, + unsigned int timeout, u32 *top) { + u32 diff = UINT_MAX, tmp; + int idx; + /* - * There are 16 possible timeout values in 0..15 where the number of - * cycles is 2 ^ (16 + i) and the watchdog counts down. + * In general case of non-fixed timeout values they can be arranged in + * any order so we have to traverse all the array values. We also try + * to find a closest timeout number and make sure its value is greater + * than the requested timeout. Note we'll return a maximum timeout + * if reachable value couldn't be found. */ - return (1U << (16 + top)) / dw_wdt->rate; + for (*top = dw_wdt->max_top, idx = 0; idx < DW_WDT_NUM_TOPS; ++idx) { + if (dw_wdt->timeouts[idx] < timeout) + continue; + + tmp = dw_wdt->timeouts[idx] - timeout; + if (tmp < diff) { + diff = tmp; + *top = idx; + } + } + + return dw_wdt->timeouts[*top]; +} + +static unsigned int dw_wdt_find_min_timeout(struct dw_wdt *dw_wdt) +{ + u32 min_timeout = UINT_MAX, top; + int idx; + + for (top = 0, idx = 0; idx < DW_WDT_NUM_TOPS; ++idx) { + if (dw_wdt->timeouts[idx] <= min_timeout) { + min_timeout = dw_wdt->timeouts[idx]; + top = idx; + } + } + + return dw_wdt->timeouts[top]; +} + +static unsigned int dw_wdt_find_max_top(struct dw_wdt *dw_wdt, u32 *top) +{ + u32 max_timeout = 0; + int idx; + + for (*top = 0, idx = 0; idx < DW_WDT_NUM_TOPS; ++idx) { + if (dw_wdt->timeouts[idx] >= max_timeout) { + max_timeout = dw_wdt->timeouts[idx]; + *top = idx; + } + } + + return dw_wdt->timeouts[*top]; } -static int dw_wdt_get_top(struct dw_wdt *dw_wdt) +static unsigned int dw_wdt_get_timeout(struct dw_wdt *dw_wdt) { int top = readl(dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET) & 0xF; - return dw_wdt_top_in_seconds(dw_wdt, top); + return dw_wdt->timeouts[top]; } static int dw_wdt_ping(struct watchdog_device *wdd) @@ -90,20 +153,13 @@ static int dw_wdt_ping(struct watchdog_device *wdd) return 0; } -static int dw_wdt_set_timeout(struct watchdog_device *wdd, unsigned int top_s) +static int dw_wdt_set_timeout(struct watchdog_device *wdd, unsigned int req) { struct dw_wdt *dw_wdt = to_dw_wdt(wdd); - int i, top_val = DW_WDT_MAX_TOP; + unsigned int timeout; + u32 top; - /* - * Iterate over the timeout values until we find the closest match. We - * always look for >=. - */ - for (i = 0; i <= DW_WDT_MAX_TOP; ++i) - if (dw_wdt_top_in_seconds(dw_wdt, i) >= top_s) { - top_val = i; - break; - } + timeout = dw_wdt_find_best_top(dw_wdt, req * MSEC_PER_SEC, &top); /* * Set the new value in the watchdog. Some versions of dw_wdt @@ -111,7 +167,7 @@ static int dw_wdt_set_timeout(struct watchdog_device *wdd, unsigned int top_s) * CP_WDT_DUAL_TOP in WDT_COMP_PARAMS_1). On those we * effectively get a pat of the watchdog right here. */ - writel(top_val | top_val << WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT, + writel(top | top << WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT, dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); /* @@ -119,10 +175,10 @@ static int dw_wdt_set_timeout(struct watchdog_device *wdd, unsigned int top_s) * kernel(watchdog_dev.c) helps to feed watchdog before * wdd->max_hw_heartbeat_ms */ - if (top_s * 1000 <= wdd->max_hw_heartbeat_ms) - wdd->timeout = dw_wdt_top_in_seconds(dw_wdt, top_val); + if (req * MSEC_PER_SEC > wdd->max_hw_heartbeat_ms) + wdd->timeout = req; else - wdd->timeout = top_s; + wdd->timeout = timeout / MSEC_PER_SEC; return 0; } @@ -238,6 +294,41 @@ static int dw_wdt_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(dw_wdt_pm_ops, dw_wdt_suspend, dw_wdt_resume); +static void dw_wdt_init_timeouts(struct dw_wdt *dw_wdt, struct device *dev) +{ + u32 data, of_tops[DW_WDT_NUM_TOPS]; + const u32 *tops; + int ret, idx; + + /* + * Retrieve custom or fixed counter values depending on the + * WDT_USE_FIX_TOP flag found in the component specific parameters + * #1 register. + */ + data = readl(dw_wdt->regs + WDOG_COMP_PARAMS_1_REG_OFFSET); + if (data & WDOG_COMP_PARAMS_1_USE_FIX_TOP) { + tops = dw_wdt_fix_tops; + } else { + ret = of_property_read_variable_u32_array(dev_of_node(dev), + "snps,watchdog-tops", of_tops, DW_WDT_NUM_TOPS, + DW_WDT_NUM_TOPS); + if (ret < 0) { + dev_warn(dev, "No valid TOPs array specified\n"); + tops = dw_wdt_fix_tops; + } else { + tops = of_tops; + } + } + + /* + * We'll keep the timeout values in ms to approximate requested + * timeouts with better accuracy. + */ + for (idx = 0; idx < DW_WDT_NUM_TOPS; ++idx) + dw_wdt->timeouts[idx] = + mult_frac(tops[idx], MSEC_PER_SEC, dw_wdt->rate); +} + static int dw_wdt_drv_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -275,12 +366,14 @@ static int dw_wdt_drv_probe(struct platform_device *pdev) reset_control_deassert(dw_wdt->rst); + dw_wdt_init_timeouts(dw_wdt, dev); + wdd = &dw_wdt->wdd; wdd->info = &dw_wdt_ident; wdd->ops = &dw_wdt_ops; - wdd->min_timeout = 1; + wdd->min_timeout = dw_wdt_find_min_timeout(dw_wdt) / MSEC_PER_SEC; wdd->max_hw_heartbeat_ms = - dw_wdt_top_in_seconds(dw_wdt, DW_WDT_MAX_TOP) * 1000; + dw_wdt_find_max_top(dw_wdt, &dw_wdt->max_top); wdd->parent = dev; watchdog_set_drvdata(wdd, dw_wdt); @@ -293,7 +386,7 @@ static int dw_wdt_drv_probe(struct platform_device *pdev) * devicetree. */ if (dw_wdt_is_enabled(dw_wdt)) { - wdd->timeout = dw_wdt_get_top(dw_wdt); + wdd->timeout = dw_wdt_get_timeout(dw_wdt) / MSEC_PER_SEC; set_bit(WDOG_HW_RUNNING, &wdd->status); } else { wdd->timeout = DW_WDT_DEFAULT_SECONDS; From patchwork Fri Mar 6 13:27:45 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serge Semin X-Patchwork-Id: 11424039 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id E5CEB18B8 for ; Fri, 6 Mar 2020 13:38:05 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id C735F20866 for ; Fri, 6 Mar 2020 13:38:05 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726861AbgCFNiF (ORCPT ); Fri, 6 Mar 2020 08:38:05 -0500 Received: from mail.baikalelectronics.com ([87.245.175.226]:37310 "EHLO mail.baikalelectronics.ru" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726579AbgCFNiF (ORCPT ); Fri, 6 Mar 2020 08:38:05 -0500 Received: from localhost (unknown [127.0.0.1]) by mail.baikalelectronics.ru (Postfix) with ESMTP id 89B658030706; Fri, 6 Mar 2020 13:28:31 +0000 (UTC) X-Virus-Scanned: amavisd-new at baikalelectronics.ru Received: from mail.baikalelectronics.ru ([127.0.0.1]) by localhost (mail.baikalelectronics.ru [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id jje8UqOvDMf3; Fri, 6 Mar 2020 16:28:30 +0300 (MSK) From: To: Wim Van Sebroeck , Guenter Roeck CC: Serge Semin , Serge Semin , Alexey Malahov , Thomas Bogendoerfer , Paul Burton , Ralf Baechle , , Subject: [PATCH 5/7] watchdog: dw_wdt: Support devices with asynch clocks Date: Fri, 6 Mar 2020 16:27:45 +0300 In-Reply-To: <20200306132747.14701-1-Sergey.Semin@baikalelectronics.ru> References: <20200306132747.14701-1-Sergey.Semin@baikalelectronics.ru> MIME-Version: 1.0 X-ClientProxiedBy: MAIL.baikal.int (192.168.51.25) To mail (192.168.51.25) Message-Id: <20200306132831.89B658030706@mail.baikalelectronics.ru> Sender: linux-watchdog-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-watchdog@vger.kernel.org From: Serge Semin DW Watchdog IP core can be synthesised with asynchronous timer/APB clocks support (WDT_ASYNC_CLK_MODE_ENABLE == 1). In this case separate clock signals are supposed to be used to feed watchdog timer and APB interface of the device. Currently the driver supports the synchronous mode only. Since there is no way to determine which mode was actually activated for device from its registers, we have to rely on the platform device configuration data. If optional "pclk" clock source is supplied, we consider the device working in asynchronous mode, otherwise the driver falls back to the synchronous configuration. Signed-off-by: Serge Semin Signed-off-by: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Paul Burton Cc: Ralf Baechle --- drivers/watchdog/dw_wdt.c | 48 +++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index 4a57b7d777dc..eb909c63a1b5 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -61,6 +61,7 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " struct dw_wdt { void __iomem *regs; struct clk *clk; + struct clk *pclk; unsigned long rate; unsigned int max_top; unsigned int timeouts[DW_WDT_NUM_TOPS]; @@ -270,6 +271,7 @@ static int dw_wdt_suspend(struct device *dev) dw_wdt->control = readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); dw_wdt->timeout = readl(dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); + clk_disable_unprepare(dw_wdt->pclk); clk_disable_unprepare(dw_wdt->clk); return 0; @@ -283,6 +285,12 @@ static int dw_wdt_resume(struct device *dev) if (err) return err; + err = clk_prepare_enable(dw_wdt->pclk); + if (err) { + clk_disable_unprepare(dw_wdt->clk); + return err; + } + writel(dw_wdt->timeout, dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); writel(dw_wdt->control, dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); @@ -344,9 +352,18 @@ static int dw_wdt_drv_probe(struct platform_device *pdev) if (IS_ERR(dw_wdt->regs)) return PTR_ERR(dw_wdt->regs); - dw_wdt->clk = devm_clk_get(dev, NULL); - if (IS_ERR(dw_wdt->clk)) - return PTR_ERR(dw_wdt->clk); + /* + * Try to request the watchdog dedicated timer clock source. It must + * be supplied if asynchronous mode is enabled. Otherwise fallback + * to the common timer/bus clocks configuration, in which the very first + * found clocks supply both timer and APB signals. + */ + dw_wdt->clk = devm_clk_get(dev, "tclk"); + if (IS_ERR(dw_wdt->clk)) { + dw_wdt->clk = devm_clk_get(dev, NULL); + if (IS_ERR(dw_wdt->clk)) + return PTR_ERR(dw_wdt->clk); + } ret = clk_prepare_enable(dw_wdt->clk); if (ret) @@ -358,10 +375,27 @@ static int dw_wdt_drv_probe(struct platform_device *pdev) goto out_disable_clk; } + /* + * Request APB clocks if device is configured with async clocks mode. + * In this case both tclk and pclk clocks are supposed to be specified. + * Alas we can't know for sure whether async mode was really activated, + * so the pclk reference is left optional. If it it's failed to be + * found we consider the device configured in synchronous clocks mode. + */ + dw_wdt->pclk = devm_clk_get_optional(dev, "pclk"); + if (IS_ERR(dw_wdt->pclk)) { + ret = PTR_ERR(dw_wdt->pclk); + goto out_disable_clk; + } + + ret = clk_prepare_enable(dw_wdt->pclk); + if (ret) + goto out_disable_clk; + dw_wdt->rst = devm_reset_control_get_optional_shared(&pdev->dev, NULL); if (IS_ERR(dw_wdt->rst)) { ret = PTR_ERR(dw_wdt->rst); - goto out_disable_clk; + goto out_disable_pclk; } reset_control_deassert(dw_wdt->rst); @@ -399,10 +433,13 @@ static int dw_wdt_drv_probe(struct platform_device *pdev) ret = watchdog_register_device(wdd); if (ret) - goto out_disable_clk; + goto out_disable_pclk; return 0; +out_disable_pclk: + clk_disable_unprepare(dw_wdt->pclk); + out_disable_clk: clk_disable_unprepare(dw_wdt->clk); return ret; @@ -414,6 +451,7 @@ static int dw_wdt_drv_remove(struct platform_device *pdev) watchdog_unregister_device(&dw_wdt->wdd); reset_control_assert(dw_wdt->rst); + clk_disable_unprepare(dw_wdt->pclk); clk_disable_unprepare(dw_wdt->clk); return 0; From patchwork Fri Mar 6 13:27:46 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serge Semin X-Patchwork-Id: 11424037 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 87BB492A for ; Fri, 6 Mar 2020 13:38:05 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 61DB821D56 for ; Fri, 6 Mar 2020 13:38:05 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726650AbgCFNiE (ORCPT ); Fri, 6 Mar 2020 08:38:04 -0500 Received: from mail.baikalelectronics.com ([87.245.175.226]:37302 "EHLO mail.baikalelectronics.ru" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726300AbgCFNiE (ORCPT ); Fri, 6 Mar 2020 08:38:04 -0500 Received: from localhost (unknown [127.0.0.1]) by mail.baikalelectronics.ru (Postfix) with ESMTP id 490C68030707; Fri, 6 Mar 2020 13:28:34 +0000 (UTC) X-Virus-Scanned: amavisd-new at baikalelectronics.ru Received: from mail.baikalelectronics.ru ([127.0.0.1]) by localhost (mail.baikalelectronics.ru [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id AsiVKpajiOEy; Fri, 6 Mar 2020 16:28:33 +0300 (MSK) From: To: Wim Van Sebroeck , Guenter Roeck CC: Serge Semin , Serge Semin , Alexey Malahov , Thomas Bogendoerfer , Paul Burton , Ralf Baechle , , Subject: [PATCH 6/7] watchdog: dw_wdt: Add pre-timeouts support Date: Fri, 6 Mar 2020 16:27:46 +0300 In-Reply-To: <20200306132747.14701-1-Sergey.Semin@baikalelectronics.ru> References: <20200306132747.14701-1-Sergey.Semin@baikalelectronics.ru> MIME-Version: 1.0 X-ClientProxiedBy: MAIL.baikal.int (192.168.51.25) To mail (192.168.51.25) Message-Id: <20200306132834.490C68030707@mail.baikalelectronics.ru> Sender: linux-watchdog-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-watchdog@vger.kernel.org From: Serge Semin DW Watchdog can rise an interrupt in case if IRQ request mode is enabled and timer reaches the zero value. In this case the IRQ lane is left pending until either the next watchdog kick event (watchdog restart) or until the WDT_EOI register is read or the device/system reset. This interface can be used to implement the pre-timeout functionality optionally provided by the Linux kernel watchdog devices. IRQ mode provides a two stages timeout interface. It means the IRQ is raised when the counter reaches zero, while the system reset occurs only after subsequent timeout if the timer restart is not performed. Due to this peculiarity the pre-timeout value is actually set to the achieved hardware timeout, while the real watchdog timeout is considered to be twice as much of it. This applies a significant limitation on the pre-timeout values, so current implementation supports either zero value, which disables the pre-timeout events, or non-zero values, which imply the pre-timeout to be at least half of the current watchdog timeout. Note that we ask the interrupt controller to detect the rising-edge pre-timeout interrupts to prevent the high-level-IRQs flood, since if the pre-timeout happens, the IRQ lane will be left pending until it's cleared by the timer restart. Seeing all currently supported platforms, which have the DW Watchdog installed, provide the interrupt property in the corresponding watchdog dts node, we can define the IRQ to be mandatory. Signed-off-by: Serge Semin Signed-off-by: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Paul Burton Cc: Ralf Baechle --- drivers/watchdog/dw_wdt.c | 125 +++++++++++++++++++++++++++++++++++--- 1 file changed, 117 insertions(+), 8 deletions(-) diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index eb909c63a1b5..3000120f7e39 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,8 @@ #define WDOG_CURRENT_COUNT_REG_OFFSET 0x08 #define WDOG_COUNTER_RESTART_REG_OFFSET 0x0c #define WDOG_COUNTER_RESTART_KICK_VALUE 0x76 +#define WDOG_INTERRUPT_STATUS_REG_OFFSET 0x10 +#define WDOG_INTERRUPT_CLEAR_REG_OFFSET 0x14 #define WDOG_COMP_PARAMS_1_REG_OFFSET 0xf4 #define WDOG_COMP_PARAMS_1_USE_FIX_TOP BIT(6) @@ -58,11 +61,17 @@ module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); +enum dw_wdt_rmod { + DW_WDT_RMOD_RESET = 1, + DW_WDT_RMOD_IRQ = 2 +}; + struct dw_wdt { void __iomem *regs; struct clk *clk; struct clk *pclk; unsigned long rate; + enum dw_wdt_rmod rmod; unsigned int max_top; unsigned int timeouts[DW_WDT_NUM_TOPS]; struct watchdog_device wdd; @@ -80,6 +89,20 @@ static inline int dw_wdt_is_enabled(struct dw_wdt *dw_wdt) WDOG_CONTROL_REG_WDT_EN_MASK; } +static void dw_wdt_update_mode(struct dw_wdt *dw_wdt, enum dw_wdt_rmod rmod) +{ + u32 val; + + val = readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); + if (rmod == DW_WDT_RMOD_IRQ) + val |= WDOG_CONTROL_REG_RESP_MODE_MASK; + else + val &= ~WDOG_CONTROL_REG_RESP_MODE_MASK; + writel(val, dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); + + dw_wdt->rmod = rmod; +} + static unsigned int dw_wdt_find_best_top(struct dw_wdt *dw_wdt, unsigned int timeout, u32 *top) { @@ -141,7 +164,11 @@ static unsigned int dw_wdt_get_timeout(struct dw_wdt *dw_wdt) { int top = readl(dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET) & 0xF; - return dw_wdt->timeouts[top]; + /* + * In IRQ mode due to the two stages counter, the actual timeout is + * twice greater than the TOP setting. + */ + return (dw_wdt->timeouts[top] * dw_wdt->rmod); } static int dw_wdt_ping(struct watchdog_device *wdd) @@ -160,7 +187,21 @@ static int dw_wdt_set_timeout(struct watchdog_device *wdd, unsigned int req) unsigned int timeout; u32 top; - timeout = dw_wdt_find_best_top(dw_wdt, req * MSEC_PER_SEC, &top); + /* + * We try to find a timeout achievable by the device or set the maximum + * one. Note IRQ mode being enabled means having a non-zero pre-timeout + * setup. In this case we try to find a TOP as close to the half of the + * requested timeout as possible since DW Watchdog IRQ mode is designed + * in two stages way - first timeout rises the pre-timeout interrupt, + * second timeout performs the system reset. + */ + timeout = dw_wdt_find_best_top(dw_wdt, + req * (MSEC_PER_SEC / dw_wdt->rmod), &top); + timeout = (timeout * dw_wdt->rmod) / MSEC_PER_SEC; + if (dw_wdt->rmod == DW_WDT_RMOD_IRQ) + wdd->pretimeout = timeout / dw_wdt->rmod; + else + wdd->pretimeout = 0; /* * Set the new value in the watchdog. Some versions of dw_wdt @@ -171,6 +212,10 @@ static int dw_wdt_set_timeout(struct watchdog_device *wdd, unsigned int req) writel(top | top << WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT, dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); + /* Kick new TOP value into the watchdog counter if activated. */ + if (watchdog_active(wdd)) + dw_wdt_ping(wdd); + /* * In case users set bigger timeout value than HW can support, * kernel(watchdog_dev.c) helps to feed watchdog before @@ -179,7 +224,22 @@ static int dw_wdt_set_timeout(struct watchdog_device *wdd, unsigned int req) if (req * MSEC_PER_SEC > wdd->max_hw_heartbeat_ms) wdd->timeout = req; else - wdd->timeout = timeout / MSEC_PER_SEC; + wdd->timeout = timeout; + + return 0; +} + +static int dw_wdt_set_pretimeout(struct watchdog_device *wdd, unsigned int req) +{ + struct dw_wdt *dw_wdt = to_dw_wdt(wdd); + + /* + * We ignore actual value of the timeout passed from user-space + * using it as a flag whether the pretimeout functionality is intended + * to be activated. + */ + dw_wdt_update_mode(dw_wdt, req ? DW_WDT_RMOD_IRQ : DW_WDT_RMOD_RESET); + dw_wdt_set_timeout(wdd, wdd->timeout); return 0; } @@ -188,8 +248,11 @@ static void dw_wdt_arm_system_reset(struct dw_wdt *dw_wdt) { u32 val = readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); - /* Disable interrupt mode; always perform system reset. */ - val &= ~WDOG_CONTROL_REG_RESP_MODE_MASK; + /* Disable/enable interrupt mode depending on the RMOD flag. */ + if (dw_wdt->rmod == DW_WDT_RMOD_IRQ) + val |= WDOG_CONTROL_REG_RESP_MODE_MASK; + else + val &= ~WDOG_CONTROL_REG_RESP_MODE_MASK; /* Enable watchdog. */ val |= WDOG_CONTROL_REG_WDT_EN_MASK; writel(val, dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); @@ -227,6 +290,7 @@ static int dw_wdt_restart(struct watchdog_device *wdd, struct dw_wdt *dw_wdt = to_dw_wdt(wdd); writel(0, dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); + dw_wdt_update_mode(dw_wdt, DW_WDT_RMOD_RESET); if (dw_wdt_is_enabled(dw_wdt)) writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt->regs + WDOG_COUNTER_RESTART_REG_OFFSET); @@ -242,14 +306,24 @@ static int dw_wdt_restart(struct watchdog_device *wdd, static unsigned int dw_wdt_get_timeleft(struct watchdog_device *wdd) { struct dw_wdt *dw_wdt = to_dw_wdt(wdd); + unsigned int time; + u32 val; + + val = readl(dw_wdt->regs + WDOG_CURRENT_COUNT_REG_OFFSET); + time = val / dw_wdt->rate; + + if (dw_wdt->rmod == DW_WDT_RMOD_IRQ) { + val = readl(dw_wdt->regs + WDOG_INTERRUPT_STATUS_REG_OFFSET); + if (!val) + time += wdd->pretimeout; + } - return readl(dw_wdt->regs + WDOG_CURRENT_COUNT_REG_OFFSET) / - dw_wdt->rate; + return time; } static const struct watchdog_info dw_wdt_ident = { .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | - WDIOF_MAGICCLOSE, + WDIOF_PRETIMEOUT | WDIOF_MAGICCLOSE, .identity = "Synopsys DesignWare Watchdog", }; @@ -259,10 +333,29 @@ static const struct watchdog_ops dw_wdt_ops = { .stop = dw_wdt_stop, .ping = dw_wdt_ping, .set_timeout = dw_wdt_set_timeout, + .set_pretimeout = dw_wdt_set_pretimeout, .get_timeleft = dw_wdt_get_timeleft, .restart = dw_wdt_restart, }; +static irqreturn_t dw_wdt_irq(int irq, void *devid) +{ + struct dw_wdt *dw_wdt = devid; + u32 val; + + /* + * We don't clear the IRQ status. It's supposed to be done by following + * ping operations. + */ + val = readl(dw_wdt->regs + WDOG_INTERRUPT_STATUS_REG_OFFSET); + if (!val) + return IRQ_NONE; + + watchdog_notify_pretimeout(&dw_wdt->wdd); + + return IRQ_HANDLED; +} + #ifdef CONFIG_PM_SLEEP static int dw_wdt_suspend(struct device *dev) { @@ -398,10 +491,26 @@ static int dw_wdt_drv_probe(struct platform_device *pdev) goto out_disable_pclk; } + ret = platform_get_irq(pdev, 0); + if (ret < 0) + goto out_disable_pclk; + + /* + * We must request rising-edge IRQ, since the lane is left pending + * either until the next watchdog kick event or up to the system reset. + */ + ret = devm_request_irq(dev, ret, dw_wdt_irq, + IRQF_SHARED | IRQF_TRIGGER_RISING, + pdev->name, dw_wdt); + if (ret) + goto out_disable_pclk; + reset_control_deassert(dw_wdt->rst); dw_wdt_init_timeouts(dw_wdt, dev); + dw_wdt_update_mode(dw_wdt, DW_WDT_RMOD_RESET); + wdd = &dw_wdt->wdd; wdd->info = &dw_wdt_ident; wdd->ops = &dw_wdt_ops; From patchwork Fri Mar 6 13:27:47 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serge Semin X-Patchwork-Id: 11424035 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id B4A7B18B8 for ; Fri, 6 Mar 2020 13:38:03 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 9FECF20848 for ; Fri, 6 Mar 2020 13:38:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726642AbgCFNiD (ORCPT ); Fri, 6 Mar 2020 08:38:03 -0500 Received: from mail.baikalelectronics.com ([87.245.175.226]:37300 "EHLO mail.baikalelectronics.ru" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726231AbgCFNiD (ORCPT ); Fri, 6 Mar 2020 08:38:03 -0500 Received: from localhost (unknown [127.0.0.1]) by mail.baikalelectronics.ru (Postfix) with ESMTP id 763718030786; Fri, 6 Mar 2020 13:28:36 +0000 (UTC) X-Virus-Scanned: amavisd-new at baikalelectronics.ru Received: from mail.baikalelectronics.ru ([127.0.0.1]) by localhost (mail.baikalelectronics.ru [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id WApw7rWc6R4s; Fri, 6 Mar 2020 16:28:35 +0300 (MSK) From: To: Wim Van Sebroeck , Guenter Roeck CC: Serge Semin , Serge Semin , Alexey Malahov , Thomas Bogendoerfer , Paul Burton , Ralf Baechle , , Subject: [PATCH 7/7] watchdog: dw_wdt: Add DebugFS files Date: Fri, 6 Mar 2020 16:27:47 +0300 In-Reply-To: <20200306132747.14701-1-Sergey.Semin@baikalelectronics.ru> References: <20200306132747.14701-1-Sergey.Semin@baikalelectronics.ru> MIME-Version: 1.0 X-ClientProxiedBy: MAIL.baikal.int (192.168.51.25) To mail (192.168.51.25) Message-Id: <20200306132836.763718030786@mail.baikalelectronics.ru> Sender: linux-watchdog-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-watchdog@vger.kernel.org From: Serge Semin For the sake of the easier device-driver debug procedure, we added a few DebugFS files with the next content: watchdog timeout, watchdog pre-timeout, watchdog ping/kick operation, watchdog start/stop, device registers dump. They are available only if kernel is configured with DebugFS support. Signed-off-by: Serge Semin Signed-off-by: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Paul Burton Cc: Ralf Baechle --- drivers/watchdog/dw_wdt.c | 150 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index 3000120f7e39..9353d83f3e45 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -27,6 +27,7 @@ #include #include #include +#include #define WDOG_CONTROL_REG_OFFSET 0x00 #define WDOG_CONTROL_REG_WDT_EN_MASK 0x01 @@ -38,8 +39,14 @@ #define WDOG_COUNTER_RESTART_KICK_VALUE 0x76 #define WDOG_INTERRUPT_STATUS_REG_OFFSET 0x10 #define WDOG_INTERRUPT_CLEAR_REG_OFFSET 0x14 +#define WDOG_COMP_PARAMS_5_REG_OFFSET 0xe4 +#define WDOG_COMP_PARAMS_4_REG_OFFSET 0xe8 +#define WDOG_COMP_PARAMS_3_REG_OFFSET 0xec +#define WDOG_COMP_PARAMS_2_REG_OFFSET 0xf0 #define WDOG_COMP_PARAMS_1_REG_OFFSET 0xf4 #define WDOG_COMP_PARAMS_1_USE_FIX_TOP BIT(6) +#define WDOG_COMP_VERSION_REG_OFFSET 0xf8 +#define WDOG_COMP_TYPE_REG_OFFSET 0xfc /* There are sixteen TOPs (timeout periods) that can be set in the watchdog. */ #define DW_WDT_NUM_TOPS 16 @@ -79,6 +86,10 @@ struct dw_wdt { /* Save/restore */ u32 control; u32 timeout; + +#ifdef CONFIG_DEBUG_FS + struct dentry *dbgfs_dir; +#endif }; #define to_dw_wdt(wdd) container_of(wdd, struct dw_wdt, wdd) @@ -430,6 +441,141 @@ static void dw_wdt_init_timeouts(struct dw_wdt *dw_wdt, struct device *dev) mult_frac(tops[idx], MSEC_PER_SEC, dw_wdt->rate); } +#ifdef CONFIG_DEBUG_FS + +static int dw_wdt_dbgfs_timeout_get(void *priv, u64 *val) +{ + struct dw_wdt *dw_wdt = priv; + + *val = dw_wdt_get_timeout(dw_wdt) / MSEC_PER_SEC; + + return 0; +} + +static int dw_wdt_dbgfs_timeout_set(void *priv, u64 val) +{ + struct dw_wdt *dw_wdt = priv; + + return dw_wdt_set_timeout(&dw_wdt->wdd, val); +} +DEFINE_DEBUGFS_ATTRIBUTE(dw_wdt_dbgfs_timeout_fops, + dw_wdt_dbgfs_timeout_get, dw_wdt_dbgfs_timeout_set, "%llu\n"); + +static int dw_wdt_dbgfs_pretimeout_get(void *priv, u64 *val) +{ + struct dw_wdt *dw_wdt = priv; + + *val = dw_wdt->wdd.pretimeout; + + return 0; +} + +static int dw_wdt_dbgfs_pretimeout_set(void *priv, u64 val) +{ + struct dw_wdt *dw_wdt = priv; + + return dw_wdt_set_pretimeout(&dw_wdt->wdd, val); +} +DEFINE_DEBUGFS_ATTRIBUTE(dw_wdt_dbgfs_pretimeout_fops, + dw_wdt_dbgfs_pretimeout_get, dw_wdt_dbgfs_pretimeout_set, "%llu\n"); + +static int dw_wdt_dbgfs_ping_set(void *priv, u64 val) +{ + struct dw_wdt *dw_wdt = priv; + + dw_wdt_ping(&dw_wdt->wdd); + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(dw_wdt_dbgfs_ping_fops, + NULL, dw_wdt_dbgfs_ping_set, "%llu\n"); + +static int dw_wdt_dbgfs_en_get(void *priv, u64 *val) +{ + struct dw_wdt *dw_wdt = priv; + + *val = !!dw_wdt_is_enabled(dw_wdt); + + return 0; +} + +static int dw_wdt_dbgfs_en_set(void *priv, u64 val) +{ + struct dw_wdt *dw_wdt = priv; + + if (val) + dw_wdt_start(&dw_wdt->wdd); + else + dw_wdt_stop(&dw_wdt->wdd); + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(dw_wdt_dbgfs_en_fops, + dw_wdt_dbgfs_en_get, dw_wdt_dbgfs_en_set, "%llu\n"); + +#define DW_WDT_DBGFS_REG(_name, _off) \ +{ \ + .name = _name, \ + .offset = _off \ +} + +static const struct debugfs_reg32 dw_wdt_dbgfs_regs[] = { + DW_WDT_DBGFS_REG("cr", WDOG_CONTROL_REG_OFFSET), + DW_WDT_DBGFS_REG("torr", WDOG_TIMEOUT_RANGE_REG_OFFSET), + DW_WDT_DBGFS_REG("ccvr", WDOG_CURRENT_COUNT_REG_OFFSET), + DW_WDT_DBGFS_REG("crr", WDOG_COUNTER_RESTART_REG_OFFSET), + DW_WDT_DBGFS_REG("stat", WDOG_INTERRUPT_STATUS_REG_OFFSET), + DW_WDT_DBGFS_REG("param5", WDOG_COMP_PARAMS_5_REG_OFFSET), + DW_WDT_DBGFS_REG("param4", WDOG_COMP_PARAMS_4_REG_OFFSET), + DW_WDT_DBGFS_REG("param3", WDOG_COMP_PARAMS_3_REG_OFFSET), + DW_WDT_DBGFS_REG("param2", WDOG_COMP_PARAMS_2_REG_OFFSET), + DW_WDT_DBGFS_REG("param1", WDOG_COMP_PARAMS_1_REG_OFFSET), + DW_WDT_DBGFS_REG("version", WDOG_COMP_VERSION_REG_OFFSET), + DW_WDT_DBGFS_REG("type", WDOG_COMP_TYPE_REG_OFFSET) +}; + +static void dw_wdt_dbgfs_init(struct dw_wdt *dw_wdt) +{ + struct device *dev = dw_wdt->wdd.parent; + struct debugfs_regset32 *regset; + + regset = devm_kzalloc(dev, sizeof(*regset), GFP_KERNEL); + if (!regset) + return; + + regset->regs = dw_wdt_dbgfs_regs; + regset->nregs = ARRAY_SIZE(dw_wdt_dbgfs_regs); + regset->base = dw_wdt->regs; + + dw_wdt->dbgfs_dir = debugfs_create_dir(dev_name(dev), NULL); + + debugfs_create_regset32("registers", 0444, dw_wdt->dbgfs_dir, regset); + + debugfs_create_file_unsafe("timeout", 0600, dw_wdt->dbgfs_dir, + dw_wdt, &dw_wdt_dbgfs_timeout_fops); + + debugfs_create_file_unsafe("pretimeout", 0600, dw_wdt->dbgfs_dir, + dw_wdt, &dw_wdt_dbgfs_pretimeout_fops); + + debugfs_create_file_unsafe("ping", 0200, dw_wdt->dbgfs_dir, + dw_wdt, &dw_wdt_dbgfs_ping_fops); + + debugfs_create_file_unsafe("enable", 0600, dw_wdt->dbgfs_dir, + dw_wdt, &dw_wdt_dbgfs_en_fops); +} + +static void dw_wdt_dbgfs_clear(struct dw_wdt *dw_wdt) +{ + debugfs_remove_recursive(dw_wdt->dbgfs_dir); +} + +#else /* !CONFIG_DEBUG_FS */ + +static void dw_wdt_dbgfs_init(struct dw_wdt *dw_wdt) {} +static void dw_wdt_dbgfs_clear(struct dw_wdt *dw_wdt) {} + +#endif /* !CONFIG_DEBUG_FS */ + static int dw_wdt_drv_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -544,6 +690,8 @@ static int dw_wdt_drv_probe(struct platform_device *pdev) if (ret) goto out_disable_pclk; + dw_wdt_dbgfs_init(dw_wdt); + return 0; out_disable_pclk: @@ -558,6 +706,8 @@ static int dw_wdt_drv_remove(struct platform_device *pdev) { struct dw_wdt *dw_wdt = platform_get_drvdata(pdev); + dw_wdt_dbgfs_clear(dw_wdt); + watchdog_unregister_device(&dw_wdt->wdd); reset_control_assert(dw_wdt->rst); clk_disable_unprepare(dw_wdt->pclk);