From patchwork Tue Mar 5 20:19:24 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chris Packham X-Patchwork-Id: 10840099 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id B7B1B922 for ; Tue, 5 Mar 2019 20:19:47 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id B37C32CACA for ; Tue, 5 Mar 2019 20:19:46 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id A79E22CACF; Tue, 5 Mar 2019 20:19:46 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 1D8B32CACA for ; Tue, 5 Mar 2019 20:19:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728662AbfCEUTg (ORCPT ); Tue, 5 Mar 2019 15:19:36 -0500 Received: from gate2.alliedtelesis.co.nz ([202.36.163.20]:55877 "EHLO gate2.alliedtelesis.co.nz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728628AbfCEUTg (ORCPT ); Tue, 5 Mar 2019 15:19:36 -0500 Received: from mmarshal3.atlnz.lc (mmarshal3.atlnz.lc [10.32.18.43]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by gate2.alliedtelesis.co.nz (Postfix) with ESMTPS id 5B3DB891AB; Wed, 6 Mar 2019 09:19:33 +1300 (NZDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=alliedtelesis.co.nz; s=mail181024; t=1551817173; bh=0rWeG64ablQ4QcEeytKY5EW2azDS057EZOZDEMjLjMI=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=OTZlIbAphES+CRqp2zeMn66Prvk85OM3q/xqgqoPYVk3ovmvjMuM64O23d4TgkfA6 3Q0WaCwyG1H9RhgMugdWgtAzV8d6TgFiazfTG5hR+ou+7nGArXVoGtRvS3MaJcknPU WQsiG/zjsbEo65NuwNcgwhNC6ZIr9csz4KXc4AhqStZ16vNb6cfr9kSRccGVKfB5ZI P4uMSUWLolrkMRHfMI8YOiy09WDyUP3E7SC8BVGzIYOz4sdp3aTnbc3c+hR95WsKnm jczqydAwRLr+FddC4ghcy1bW0DQPsojtBoGrQUdUoT4SFn7Jm3jK9EkaX3CtOIoKxo RAREuRSRs98IQ== Received: from smtp (Not Verified[10.32.16.33]) by mmarshal3.atlnz.lc with Trustwave SEG (v7,5,8,10121) id ; Wed, 06 Mar 2019 09:19:33 +1300 Received: from chrisp-dl.ws.atlnz.lc (chrisp-dl.ws.atlnz.lc [10.33.22.30]) by smtp (Postfix) with ESMTP id F17DF13EF3B; Wed, 6 Mar 2019 09:19:32 +1300 (NZDT) Received: by chrisp-dl.ws.atlnz.lc (Postfix, from userid 1030) id BC6B61E1D96; Wed, 6 Mar 2019 09:19:32 +1300 (NZDT) From: Chris Packham To: jason@lakedaemon.net, andrew@lunn.ch, gregory.clement@bootlin.com, linux@roeck-us.net Cc: linux-watchdog@vger.kernel.org, linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Chris Packham , Wim Van Sebroeck Subject: [PATCH v2 3/3] watchdog: orion_wdt: use timer1 as a pretimeout Date: Wed, 6 Mar 2019 09:19:24 +1300 Message-Id: <20190305201924.14853-4-chris.packham@alliedtelesis.co.nz> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190305201924.14853-1-chris.packham@alliedtelesis.co.nz> References: <20190305201924.14853-1-chris.packham@alliedtelesis.co.nz> MIME-Version: 1.0 x-atlnz-ls: pat Sender: linux-watchdog-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-watchdog@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The orion watchdog can either reset the CPU or generate an interrupt. The interrupt would be useful for debugging as it provides panic() output about the watchdog expiry, however if the interrupt is used the watchdog can't reset the CPU in the event of being stuck in a loop with interrupts disabled or if the CPU is prevented from accessing memory (e.g. an unterminated DMA). The Armada SoCs have spare timers that aren't currently used by the Linux kernel. We can use timer1 to provide a pre-timeout ahead of the watchdog timer and provide the possibility of gathering debug before the reset triggers. Signed-off-by: Chris Packham --- Changes in v2: - apply changes to armada-38x only - use watchdog_notify_pretimeout() as suggested by Andrew and Guenter drivers/watchdog/orion_wdt.c | 59 ++++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c index 8b259c712c52..aa295f37e563 100644 --- a/drivers/watchdog/orion_wdt.c +++ b/drivers/watchdog/orion_wdt.c @@ -46,6 +46,11 @@ #define WDT_AXP_FIXED_ENABLE_BIT BIT(10) #define WDT_A370_EXPIRED BIT(31) +#define TIMER1_VAL_OFF 0x001c +#define TIMER1_ENABLE_BIT BIT(2) +#define TIMER1_FIXED_ENABLE_BIT BIT(12) +#define TIMER1_STATUS_BIT BIT(8) + static bool nowayout = WATCHDOG_NOWAYOUT; static int heartbeat = -1; /* module parameter (seconds) */ @@ -158,6 +163,7 @@ static int armadaxp_wdt_clock_init(struct platform_device *pdev, struct orion_watchdog *dev) { int ret; + u32 val; dev->clk = of_clk_get_by_name(pdev->dev.of_node, "fixed"); if (IS_ERR(dev->clk)) @@ -169,38 +175,48 @@ static int armadaxp_wdt_clock_init(struct platform_device *pdev, } /* Enable the fixed watchdog clock input */ - atomic_io_modify(dev->reg + TIMER_CTRL, - WDT_AXP_FIXED_ENABLE_BIT, - WDT_AXP_FIXED_ENABLE_BIT); + val = WDT_AXP_FIXED_ENABLE_BIT | TIMER1_FIXED_ENABLE_BIT; + atomic_io_modify(dev->reg + TIMER_CTRL, val, val); dev->clk_rate = clk_get_rate(dev->clk); + return 0; } static int orion_wdt_ping(struct watchdog_device *wdt_dev) { struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); + /* Reload watchdog duration */ writel(dev->clk_rate * wdt_dev->timeout, dev->reg + dev->data->wdt_counter_offset); + if (dev->wdt.info->options & WDIOF_PRETIMEOUT) + writel(dev->clk_rate * (wdt_dev->timeout - wdt_dev->pretimeout), + dev->reg + TIMER1_VAL_OFF); + return 0; } static int armada375_start(struct watchdog_device *wdt_dev) { struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); - u32 reg; + u32 reg, val; /* Set watchdog duration */ writel(dev->clk_rate * wdt_dev->timeout, dev->reg + dev->data->wdt_counter_offset); + if (dev->wdt.info->options & WDIOF_PRETIMEOUT) + writel(dev->clk_rate * (wdt_dev->timeout - wdt_dev->pretimeout), + dev->reg + TIMER1_VAL_OFF); /* Clear the watchdog expiration bit */ atomic_io_modify(dev->reg + TIMER_A370_STATUS, WDT_A370_EXPIRED, 0); /* Enable watchdog timer */ - atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, - dev->data->wdt_enable_bit); + val = dev->data->wdt_enable_bit; + if (dev->wdt.info->options & WDIOF_PRETIMEOUT) + val |= TIMER1_ENABLE_BIT; + atomic_io_modify(dev->reg + TIMER_CTRL, val, val); /* Enable reset on watchdog */ reg = readl(dev->rstout); @@ -277,7 +293,7 @@ static int orion_stop(struct watchdog_device *wdt_dev) static int armada375_stop(struct watchdog_device *wdt_dev) { struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); - u32 reg; + u32 reg, mask; /* Disable reset on watchdog */ atomic_io_modify(dev->rstout_mask, dev->data->rstout_mask_bit, @@ -287,7 +303,10 @@ static int armada375_stop(struct watchdog_device *wdt_dev) writel(reg, dev->rstout); /* Disable watchdog timer */ - atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, 0); + mask = dev->data->wdt_enable_bit; + if (wdt_dev->info->options & WDIOF_PRETIMEOUT) + mask += TIMER1_ENABLE_BIT; + atomic_io_modify(dev->reg + TIMER_CTRL, mask, 0); return 0; } @@ -349,7 +368,7 @@ static unsigned int orion_wdt_get_timeleft(struct watchdog_device *wdt_dev) return readl(dev->reg + dev->data->wdt_counter_offset) / dev->clk_rate; } -static const struct watchdog_info orion_wdt_info = { +static struct watchdog_info orion_wdt_info = { .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, .identity = "Orion Watchdog", }; @@ -368,6 +387,16 @@ static irqreturn_t orion_wdt_irq(int irq, void *devid) return IRQ_HANDLED; } +static irqreturn_t orion_wdt_pre_irq(int irq, void *devid) +{ + struct orion_watchdog *dev = devid; + + atomic_io_modify(dev->reg + TIMER_A370_STATUS, + TIMER1_STATUS_BIT, 0); + watchdog_notify_pretimeout(&dev->wdt); + return IRQ_HANDLED; +} + /* * The original devicetree binding for this driver specified only * one memory resource, so in order to keep DT backwards compatibility @@ -591,6 +620,18 @@ static int orion_wdt_probe(struct platform_device *pdev) } } + irq = platform_get_irq(pdev, 1); + if (irq > 0) { + orion_wdt_info.options |= WDIOF_PRETIMEOUT; + ret = devm_request_irq(&pdev->dev, irq, orion_wdt_pre_irq, + 0, pdev->name, dev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request IRQ\n"); + goto disable_clk; + } + } + + watchdog_set_nowayout(&dev->wdt, nowayout); ret = watchdog_register_device(&dev->wdt); if (ret)