From patchwork Sun Nov 1 19:19:39 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wolfram Sang X-Patchwork-Id: 7532591 X-Patchwork-Delegate: geert@linux-m68k.org Return-Path: X-Original-To: patchwork-linux-sh@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id F307EBEEA4 for ; Sun, 1 Nov 2015 19:20:06 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id AAD64205B9 for ; Sun, 1 Nov 2015 19:20:05 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 2F6012055B for ; Sun, 1 Nov 2015 19:20:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752549AbbKATUD (ORCPT ); Sun, 1 Nov 2015 14:20:03 -0500 Received: from sauhun.de ([89.238.76.85]:48179 "EHLO pokefinder.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752583AbbKATUC (ORCPT ); Sun, 1 Nov 2015 14:20:02 -0500 Received: from p4fe2433c.dip0.t-ipconnect.de ([79.226.67.60]:37749 helo=localhost) by pokefinder.org with esmtpsa (TLS1.2:RSA_AES_128_CBC_SHA1:128) (Exim 4.80) (envelope-from ) id 1ZsyAa-0002cD-Lf; Sun, 01 Nov 2015 20:20:00 +0100 From: Wolfram Sang To: linux-sh@vger.kernel.org Cc: Magnus Damm , Simon Horman , Laurent Pinchart , Geert Uytterhoeven , Wolfram Sang , Yoshihiro Shimoda Subject: [RFC v3 5/5] watchdog: renesas-rwdt: add driver Date: Sun, 1 Nov 2015 20:19:39 +0100 Message-Id: <1446405579-5367-6-git-send-email-wsa@the-dreams.de> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1446405579-5367-1-git-send-email-wsa@the-dreams.de> References: <1446405579-5367-1-git-send-email-wsa@the-dreams.de> Sender: linux-sh-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sh@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Wolfram Sang Add support for an RCLK watchdog found in at least all RCar Gen2 and Gen3 based SoCs from Renesas. It probably works even for the "Secure watchdog" of some of those SoCs according to the specs I have. A restart handler is in place, too. Signed-off-by: Wolfram Sang --- drivers/watchdog/Kconfig | 8 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/renesas_rwdt.c | 222 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 231 insertions(+) create mode 100644 drivers/watchdog/renesas_rwdt.c diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 79e1aa1b0959f1..58c4cd875c48d7 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -578,6 +578,14 @@ config LPC18XX_WATCHDOG To compile this driver as a module, choose M here: the module will be called lpc18xx_wdt. +config RENESAS_RWDT + tristate "Renesas RWDT Watchdog" + depends on ARCH_SHMOBILE || COMPILE_TEST + select WATCHDOG_CORE + help + This driver adds watchdog support for the integrated watchdog in the + Renesas RCar and other SH Mobile SoCs. + # AVR32 Architecture config AT32AP700X_WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 0c616e3f67bb57..9ea88b1115af11 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -68,6 +68,7 @@ obj-$(CONFIG_MESON_WATCHDOG) += meson_wdt.o obj-$(CONFIG_MEDIATEK_WATCHDOG) += mtk_wdt.o obj-$(CONFIG_DIGICOLOR_WATCHDOG) += digicolor_wdt.o obj-$(CONFIG_LPC18XX_WATCHDOG) += lpc18xx_wdt.o +obj-$(CONFIG_RENESAS_RWDT) += renesas_rwdt.o # AVR32 Architecture obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o diff --git a/drivers/watchdog/renesas_rwdt.c b/drivers/watchdog/renesas_rwdt.c new file mode 100644 index 00000000000000..a6644cb88e4e88 --- /dev/null +++ b/drivers/watchdog/renesas_rwdt.c @@ -0,0 +1,222 @@ +/* + * Watchdog driver for Renesas RWDT watchdog + * + * Copyright (C) 2015 Wolfram Sang, Sang Engineering + * Copyright (C) 2015 Renesas Electronics Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define RWTCNT 0 +#define RWTCSRA 4 +#define RWTCSRA_WOVF BIT(4) +#define RWTCSRA_WRFLG BIT(5) +#define RWTCSRA_TME BIT(7) + +#define RWDT_DEFAULT_TIMEOUT 60 + +static const unsigned clk_divs[] = { 1, 4, 16, 32, 64, 128, 1024 }; + +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, S_IRUGO); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +static unsigned timeout = RWDT_DEFAULT_TIMEOUT; +module_param(timeout, uint, 0); +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (default=" + __MODULE_STRING(RWDT_DEFAULT_TIMEOUT) ")"); + +struct rwdt_priv { + void __iomem *base; + struct watchdog_device wdev; + struct clk *clk; + struct notifier_block restart_handler; + unsigned clks_per_sec; + u8 cks; +}; + +static void rwdt_write(struct rwdt_priv *priv, u32 val, unsigned reg) +{ + if (reg == RWTCNT) + val |= 0x5a5a0000; + else + val |= 0xa5a5a500; + + writel_relaxed(val, priv->base + reg); +} + +static int rwdt_init_timeout(struct watchdog_device *wdev) +{ + struct rwdt_priv *priv = watchdog_get_drvdata(wdev); + + rwdt_write(priv, 65536 - wdev->timeout * priv->clks_per_sec, RWTCNT); + + return 0; +} + +static int rwdt_set_timeout(struct watchdog_device *wdev, unsigned new_timeout) +{ + wdev->timeout = new_timeout; + rwdt_init_timeout(wdev); + + return 0; +} + +static int rwdt_start(struct watchdog_device *wdev) +{ + struct rwdt_priv *priv = watchdog_get_drvdata(wdev); + + clk_prepare_enable(priv->clk); + + rwdt_write(priv, priv->cks, RWTCSRA); + rwdt_init_timeout(wdev); + + while (readb_relaxed(priv->base + RWTCSRA) & RWTCSRA_WRFLG) + cpu_relax(); + + rwdt_write(priv, priv->cks | RWTCSRA_TME, RWTCSRA); + + return 0; +} + +static int rwdt_stop(struct watchdog_device *wdev) +{ + struct rwdt_priv *priv = watchdog_get_drvdata(wdev); + + rwdt_write(priv, priv->cks, RWTCSRA); + clk_disable_unprepare(priv->clk); + + return 0; +} + +static const struct watchdog_info rwdt_ident = { + .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, + .identity = "Renesas RWDT Watchdog", +}; + +static const struct watchdog_ops rwdt_ops = { + .owner = THIS_MODULE, + .start = rwdt_start, + .stop = rwdt_stop, + .ping = rwdt_init_timeout, + .set_timeout = rwdt_set_timeout, +}; + +static int rwdt_restart_handler(struct notifier_block *nb, unsigned long mode, void *cmd) +{ + struct rwdt_priv *priv = container_of(nb, struct rwdt_priv, restart_handler); + + rwdt_start(&priv->wdev); + rwdt_write(priv, 0xffff, RWTCNT); + + return NOTIFY_DONE; +} + +static int rwdt_probe(struct platform_device *pdev) +{ + struct rwdt_priv *priv; + struct resource *res; + unsigned long rate; + unsigned clks_per_sec; + int ret, i; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + priv->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk); + + clk_prepare_enable(priv->clk); + rate = clk_get_rate(priv->clk); + clk_disable_unprepare(priv->clk); + + if (!rate) + return -ENOENT; + + for (i = ARRAY_SIZE(clk_divs); i >= 0; i--) { + clks_per_sec = rate / clk_divs[i]; + if (clks_per_sec) { + priv->clks_per_sec = clks_per_sec; + priv->cks = i; + break; + } + } + + if (!clks_per_sec) { + dev_err(&pdev->dev, "Can't find suitable clock divider!\n"); + return -ERANGE; + } + + priv->wdev.info = &rwdt_ident, + priv->wdev.ops = &rwdt_ops, + priv->wdev.parent = &pdev->dev; + priv->wdev.min_timeout = 1; + priv->wdev.max_timeout = 65536 / clks_per_sec; + priv->wdev.timeout = min(priv->wdev.max_timeout, (unsigned)RWDT_DEFAULT_TIMEOUT); + + platform_set_drvdata(pdev, priv); + watchdog_set_drvdata(&priv->wdev, priv); + watchdog_set_nowayout(&priv->wdev, nowayout); + ret = watchdog_init_timeout(&priv->wdev, timeout, &pdev->dev); + if (ret) + dev_warn(&pdev->dev, "Specified timeout value invalid, using default\n"); + + ret = watchdog_register_device(&priv->wdev); + if (ret < 0) + return ret; + + priv->restart_handler.notifier_call = rwdt_restart_handler; + priv->restart_handler.priority = 192; + ret = register_restart_handler(&priv->restart_handler); + if (ret) + dev_warn(&pdev->dev, "Failed to register restart handler (err = %d)\n", ret); + + return 0; +} + +static int rwdt_remove(struct platform_device *pdev) +{ + struct rwdt_priv *priv = platform_get_drvdata(pdev); + + unregister_restart_handler(&priv->restart_handler); + watchdog_unregister_device(&priv->wdev); + return 0; +} + +static const struct of_device_id rwdt_ids[] = { + { .compatible = "renesas,rwdt", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rwdt_ids); + +static struct platform_driver rwdt_driver = { + .driver = { + .name = "renesas_rwdt", + .of_match_table = rwdt_ids, + }, + .probe = rwdt_probe, + .remove = rwdt_remove, +}; +module_platform_driver(rwdt_driver); + +MODULE_DESCRIPTION("Renesas RWDT Watchdog Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Wolfram Sang ");