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.fat...@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, &reg);
> +     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.hie...@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

Reply via email to