watchdog: stm32mp: Add watchdog driver

This patch adds IWDG (Independent WatchDoG) support for
STM32MP platform.

Signed-off-by: Christophe Kerello <christophe.kerello@st.com>
Signed-off-by: Patrice Chotard <patrice.chotard@st.com>
Reviewed-by: Stefan Roese <sr@denx.de>
This commit is contained in:
Patrice Chotard 2019-04-30 17:26:22 +02:00
parent 75500a4182
commit 8c1007a2cb
5 changed files with 146 additions and 0 deletions

View File

@ -312,6 +312,7 @@ F: drivers/ram/stm32mp1/
F: drivers/misc/stm32_rcc.c F: drivers/misc/stm32_rcc.c
F: drivers/reset/stm32-reset.c F: drivers/reset/stm32-reset.c
F: drivers/spi/stm32_qspi.c F: drivers/spi/stm32_qspi.c
F: drivers/watchdog/stm32mp_wdt.c
ARM STM STV0991 ARM STM STV0991
M: Vikas Manocha <vikas.manocha@st.com> M: Vikas Manocha <vikas.manocha@st.com>

View File

@ -17,6 +17,7 @@ config SPL
select SPL_DM_RESET select SPL_DM_RESET
select SPL_SERIAL_SUPPORT select SPL_SERIAL_SUPPORT
select SPL_SYSCON select SPL_SYSCON
select SPL_WATCHDOG_SUPPORT
imply BOOTSTAGE_STASH if SPL_BOOTSTAGE imply BOOTSTAGE_STASH if SPL_BOOTSTAGE
imply SPL_BOOTSTAGE if BOOTSTAGE imply SPL_BOOTSTAGE if BOOTSTAGE
imply SPL_DISPLAY_PRINT imply SPL_DISPLAY_PRINT

View File

@ -154,6 +154,14 @@ config WDT_SP805
Select this to enable SP805 watchdog timer, which can be found on some Select this to enable SP805 watchdog timer, which can be found on some
nxp layerscape chips. nxp layerscape chips.
config WDT_STM32MP
bool "IWDG watchdog driver for STM32 MP's family"
depends on WDT
imply WATCHDOG
help
Enable the STM32 watchdog (IWDG) driver. Enable support to
configure STM32's on-SoC watchdog.
config XILINX_TB_WATCHDOG config XILINX_TB_WATCHDOG
bool "Xilinx Axi watchdog timer support" bool "Xilinx Axi watchdog timer support"
depends on WDT depends on WDT

View File

@ -28,3 +28,4 @@ obj-$(CONFIG_WDT_MPC8xx) += mpc8xx_wdt.o
obj-$(CONFIG_WDT_MT7621) += mt7621_wdt.o obj-$(CONFIG_WDT_MT7621) += mt7621_wdt.o
obj-$(CONFIG_WDT_MTK) += mtk_wdt.o obj-$(CONFIG_WDT_MTK) += mtk_wdt.o
obj-$(CONFIG_WDT_SP805) += sp805_wdt.o obj-$(CONFIG_WDT_SP805) += sp805_wdt.o
obj-$(CONFIG_WDT_STM32MP) += stm32mp_wdt.o

View File

@ -0,0 +1,135 @@
// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
/*
* Copyright (C) 2019, STMicroelectronics - All Rights Reserved
*/
#include <common.h>
#include <clk.h>
#include <dm.h>
#include <syscon.h>
#include <wdt.h>
#include <asm/io.h>
#include <linux/iopoll.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_256 0x06 /* Prescaler set to 256 */
/* IWDG_RLR register values */
#define RLR_MAX 0xFFF /* Max value supported by reload register */
/* IWDG_SR register bit values */
#define SR_PVU BIT(0) /* Watchdog prescaler value update */
#define SR_RVU BIT(1) /* Watchdog counter reload value update */
struct stm32mp_wdt_priv {
fdt_addr_t base; /* registers addr in physical memory */
unsigned long wdt_clk_rate; /* Watchdog dedicated clock rate */
};
static int stm32mp_wdt_reset(struct udevice *dev)
{
struct stm32mp_wdt_priv *priv = dev_get_priv(dev);
writel(KR_KEY_RELOAD, priv->base + IWDG_KR);
return 0;
}
static int stm32mp_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
{
struct stm32mp_wdt_priv *priv = dev_get_priv(dev);
int reload;
u32 val;
int ret;
/* Prescaler fixed to 256 */
reload = timeout_ms * priv->wdt_clk_rate / 256;
if (reload > RLR_MAX + 1)
/* Force to max watchdog counter reload value */
reload = RLR_MAX + 1;
else if (!reload)
/* Force to min watchdog counter reload value */
reload = priv->wdt_clk_rate / 256;
/* Set prescaler & reload registers */
writel(KR_KEY_EWA, priv->base + IWDG_KR);
writel(PR_256, priv->base + IWDG_PR);
writel(reload - 1, priv->base + IWDG_RLR);
/* Enable watchdog */
writel(KR_KEY_ENABLE, priv->base + IWDG_KR);
/* Wait for the registers to be updated */
ret = readl_poll_timeout(priv->base + IWDG_SR, val,
val & (SR_PVU | SR_RVU), CONFIG_SYS_HZ);
if (ret < 0) {
pr_err("Updating IWDG registers timeout");
return -ETIMEDOUT;
}
return 0;
}
static int stm32mp_wdt_probe(struct udevice *dev)
{
struct stm32mp_wdt_priv *priv = dev_get_priv(dev);
struct clk clk;
int ret;
debug("IWDG init\n");
priv->base = devfdt_get_addr(dev);
if (priv->base == FDT_ADDR_T_NONE)
return -EINVAL;
/* Enable clock */
ret = clk_get_by_name(dev, "pclk", &clk);
if (ret)
return ret;
ret = clk_enable(&clk);
if (ret)
return ret;
/* Get LSI clock */
ret = clk_get_by_name(dev, "lsi", &clk);
if (ret)
return ret;
priv->wdt_clk_rate = clk_get_rate(&clk);
debug("IWDG init done\n");
return 0;
}
static const struct wdt_ops stm32mp_wdt_ops = {
.start = stm32mp_wdt_start,
.reset = stm32mp_wdt_reset,
};
static const struct udevice_id stm32mp_wdt_match[] = {
{ .compatible = "st,stm32mp1-iwdg" },
{ /* sentinel */ }
};
U_BOOT_DRIVER(stm32mp_wdt) = {
.name = "stm32mp-wdt",
.id = UCLASS_WDT,
.of_match = stm32mp_wdt_match,
.priv_auto_alloc_size = sizeof(struct stm32mp_wdt_priv),
.probe = stm32mp_wdt_probe,
.ops = &stm32mp_wdt_ops,
};