From patchwork Tue Aug 12 08:31:23 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Markus Pargmann X-Patchwork-Id: 4711921 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 50CA29F319 for ; Tue, 12 Aug 2014 08:35:51 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 48A9320123 for ; Tue, 12 Aug 2014 08:35:49 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 8CCB2200E9 for ; Tue, 12 Aug 2014 08:35:47 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1XH7VB-00019E-5r; Tue, 12 Aug 2014 08:32:17 +0000 Received: from metis.ext.pengutronix.de ([2001:6f8:1178:4:290:27ff:fe1d:cc33]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1XH7V8-00018Z-3a for linux-arm-kernel@lists.infradead.org; Tue, 12 Aug 2014 08:32:15 +0000 Received: from dude.hi.pengutronix.de ([2001:6f8:1178:2:a236:9fff:fe00:814]) by metis.ext.pengutronix.de with esmtp (Exim 4.72) (envelope-from ) id 1XH7Ue-0006ji-5D; Tue, 12 Aug 2014 10:31:44 +0200 Received: from mpa by dude.hi.pengutronix.de with local (Exim 4.84_RC1) (envelope-from ) id 1XH7Ub-0000SE-ND; Tue, 12 Aug 2014 10:31:41 +0200 From: Markus Pargmann To: Wim Van Sebroeck Subject: [PATCH] watchdog: Add DA906x PMIC watchdog driver. Date: Tue, 12 Aug 2014 10:31:23 +0200 Message-Id: <1407832283-26444-1-git-send-email-mpa@pengutronix.de> X-Mailer: git-send-email 2.0.1 X-SA-Exim-Connect-IP: 2001:6f8:1178:2:a236:9fff:fe00:814 X-SA-Exim-Mail-From: mpa@pengutronix.de X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-arm-kernel@lists.infradead.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20140812_013214_566315_EEAFB662 X-CRM114-Status: GOOD ( 18.60 ) X-Spam-Score: -0.7 (/) Cc: linux-watchdog@vger.kernel.org, Philipp Zabel , Krystian Garbaciak , kernel@pengutronix.de, Markus Pargmann , linux-arm-kernel@lists.infradead.org, Support Opensource X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-2.6 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_NONE, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable 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: Krystian Garbaciak This driver supports watchdog device inside DA906x PMIC. Signed-off-by: Krystian Garbaciak Signed-off-by: Philipp Zabel Signed-off-by: Markus Pargmann --- drivers/watchdog/Kconfig | 7 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/da9063_wdt.c | 229 +++++++++++++++++++++++++++++++++++ include/linux/mfd/da9063/registers.h | 1 + 4 files changed, 238 insertions(+) create mode 100644 drivers/watchdog/da9063_wdt.c diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index c845527b503a..8d5c9b33552a 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -87,6 +87,13 @@ config DA9055_WATCHDOG This driver can also be built as a module. If so, the module will be called da9055_wdt. +config DA9063_WATCHDOG + tristate "Dialog DA9063 Watchdog" + depends on MFD_DA9063 + select WATCHDOG_CORE + help + Support for the watchdog in the DA9063 PMIC. + config GPIO_WATCHDOG tristate "Watchdog device controlled through GPIO-line" depends on OF_GPIO diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 7b8a91ed20e7..ce4a7632d863 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -172,6 +172,7 @@ obj-$(CONFIG_XEN_WDT) += xen_wdt.o # Architecture Independent obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o obj-$(CONFIG_DA9055_WATCHDOG) += da9055_wdt.o +obj-$(CONFIG_DA9063_WATCHDOG) += da9063_wdt.o obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o diff --git a/drivers/watchdog/da9063_wdt.c b/drivers/watchdog/da9063_wdt.c new file mode 100644 index 000000000000..cd32de6c5e9d --- /dev/null +++ b/drivers/watchdog/da9063_wdt.c @@ -0,0 +1,229 @@ +/* + * Watchdog driver for DA9063 PMICs. + * + * Copyright(c) 2012 Dialog Semiconductor Ltd. + * + * Author: Mariusz Wojtasik + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DA9063_SUPPORTED_TIMEOUTS_STR "2, 4, 8, 16, 32, 65, 131" + +/* Watchdog selector to timeout in seconds. + 0: WDT disabled; + others: timeout = 2048 ms * 2^(TWDSCALE-1). */ +static const int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 }; +#define DA9063_TWDSCALE_DISABLE 0 +#define DA9063_TWDSCALE_MIN 1 +#define DA9063_TWDSCALE_MAX (ARRAY_SIZE(wdt_timeout) - 1) +#define DA9063_WDT_MIN_TIMEOUT wdt_timeout[DA9063_TWDSCALE_MIN] +#define DA9063_WDT_MAX_TIMEOUT wdt_timeout[DA9063_TWDSCALE_MAX] +#define DA9063_WDG_TIMEOUT wdt_timeout[3] + + +struct da9063_watchdog { + struct da9063 *da9063; + struct watchdog_device *wdtdev; + struct mutex lock; +}; + +static int _da9063_wdt_timeout_to_sel(int secs) +{ + int i; + + for (i = DA9063_TWDSCALE_MIN; i <= DA9063_TWDSCALE_MAX; i++) { + if (wdt_timeout[i] >= secs) + return i; + } + + return 0; +} + +static int _da9063_wdt_disable(struct da9063 *da9063) +{ + return regmap_update_bits(da9063->regmap, DA9063_REG_CONTROL_D, + DA9063_TWDSCALE_MASK, + DA9063_TWDSCALE_DISABLE << DA9063_TWDSCALE_SHIFT); +} + +static int _da9063_wdt_set_timeout(struct da9063 *da9063, unsigned int regval) +{ + if (regval < DA9063_TWDSCALE_MIN || regval > DA9063_TWDSCALE_MAX) + return -EINVAL; + + return regmap_update_bits(da9063->regmap, DA9063_REG_CONTROL_D, + DA9063_TWDSCALE_MASK, regval << DA9063_TWDSCALE_SHIFT); +} + +static int _da9063_wdt_kick(struct da9063 *da9063) +{ + return regmap_write(da9063->regmap, DA9063_REG_CONTROL_F, + DA9063_WATCHDOG); +} + +static int da9063_wdt_start(struct watchdog_device *wdd) +{ + struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd); + unsigned int selector; + int ret = 0; + + mutex_lock(&wdt->lock); + selector = _da9063_wdt_timeout_to_sel(wdt->wdtdev->timeout); + ret = _da9063_wdt_set_timeout(wdt->da9063, selector); + if (ret) { + dev_err(wdt->da9063->dev, "Watchdog failed to start (err = %d)\n", + ret); + } + mutex_unlock(&wdt->lock); + + return ret; +} + +static int da9063_wdt_stop(struct watchdog_device *wdd) +{ + struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd); + int ret = 0; + + mutex_lock(&wdt->lock); + ret = _da9063_wdt_disable(wdt->da9063); + if (ret) + dev_alert(wdt->da9063->dev, + "Watchdog failed to stop (err = %d)\n", ret); + mutex_unlock(&wdt->lock); + + return ret; +} + +static int da9063_wdt_ping(struct watchdog_device *wdd) +{ + struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd); + int ret = 0; + + mutex_lock(&wdt->lock); + ret = _da9063_wdt_kick(wdt->da9063); + if (ret) + dev_alert(wdt->da9063->dev, "Failed to ping the watchdog (err = %d)\n", + ret); + mutex_unlock(&wdt->lock); + + return ret; +} + +static int da9063_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd); + unsigned int selector; + int ret; + + selector = _da9063_wdt_timeout_to_sel(timeout); + if (selector == 0) { + dev_err(wdt->da9063->dev, "Unsupported watchdog timeout (select from: " + DA9063_SUPPORTED_TIMEOUTS_STR ")\n"); + return -EINVAL; + } + + mutex_lock(&wdt->lock); + + ret = _da9063_wdt_set_timeout(wdt->da9063, selector); + if (ret) + dev_err(wdt->da9063->dev, "Failed to set watchdog timeout (err = %d)\n", + ret); + else + wdd->timeout = wdt_timeout[selector]; + + mutex_unlock(&wdt->lock); + + return ret; +} + +static struct watchdog_info da9063_watchdog_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, + .identity = "DA9063 Watchdog", +}; + +static struct watchdog_ops da9063_watchdog_ops = { + .owner = THIS_MODULE, + .start = da9063_wdt_start, + .stop = da9063_wdt_stop, + .ping = da9063_wdt_ping, + .set_timeout = da9063_wdt_set_timeout, +}; + +static struct watchdog_device da9063_watchdog_device = { + .info = &da9063_watchdog_info, + .ops = &da9063_watchdog_ops, +}; + +static int da9063_wdt_probe(struct platform_device *pdev) +{ + int ret; + struct da9063 *da9063 = dev_get_drvdata(pdev->dev.parent); + struct da9063_watchdog *wdt; + + if (!da9063) + return -EPROBE_DEFER; + + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + wdt->da9063 = da9063; + wdt->wdtdev = &da9063_watchdog_device; + mutex_init(&wdt->lock); + + if (IS_ENABLED(CONFIG_WATCHDOG_NOWAYOUT)) + set_bit(WDOG_NO_WAY_OUT, &wdt->wdtdev->status); + + wdt->wdtdev->min_timeout = DA9063_WDT_MIN_TIMEOUT; + wdt->wdtdev->max_timeout = DA9063_WDT_MAX_TIMEOUT; + wdt->wdtdev->timeout = DA9063_WDG_TIMEOUT; + + ret = watchdog_register_device(wdt->wdtdev); + if (ret) { + dev_err(da9063->dev, "da9063-watchdog registration failed (err = %d)", + ret); + return ret; + } + + watchdog_set_drvdata(wdt->wdtdev, wdt); + + return 0; +} + +static int da9063_wdt_remove(struct platform_device *pdev) +{ + watchdog_unregister_device(&da9063_watchdog_device); + + return 0; +} + +static struct platform_driver da9063_wdt_driver = { + .probe = da9063_wdt_probe, + .remove = da9063_wdt_remove, + .driver = { + .name = DA9063_DRVNAME_WATCHDOG, + }, +}; +module_platform_driver(da9063_wdt_driver); + +MODULE_AUTHOR("Mariusz Wojtasik "); +MODULE_DESCRIPTION("Watchdog driver for Dialog DA9063"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DA9063_DRVNAME_WATCHDOG); diff --git a/include/linux/mfd/da9063/registers.h b/include/linux/mfd/da9063/registers.h index 09a85c699da1..cc73f5d76cc8 100644 --- a/include/linux/mfd/da9063/registers.h +++ b/include/linux/mfd/da9063/registers.h @@ -431,6 +431,7 @@ /* DA9063_REG_CONTROL_D (addr=0x11) */ #define DA9063_TWDSCALE_MASK 0x07 +#define DA9063_TWDSCALE_SHIFT 0x00 #define DA9063_BLINK_FRQ_MASK 0x38 #define DA9063_BLINK_FRQ_OFF 0x00 #define DA9063_BLINK_FRQ_1S0 0x08