From patchwork Sun Mar 22 12:40:36 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Baruch Siach X-Patchwork-Id: 6066651 Return-Path: X-Original-To: patchwork-linux-arm@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 766D2BF90F for ; Sun, 22 Mar 2015 12:43:40 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 330B320295 for ; Sun, 22 Mar 2015 12:43:39 +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 1E5B12027D for ; Sun, 22 Mar 2015 12:43:38 +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 1YZfBu-0005Sr-Pq; Sun, 22 Mar 2015 12:41:18 +0000 Received: from guitar.tcltek.co.il ([192.115.133.116] helo=mx.tkos.co.il) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1YZfBl-0005QH-7Y for linux-arm-kernel@lists.infradead.org; Sun, 22 Mar 2015 12:41:10 +0000 Received: from tarshish.tkos.co.il (unknown [10.0.8.6]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (128/128 bits)) (No client certificate requested) by mx.tkos.co.il (Postfix) with ESMTPSA id 37D5C440688; Sun, 22 Mar 2015 14:40:45 +0200 (IST) From: Baruch Siach To: Wim Van Sebroeck Subject: [PATCH 2/2] watchdog: digicolor: driver for Conexant Digicolor CX92755 SoC Date: Sun, 22 Mar 2015 14:40:36 +0200 Message-Id: <409ece75204d3911dc526a9bc0c908fc30893b26.1427028036.git.baruch@tkos.co.il> X-Mailer: git-send-email 2.1.4 In-Reply-To: <6bc6e867b500cd3cfc629b96f90442f5c0aced3a.1427028036.git.baruch@tkos.co.il> References: <6bc6e867b500cd3cfc629b96f90442f5c0aced3a.1427028036.git.baruch@tkos.co.il> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20150322_054109_767324_1257CA5B X-CRM114-Status: GOOD ( 19.77 ) X-Spam-Score: 0.0 (/) Cc: devicetree@vger.kernel.org, Baruch Siach , linux-watchdog@vger.kernel.org, linux-arm-kernel@lists.infradead.org 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=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, T_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 This commit add a driver for the watchdog functionality of the Conexant CX92755 SoC, from the Digicolor series of SoCs. Of 8 system timers provided by the CX92755, the first one, timer A, can reset the chip when its counter reaches zero. This driver uses this capability to provide userspace with a standard watchdog, using the watchdog timer driver core framework. This driver also implements a reboot handler for the reboot(2) system call. The watchdog driver shares the timer registers with the CX92755 timer driver (drivers/clocksource/timer-digicolor.c). The timer driver, however, uses only timers other than A, so both drivers should coexist. Signed-off-by: Baruch Siach --- drivers/watchdog/Kconfig | 10 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/digicolor_wdt.c | 203 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 214 insertions(+) create mode 100644 drivers/watchdog/digicolor_wdt.c diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 16f202350997..7d73d6c78cf6 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -515,6 +515,16 @@ config MEDIATEK_WATCHDOG To compile this driver as a module, choose M here: the module will be called mtk_wdt. +config DIGICOLOR_WATCHDOG + tristate "Conexant Digicolor SoCs watchdog support" + depends on ARCH_DIGICOLOR + select WATCHDOG_CORE + help + Say Y here to include support for the watchdog timer + in Conexant Digicolor SoCs. + To compile this driver as a module, choose M here: the + module will be called digicolor_wdt. + # AVR32 Architecture config AT32AP700X_WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 5c19294d1c30..0721f10e8d13 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -64,6 +64,7 @@ obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o obj-$(CONFIG_MESON_WATCHDOG) += meson_wdt.o obj-$(CONFIG_MEDIATEK_WATCHDOG) += mtk_wdt.o +obj-$(CONFIG_DIGICOLOR_WATCHDOG) += digicolor_wdt.o # AVR32 Architecture obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o diff --git a/drivers/watchdog/digicolor_wdt.c b/drivers/watchdog/digicolor_wdt.c new file mode 100644 index 000000000000..260cc4495370 --- /dev/null +++ b/drivers/watchdog/digicolor_wdt.c @@ -0,0 +1,203 @@ +/* + * Watchdog driver for Conexant Digicolor + * + * Copyright (C) 2015 Paradox Innovation Ltd. + * + * 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 + +#define TIMER_A_CONTROL 0 +#define TIMER_A_COUNT 4 + +#define TIMER_A_ENABLE_COUNT BIT(0) +#define TIMER_A_ENABLE_WATCHDOG BIT(1) + +#define DC_WDT_DEFAULT_TIME 5 + +struct dc_wdt { + void __iomem *base; + struct clk *clk; + struct notifier_block restart_handler; + spinlock_t lock; +}; + +static unsigned timeout; +module_param(timeout, uint, 0); +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds"); + +static int dc_restart_handler(struct notifier_block *this, unsigned long mode, + void *cmd) +{ + struct dc_wdt *wdt = container_of(this, struct dc_wdt, restart_handler); + unsigned long flags; + + spin_lock_irqsave(&wdt->lock, flags); + + writel_relaxed(0, wdt->base + TIMER_A_CONTROL); + writel_relaxed(1, wdt->base + TIMER_A_COUNT); + writel_relaxed(TIMER_A_ENABLE_COUNT | TIMER_A_ENABLE_WATCHDOG, + wdt->base + TIMER_A_CONTROL); + + spin_unlock_irqrestore(&wdt->lock, flags); + + /* wait for reset to assert... */ + mdelay(500); + + return NOTIFY_DONE; +} + +static int dc_wdt_start(struct watchdog_device *wdog) +{ + struct dc_wdt *wdt = watchdog_get_drvdata(wdog); + unsigned long flags; + + spin_lock_irqsave(&wdt->lock, flags); + + writel_relaxed(0, wdt->base + TIMER_A_CONTROL); + writel_relaxed(wdog->timeout * clk_get_rate(wdt->clk), + wdt->base + TIMER_A_COUNT); + writel_relaxed(TIMER_A_ENABLE_COUNT | TIMER_A_ENABLE_WATCHDOG, + wdt->base + TIMER_A_CONTROL); + + spin_unlock_irqrestore(&wdt->lock, flags); + + return 0; +} + +static int dc_wdt_stop(struct watchdog_device *wdog) +{ + struct dc_wdt *wdt = watchdog_get_drvdata(wdog); + + writel_relaxed(0, wdt->base + TIMER_A_CONTROL); + + return 0; +} + +static int dc_wdt_set_timeout(struct watchdog_device *wdog, unsigned int t) +{ + wdog->timeout = t; + + return 0; +} + +static unsigned int dc_wdt_get_timeleft(struct watchdog_device *wdog) +{ + struct dc_wdt *wdt = watchdog_get_drvdata(wdog); + uint32_t count = readl_relaxed(wdt->base + TIMER_A_COUNT); + + return (count / clk_get_rate(wdt->clk)); +} + +static struct watchdog_ops dc_wdt_ops = { + .owner = THIS_MODULE, + .start = dc_wdt_start, + .stop = dc_wdt_stop, + .set_timeout = dc_wdt_set_timeout, + .get_timeleft = dc_wdt_get_timeleft, +}; + +static struct watchdog_info dc_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE + | WDIOF_KEEPALIVEPING, + .identity = "Conexant Digicolor Watchdog", +}; + +static struct watchdog_device dc_wdt_wdd = { + .info = &dc_wdt_info, + .ops = &dc_wdt_ops, + .min_timeout = 1, + .timeout = DC_WDT_DEFAULT_TIME, +}; + +static int dc_wdt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct dc_wdt *wdt; + int ret; + + wdt = devm_kzalloc(dev, sizeof(struct dc_wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + platform_set_drvdata(pdev, wdt); + + wdt->base = of_iomap(np, 0); + if (!wdt->base) { + dev_err(dev, "Failed to remap watchdog regs"); + return -ENODEV; + } + + wdt->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(wdt->clk)) + return PTR_ERR(wdt->clk); + dc_wdt_wdd.max_timeout = U32_MAX / clk_get_rate(wdt->clk); + + spin_lock_init(&wdt->lock); + + watchdog_set_drvdata(&dc_wdt_wdd, wdt); + watchdog_init_timeout(&dc_wdt_wdd, timeout, dev); + ret = watchdog_register_device(&dc_wdt_wdd); + if (ret) { + dev_err(dev, "Failed to register watchdog device"); + iounmap(wdt->base); + return ret; + } + + wdt->restart_handler.notifier_call = dc_restart_handler; + wdt->restart_handler.priority = 128; + ret = register_restart_handler(&wdt->restart_handler); + if (ret) + dev_err(&pdev->dev, "cannot register restart handler\n"); + + return 0; +} + +static int dc_wdt_remove(struct platform_device *pdev) +{ + struct dc_wdt *wdt = platform_get_drvdata(pdev); + + unregister_restart_handler(&wdt->restart_handler); + watchdog_unregister_device(&dc_wdt_wdd); + iounmap(wdt->base); + + return 0; +} + +static void dc_wdt_shutdown(struct platform_device *pdev) +{ + dc_wdt_stop(&dc_wdt_wdd); +} + +static const struct of_device_id dc_wdt_of_match[] = { + { .compatible = "cnxt,cx92755-wdt", }, + {}, +}; +MODULE_DEVICE_TABLE(of, dc_wdt_of_match); + +static struct platform_driver dc_wdt_driver = { + .probe = dc_wdt_probe, + .remove = dc_wdt_remove, + .shutdown = dc_wdt_shutdown, + .driver = { + .name = "digicolor-wdt", + .of_match_table = dc_wdt_of_match, + }, +}; +module_platform_driver(dc_wdt_driver); + +MODULE_AUTHOR("Baruch Siach "); +MODULE_DESCRIPTION("Driver for Conexant Digicolor watchdog timer"); +MODULE_LICENSE("GPL");