From: Roland Hieber <rhi@pengutronix.de>
To: Ahmad Fatoum <a.fatoum@pengutronix.de>
Cc: barebox@lists.infradead.org
Subject: Re: [PATCH 3/7] watchdog: add stm32 watchdog and reset driver
Date: Tue, 4 Jun 2019 19:23:33 +0200 [thread overview]
Message-ID: <20190604172333.37hfgytxvbq2morf@pengutronix.de> (raw)
In-Reply-To: <20190603190559.16715-3-a.fatoum@pengutronix.de>
On Mon, Jun 03, 2019 at 09:05:55PM +0200, Ahmad Fatoum wrote:
> The driver supports setting watchdog timeout, system reset
> and querying reset reason. Disabling watchdog isn't possible
> in hardware, thus users should either enable it before boot
> or have the poller take care of feeding it.
>
> Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
> ---
> .../mach-stm32mp/include/mach/reset-reason.h | 28 ++
> drivers/watchdog/Kconfig | 8 +
> drivers/watchdog/Makefile | 1 +
> drivers/watchdog/stm32_wdt.c | 288 ++++++++++++++++++
> 4 files changed, 325 insertions(+)
> create mode 100644 arch/arm/mach-stm32mp/include/mach/reset-reason.h
> create mode 100644 drivers/watchdog/stm32_wdt.c
>
> diff --git a/arch/arm/mach-stm32mp/include/mach/reset-reason.h b/arch/arm/mach-stm32mp/include/mach/reset-reason.h
> new file mode 100644
> index 000000000000..1165b347c31f
> --- /dev/null
> +++ b/arch/arm/mach-stm32mp/include/mach/reset-reason.h
> @@ -0,0 +1,28 @@
> +#ifndef __MACH_RESET_REASON_H__
> +#define __MACH_RESET_REASON_H__
> +
> +#include <reset_source.h>
> +
> +#define RCC_RSTF_POR BIT(0)
> +#define RCC_RSTF_BOR BIT(1)
> +#define RCC_RSTF_PAD BIT(2)
> +#define RCC_RSTF_HCSS BIT(3)
> +#define RCC_RSTF_VCORE BIT(4)
> +
> +#define RCC_RSTF_MPSYS BIT(6)
> +#define RCC_RSTF_MCSYS BIT(7)
> +#define RCC_RSTF_IWDG1 BIT(8)
> +#define RCC_RSTF_IWDG2 BIT(9)
> +
> +#define RCC_RSTF_STDBY BIT(11)
> +#define RCC_RSTF_CSTDBY BIT(12)
> +#define RCC_RSTF_MPUP0 BIT(13)
> +#define RCC_RSTF_MPUP1 BIT(14)
> +
> +struct stm32_reset_reason {
> + uint32_t mask;
> + enum reset_src_type type;
> + int instance;
> +};
> +
> +#endif
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index 04efb1a3c866..5a28b530099d 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -81,4 +81,12 @@ config RAVE_SP_WATCHDOG
> depends on RAVE_SP_CORE
> help
> Support for the watchdog on RAVE SP device.
> +
> +config STM32_IWDG_WATCHDOG
> + bool "Enable IWDG watchdog driver for STM32 processors"
"STM32 IWDG" should be enough, the other options in that menu are
also very short.
- Roland
> + depends on ARCH_STM32MP
> + select MFD_SYSCON
> + help
> + Enable the STM32 watchdog (IWDG) driver. Enable support to
> + configure STM32's on-SoC watchdog.
> endif
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index 6c8d36c8b805..b2f39fa3719e 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -11,3 +11,4 @@ obj-$(CONFIG_WATCHDOG_IMX) += imxwd.o
> obj-$(CONFIG_WATCHDOG_ORION) += orion_wdt.o
> obj-$(CONFIG_ARCH_BCM283X) += bcm2835_wdt.o
> obj-$(CONFIG_RAVE_SP_WATCHDOG) += rave-sp-wdt.o
> +obj-$(CONFIG_STM32_IWDG_WATCHDOG) += stm32_wdt.o
> diff --git a/drivers/watchdog/stm32_wdt.c b/drivers/watchdog/stm32_wdt.c
> new file mode 100644
> index 000000000000..a972aade5900
> --- /dev/null
> +++ b/drivers/watchdog/stm32_wdt.c
> @@ -0,0 +1,288 @@
> +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
> +/*
> + * Copyright (C) 2018, STMicroelectronics - All Rights Reserved
> + */
> +
> +#include <common.h>
> +#include <init.h>
> +#include <watchdog.h>
> +#include <restart.h>
> +#include <asm/io.h>
> +#include <of_device.h>
> +#include <linux/log2.h>
> +#include <linux/iopoll.h>
> +#include <linux/clk.h>
> +#include <mfd/syscon.h>
> +#include <mach/reset-reason.h>
> +
> +/* IWDG registers */
> +#define IWDG_KR 0x00 /* Key register */
> +#define IWDG_PR 0x04 /* Prescaler Register */
> +#define IWDG_RLR 0x08 /* ReLoad Register */
> +#define IWDG_SR 0x0C /* Status Register */
> +
> +/* IWDG_KR register bit mask */
> +#define KR_KEY_RELOAD 0xAAAA /* Reload counter enable */
> +#define KR_KEY_ENABLE 0xCCCC /* Peripheral enable */
> +#define KR_KEY_EWA 0x5555 /* Write access enable */
> +
> +/* IWDG_PR register bit values */
> +#define PR_SHIFT 2
> +
> +/* IWDG_RLR register values */
> +#define RLR_MAX GENMASK(11, 0)
> +
> +/* IWDG_SR register bit mask */
> +#define SR_PVU BIT(0) /* Watchdog prescaler value update */
> +#define SR_RVU BIT(1) /* Watchdog counter reload value update */
> +
> +#define RCC_MP_GRSTCSETR 0x404
> +#define RCC_MP_RSTSCLRR 0x408
> +#define RCC_MP_GRSTCSETR_MPSYSRST BIT(0)
> +
> +/* set timeout to 100 ms */
> +#define TIMEOUT_US 100000
> +
> +struct stm32_iwdg {
> + struct watchdog wdd;
> + struct restart_handler restart;
> + void __iomem *iwdg_base;
> + struct regmap *rcc_regmap;
> + unsigned int timeout;
> + unsigned int rate;
> +};
> +
> +static inline struct stm32_iwdg *to_stm32_iwdg(struct watchdog *wdd)
> +{
> + return container_of(wdd, struct stm32_iwdg, wdd);
> +}
> +
> +static void __noreturn stm32_iwdg_restart_handler(struct restart_handler *rst)
> +{
> + struct stm32_iwdg *wd = container_of(rst, struct stm32_iwdg, restart);
> +
> + regmap_update_bits(wd->rcc_regmap, RCC_MP_GRSTCSETR,
> + RCC_MP_GRSTCSETR_MPSYSRST, RCC_MP_GRSTCSETR_MPSYSRST);
> +
> + mdelay(1000);
> + hang();
> +}
> +
> +static void stm32_iwdg_ping(struct stm32_iwdg *wd)
> +{
> + writel(KR_KEY_RELOAD, wd->iwdg_base + IWDG_KR);
> +}
> +
> +static int stm32_iwdg_start(struct stm32_iwdg *wd, unsigned int timeout)
> +{
> + u32 presc, iwdg_rlr, iwdg_pr, iwdg_sr;
> + int ret;
> +
> + presc = DIV_ROUND_UP(timeout * wd->rate, RLR_MAX + 1);
> +
> + /* The prescaler is align on power of 2 and start at 2 ^ PR_SHIFT. */
> + presc = roundup_pow_of_two(presc);
> + iwdg_pr = presc <= 1 << PR_SHIFT ? 0 : ilog2(presc) - PR_SHIFT;
> + iwdg_rlr = ((timeout * wd->rate) / presc) - 1;
> +
> + /* enable write access */
> + writel(KR_KEY_EWA, wd->iwdg_base + IWDG_KR);
> +
> + /* set prescaler & reload registers */
> + writel(iwdg_pr, wd->iwdg_base + IWDG_PR);
> + writel(iwdg_rlr, wd->iwdg_base + IWDG_RLR);
> + writel(KR_KEY_ENABLE, wd->iwdg_base + IWDG_KR);
> +
> + /* wait for the registers to be updated (max 100ms) */
> + ret = readl_poll_timeout(wd->iwdg_base + IWDG_SR, iwdg_sr,
> + !(iwdg_sr & (SR_PVU | SR_RVU)),
> + TIMEOUT_US);
> + if (!ret)
> + wd->timeout = timeout;
> +
> + return ret;
> +}
> +
> +
> +static int stm32_iwdg_set_timeout(struct watchdog *wdd, unsigned int timeout)
> +{
> + struct stm32_iwdg *wd = to_stm32_iwdg(wdd);
> + int ret;
> +
> + if (!timeout)
> + return -EINVAL; /* can't disable */
> +
> + if (timeout > wdd->timeout_max)
> + return -EINVAL;
> +
> + if (wd->timeout != timeout) {
> + ret = stm32_iwdg_start(wd, timeout);
> + if (ret) {
> + dev_err(wdd->hwdev, "Fail to (re)start watchdog\n");
> + return ret;
> + }
> + }
> +
> + stm32_iwdg_ping(wd);
> + return 0;
> +}
> +
> +static const struct stm32_reset_reason stm32_reset_reasons[] = {
> + { RCC_RSTF_POR, RESET_POR, 0 },
> + { RCC_RSTF_BOR, RESET_BOR, 0 },
> + { RCC_RSTF_STDBY, RESET_WKE, 0 },
> + { RCC_RSTF_CSTDBY, RESET_WKE, 1 },
> + { RCC_RSTF_MPSYS, RESET_RST, 2 },
> + { RCC_RSTF_MPUP0, RESET_RST, 0 },
> + { RCC_RSTF_MPUP1, RESET_RST, 1 },
> + { RCC_RSTF_IWDG1, RESET_WDG, 0 },
> + { RCC_RSTF_IWDG2, RESET_WDG, 1 },
> + { RCC_RSTF_PAD, RESET_EXT, 1 },
> + { /* sentinel */ }
> +};
> +
> +static int stm32_set_reset_reason(struct regmap *rcc)
> +{
> + enum reset_src_type type = RESET_UKWN;
> + u32 reg;
> + int ret;
> + int i, instance = 0;
> +
> + /*
> + * SRSR register captures ALL reset event that occured since
> + * POR, so we need to clear it to make sure we only caputre
> + * the latest one.
> + */
> + ret = regmap_read(rcc, RCC_MP_RSTSCLRR, ®);
> + if (ret)
> + return ret;
> +
> + for (i = 0; stm32_reset_reasons[i].mask; i++) {
> + if (reg & stm32_reset_reasons[i].mask) {
> + type = stm32_reset_reasons[i].type;
> + instance = stm32_reset_reasons[i].instance;
> + break;
> + }
> + }
> +
> + reset_source_set_priority(type, RESET_SOURCE_DEFAULT_PRIORITY);
> + reset_source_set_instance(type, instance);
> +
> + pr_info("STM32 RCC reset reason %s (MP_RSTSR: 0x%08x)\n",
> + reset_source_name(), reg);
> +
> + return 0;
> +}
> +
> +struct stm32_iwdg_data {
> + bool has_pclk;
> + u32 max_prescaler;
> +};
> +
> +static const struct stm32_iwdg_data stm32_iwdg_data = {
> + .has_pclk = false, .max_prescaler = 256,
> +};
> +
> +static const struct stm32_iwdg_data stm32mp1_iwdg_data = {
> + .has_pclk = true, .max_prescaler = 1024,
> +};
> +
> +static const struct of_device_id stm32_iwdg_of_match[] = {
> + { .compatible = "st,stm32-iwdg", .data = &stm32_iwdg_data },
> + { .compatible = "st,stm32mp1-iwdg", .data = &stm32mp1_iwdg_data },
> + { /* sentinel */ }
> +};
> +
> +static int stm32_iwdg_probe(struct device_d *dev)
> +{
> + struct stm32_iwdg_data *data;
> + struct stm32_iwdg *wd;
> + struct resource *res;
> + struct watchdog *wdd;
> + struct clk *clk;
> + int ret;
> +
> + wd = xzalloc(sizeof(*wd));
> +
> + ret = dev_get_drvdata(dev, (const void **)&data);
> + if (ret)
> + return -ENODEV;
> +
> + res = dev_request_mem_resource(dev, 0);
> + if (IS_ERR(res)) {
> + dev_err(dev, "could not get timer memory region\n");
> + return PTR_ERR(res);
> + }
> + wd->iwdg_base = IOMEM(res->start);
> +
> + clk = of_clk_get_by_name(dev->device_node, "lsi");
> + if (IS_ERR(clk))
> + return PTR_ERR(clk);
> +
> + ret = clk_enable(clk);
> + if (ret)
> + return ret;
> +
> + wd->rate = clk_get_rate(clk);
> +
> + if (data->has_pclk) {
> + clk = of_clk_get_by_name(dev->device_node, "pclk");
> + if (IS_ERR(clk))
> + return PTR_ERR(clk);
> +
> + ret = clk_enable(clk);
> + if (ret)
> + return ret;
> + }
> +
> + wdd = &wd->wdd;
> + wdd->hwdev = dev;
> + wdd->set_timeout = stm32_iwdg_set_timeout;
> + wdd->timeout_max = (RLR_MAX + 1) * data->max_prescaler * 1000;
> + wdd->timeout_max /= wd->rate * 1000;
> + wdd->timeout_cur = wdd->timeout_max;
> +
> + ret = stm32_iwdg_set_timeout(wdd, wdd->timeout_max);
> + if (ret) {
> + dev_err(dev, "Failed to set initial watchdog timeout\n");
> + return ret;
> + }
> +
> + ret = watchdog_register(wdd);
> + if (ret) {
> + dev_err(dev, "Failed to register watchdog device\n");
> + return ret;
> + }
> +
> + wd->restart.name = "stm32-iwdg";
> + wd->restart.restart = stm32_iwdg_restart_handler;
> + wd->restart.priority = 200;
> +
> + wd->rcc_regmap = syscon_regmap_lookup_by_compatible("st,stm32mp1-rcc");
> + if (IS_ERR(wd->rcc_regmap)) {
> + dev_warn(dev, "Cannot register restart handler\n");
> + goto end;
> + }
> +
> + ret = restart_handler_register(&wd->restart);
> + if (ret) {
> + dev_warn(dev, "Cannot register restart handler\n");
> + goto end;
> + }
> +
> + ret = stm32_set_reset_reason(wd->rcc_regmap);
> + if (ret) {
> + dev_warn(dev, "Cannot determine reset reason\n");
> + goto end;
> + }
> +end:
> + dev_info(dev, "probed\n");
> + return 0;
> +}
> +
> +static struct driver_d stm32_iwdg_driver = {
> + .name = "stm32-iwdg",
> + .probe = stm32_iwdg_probe,
> + .of_compatible = DRV_OF_COMPAT(stm32_iwdg_of_match),
> +};
> +device_platform_driver(stm32_iwdg_driver);
> --
> 2.20.1
>
>
> _______________________________________________
> barebox mailing list
> barebox@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/barebox
>
--
Roland Hieber | r.hieber@pengutronix.de |
Pengutronix e.K. | https://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim | Phone: +49-5121-206917-5086 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
next prev parent reply other threads:[~2019-06-04 17:23 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-06-03 19:05 [PATCH 1/7] ARM: stm32mp1: rename to stm32mp Ahmad Fatoum
2019-06-03 19:05 ` [PATCH 2/7] reset_source: add new Brownout reset (BOR) source Ahmad Fatoum
2019-06-04 8:11 ` Sascha Hauer
2019-06-04 8:31 ` Ahmad Fatoum
2019-06-03 19:05 ` [PATCH 3/7] watchdog: add stm32 watchdog and reset driver Ahmad Fatoum
2019-06-04 8:24 ` Sascha Hauer
2019-06-04 8:51 ` Ahmad Fatoum
2019-06-04 11:07 ` Sascha Hauer
2019-06-04 17:23 ` Roland Hieber [this message]
2019-06-03 19:05 ` [PATCH 4/7] ARM: stm32mp1: enable watchdog in oftree and defconfig Ahmad Fatoum
2019-06-03 19:34 ` Sam Ravnborg
2019-06-03 19:46 ` Ahmad Fatoum
2019-06-03 19:05 ` [PATCH 5/7] ARM: stm32mp: stm32mp157c-dk2: configure debug_ll UART Ahmad Fatoum
2019-06-03 19:05 ` [PATCH 6/7] ARM: stm32mp: stm32mp157c-dk2: compress the DTB Ahmad Fatoum
2019-06-03 19:05 ` [PATCH 7/7] ARM: stm32mp: add entry point, not point.pblb to pblb-y Ahmad Fatoum
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20190604172333.37hfgytxvbq2morf@pengutronix.de \
--to=rhi@pengutronix.de \
--cc=a.fatoum@pengutronix.de \
--cc=barebox@lists.infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox