Re: [PATCH v4 2/2] watchdog: Add Nuvoton NPCM watchdog driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Mar 13, 2018 at 04:47:26PM +1030, Joel Stanley wrote:
> The Nuvoton NPCM750 has a watchdog implemented as a single register
> inside the timer peripheral.
> 
> This driver exposes that watchdog as a standard watchdog device with
> coarse timeout intervals, limited by the combination of prescaler and
> counter that is provided by the hardware. The calculation is taken from
> the Nuvoton vendor tree.
> 
> The watchdog is left running if a bootloader had it going. The rate is
> the one specified in the device tree, or the default value (obtained
> from the datasheet).
> 
> There is a pre-timeout IRQ that is wired up. This timeout always occurs
> 1024 clocks before the timeout.
> 
> Signed-off-by: Joel Stanley <joel@xxxxxxxxx>

Reviewed-by: Guenter Roeck <linux@xxxxxxxxxxxx>

> ---
> v2:
>  - Make MODULE_LICENCE gpl v2 to match SPDX
>  - Remove unused struct device pointer
>  - Remove unused setting of drvdata
>  - Add linux/bitops.h
>  - Sort includes
>  - Remove unused fiq include
>  - Update timeout with achieved value
> v3:
>  - Calculate the time and register value separately
>  - Pass device tree value through set_timeout to ensure it can
>    be represented by hardware
> v4:
>  - Propagate return code from platform_get_irq
> ---
>  drivers/watchdog/Kconfig    |  11 ++
>  drivers/watchdog/Makefile   |   1 +
>  drivers/watchdog/npcm_wdt.c | 254 ++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 266 insertions(+)
>  create mode 100644 drivers/watchdog/npcm_wdt.c
> 
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index aff773bcebdb..0c1cc68894e6 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -513,6 +513,17 @@ config COH901327_WATCHDOG
>  	  This watchdog is used to reset the system and thus cannot be
>  	  compiled as a module.
>  
> +config NPCM7XX_WATCHDOG
> +	bool "Nuvoton NPCM750 watchdog"
> +	depends on ARCH_NPCM || COMPILE_TEST
> +	default y if ARCH_NPCM750
> +	select WATCHDOG_CORE
> +	help
> +	  Say Y here to include Watchdog timer support for the
> +	  watchdog embedded into the NPCM7xx.
> +	  This watchdog is used to reset the system and thus cannot be
> +	  compiled as a module.
> +
>  config TWL4030_WATCHDOG
>  	tristate "TWL4030 Watchdog"
>  	depends on TWL4030_CORE
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index 0474d38aa854..97a5afb5cad2 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -61,6 +61,7 @@ obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o
>  obj-$(CONFIG_SUNXI_WATCHDOG) += sunxi_wdt.o
>  obj-$(CONFIG_RN5T618_WATCHDOG) += rn5t618_wdt.o
>  obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o
> +obj-$(CONFIG_NPCM7XX_WATCHDOG) += npcm_wdt.o
>  obj-$(CONFIG_STMP3XXX_RTC_WATCHDOG) += stmp3xxx_rtc_wdt.o
>  obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o
>  obj-$(CONFIG_TS4800_WATCHDOG) += ts4800_wdt.o
> diff --git a/drivers/watchdog/npcm_wdt.c b/drivers/watchdog/npcm_wdt.c
> new file mode 100644
> index 000000000000..0d4213652ecc
> --- /dev/null
> +++ b/drivers/watchdog/npcm_wdt.c
> @@ -0,0 +1,254 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (c) 2018 Nuvoton Technology corporation.
> +// Copyright (c) 2018 IBM Corp.
> +
> +#include <linux/bitops.h>
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of_irq.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/watchdog.h>
> +
> +#define NPCM_WTCR	0x1C
> +
> +#define NPCM_WTCLK	(BIT(10) | BIT(11))	/* Clock divider */
> +#define NPCM_WTE	BIT(7)			/* Enable */
> +#define NPCM_WTIE	BIT(6)			/* Enable irq */
> +#define NPCM_WTIS	(BIT(4) | BIT(5))	/* Interval selection */
> +#define NPCM_WTIF	BIT(3)			/* Interrupt flag*/
> +#define NPCM_WTRF	BIT(2)			/* Reset flag */
> +#define NPCM_WTRE	BIT(1)			/* Reset enable */
> +#define NPCM_WTR	BIT(0)			/* Reset counter */
> +
> +/*
> + * Watchdog timeouts
> + *
> + * 170     msec:    WTCLK=01 WTIS=00     VAL= 0x400
> + * 670     msec:    WTCLK=01 WTIS=01     VAL= 0x410
> + * 1360    msec:    WTCLK=10 WTIS=00     VAL= 0x800
> + * 2700    msec:    WTCLK=01 WTIS=10     VAL= 0x420
> + * 5360    msec:    WTCLK=10 WTIS=01     VAL= 0x810
> + * 10700   msec:    WTCLK=01 WTIS=11     VAL= 0x430
> + * 21600   msec:    WTCLK=10 WTIS=10     VAL= 0x820
> + * 43000   msec:    WTCLK=11 WTIS=00     VAL= 0xC00
> + * 85600   msec:    WTCLK=10 WTIS=11     VAL= 0x830
> + * 172000  msec:    WTCLK=11 WTIS=01     VAL= 0xC10
> + * 687000  msec:    WTCLK=11 WTIS=10     VAL= 0xC20
> + * 2750000 msec:    WTCLK=11 WTIS=11     VAL= 0xC30
> + */
> +
> +struct npcm_wdt {
> +	struct watchdog_device  wdd;
> +	void __iomem		*reg;
> +};
> +
> +static inline struct npcm_wdt *to_npcm_wdt(struct watchdog_device *wdd)
> +{
> +	return container_of(wdd, struct npcm_wdt, wdd);
> +}
> +
> +static int npcm_wdt_ping(struct watchdog_device *wdd)
> +{
> +	struct npcm_wdt *wdt = to_npcm_wdt(wdd);
> +	u32 val;
> +
> +	val = readl(wdt->reg);
> +	writel(val | NPCM_WTR, wdt->reg);
> +
> +	return 0;
> +}
> +
> +static int npcm_wdt_start(struct watchdog_device *wdd)
> +{
> +	struct npcm_wdt *wdt = to_npcm_wdt(wdd);
> +	u32 val;
> +
> +	if (wdd->timeout < 2)
> +		val = 0x800;
> +	else if (wdd->timeout < 3)
> +		val = 0x420;
> +	else if (wdd->timeout < 6)
> +		val = 0x810;
> +	else if (wdd->timeout < 11)
> +		val = 0x430;
> +	else if (wdd->timeout < 22)
> +		val = 0x820;
> +	else if (wdd->timeout < 44)
> +		val = 0xC00;
> +	else if (wdd->timeout < 87)
> +		val = 0x830;
> +	else if (wdd->timeout < 173)
> +		val = 0xC10;
> +	else if (wdd->timeout < 688)
> +		val = 0xC20;
> +	else
> +		val = 0xC30;
> +
> +	val |= NPCM_WTRE | NPCM_WTE | NPCM_WTR | NPCM_WTIE;
> +
> +	writel(val, wdt->reg);
> +
> +	return 0;
> +}
> +
> +static int npcm_wdt_stop(struct watchdog_device *wdd)
> +{
> +	struct npcm_wdt *wdt = to_npcm_wdt(wdd);
> +
> +	writel(0, wdt->reg);
> +
> +	return 0;
> +}
> +
> +
> +static int npcm_wdt_set_timeout(struct watchdog_device *wdd,
> +				unsigned int timeout)
> +{
> +	if (timeout < 2)
> +		wdd->timeout = 1;
> +	else if (timeout < 3)
> +	      wdd->timeout = 2;
> +	else if (timeout < 6)
> +	      wdd->timeout = 5;
> +	else if (timeout < 11)
> +	      wdd->timeout = 10;
> +	else if (timeout < 22)
> +	      wdd->timeout = 21;
> +	else if (timeout < 44)
> +	      wdd->timeout = 43;
> +	else if (timeout < 87)
> +	      wdd->timeout = 86;
> +	else if (timeout < 173)
> +	      wdd->timeout = 172;
> +	else if (timeout < 688)
> +	      wdd->timeout = 687;
> +	else
> +	      wdd->timeout = 2750;
> +
> +	if (watchdog_active(wdd))
> +		npcm_wdt_start(wdd);
> +
> +	return 0;
> +}
> +
> +static irqreturn_t npcm_wdt_interrupt(int irq, void *data)
> +{
> +	struct npcm_wdt *wdt = data;
> +
> +	watchdog_notify_pretimeout(&wdt->wdd);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int npcm_wdt_restart(struct watchdog_device *wdd,
> +			    unsigned long action, void *data)
> +{
> +	struct npcm_wdt *wdt = to_npcm_wdt(wdd);
> +
> +	writel(NPCM_WTR | NPCM_WTRE | NPCM_WTE, wdt->reg);
> +	udelay(1000);
> +
> +	return 0;
> +}
> +
> +static bool npcm_is_running(struct watchdog_device *wdd)
> +{
> +	struct npcm_wdt *wdt = to_npcm_wdt(wdd);
> +
> +	return readl(wdt->reg) & NPCM_WTE;
> +}
> +
> +static const struct watchdog_info npcm_wdt_info = {
> +	.identity	= KBUILD_MODNAME,
> +	.options	= WDIOF_SETTIMEOUT
> +			| WDIOF_KEEPALIVEPING
> +			| WDIOF_MAGICCLOSE,
> +};
> +
> +static const struct watchdog_ops npcm_wdt_ops = {
> +	.owner = THIS_MODULE,
> +	.start = npcm_wdt_start,
> +	.stop = npcm_wdt_stop,
> +	.ping = npcm_wdt_ping,
> +	.set_timeout = npcm_wdt_set_timeout,
> +	.restart = npcm_wdt_restart,
> +};
> +
> +static int npcm_wdt_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct npcm_wdt *wdt;
> +	struct resource *res;
> +	int irq;
> +	int ret;
> +
> +	wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
> +	if (!wdt)
> +		return -ENOMEM;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	wdt->reg = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(wdt->reg))
> +		return PTR_ERR(wdt->reg);
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0)
> +		return irq;
> +
> +	wdt->wdd.info = &npcm_wdt_info;
> +	wdt->wdd.ops = &npcm_wdt_ops;
> +	wdt->wdd.min_timeout = 1;
> +	wdt->wdd.max_timeout = 2750;
> +	wdt->wdd.parent = dev;
> +
> +	wdt->wdd.timeout = 86;
> +	watchdog_init_timeout(&wdt->wdd, 0, dev);
> +
> +	/* Ensure timeout is able to be represented by the hardware */
> +	npcm_wdt_set_timeout(&wdt->wdd, wdt->wdd.timeout);
> +
> +	if (npcm_is_running(&wdt->wdd)) {
> +		/* Restart with the default or device-tree specified timeout */
> +		npcm_wdt_start(&wdt->wdd);
> +		set_bit(WDOG_HW_RUNNING, &wdt->wdd.status);
> +	}
> +
> +	ret = devm_request_irq(dev, irq, npcm_wdt_interrupt, 0,
> +			       "watchdog", wdt);
> +	if (ret)
> +		return ret;
> +
> +	ret = devm_watchdog_register_device(dev, &wdt->wdd);
> +	if (ret) {
> +		dev_err(dev, "failed to register watchdog\n");
> +		return ret;
> +	}
> +
> +	dev_info(dev, "NPCM watchdog driver enabled\n");
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id npcm_wdt_match[] = {
> +	{.compatible = "nuvoton,npcm750-wdt"},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, npcm_wdt_match);
> +#endif
> +
> +static struct platform_driver npcm_wdt_driver = {
> +	.probe		= npcm_wdt_probe,
> +	.driver		= {
> +		.name	= "npcm-wdt",
> +		.of_match_table = of_match_ptr(npcm_wdt_match),
> +	},
> +};
> +module_platform_driver(npcm_wdt_driver);
> +
> +MODULE_AUTHOR("Joel Stanley");
> +MODULE_DESCRIPTION("Watchdog driver for NPCM");
> +MODULE_LICENSE("GPL v2");
> -- 
> 2.15.1
> 
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux