GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)

This commit is contained in:
lai
2026-09-06 03:52:57 +08:00
commit b1928b41c0
21813 changed files with 4413081 additions and 0 deletions
@@ -0,0 +1,186 @@
menu "Watchdog Timer Support"
config WATCHDOG
bool "Enable U-Boot watchdog reset"
help
This option enables U-Boot watchdog support where U-Boot is using
watchdog_reset function to service watchdog device in U-Boot. Enable
this option if you want to service enabled watchdog by U-Boot. Disable
this option if you want U-Boot to start watchdog but never service it.
config WATCHDOG_TIMEOUT_MSECS
int "Watchdog timeout in msec"
default 128000 if ARCH_MX25 || ARCH_MX31 || ARCH_MX5 || ARCH_MX6
default 128000 if ARCH_MX7 || ARCH_VF610
default 30000 if ARCH_SOCFPGA
default 60000
help
Watchdog timeout in msec
config HW_WATCHDOG
bool
config WATCHDOG_RESET_DISABLE
bool "Disable reset watchdog"
help
Disable reset watchdog, which can let WATCHDOG_RESET invalid, so
that the watchdog will not be fed in u-boot.
config IMX_WATCHDOG
bool "Enable Watchdog Timer support for IMX and LSCH2 of NXP"
select HW_WATCHDOG if !WDT
help
Select this to enable the IMX and LSCH2 of Layerscape watchdog
driver.
config OMAP_WATCHDOG
bool "TI OMAP watchdog driver"
depends on ARCH_OMAP2PLUS
select HW_WATCHDOG
help
Say Y here to enable the OMAP3+ watchdog driver.
config ULP_WATCHDOG
bool "i.MX7ULP watchdog"
help
Say Y here to enable i.MX7ULP watchdog driver.
config WDT
bool "Enable driver model for watchdog timer drivers"
depends on DM
imply WATCHDOG
help
Enable driver model for watchdog timer. At the moment the API
is very simple and only supports four operations:
start, stop, reset and expire_now (expire immediately).
What exactly happens when the timer expires is up to a particular
device/driver.
config WDT_ARMADA_37XX
bool "Marvell Armada 37xx watchdog timer support"
depends on WDT && ARMADA_3700
help
Enable this to support Watchdog Timer on Marvell Armada 37xx SoC.
There are 4 possible clocks which can be used on these SoCs. This
driver uses the second clock (ID 1), assuming that so will also
Linux's driver.
config WDT_ASPEED
bool "Aspeed ast2400/ast2500 watchdog timer support"
depends on WDT
default y if ARCH_ASPEED
help
Select this to enable watchdog timer for Aspeed ast2500/ast2400 devices.
The watchdog timer is stopped when initialized. It performs reset, either
full SoC reset or CPU or just some peripherals, based on the flags.
It currently does not support Boot Flash Addressing Mode Detection or
Second Boot.
config WDT_AT91
bool "AT91 watchdog timer support"
depends on WDT
help
Select this to enable Microchip watchdog timer, which can be found on
some AT91 devices.
config WDT_BCM6345
bool "BCM6345 watchdog timer support"
depends on WDT && (ARCH_BMIPS || ARCH_BCM6858 || ARCH_BCM63158)
help
Select this to enable watchdog timer for BCM6345 SoCs.
The watchdog timer is stopped when initialized.
It performs full SoC reset.
config WDT_CDNS
bool "Cadence watchdog timer support"
depends on WDT
imply WATCHDOG
help
Select this to enable Cadence watchdog timer, which can be found on some
Xilinx Microzed Platform.
config WDT_MPC8xx
bool "MPC8xx watchdog timer support"
depends on WDT && MPC8xx
select CONFIG_MPC8xx_WATCHDOG
help
Select this to enable mpc8xx watchdog timer
config WDT_MT7621
bool "MediaTek MT7621 watchdog timer support"
depends on WDT && SOC_MT7628
help
Select this to enable Ralink / Mediatek watchdog timer,
which can be found on some MediaTek chips.
config WDT_MTK
bool "MediaTek watchdog timer support"
depends on WDT && ARCH_MEDIATEK
help
Select this to enable watchdog timer for MediaTek SoCs.
The watchdog timer is stopped when initialized.
It performs full SoC reset.
config WDT_OMAP3
bool "TI OMAP watchdog timer support"
depends on WDT && ARCH_OMAP2PLUS
default y if AM33XX
help
This enables OMAP3+ watchdog timer driver, which can be
found on some TI chipsets and inline with driver model.
config WDT_ORION
bool "Orion watchdog timer support"
depends on WDT
select CLK
help
Select this to enable Orion watchdog timer, which can be found on some
Marvell Armada chips.
config WDT_SANDBOX
bool "Enable Watchdog Timer support for Sandbox"
depends on SANDBOX && WDT
help
Enable Watchdog Timer support in Sandbox. This is a dummy device that
can be probed and supports all of the methods of WDT, but does not
really do anything.
config WDT_SP805
bool "SP805 watchdog timer support"
depends on WDT
help
Select this to enable SP805 watchdog timer, which can be found on some
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
bool "Xilinx Axi watchdog timer support"
depends on WDT
imply WATCHDOG
help
Select this to enable Xilinx Axi watchdog timer, which can be found on some
Xilinx Microblaze Platforms.
config WDT_TANGIER
bool "Intel Tangier watchdog timer support"
depends on WDT && INTEL_MID
help
This enables support for watchdog controller available on
Intel Tangier SoC. If you're using a board with Intel Tangier
SoC, say Y here.
config SPL_WDT
bool "Enable driver model for watchdog timer drivers in SPL"
depends on SPL_DM
help
Enable driver model for watchdog timer in SPL.
This is similar to CONFIG_WDT in U-Boot.
endmenu
@@ -0,0 +1,31 @@
# SPDX-License-Identifier: GPL-2.0+
#
# (C) Copyright 2008
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
obj-$(CONFIG_WDT_AT91) += at91sam9_wdt.o
obj-$(CONFIG_FTWDT010_WATCHDOG) += ftwdt010_wdt.o
ifneq (,$(filter $(SOC), mx25 mx31 mx35 mx5 mx6 mx7 vf610))
obj-y += imx_watchdog.o
else
obj-$(CONFIG_IMX_WATCHDOG) += imx_watchdog.o
endif
obj-$(CONFIG_S5P) += s5p_wdt.o
obj-$(CONFIG_XILINX_TB_WATCHDOG) += xilinx_tb_wdt.o
obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o
obj-$(CONFIG_DESIGNWARE_WATCHDOG) += designware_wdt.o
obj-$(CONFIG_ULP_WATCHDOG) += ulp_wdog.o
obj-$(CONFIG_$(SPL_TPL_)WDT) += wdt-uclass.o
obj-$(CONFIG_WDT_SANDBOX) += sandbox_wdt.o
obj-$(CONFIG_WDT_ARMADA_37XX) += armada-37xx-wdt.o
obj-$(CONFIG_WDT_ASPEED) += ast_wdt.o
obj-$(CONFIG_WDT_BCM6345) += bcm6345_wdt.o
obj-$(CONFIG_WDT_ORION) += orion_wdt.o
obj-$(CONFIG_WDT_CDNS) += cdns_wdt.o
obj-$(CONFIG_WDT_MPC8xx) += mpc8xx_wdt.o
obj-$(CONFIG_WDT_MT7621) += mt7621_wdt.o
obj-$(CONFIG_WDT_MTK) += mtk_wdt.o
obj-$(CONFIG_WDT_OMAP3) += omap_wdt.o
obj-$(CONFIG_WDT_SP805) += sp805_wdt.o
obj-$(CONFIG_WDT_STM32MP) += stm32mp_wdt.o
obj-$(CONFIG_WDT_TANGIER) += tangier_wdt.o
@@ -0,0 +1,200 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Marvell Armada 37xx SoC Watchdog Driver
*
* Marek Behun <marek.behun@nic.cz>
*/
#include <common.h>
#include <dm.h>
#include <wdt.h>
#include <asm/io.h>
#include <asm/arch/cpu.h>
#include <asm/arch/soc.h>
DECLARE_GLOBAL_DATA_PTR;
struct a37xx_wdt {
void __iomem *sel_reg;
void __iomem *reg;
ulong clk_rate;
u64 timeout;
};
/*
* We use Counter 1 as watchdog timer, and Counter 0 for re-triggering Counter 1
*/
#define CNTR_CTRL(id) ((id) * 0x10)
#define CNTR_CTRL_ENABLE 0x0001
#define CNTR_CTRL_ACTIVE 0x0002
#define CNTR_CTRL_MODE_MASK 0x000c
#define CNTR_CTRL_MODE_ONESHOT 0x0000
#define CNTR_CTRL_MODE_HWSIG 0x000c
#define CNTR_CTRL_TRIG_SRC_MASK 0x00f0
#define CNTR_CTRL_TRIG_SRC_PREV_CNTR 0x0050
#define CNTR_CTRL_PRESCALE_MASK 0xff00
#define CNTR_CTRL_PRESCALE_MIN 2
#define CNTR_CTRL_PRESCALE_SHIFT 8
#define CNTR_COUNT_LOW(id) (CNTR_CTRL(id) + 0x4)
#define CNTR_COUNT_HIGH(id) (CNTR_CTRL(id) + 0x8)
static void set_counter_value(struct a37xx_wdt *priv, int id, u64 val)
{
writel(val & 0xffffffff, priv->reg + CNTR_COUNT_LOW(id));
writel(val >> 32, priv->reg + CNTR_COUNT_HIGH(id));
}
static void counter_enable(struct a37xx_wdt *priv, int id)
{
setbits_le32(priv->reg + CNTR_CTRL(id), CNTR_CTRL_ENABLE);
}
static void counter_disable(struct a37xx_wdt *priv, int id)
{
clrbits_le32(priv->reg + CNTR_CTRL(id), CNTR_CTRL_ENABLE);
}
static int init_counter(struct a37xx_wdt *priv, int id, u32 mode, u32 trig_src)
{
u32 reg;
reg = readl(priv->reg + CNTR_CTRL(id));
if (reg & CNTR_CTRL_ACTIVE)
return -EBUSY;
reg &= ~(CNTR_CTRL_MODE_MASK | CNTR_CTRL_PRESCALE_MASK |
CNTR_CTRL_TRIG_SRC_MASK);
/* set mode */
reg |= mode;
/* set prescaler to the min value */
reg |= CNTR_CTRL_PRESCALE_MIN << CNTR_CTRL_PRESCALE_SHIFT;
/* set trigger source */
reg |= trig_src;
writel(reg, priv->reg + CNTR_CTRL(id));
return 0;
}
static int a37xx_wdt_reset(struct udevice *dev)
{
struct a37xx_wdt *priv = dev_get_priv(dev);
if (!priv->timeout)
return -EINVAL;
/* counter 1 is retriggered by forcing end count on counter 0 */
counter_disable(priv, 0);
counter_enable(priv, 0);
return 0;
}
static int a37xx_wdt_expire_now(struct udevice *dev, ulong flags)
{
struct a37xx_wdt *priv = dev_get_priv(dev);
/* first we set timeout to 0 */
counter_disable(priv, 1);
set_counter_value(priv, 1, 0);
counter_enable(priv, 1);
/* and then we start counter 1 by forcing end count on counter 0 */
counter_disable(priv, 0);
counter_enable(priv, 0);
return 0;
}
static int a37xx_wdt_start(struct udevice *dev, u64 ms, ulong flags)
{
struct a37xx_wdt *priv = dev_get_priv(dev);
int err;
err = init_counter(priv, 0, CNTR_CTRL_MODE_ONESHOT, 0);
if (err < 0)
return err;
err = init_counter(priv, 1, CNTR_CTRL_MODE_HWSIG,
CNTR_CTRL_TRIG_SRC_PREV_CNTR);
if (err < 0)
return err;
priv->timeout = ms * priv->clk_rate / 1000 / CNTR_CTRL_PRESCALE_MIN;
set_counter_value(priv, 0, 0);
set_counter_value(priv, 1, priv->timeout);
counter_enable(priv, 1);
/* we have to force end count on counter 0 to start counter 1 */
counter_enable(priv, 0);
return 0;
}
static int a37xx_wdt_stop(struct udevice *dev)
{
struct a37xx_wdt *priv = dev_get_priv(dev);
counter_disable(priv, 1);
counter_disable(priv, 0);
writel(0, priv->sel_reg);
return 0;
}
static int a37xx_wdt_probe(struct udevice *dev)
{
struct a37xx_wdt *priv = dev_get_priv(dev);
fdt_addr_t addr;
addr = dev_read_addr_index(dev, 0);
if (addr == FDT_ADDR_T_NONE)
goto err;
priv->sel_reg = (void __iomem *)addr;
addr = dev_read_addr_index(dev, 1);
if (addr == FDT_ADDR_T_NONE)
goto err;
priv->reg = (void __iomem *)addr;
priv->clk_rate = (ulong)get_ref_clk() * 1000000;
/*
* We use counter 1 as watchdog timer, therefore we only set bit
* TIMER1_IS_WCHDOG_TIMER. Counter 0 is only used to force re-trigger on
* counter 1.
*/
writel(1 << 1, priv->sel_reg);
return 0;
err:
dev_err(dev, "no io address\n");
return -ENODEV;
}
static const struct wdt_ops a37xx_wdt_ops = {
.start = a37xx_wdt_start,
.reset = a37xx_wdt_reset,
.stop = a37xx_wdt_stop,
.expire_now = a37xx_wdt_expire_now,
};
static const struct udevice_id a37xx_wdt_ids[] = {
{ .compatible = "marvell,armada-3700-wdt" },
{}
};
U_BOOT_DRIVER(a37xx_wdt) = {
.name = "armada_37xx_wdt",
.id = UCLASS_WDT,
.of_match = a37xx_wdt_ids,
.probe = a37xx_wdt_probe,
.priv_auto_alloc_size = sizeof(struct a37xx_wdt),
.ops = &a37xx_wdt_ops,
};
@@ -0,0 +1,128 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2017 Google, Inc
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <wdt.h>
#include <asm/io.h>
#include <asm/arch/wdt.h>
#define WDT_AST2500 2500
#define WDT_AST2400 2400
struct ast_wdt_priv {
struct ast_wdt *regs;
};
static int ast_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
{
struct ast_wdt_priv *priv = dev_get_priv(dev);
ulong driver_data = dev_get_driver_data(dev);
u32 reset_mode = ast_reset_mode_from_flags(flags);
/* 32 bits at 1MHz is 4294967ms */
timeout = min_t(u64, timeout, 4294967);
/* WDT counts in ticks of 1MHz clock. 1ms / 1e3 * 1e6 */
timeout *= 1000;
clrsetbits_le32(&priv->regs->ctrl,
WDT_CTRL_RESET_MASK << WDT_CTRL_RESET_MODE_SHIFT,
reset_mode << WDT_CTRL_RESET_MODE_SHIFT);
if (driver_data >= WDT_AST2500 && reset_mode == WDT_CTRL_RESET_SOC)
writel(ast_reset_mask_from_flags(flags),
&priv->regs->reset_mask);
writel((u32) timeout, &priv->regs->counter_reload_val);
writel(WDT_COUNTER_RESTART_VAL, &priv->regs->counter_restart);
/*
* Setting CLK1MHZ bit is just for compatibility with ast2400 part.
* On ast2500 watchdog timer clock is fixed at 1MHz and the bit is
* read-only
*/
setbits_le32(&priv->regs->ctrl,
WDT_CTRL_EN | WDT_CTRL_RESET | WDT_CTRL_CLK1MHZ);
return 0;
}
static int ast_wdt_stop(struct udevice *dev)
{
struct ast_wdt_priv *priv = dev_get_priv(dev);
clrbits_le32(&priv->regs->ctrl, WDT_CTRL_EN);
writel(WDT_RESET_DEFAULT, &priv->regs->reset_mask);
return 0;
}
static int ast_wdt_reset(struct udevice *dev)
{
struct ast_wdt_priv *priv = dev_get_priv(dev);
writel(WDT_COUNTER_RESTART_VAL, &priv->regs->counter_restart);
return 0;
}
static int ast_wdt_expire_now(struct udevice *dev, ulong flags)
{
struct ast_wdt_priv *priv = dev_get_priv(dev);
int ret;
ret = ast_wdt_start(dev, 1, flags);
if (ret)
return ret;
while (readl(&priv->regs->ctrl) & WDT_CTRL_EN)
;
return ast_wdt_stop(dev);
}
static int ast_wdt_ofdata_to_platdata(struct udevice *dev)
{
struct ast_wdt_priv *priv = dev_get_priv(dev);
priv->regs = devfdt_get_addr_ptr(dev);
if (IS_ERR(priv->regs))
return PTR_ERR(priv->regs);
return 0;
}
static const struct wdt_ops ast_wdt_ops = {
.start = ast_wdt_start,
.reset = ast_wdt_reset,
.stop = ast_wdt_stop,
.expire_now = ast_wdt_expire_now,
};
static const struct udevice_id ast_wdt_ids[] = {
{ .compatible = "aspeed,wdt", .data = WDT_AST2500 },
{ .compatible = "aspeed,ast2500-wdt", .data = WDT_AST2500 },
{ .compatible = "aspeed,ast2400-wdt", .data = WDT_AST2400 },
{}
};
static int ast_wdt_probe(struct udevice *dev)
{
debug("%s() wdt%u\n", __func__, dev->seq);
ast_wdt_stop(dev);
return 0;
}
U_BOOT_DRIVER(ast_wdt) = {
.name = "ast_wdt",
.id = UCLASS_WDT,
.of_match = ast_wdt_ids,
.probe = ast_wdt_probe,
.priv_auto_alloc_size = sizeof(struct ast_wdt_priv),
.ofdata_to_platdata = ast_wdt_ofdata_to_platdata,
.ops = &ast_wdt_ops,
};
@@ -0,0 +1,122 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* [origin: Linux kernel drivers/watchdog/at91sam9_wdt.c]
*
* Watchdog driver for AT91SAM9x processors.
*
* Copyright (C) 2008 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
* Copyright (C) 2008 Renaud CERRATO r.cerrato@til-technologies.fr
*/
/*
* The Watchdog Timer Mode Register can be only written to once. If the
* timeout need to be set from U-Boot, be sure that the bootstrap doesn't
* write to this register. Inform Linux to it too
*/
#include <asm/io.h>
#include <asm/arch/at91_wdt.h>
#include <common.h>
#include <div64.h>
#include <dm.h>
#include <errno.h>
#include <wdt.h>
DECLARE_GLOBAL_DATA_PTR;
/*
* AT91SAM9 watchdog runs a 12bit counter @ 256Hz,
* use this to convert a watchdog
* value from seconds.
*/
#define WDT_SEC2TICKS(s) (((s) << 8) - 1)
/*
* Set the watchdog time interval in 1/256Hz (write-once)
* Counter is 12 bit.
*/
static int at91_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
{
struct at91_wdt_priv *priv = dev_get_priv(dev);
u64 timeout;
u32 ticks;
/* Calculate timeout in seconds and the resulting ticks */
timeout = timeout_ms;
do_div(timeout, 1000);
timeout = min_t(u64, timeout, WDT_MAX_TIMEOUT);
ticks = WDT_SEC2TICKS(timeout);
/* Check if disabled */
if (readl(priv->regs + AT91_WDT_MR) & AT91_WDT_MR_WDDIS) {
printf("sorry, watchdog is disabled\n");
return -1;
}
/*
* All counting occurs at SLOW_CLOCK / 128 = 256 Hz
*
* Since WDV is a 12-bit counter, the maximum period is
* 4096 / 256 = 16 seconds.
*/
priv->regval = AT91_WDT_MR_WDRSTEN /* causes watchdog reset */
| AT91_WDT_MR_WDDBGHLT /* disabled in debug mode */
| AT91_WDT_MR_WDD(0xfff) /* restart at any time */
| AT91_WDT_MR_WDV(ticks); /* timer value */
writel(priv->regval, priv->regs + AT91_WDT_MR);
return 0;
}
static int at91_wdt_stop(struct udevice *dev)
{
struct at91_wdt_priv *priv = dev_get_priv(dev);
/* Disable Watchdog Timer */
priv->regval |= AT91_WDT_MR_WDDIS;
writel(priv->regval, priv->regs + AT91_WDT_MR);
return 0;
}
static int at91_wdt_reset(struct udevice *dev)
{
struct at91_wdt_priv *priv = dev_get_priv(dev);
writel(AT91_WDT_CR_WDRSTT | AT91_WDT_CR_KEY, priv->regs + AT91_WDT_CR);
return 0;
}
static const struct wdt_ops at91_wdt_ops = {
.start = at91_wdt_start,
.stop = at91_wdt_stop,
.reset = at91_wdt_reset,
};
static const struct udevice_id at91_wdt_ids[] = {
{ .compatible = "atmel,at91sam9260-wdt" },
{}
};
static int at91_wdt_probe(struct udevice *dev)
{
struct at91_wdt_priv *priv = dev_get_priv(dev);
priv->regs = dev_remap_addr(dev);
if (!priv->regs)
return -EINVAL;
debug("%s: Probing wdt%u\n", __func__, dev->seq);
return 0;
}
U_BOOT_DRIVER(at91_wdt) = {
.name = "at91_wdt",
.id = UCLASS_WDT,
.of_match = at91_wdt_ids,
.priv_auto_alloc_size = sizeof(struct at91_wdt_priv),
.ops = &at91_wdt_ops,
.probe = at91_wdt_probe,
};
@@ -0,0 +1,116 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com>
*
* Derived from linux/drivers/watchdog/bcm63xx_wdt.c:
* Copyright (C) 2007 Miguel Gaio <miguel.gaio@efixo.com>
* Copyright (C) 2008 Florian Fainelli <florian@openwrt.org>
*/
#include <common.h>
#include <dm.h>
#include <wdt.h>
#include <clk.h>
#include <asm/io.h>
/* WDT Value register */
#define WDT_VAL_REG 0x0
#define WDT_VAL_MIN 0x00000002
#define WDT_VAL_MAX 0xfffffffe
/* WDT Control register */
#define WDT_CTL_REG 0x4
#define WDT_CTL_START1_MASK 0x0000ff00
#define WDT_CTL_START2_MASK 0x000000ff
#define WDT_CTL_STOP1_MASK 0x0000ee00
#define WDT_CTL_STOP2_MASK 0x000000ee
struct bcm6345_wdt_priv {
void __iomem *regs;
unsigned long clk_rate;
};
static int bcm6345_wdt_reset(struct udevice *dev)
{
struct bcm6345_wdt_priv *priv = dev_get_priv(dev);
writel(WDT_CTL_START1_MASK, priv->regs + WDT_CTL_REG);
writel(WDT_CTL_START2_MASK, priv->regs + WDT_CTL_REG);
return 0;
}
static int bcm6345_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
{
struct bcm6345_wdt_priv *priv = dev_get_priv(dev);
u32 val = priv->clk_rate / 1000 * timeout;
if (val < WDT_VAL_MIN) {
debug("watchdog won't fire with less than 2 ticks\n");
val = WDT_VAL_MIN;
} else if (val > WDT_VAL_MAX) {
debug("maximum watchdog timeout exceeded\n");
val = WDT_VAL_MAX;
}
writel(val, priv->regs + WDT_VAL_REG);
return bcm6345_wdt_reset(dev);
}
static int bcm6345_wdt_expire_now(struct udevice *dev, ulong flags)
{
return bcm6345_wdt_start(dev, WDT_VAL_MIN, flags);
}
static int bcm6345_wdt_stop(struct udevice *dev)
{
struct bcm6345_wdt_priv *priv = dev_get_priv(dev);
writel(WDT_CTL_STOP1_MASK, priv->regs + WDT_CTL_REG);
writel(WDT_CTL_STOP2_MASK, priv->regs + WDT_CTL_REG);
return 0;
}
static const struct wdt_ops bcm6345_wdt_ops = {
.expire_now = bcm6345_wdt_expire_now,
.reset = bcm6345_wdt_reset,
.start = bcm6345_wdt_start,
.stop = bcm6345_wdt_stop,
};
static const struct udevice_id bcm6345_wdt_ids[] = {
{ .compatible = "brcm,bcm6345-wdt" },
{ /* sentinel */ }
};
static int bcm6345_wdt_probe(struct udevice *dev)
{
struct bcm6345_wdt_priv *priv = dev_get_priv(dev);
struct clk clk;
int ret;
priv->regs = dev_remap_addr(dev);
if (!priv->regs)
return -EINVAL;
ret = clk_get_by_index(dev, 0, &clk);
if (!ret)
priv->clk_rate = clk_get_rate(&clk);
else
return -EINVAL;
bcm6345_wdt_stop(dev);
return 0;
}
U_BOOT_DRIVER(wdt_bcm6345) = {
.name = "wdt_bcm6345",
.id = UCLASS_WDT,
.of_match = bcm6345_wdt_ids,
.ops = &bcm6345_wdt_ops,
.priv_auto_alloc_size = sizeof(struct bcm6345_wdt_priv),
.probe = bcm6345_wdt_probe,
};
@@ -0,0 +1,265 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Cadence WDT driver - Used by Xilinx Zynq
* Reference: Linux kernel Cadence watchdog driver.
*
* Author(s): Shreenidhi Shedi <yesshedi@gmail.com>
*/
#include <common.h>
#include <dm.h>
#include <wdt.h>
#include <clk.h>
#include <div64.h>
#include <linux/io.h>
DECLARE_GLOBAL_DATA_PTR;
struct cdns_regs {
u32 zmr; /* WD Zero mode register, offset - 0x0 */
u32 ccr; /* Counter Control Register offset - 0x4 */
u32 restart; /* Restart key register, offset - 0x8 */
u32 status; /* Status Register, offset - 0xC */
};
struct cdns_wdt_priv {
bool rst;
struct cdns_regs *regs;
};
#define CDNS_WDT_DEFAULT_TIMEOUT 10
/* Supports 1 - 516 sec */
#define CDNS_WDT_MIN_TIMEOUT 1
#define CDNS_WDT_MAX_TIMEOUT 516
/* Restart key */
#define CDNS_WDT_RESTART_KEY 0x00001999
/* Counter register access key */
#define CDNS_WDT_REGISTER_ACCESS_KEY 0x00920000
/* Counter value divisor */
#define CDNS_WDT_COUNTER_VALUE_DIVISOR 0x1000
/* Clock prescaler value and selection */
#define CDNS_WDT_PRESCALE_64 64
#define CDNS_WDT_PRESCALE_512 512
#define CDNS_WDT_PRESCALE_4096 4096
#define CDNS_WDT_PRESCALE_SELECT_64 1
#define CDNS_WDT_PRESCALE_SELECT_512 2
#define CDNS_WDT_PRESCALE_SELECT_4096 3
/* Input clock frequency */
#define CDNS_WDT_CLK_75MHZ 75000000
/* Counter maximum value */
#define CDNS_WDT_COUNTER_MAX 0xFFF
/********************* Register Map **********************************/
/*
* Zero Mode Register - This register controls how the time out is indicated
* and also contains the access code to allow writes to the register (0xABC).
*/
#define CDNS_WDT_ZMR_WDEN_MASK 0x00000001 /* Enable the WDT */
#define CDNS_WDT_ZMR_RSTEN_MASK 0x00000002 /* Enable the reset output */
#define CDNS_WDT_ZMR_IRQEN_MASK 0x00000004 /* Enable IRQ output */
#define CDNS_WDT_ZMR_RSTLEN_16 0x00000030 /* Reset pulse of 16 pclk cycles */
#define CDNS_WDT_ZMR_ZKEY_VAL 0x00ABC000 /* Access key, 0xABC << 12 */
/*
* Counter Control register - This register controls how fast the timer runs
* and the reset value and also contains the access code to allow writes to
* the register.
*/
#define CDNS_WDT_CCR_CRV_MASK 0x00003FFC /* Counter reset value */
/* Write access to Registers */
static inline void cdns_wdt_writereg(u32 *addr, u32 val)
{
writel(val, addr);
}
/**
* cdns_wdt_reset - Reload the watchdog timer (i.e. pat the watchdog).
*
* @dev: Watchdog device
*
* Write the restart key value (0x00001999) to the restart register.
*
* Return: Always 0
*/
static int cdns_wdt_reset(struct udevice *dev)
{
struct cdns_wdt_priv *priv = dev_get_priv(dev);
debug("%s\n", __func__);
cdns_wdt_writereg(&priv->regs->restart, CDNS_WDT_RESTART_KEY);
return 0;
}
/**
* cdns_wdt_start - Enable and start the watchdog.
*
* @dev: Watchdog device
* @timeout: Timeout value
* @flags: Driver flags
*
* The counter value is calculated according to the formula:
* count = (timeout * clock) / prescaler + 1.
*
* The calculated count is divided by 0x1000 to obtain the field value
* to write to counter control register.
*
* Clears the contents of prescaler and counter reset value. Sets the
* prescaler to 4096 and the calculated count and access key
* to write to CCR Register.
*
* Sets the WDT (WDEN bit) and either the Reset signal(RSTEN bit)
* or Interrupt signal(IRQEN) with a specified cycles and the access
* key to write to ZMR Register.
*
* Return: Upon success 0, failure -1.
*/
static int cdns_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
{
ulong clk_f;
u32 count, prescaler, ctrl_clksel, data = 0;
struct clk clock;
struct cdns_wdt_priv *priv = dev_get_priv(dev);
if (clk_get_by_index(dev, 0, &clock) < 0) {
dev_err(dev, "failed to get clock\n");
return -1;
}
clk_f = clk_get_rate(&clock);
if (IS_ERR_VALUE(clk_f)) {
dev_err(dev, "failed to get rate\n");
return -1;
}
/* Calculate timeout in seconds and restrict to min and max value */
do_div(timeout, 1000);
timeout = max_t(u64, timeout, CDNS_WDT_MIN_TIMEOUT);
timeout = min_t(u64, timeout, CDNS_WDT_MAX_TIMEOUT);
debug("%s: CLK_FREQ %ld, timeout %lld\n", __func__, clk_f, timeout);
if (clk_f <= CDNS_WDT_CLK_75MHZ) {
prescaler = CDNS_WDT_PRESCALE_512;
ctrl_clksel = CDNS_WDT_PRESCALE_SELECT_512;
} else {
prescaler = CDNS_WDT_PRESCALE_4096;
ctrl_clksel = CDNS_WDT_PRESCALE_SELECT_4096;
}
/*
* Counter value divisor to obtain the value of
* counter reset to be written to control register.
*/
count = (timeout * (clk_f / prescaler)) /
CDNS_WDT_COUNTER_VALUE_DIVISOR + 1;
if (count > CDNS_WDT_COUNTER_MAX)
count = CDNS_WDT_COUNTER_MAX;
cdns_wdt_writereg(&priv->regs->zmr, CDNS_WDT_ZMR_ZKEY_VAL);
count = (count << 2) & CDNS_WDT_CCR_CRV_MASK;
/* Write counter access key first to be able write to register */
data = count | CDNS_WDT_REGISTER_ACCESS_KEY | ctrl_clksel;
cdns_wdt_writereg(&priv->regs->ccr, data);
data = CDNS_WDT_ZMR_WDEN_MASK | CDNS_WDT_ZMR_RSTLEN_16 |
CDNS_WDT_ZMR_ZKEY_VAL;
/* Reset on timeout if specified in device tree. */
if (priv->rst) {
data |= CDNS_WDT_ZMR_RSTEN_MASK;
data &= ~CDNS_WDT_ZMR_IRQEN_MASK;
} else {
data &= ~CDNS_WDT_ZMR_RSTEN_MASK;
data |= CDNS_WDT_ZMR_IRQEN_MASK;
}
cdns_wdt_writereg(&priv->regs->zmr, data);
cdns_wdt_writereg(&priv->regs->restart, CDNS_WDT_RESTART_KEY);
return 0;
}
/**
* cdns_wdt_stop - Stop the watchdog.
*
* @dev: Watchdog device
*
* Read the contents of the ZMR register, clear the WDEN bit in the register
* and set the access key for successful write.
*
* Return: Always 0
*/
static int cdns_wdt_stop(struct udevice *dev)
{
struct cdns_wdt_priv *priv = dev_get_priv(dev);
cdns_wdt_writereg(&priv->regs->zmr,
CDNS_WDT_ZMR_ZKEY_VAL & (~CDNS_WDT_ZMR_WDEN_MASK));
return 0;
}
/**
* cdns_wdt_probe - Probe call for the device.
*
* @dev: Handle to the udevice structure.
*
* Return: Always 0.
*/
static int cdns_wdt_probe(struct udevice *dev)
{
debug("%s: Probing wdt%u\n", __func__, dev->seq);
return 0;
}
static int cdns_wdt_ofdata_to_platdata(struct udevice *dev)
{
struct cdns_wdt_priv *priv = dev_get_priv(dev);
priv->regs = (struct cdns_regs *)dev_read_addr(dev);
if (IS_ERR(priv->regs))
return PTR_ERR(priv->regs);
priv->rst = dev_read_bool(dev, "reset-on-timeout");
debug("%s: reset %d\n", __func__, priv->rst);
return 0;
}
static const struct wdt_ops cdns_wdt_ops = {
.start = cdns_wdt_start,
.reset = cdns_wdt_reset,
.stop = cdns_wdt_stop,
/* There is no bit/reg/support in IP for expire_now functionality */
};
static const struct udevice_id cdns_wdt_ids[] = {
{ .compatible = "cdns,wdt-r1p2" },
{}
};
U_BOOT_DRIVER(cdns_wdt) = {
.name = "cdns_wdt",
.id = UCLASS_WDT,
.of_match = cdns_wdt_ids,
.probe = cdns_wdt_probe,
.priv_auto_alloc_size = sizeof(struct cdns_wdt_priv),
.ofdata_to_platdata = cdns_wdt_ofdata_to_platdata,
.ops = &cdns_wdt_ops,
};
@@ -0,0 +1,73 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2013 Altera Corporation <www.altera.com>
*/
#include <common.h>
#include <watchdog.h>
#include <asm/io.h>
#include <asm/utils.h>
#define DW_WDT_CR 0x00
#define DW_WDT_TORR 0x04
#define DW_WDT_CRR 0x0C
#define DW_WDT_CR_EN_OFFSET 0x00
#define DW_WDT_CR_RMOD_OFFSET 0x01
#define DW_WDT_CR_RMOD_VAL 0x00
#define DW_WDT_CRR_RESTART_VAL 0x76
/*
* Set the watchdog time interval.
* Counter is 32 bit.
*/
static int designware_wdt_settimeout(unsigned int timeout)
{
signed int i;
/* calculate the timeout range value */
i = (log_2_n_round_up(timeout * CONFIG_DW_WDT_CLOCK_KHZ)) - 16;
if (i > 15)
i = 15;
if (i < 0)
i = 0;
writel((i | (i << 4)), (CONFIG_DW_WDT_BASE + DW_WDT_TORR));
return 0;
}
static void designware_wdt_enable(void)
{
writel(((DW_WDT_CR_RMOD_VAL << DW_WDT_CR_RMOD_OFFSET) |
(0x1 << DW_WDT_CR_EN_OFFSET)),
(CONFIG_DW_WDT_BASE + DW_WDT_CR));
}
static unsigned int designware_wdt_is_enabled(void)
{
unsigned long val;
val = readl((CONFIG_DW_WDT_BASE + DW_WDT_CR));
return val & 0x1;
}
#if defined(CONFIG_HW_WATCHDOG)
void hw_watchdog_reset(void)
{
if (designware_wdt_is_enabled())
/* restart the watchdog counter */
writel(DW_WDT_CRR_RESTART_VAL,
(CONFIG_DW_WDT_BASE + DW_WDT_CRR));
}
void hw_watchdog_init(void)
{
/* reset to disable the watchdog */
hw_watchdog_reset();
/* set timer in miliseconds */
designware_wdt_settimeout(CONFIG_WATCHDOG_TIMEOUT_MSECS);
/* enable the watchdog */
designware_wdt_enable();
/* reset the watchdog */
hw_watchdog_reset();
}
#endif
@@ -0,0 +1,91 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Watchdog driver for the FTWDT010 Watch Dog Driver
*
* (c) Copyright 2004 Faraday Technology Corp. (www.faraday-tech.com)
* Based on sa1100_wdt.c by Oleg Drokin <green@crimea.edu>
* Based on SoftDog driver by Alan Cox <alan@redhat.com>
*
* Copyright (C) 2011 Andes Technology Corporation
* Macpaul Lin, Andes Technology Corporation <macpaul@andestech.com>
*
* 27/11/2004 Initial release, Faraday.
* 12/01/2011 Port to u-boot, Macpaul Lin.
*/
#include <common.h>
#include <watchdog.h>
#include <asm/io.h>
#include <faraday/ftwdt010_wdt.h>
/*
* Set the watchdog time interval.
* Counter is 32 bit.
*/
int ftwdt010_wdt_settimeout(unsigned int timeout)
{
unsigned int reg;
struct ftwdt010_wdt *wd = (struct ftwdt010_wdt *)CONFIG_FTWDT010_BASE;
debug("Activating WDT..\n");
/* Check if disabled */
if (readl(&wd->wdcr) & ~FTWDT010_WDCR_ENABLE) {
printf("sorry, watchdog is disabled\n");
return -1;
}
/*
* In a 66MHz system,
* if you set WDLOAD as 0x03EF1480 (66000000)
* the reset timer is 1 second.
*/
reg = FTWDT010_WDLOAD(timeout * FTWDT010_TIMEOUT_FACTOR);
writel(reg, &wd->wdload);
return 0;
}
void ftwdt010_wdt_reset(void)
{
struct ftwdt010_wdt *wd = (struct ftwdt010_wdt *)CONFIG_FTWDT010_BASE;
/* clear control register */
writel(0, &wd->wdcr);
/* Write Magic number */
writel(FTWDT010_WDRESTART_MAGIC, &wd->wdrestart);
/* Enable WDT */
writel((FTWDT010_WDCR_RST | FTWDT010_WDCR_ENABLE), &wd->wdcr);
}
void ftwdt010_wdt_disable(void)
{
struct ftwdt010_wdt *wd = (struct ftwdt010_wdt *)CONFIG_FTWDT010_BASE;
debug("Deactivating WDT..\n");
/*
* It was defined with CONFIG_WATCHDOG_NOWAYOUT in Linux
*
* Shut off the timer.
* Lock it in if it's a module and we defined ...NOWAYOUT
*/
writel(0, &wd->wdcr);
}
#if defined(CONFIG_HW_WATCHDOG)
void hw_watchdog_reset(void)
{
ftwdt010_wdt_reset();
}
void hw_watchdog_init(void)
{
/* set timer in ms */
ftwdt010_wdt_settimeout(CONFIG_FTWDT010_HW_TIMEOUT * 1000);
}
#endif
@@ -0,0 +1,166 @@
/*
* watchdog.c - driver for i.mx on-chip watchdog
*
* Licensed under the GPL-2 or later.
*/
#include <common.h>
#include <dm.h>
#include <asm/io.h>
#include <wdt.h>
#include <watchdog.h>
#include <asm/arch/imx-regs.h>
#ifdef CONFIG_FSL_LSCH2
#include <asm/arch/immap_lsch2.h>
#endif
#include <fsl_wdog.h>
static void imx_watchdog_expire_now(struct watchdog_regs *wdog, bool ext_reset)
{
u16 wcr = WCR_WDE;
if (ext_reset)
wcr |= WCR_SRS; /* do not assert internal reset */
else
wcr |= WCR_WDA; /* do not assert external reset */
/* Write 3 times to ensure it works, due to IMX6Q errata ERR004346 */
writew(wcr, &wdog->wcr);
writew(wcr, &wdog->wcr);
writew(wcr, &wdog->wcr);
while (1) {
/*
* spin before reset
*/
}
}
#if !defined(CONFIG_IMX_WATCHDOG) || \
(defined(CONFIG_IMX_WATCHDOG) && !CONFIG_IS_ENABLED(WDT))
void __attribute__((weak)) reset_cpu(ulong addr)
{
struct watchdog_regs *wdog = (struct watchdog_regs *)WDOG1_BASE_ADDR;
imx_watchdog_expire_now(wdog, true);
}
#endif
#if defined(CONFIG_IMX_WATCHDOG)
static void imx_watchdog_reset(struct watchdog_regs *wdog)
{
#ifndef CONFIG_WATCHDOG_RESET_DISABLE
writew(0x5555, &wdog->wsr);
writew(0xaaaa, &wdog->wsr);
#endif /* CONFIG_WATCHDOG_RESET_DISABLE*/
}
static void imx_watchdog_init(struct watchdog_regs *wdog, bool ext_reset)
{
u16 timeout;
u16 wcr;
/*
* The timer watchdog can be set between
* 0.5 and 128 Seconds. If not defined
* in configuration file, sets 128 Seconds
*/
#ifndef CONFIG_WATCHDOG_TIMEOUT_MSECS
#define CONFIG_WATCHDOG_TIMEOUT_MSECS 128000
#endif
timeout = (CONFIG_WATCHDOG_TIMEOUT_MSECS / 500) - 1;
#ifdef CONFIG_FSL_LSCH2
wcr = (WCR_WDA | WCR_SRS | WCR_WDE) << 8 | timeout;
#else
wcr = WCR_WDZST | WCR_WDBG | WCR_WDE | WCR_SRS |
WCR_WDA | SET_WCR_WT(timeout);
if (ext_reset)
wcr |= WCR_WDT;
#endif /* CONFIG_FSL_LSCH2*/
writew(wcr, &wdog->wcr);
imx_watchdog_reset(wdog);
}
#if !CONFIG_IS_ENABLED(WDT)
void hw_watchdog_reset(void)
{
struct watchdog_regs *wdog = (struct watchdog_regs *)WDOG1_BASE_ADDR;
imx_watchdog_reset(wdog);
}
void hw_watchdog_init(void)
{
struct watchdog_regs *wdog = (struct watchdog_regs *)WDOG1_BASE_ADDR;
imx_watchdog_init(wdog, true);
}
#else
struct imx_wdt_priv {
void __iomem *base;
bool ext_reset;
};
static int imx_wdt_reset(struct udevice *dev)
{
struct imx_wdt_priv *priv = dev_get_priv(dev);
imx_watchdog_reset(priv->base);
return 0;
}
static int imx_wdt_expire_now(struct udevice *dev, ulong flags)
{
struct imx_wdt_priv *priv = dev_get_priv(dev);
imx_watchdog_expire_now(priv->base, priv->ext_reset);
hang();
return 0;
}
static int imx_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
{
struct imx_wdt_priv *priv = dev_get_priv(dev);
imx_watchdog_init(priv->base, priv->ext_reset);
return 0;
}
static int imx_wdt_probe(struct udevice *dev)
{
struct imx_wdt_priv *priv = dev_get_priv(dev);
priv->base = dev_read_addr_ptr(dev);
if (!priv->base)
return -ENOENT;
priv->ext_reset = dev_read_bool(dev, "fsl,ext-reset-output");
return 0;
}
static const struct wdt_ops imx_wdt_ops = {
.start = imx_wdt_start,
.reset = imx_wdt_reset,
.expire_now = imx_wdt_expire_now,
};
static const struct udevice_id imx_wdt_ids[] = {
{ .compatible = "fsl,imx21-wdt" },
{}
};
U_BOOT_DRIVER(imx_wdt) = {
.name = "imx_wdt",
.id = UCLASS_WDT,
.of_match = imx_wdt_ids,
.probe = imx_wdt_probe,
.ops = &imx_wdt_ops,
.priv_auto_alloc_size = sizeof(struct imx_wdt_priv),
.flags = DM_FLAG_PRE_RELOC,
};
#endif
#endif
@@ -0,0 +1,67 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2017 CS Systemes d'Information
*/
#include <common.h>
#include <dm.h>
#include <wdt.h>
#include <mpc8xx.h>
#include <asm/cpm_8xx.h>
#include <asm/io.h>
static void hw_watchdog_reset(void)
{
immap_t __iomem *immap = (immap_t __iomem *)CONFIG_SYS_IMMR;
out_be16(&immap->im_siu_conf.sc_swsr, 0x556c); /* write magic1 */
out_be16(&immap->im_siu_conf.sc_swsr, 0xaa39); /* write magic2 */
}
static int mpc8xx_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
{
immap_t __iomem *immap = (immap_t __iomem *)CONFIG_SYS_IMMR;
out_be32(&immap->im_siu_conf.sc_sypcr, CONFIG_SYS_SYPCR);
if (!(in_be32(&immap->im_siu_conf.sc_sypcr) & SYPCR_SWE))
return -EBUSY;
return 0;
}
static int mpc8xx_wdt_stop(struct udevice *dev)
{
immap_t __iomem *immap = (immap_t __iomem *)CONFIG_SYS_IMMR;
out_be32(&immap->im_siu_conf.sc_sypcr, CONFIG_SYS_SYPCR & ~SYPCR_SWE);
if (in_be32(&immap->im_siu_conf.sc_sypcr) & SYPCR_SWE)
return -EBUSY;
return 0;
}
static int mpc8xx_wdt_reset(struct udevice *dev)
{
hw_watchdog_reset();
return 0;
}
static const struct wdt_ops mpc8xx_wdt_ops = {
.start = mpc8xx_wdt_start,
.reset = mpc8xx_wdt_reset,
.stop = mpc8xx_wdt_stop,
};
static const struct udevice_id mpc8xx_wdt_ids[] = {
{ .compatible = "fsl,pq1-wdt" },
{}
};
U_BOOT_DRIVER(wdt_mpc8xx) = {
.name = "wdt_mpc8xx",
.id = UCLASS_WDT,
.of_match = mpc8xx_wdt_ids,
.ops = &mpc8xx_wdt_ops,
};
@@ -0,0 +1,102 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Ralink / Mediatek RT288x/RT3xxx/MT76xx built-in hardware watchdog timer
*
* Copyright (C) 2018 Stefan Roese <sr@denx.de>
*
* Based on the Linux driver version which is:
* Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2013 John Crispin <blogic@openwrt.org>
*/
#include <common.h>
#include <dm.h>
#include <wdt.h>
#include <linux/io.h>
DECLARE_GLOBAL_DATA_PTR;
struct mt762x_wdt {
void __iomem *regs;
};
#define TIMER_REG_TMRSTAT 0x00
#define TIMER_REG_TMR1CTL 0x20
#define TIMER_REG_TMR1LOAD 0x24
#define TMR1CTL_ENABLE BIT(7)
#define TMR1CTL_RESTART BIT(9)
#define TMR1CTL_PRESCALE_SHIFT 16
static int mt762x_wdt_ping(struct mt762x_wdt *priv)
{
writel(TMR1CTL_RESTART, priv->regs + TIMER_REG_TMRSTAT);
return 0;
}
static int mt762x_wdt_start(struct udevice *dev, u64 ms, ulong flags)
{
struct mt762x_wdt *priv = dev_get_priv(dev);
/* set the prescaler to 1ms == 1000us */
writel(1000 << TMR1CTL_PRESCALE_SHIFT, priv->regs + TIMER_REG_TMR1CTL);
writel(ms, priv->regs + TIMER_REG_TMR1LOAD);
setbits_le32(priv->regs + TIMER_REG_TMR1CTL, TMR1CTL_ENABLE);
return 0;
}
static int mt762x_wdt_stop(struct udevice *dev)
{
struct mt762x_wdt *priv = dev_get_priv(dev);
mt762x_wdt_ping(priv);
clrbits_le32(priv->regs + TIMER_REG_TMR1CTL, TMR1CTL_ENABLE);
return 0;
}
static int mt762x_wdt_reset(struct udevice *dev)
{
struct mt762x_wdt *priv = dev_get_priv(dev);
mt762x_wdt_ping(priv);
return 0;
}
static int mt762x_wdt_probe(struct udevice *dev)
{
struct mt762x_wdt *priv = dev_get_priv(dev);
priv->regs = dev_remap_addr(dev);
if (!priv->regs)
return -EINVAL;
mt762x_wdt_stop(dev);
return 0;
}
static const struct wdt_ops mt762x_wdt_ops = {
.start = mt762x_wdt_start,
.reset = mt762x_wdt_reset,
.stop = mt762x_wdt_stop,
};
static const struct udevice_id mt762x_wdt_ids[] = {
{ .compatible = "mediatek,mt7621-wdt" },
{}
};
U_BOOT_DRIVER(mt762x_wdt) = {
.name = "mt762x_wdt",
.id = UCLASS_WDT,
.of_match = mt762x_wdt_ids,
.probe = mt762x_wdt_probe,
.priv_auto_alloc_size = sizeof(struct mt762x_wdt),
.ops = &mt762x_wdt_ops,
};
@@ -0,0 +1,156 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Watchdog driver for MediaTek SoCs
*
* Copyright (C) 2018 MediaTek Inc.
* Author: Ryder Lee <ryder.lee@mediatek.com>
*/
#include <common.h>
#include <dm.h>
#include <wdt.h>
#include <asm/io.h>
#define MTK_WDT_MODE 0x00
#define MTK_WDT_LENGTH 0x04
#define MTK_WDT_RESTART 0x08
#define MTK_WDT_STATUS 0x0c
#define MTK_WDT_INTERVAL 0x10
#define MTK_WDT_SWRST 0x14
#define MTK_WDT_REQ_MODE 0x30
#define MTK_WDT_DEBUG_CTL 0x40
#define WDT_MODE_KEY (0x22 << 24)
#define WDT_MODE_EN BIT(0)
#define WDT_MODE_EXTPOL BIT(1)
#define WDT_MODE_EXTEN BIT(2)
#define WDT_MODE_IRQ_EN BIT(3)
#define WDT_MODE_DUAL_EN BIT(6)
#define WDT_LENGTH_KEY 0x8
#define WDT_LENGTH_TIMEOUT(n) ((n) << 5)
#define WDT_RESTART_KEY 0x1971
#define WDT_SWRST_KEY 0x1209
struct mtk_wdt_priv {
void __iomem *base;
};
static int mtk_wdt_reset(struct udevice *dev)
{
struct mtk_wdt_priv *priv = dev_get_priv(dev);
/* Reload watchdog duration */
writel(WDT_RESTART_KEY, priv->base + MTK_WDT_RESTART);
return 0;
}
static int mtk_wdt_stop(struct udevice *dev)
{
struct mtk_wdt_priv *priv = dev_get_priv(dev);
clrsetbits_le32(priv->base + MTK_WDT_MODE, WDT_MODE_EN, WDT_MODE_KEY);
return 0;
}
static int mtk_wdt_expire_now(struct udevice *dev, ulong flags)
{
struct mtk_wdt_priv *priv = dev_get_priv(dev);
/* Kick watchdog to prevent counter == 0 */
writel(WDT_RESTART_KEY, priv->base + MTK_WDT_RESTART);
/* Reset */
writel(WDT_SWRST_KEY, priv->base + MTK_WDT_SWRST);
hang();
return 0;
}
static void mtk_wdt_set_timeout(struct udevice *dev, u64 timeout_ms)
{
struct mtk_wdt_priv *priv = dev_get_priv(dev);
u64 timeout_us;
u32 timeout_cc;
u32 length;
/*
* One WDT_LENGTH count is 512 ticks of the wdt clock
* Clock runs at 32768 Hz
* e.g. 15.625 ms per count (nominal)
* We want the ceiling after dividing timeout_ms by 15.625 ms
* We add 15624 prior to the divide to implement the ceiling
* We prevent over-flow by clamping the timeout_ms value here
* as the maximum WDT_LENGTH counts is 1023 -> 15.984375 sec
* We also enforce a minimum of 1 count
* Many watchdog peripherals have a self-imposed count of 1
* that is added to the register counts.
* The MediaTek docs lack details to know if this is the case here.
* So we enforce a minimum of 1 to guarantee operation.
*/
if (timeout_ms > 15984)
timeout_ms = 15984;
timeout_us = timeout_ms * 1000;
timeout_cc = (15624 + timeout_us) / 15625;
if (timeout_cc == 0)
timeout_cc = 1;
length = WDT_LENGTH_TIMEOUT(timeout_cc) | WDT_LENGTH_KEY;
writel(length, priv->base + MTK_WDT_LENGTH);
}
static int mtk_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
{
struct mtk_wdt_priv *priv = dev_get_priv(dev);
mtk_wdt_set_timeout(dev, timeout_ms);
mtk_wdt_reset(dev);
/* Enable watchdog reset signal */
setbits_le32(priv->base + MTK_WDT_MODE,
WDT_MODE_EN | WDT_MODE_KEY | WDT_MODE_EXTEN);
return 0;
}
static int mtk_wdt_probe(struct udevice *dev)
{
struct mtk_wdt_priv *priv = dev_get_priv(dev);
priv->base = dev_read_addr_ptr(dev);
if (!priv->base)
return -ENOENT;
/* Clear status */
clrsetbits_le32(priv->base + MTK_WDT_MODE,
WDT_MODE_IRQ_EN | WDT_MODE_EXTPOL, WDT_MODE_KEY);
return mtk_wdt_stop(dev);
}
static const struct wdt_ops mtk_wdt_ops = {
.start = mtk_wdt_start,
.reset = mtk_wdt_reset,
.stop = mtk_wdt_stop,
.expire_now = mtk_wdt_expire_now,
};
static const struct udevice_id mtk_wdt_ids[] = {
{ .compatible = "mediatek,wdt"},
{}
};
U_BOOT_DRIVER(mtk_wdt) = {
.name = "mtk_wdt",
.id = UCLASS_WDT,
.of_match = mtk_wdt_ids,
.priv_auto_alloc_size = sizeof(struct mtk_wdt_priv),
.probe = mtk_wdt_probe,
.ops = &mtk_wdt_ops,
.flags = DM_FLAG_PRE_RELOC,
};
@@ -0,0 +1,257 @@
// SPDX-License-Identifier: GPL-2.0
/*
* omap_wdt.c
*
* (C) Copyright 2013
* Heiko Schocher, DENX Software Engineering, hs@denx.de.
*
* Based on:
*
* Watchdog driver for the TI OMAP 16xx & 24xx/34xx 32KHz (non-secure) watchdog
*
* commit 2d991a164a61858012651e13c59521975504e260
* Author: Bill Pemberton <wfp5p@virginia.edu>
* Date: Mon Nov 19 13:21:41 2012 -0500
*
* watchdog: remove use of __devinit
*
* CONFIG_HOTPLUG is going away as an option so __devinit is no longer
* needed.
*
* Author: MontaVista Software, Inc.
* <gdavis@mvista.com> or <source@mvista.com>
*
* History:
*
* 20030527: George G. Davis <gdavis@mvista.com>
* Initially based on linux-2.4.19-rmk7-pxa1/drivers/char/sa1100_wdt.c
* (c) Copyright 2000 Oleg Drokin <green@crimea.edu>
* Based on SoftDog driver by Alan Cox <alan@lxorguk.ukuu.org.uk>
*
* Copyright (c) 2004 Texas Instruments.
* 1. Modified to support OMAP1610 32-KHz watchdog timer
* 2. Ported to 2.6 kernel
*
* Copyright (c) 2005 David Brownell
* Use the driver model and standard identifiers; handle bigger timeouts.
*/
#include <common.h>
#include <watchdog.h>
#include <asm/arch/hardware.h>
#include <asm/io.h>
#include <asm/processor.h>
#include <asm/arch/cpu.h>
#include <wdt.h>
#include <dm.h>
#include <errno.h>
/* Hardware timeout in seconds */
#define WDT_HW_TIMEOUT 60
#if !CONFIG_IS_ENABLED(WDT)
static unsigned int wdt_trgr_pattern = 0x1234;
void hw_watchdog_reset(void)
{
struct wd_timer *wdt = (struct wd_timer *)WDT_BASE;
/*
* Somebody just triggered watchdog reset and write to WTGR register
* is in progress. It is resetting right now, no need to trigger it
* again
*/
if ((readl(&wdt->wdtwwps)) & WDT_WWPS_PEND_WTGR)
return;
wdt_trgr_pattern = ~wdt_trgr_pattern;
writel(wdt_trgr_pattern, &wdt->wdtwtgr);
/*
* Don't wait for posted write to complete, i.e. don't check
* WDT_WWPS_PEND_WTGR bit in WWPS register. There is no writes to
* WTGR register outside of this func, and if entering it
* we see WDT_WWPS_PEND_WTGR bit set, it means watchdog reset
* was just triggered. This prevents us from wasting time in busy
* polling of WDT_WWPS_PEND_WTGR bit.
*/
}
static int omap_wdt_set_timeout(unsigned int timeout)
{
struct wd_timer *wdt = (struct wd_timer *)WDT_BASE;
u32 pre_margin = GET_WLDR_VAL(timeout);
/* just count up at 32 KHz */
while (readl(&wdt->wdtwwps) & WDT_WWPS_PEND_WLDR)
;
writel(pre_margin, &wdt->wdtwldr);
while (readl(&wdt->wdtwwps) & WDT_WWPS_PEND_WLDR)
;
return 0;
}
void hw_watchdog_disable(void)
{
struct wd_timer *wdt = (struct wd_timer *)WDT_BASE;
/*
* Disable watchdog
*/
writel(0xAAAA, &wdt->wdtwspr);
while (readl(&wdt->wdtwwps) != 0x0)
;
writel(0x5555, &wdt->wdtwspr);
while (readl(&wdt->wdtwwps) != 0x0)
;
}
void hw_watchdog_init(void)
{
struct wd_timer *wdt = (struct wd_timer *)WDT_BASE;
/*
* Make sure the watchdog is disabled. This is unfortunately required
* because writing to various registers with the watchdog running has no
* effect.
*/
hw_watchdog_disable();
/* initialize prescaler */
while (readl(&wdt->wdtwwps) & WDT_WWPS_PEND_WCLR)
;
writel(WDT_WCLR_PRE | (PTV << WDT_WCLR_PTV_OFF), &wdt->wdtwclr);
while (readl(&wdt->wdtwwps) & WDT_WWPS_PEND_WCLR)
;
omap_wdt_set_timeout(WDT_HW_TIMEOUT);
/* Sequence to enable the watchdog */
writel(0xBBBB, &wdt->wdtwspr);
while ((readl(&wdt->wdtwwps)) & WDT_WWPS_PEND_WSPR)
;
writel(0x4444, &wdt->wdtwspr);
while ((readl(&wdt->wdtwwps)) & WDT_WWPS_PEND_WSPR)
;
}
void watchdog_reset(void)
{
hw_watchdog_reset();
}
#else
static int omap3_wdt_reset(struct udevice *dev)
{
struct omap3_wdt_priv *priv = dev_get_priv(dev);
/*
* Somebody just triggered watchdog reset and write to WTGR register
* is in progress. It is resetting right now, no need to trigger it
* again
*/
if ((readl(&priv->regs->wdtwwps)) & WDT_WWPS_PEND_WTGR)
return 0;
priv->wdt_trgr_pattern = ~(priv->wdt_trgr_pattern);
writel(priv->wdt_trgr_pattern, &priv->regs->wdtwtgr);
/*
* Don't wait for posted write to complete, i.e. don't check
* WDT_WWPS_PEND_WTGR bit in WWPS register. There is no writes to
* WTGR register outside of this func, and if entering it
* we see WDT_WWPS_PEND_WTGR bit set, it means watchdog reset
* was just triggered. This prevents us from wasting time in busy
* polling of WDT_WWPS_PEND_WTGR bit.
*/
return 0;
}
static int omap3_wdt_stop(struct udevice *dev)
{
struct omap3_wdt_priv *priv = dev_get_priv(dev);
/* disable watchdog */
writel(0xAAAA, &priv->regs->wdtwspr);
while (readl(&priv->regs->wdtwwps) != 0x0)
;
writel(0x5555, &priv->regs->wdtwspr);
while (readl(&priv->regs->wdtwwps) != 0x0)
;
return 0;
}
static int omap3_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
{
struct omap3_wdt_priv *priv = dev_get_priv(dev);
u32 pre_margin = GET_WLDR_VAL(timeout_ms);
/*
* Make sure the watchdog is disabled. This is unfortunately required
* because writing to various registers with the watchdog running has no
* effect.
*/
omap3_wdt_stop(dev);
/* initialize prescaler */
while (readl(&priv->regs->wdtwwps) & WDT_WWPS_PEND_WCLR)
;
writel(WDT_WCLR_PRE | (PTV << WDT_WCLR_PTV_OFF), &priv->regs->wdtwclr);
while (readl(&priv->regs->wdtwwps) & WDT_WWPS_PEND_WCLR)
;
/* just count up at 32 KHz */
while (readl(&priv->regs->wdtwwps) & WDT_WWPS_PEND_WLDR)
;
writel(pre_margin, &priv->regs->wdtwldr);
while (readl(&priv->regs->wdtwwps) & WDT_WWPS_PEND_WLDR)
;
/* Sequence to enable the watchdog */
writel(0xBBBB, &priv->regs->wdtwspr);
while ((readl(&priv->regs->wdtwwps)) & WDT_WWPS_PEND_WSPR)
;
writel(0x4444, &priv->regs->wdtwspr);
while ((readl(&priv->regs->wdtwwps)) & WDT_WWPS_PEND_WSPR)
;
return 0;
}
static int omap3_wdt_probe(struct udevice *dev)
{
struct omap3_wdt_priv *priv = dev_get_priv(dev);
priv->regs = (struct wd_timer *)devfdt_get_addr(dev);
if (!priv->regs)
return -EINVAL;
priv->wdt_trgr_pattern = 0x1234;
debug("%s: Probing wdt%u\n", __func__, dev->seq);
return 0;
}
static const struct wdt_ops omap3_wdt_ops = {
.start = omap3_wdt_start,
.stop = omap3_wdt_stop,
.reset = omap3_wdt_reset,
};
static const struct udevice_id omap3_wdt_ids[] = {
{ .compatible = "ti,omap3-wdt" },
{ }
};
U_BOOT_DRIVER(omap3_wdt) = {
.name = "omap3_wdt",
.id = UCLASS_WDT,
.of_match = omap3_wdt_ids,
.ops = &omap3_wdt_ops,
.probe = omap3_wdt_probe,
.priv_auto_alloc_size = sizeof(struct omap3_wdt_priv),
};
#endif /* !CONFIG_IS_ENABLED(WDT) */
@@ -0,0 +1,190 @@
/*
* drivers/watchdog/orion_wdt.c
*
* Watchdog driver for Orion/Kirkwood processors
*
* Authors: Tomas Hlavacek <tmshlvck@gmail.com>
* Sylver Bruneau <sylver.bruneau@googlemail.com>
* Marek Behun <marek.behun@nic.cz>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <common.h>
#include <dm.h>
#include <clk.h>
#include <wdt.h>
#include <linux/kernel.h>
#include <asm/io.h>
#include <asm/arch/cpu.h>
#include <asm/arch/soc.h>
DECLARE_GLOBAL_DATA_PTR;
struct orion_wdt_priv {
void __iomem *reg;
int wdt_counter_offset;
void __iomem *rstout;
void __iomem *rstout_mask;
u32 timeout;
unsigned long clk_rate;
struct clk clk;
};
#define RSTOUT_ENABLE_BIT BIT(8)
#define RSTOUT_MASK_BIT BIT(10)
#define WDT_ENABLE_BIT BIT(8)
#define TIMER_CTRL 0x0000
#define TIMER_A370_STATUS 0x04
#define WDT_AXP_FIXED_ENABLE_BIT BIT(10)
#define WDT_A370_EXPIRED BIT(31)
static int orion_wdt_reset(struct udevice *dev)
{
struct orion_wdt_priv *priv = dev_get_priv(dev);
/* Reload watchdog duration */
writel(priv->clk_rate * priv->timeout,
priv->reg + priv->wdt_counter_offset);
return 0;
}
static int orion_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
{
struct orion_wdt_priv *priv = dev_get_priv(dev);
u32 reg;
priv->timeout = DIV_ROUND_UP(timeout_ms, 1000);
/* Enable the fixed watchdog clock input */
reg = readl(priv->reg + TIMER_CTRL);
reg |= WDT_AXP_FIXED_ENABLE_BIT;
writel(reg, priv->reg + TIMER_CTRL);
/* Set watchdog duration */
writel(priv->clk_rate * priv->timeout,
priv->reg + priv->wdt_counter_offset);
/* Clear the watchdog expiration bit */
reg = readl(priv->reg + TIMER_A370_STATUS);
reg &= ~WDT_A370_EXPIRED;
writel(reg, priv->reg + TIMER_A370_STATUS);
/* Enable watchdog timer */
reg = readl(priv->reg + TIMER_CTRL);
reg |= WDT_ENABLE_BIT;
writel(reg, priv->reg + TIMER_CTRL);
/* Enable reset on watchdog */
reg = readl(priv->rstout);
reg |= RSTOUT_ENABLE_BIT;
writel(reg, priv->rstout);
reg = readl(priv->rstout_mask);
reg &= ~RSTOUT_MASK_BIT;
writel(reg, priv->rstout_mask);
return 0;
}
static int orion_wdt_stop(struct udevice *dev)
{
struct orion_wdt_priv *priv = dev_get_priv(dev);
u32 reg;
/* Disable reset on watchdog */
reg = readl(priv->rstout_mask);
reg |= RSTOUT_MASK_BIT;
writel(reg, priv->rstout_mask);
reg = readl(priv->rstout);
reg &= ~RSTOUT_ENABLE_BIT;
writel(reg, priv->rstout);
/* Disable watchdog timer */
reg = readl(priv->reg + TIMER_CTRL);
reg &= ~WDT_ENABLE_BIT;
writel(reg, priv->reg + TIMER_CTRL);
return 0;
}
static inline bool save_reg_from_ofdata(struct udevice *dev, int index,
void __iomem **reg, int *offset)
{
fdt_addr_t addr;
fdt_size_t off;
addr = devfdt_get_addr_size_index(dev, index, &off);
if (addr == FDT_ADDR_T_NONE)
return false;
*reg = (void __iomem *) addr;
if (offset)
*offset = off;
return true;
}
static int orion_wdt_ofdata_to_platdata(struct udevice *dev)
{
struct orion_wdt_priv *priv = dev_get_priv(dev);
if (!save_reg_from_ofdata(dev, 0, &priv->reg,
&priv->wdt_counter_offset))
goto err;
if (!save_reg_from_ofdata(dev, 1, &priv->rstout, NULL))
goto err;
if (!save_reg_from_ofdata(dev, 2, &priv->rstout_mask, NULL))
goto err;
return 0;
err:
debug("%s: Could not determine Orion wdt IO addresses\n", __func__);
return -ENXIO;
}
static int orion_wdt_probe(struct udevice *dev)
{
struct orion_wdt_priv *priv = dev_get_priv(dev);
int ret;
debug("%s: Probing wdt%u\n", __func__, dev->seq);
orion_wdt_stop(dev);
ret = clk_get_by_name(dev, "fixed", &priv->clk);
if (!ret)
priv->clk_rate = clk_get_rate(&priv->clk);
else
priv->clk_rate = 25000000;
return 0;
}
static const struct wdt_ops orion_wdt_ops = {
.start = orion_wdt_start,
.reset = orion_wdt_reset,
.stop = orion_wdt_stop,
};
static const struct udevice_id orion_wdt_ids[] = {
{ .compatible = "marvell,armada-380-wdt" },
{}
};
U_BOOT_DRIVER(orion_wdt) = {
.name = "orion_wdt",
.id = UCLASS_WDT,
.of_match = orion_wdt_ids,
.probe = orion_wdt_probe,
.priv_auto_alloc_size = sizeof(struct orion_wdt_priv),
.ofdata_to_platdata = orion_wdt_ofdata_to_platdata,
.ops = &orion_wdt_ops,
};
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2012 Samsung Electronics
* Minkyu Kang <mk7.kang@samsung.com>
*/
#include <common.h>
#include <asm/io.h>
#include <asm/arch/watchdog.h>
#define PRESCALER_VAL 255
void wdt_stop(void)
{
struct s5p_watchdog *wdt =
(struct s5p_watchdog *)samsung_get_base_watchdog();
unsigned int wtcon;
wtcon = readl(&wdt->wtcon);
wtcon &= ~(WTCON_EN | WTCON_INT | WTCON_RESET);
writel(wtcon, &wdt->wtcon);
}
void wdt_start(unsigned int timeout)
{
struct s5p_watchdog *wdt =
(struct s5p_watchdog *)samsung_get_base_watchdog();
unsigned int wtcon;
wdt_stop();
wtcon = readl(&wdt->wtcon);
wtcon |= (WTCON_EN | WTCON_CLK(WTCON_CLK_128));
wtcon &= ~WTCON_INT;
wtcon |= WTCON_RESET;
wtcon |= WTCON_PRESCALER(PRESCALER_VAL);
writel(timeout, &wdt->wtdat);
writel(timeout, &wdt->wtcnt);
writel(wtcon, &wdt->wtcon);
}
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2017 Google, Inc
*/
#include <common.h>
#include <dm.h>
#include <wdt.h>
#include <asm/state.h>
static int sandbox_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
{
struct sandbox_state *state = state_get_current();
state->wdt.counter = timeout;
state->wdt.running = true;
return 0;
}
static int sandbox_wdt_stop(struct udevice *dev)
{
struct sandbox_state *state = state_get_current();
state->wdt.running = false;
return 0;
}
static int sandbox_wdt_reset(struct udevice *dev)
{
struct sandbox_state *state = state_get_current();
state->wdt.reset_count++;
return 0;
}
static int sandbox_wdt_expire_now(struct udevice *dev, ulong flags)
{
sandbox_wdt_start(dev, 1, flags);
return 0;
}
static const struct wdt_ops sandbox_wdt_ops = {
.start = sandbox_wdt_start,
.reset = sandbox_wdt_reset,
.stop = sandbox_wdt_stop,
.expire_now = sandbox_wdt_expire_now,
};
static const struct udevice_id sandbox_wdt_ids[] = {
{ .compatible = "sandbox,wdt" },
{}
};
U_BOOT_DRIVER(wdt_sandbox) = {
.name = "wdt_sandbox",
.id = UCLASS_WDT,
.of_match = sandbox_wdt_ids,
.ops = &sandbox_wdt_ops,
};
@@ -0,0 +1,156 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Watchdog driver for SP805 on some Layerscape SoC
*
* Copyright 2019 NXP
*/
#include <asm/io.h>
#include <common.h>
#include <dm/device.h>
#include <dm/fdtaddr.h>
#include <dm/read.h>
#include <linux/bitops.h>
#include <watchdog.h>
#include <wdt.h>
#include <linux/math64.h>
#define WDTLOAD 0x000
#define WDTCONTROL 0x008
#define WDTINTCLR 0x00C
#define WDTLOCK 0xC00
#define TIME_OUT_MIN_MSECS 1
#define TIME_OUT_MAX_MSECS 120000
#define SYS_FSL_WDT_CLK_DIV 16
#define INT_ENABLE BIT(0)
#define RESET_ENABLE BIT(1)
#define DISABLE 0
#define UNLOCK 0x1ACCE551
#define LOCK 0x00000001
#define INT_MASK BIT(0)
#define WATCHDOG_BASE 0x12030000
#define WDT_CLK_RATE 24000000
DECLARE_GLOBAL_DATA_PTR;
struct sp805_wdt_priv {
unsigned long base;
void __iomem *reg;
};
static int sp805_wdt_reset(struct udevice *dev)
{
struct sp805_wdt_priv *priv = dev_get_priv(dev);
writel(UNLOCK, priv->reg + WDTLOCK);
writel(INT_MASK, priv->reg + WDTINTCLR);
writel(LOCK, priv->reg + WDTLOCK);
readl(priv->reg + WDTLOCK);
return 0;
}
static int sp805_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
{
u32 load_value;
u32 load_time;
unsigned long clk_rate;
struct sp805_wdt_priv *priv = dev_get_priv(dev);
load_time = (u32)timeout;
if (timeout < TIME_OUT_MIN_MSECS)
load_time = TIME_OUT_MIN_MSECS;
else if (timeout > TIME_OUT_MAX_MSECS)
load_time = TIME_OUT_MAX_MSECS;
/* sp805 runs counter with given value twice, so when the max timeout is
* set 120s, the gd->bus_clk is less than 1145MHz, the load_value will
* not overflow.
*/
if (!gd->bus_clk) {
clk_rate = WDT_CLK_RATE;
load_value = div_u64(clk_rate, 2) * load_time / 1000 - 1;
}
else{
load_value = (gd->bus_clk) /
(2 * 1000 * SYS_FSL_WDT_CLK_DIV) * load_time;
}
writel(UNLOCK, priv->reg + WDTLOCK);
writel(load_value, priv->reg + WDTLOAD);
writel(INT_MASK, priv->reg + WDTINTCLR);
writel(INT_ENABLE | RESET_ENABLE, priv->reg + WDTCONTROL);
writel(LOCK, priv->reg + WDTLOCK);
readl(priv->reg + WDTLOCK);
return 0;
}
static int sp805_wdt_stop(struct udevice *dev)
{
struct sp805_wdt_priv *priv = dev_get_priv(dev);
writel(UNLOCK, priv->reg + WDTLOCK);
writel(DISABLE, priv->reg + WDTCONTROL);
writel(LOCK, priv->reg + WDTLOCK);
readl(priv->reg + WDTLOCK);
return 0;
}
static int sp805_wdt_expire_now(struct udevice *dev, ulong flags)
{
sp805_wdt_start(dev, 0, flags);
return 0;
}
static int sp805_wdt_probe(struct udevice *dev)
{
debug("%s: Probing wdt%u (sp805-wdt)\n", __func__, dev->seq);
struct sp805_wdt_priv *priv = dev_get_priv(dev);
priv->base = WATCHDOG_BASE;
if (!priv->reg) {
priv->reg = (void __iomem *)priv->base;
}
return 0;
}
static int sp805_wdt_ofdata_to_platdata(struct udevice *dev)
{
struct sp805_wdt_priv *priv = dev_get_priv(dev);
priv->reg = (void __iomem *)dev_read_addr(dev);
if (IS_ERR(priv->reg))
return PTR_ERR(priv->reg);
return 0;
}
static const struct wdt_ops sp805_wdt_ops = {
.start = sp805_wdt_start,
.reset = sp805_wdt_reset,
.stop = sp805_wdt_stop,
.expire_now = sp805_wdt_expire_now,
};
static const struct udevice_id sp805_wdt_ids[] = {
{ .compatible = "arm,sp805-wdt" },
{}
};
U_BOOT_DRIVER(sp805_wdt) = {
.name = "sp805_wdt",
.id = UCLASS_WDT,
.of_match = sp805_wdt_ids,
.probe = sp805_wdt_probe,
.priv_auto_alloc_size = sizeof(struct sp805_wdt_priv),
.ofdata_to_platdata = sp805_wdt_ofdata_to_platdata,
.ops = &sp805_wdt_ops,
};
U_BOOT_DEVICE(sp805_wdt) = {
.name = "sp805_wdt",
};
@@ -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,
};
@@ -0,0 +1,92 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2017 Intel Corporation
*/
#include <common.h>
#include <dm.h>
#include <wdt.h>
#include <div64.h>
#include <asm/scu.h>
/* Hardware timeout in seconds */
#define WDT_PRETIMEOUT 15
#define WDT_TIMEOUT_MIN (1 + WDT_PRETIMEOUT)
#define WDT_TIMEOUT_MAX 170
/*
* Note, firmware chooses 90 seconds as a default timeout for watchdog on
* Intel Tangier SoC. It means that without handling it in the running code
* the reboot will happen.
*/
enum {
SCU_WATCHDOG_START = 0,
SCU_WATCHDOG_STOP = 1,
SCU_WATCHDOG_KEEPALIVE = 2,
SCU_WATCHDOG_SET_ACTION_ON_TIMEOUT = 3,
};
static int tangier_wdt_reset(struct udevice *dev)
{
scu_ipc_simple_command(IPCMSG_WATCHDOG_TIMER, SCU_WATCHDOG_KEEPALIVE);
return 0;
}
static int tangier_wdt_stop(struct udevice *dev)
{
return scu_ipc_simple_command(IPCMSG_WATCHDOG_TIMER, SCU_WATCHDOG_STOP);
}
static int tangier_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
{
u32 timeout_sec;
int in_size;
struct ipc_wd_start {
u32 pretimeout;
u32 timeout;
} ipc_wd_start;
/* Calculate timeout in seconds and restrict to min and max value */
do_div(timeout_ms, 1000);
timeout_sec = clamp_t(u32, timeout_ms, WDT_TIMEOUT_MIN, WDT_TIMEOUT_MAX);
/* Update values in the IPC request */
ipc_wd_start.pretimeout = timeout_sec - WDT_PRETIMEOUT;
ipc_wd_start.timeout = timeout_sec;
/*
* SCU expects the input size for watchdog IPC
* to be based on 4 bytes
*/
in_size = DIV_ROUND_UP(sizeof(ipc_wd_start), 4);
scu_ipc_command(IPCMSG_WATCHDOG_TIMER, SCU_WATCHDOG_START,
(u32 *)&ipc_wd_start, in_size, NULL, 0);
return 0;
}
static const struct wdt_ops tangier_wdt_ops = {
.reset = tangier_wdt_reset,
.start = tangier_wdt_start,
.stop = tangier_wdt_stop,
};
static const struct udevice_id tangier_wdt_ids[] = {
{ .compatible = "intel,tangier-wdt" },
{ /* sentinel */ }
};
static int tangier_wdt_probe(struct udevice *dev)
{
debug("%s: Probing wdt%u\n", __func__, dev->seq);
return 0;
}
U_BOOT_DRIVER(wdt_tangier) = {
.name = "wdt_tangier",
.id = UCLASS_WDT,
.of_match = tangier_wdt_ids,
.ops = &tangier_wdt_ops,
.probe = tangier_wdt_probe,
};
@@ -0,0 +1,95 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016 Freescale Semiconductor, Inc.
*/
#include <common.h>
#include <asm/io.h>
#include <asm/arch/imx-regs.h>
/*
* MX7ULP WDOG Register Map
*/
struct wdog_regs {
u8 cs1;
u8 cs2;
u16 reserve0;
u32 cnt;
u32 toval;
u32 win;
};
#ifndef CONFIG_WATCHDOG_TIMEOUT_MSECS
#define CONFIG_WATCHDOG_TIMEOUT_MSECS 0x1500
#endif
#define REFRESH_WORD0 0xA602 /* 1st refresh word */
#define REFRESH_WORD1 0xB480 /* 2nd refresh word */
#define UNLOCK_WORD0 0xC520 /* 1st unlock word */
#define UNLOCK_WORD1 0xD928 /* 2nd unlock word */
#define WDGCS1_WDGE (1<<7)
#define WDGCS1_WDGUPDATE (1<<5)
#define WDGCS2_FLG (1<<6)
#define WDG_BUS_CLK (0x0)
#define WDG_LPO_CLK (0x1)
#define WDG_32KHZ_CLK (0x2)
#define WDG_EXT_CLK (0x3)
void hw_watchdog_set_timeout(u16 val)
{
/* setting timeout value */
struct wdog_regs *wdog = (struct wdog_regs *)WDOG_BASE_ADDR;
writel(val, &wdog->toval);
}
void hw_watchdog_reset(void)
{
struct wdog_regs *wdog = (struct wdog_regs *)WDOG_BASE_ADDR;
writel(REFRESH_WORD0, &wdog->cnt);
writel(REFRESH_WORD1, &wdog->cnt);
}
void hw_watchdog_init(void)
{
u8 val;
struct wdog_regs *wdog = (struct wdog_regs *)WDOG_BASE_ADDR;
writel(UNLOCK_WORD0, &wdog->cnt);
writel(UNLOCK_WORD1, &wdog->cnt);
val = readb(&wdog->cs2);
val |= WDGCS2_FLG;
writeb(val, &wdog->cs2);
hw_watchdog_set_timeout(CONFIG_WATCHDOG_TIMEOUT_MSECS);
writel(0, &wdog->win);
writeb(WDG_LPO_CLK, &wdog->cs2);/* setting 1-kHz clock source */
writeb((WDGCS1_WDGE | WDGCS1_WDGUPDATE), &wdog->cs1);/* enable counter running */
hw_watchdog_reset();
}
void reset_cpu(ulong addr)
{
struct wdog_regs *wdog = (struct wdog_regs *)WDOG_BASE_ADDR;
writel(UNLOCK_WORD0, &wdog->cnt);
writel(UNLOCK_WORD1, &wdog->cnt);
hw_watchdog_set_timeout(5); /* 5ms timeout */
writel(0, &wdog->win);
writeb(WDG_LPO_CLK, &wdog->cs2);/* setting 1-kHz clock source */
writeb(WDGCS1_WDGE, &wdog->cs1);/* enable counter running */
hw_watchdog_reset();
while (1);
}
@@ -0,0 +1,119 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2017 Google, Inc
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <wdt.h>
#include <dm/device-internal.h>
#include <dm/lists.h>
DECLARE_GLOBAL_DATA_PTR;
int wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
{
const struct wdt_ops *ops = device_get_ops(dev);
if (!ops->start)
return -ENOSYS;
return ops->start(dev, timeout_ms, flags);
}
int wdt_stop(struct udevice *dev)
{
const struct wdt_ops *ops = device_get_ops(dev);
if (!ops->stop)
return -ENOSYS;
return ops->stop(dev);
}
int wdt_reset(struct udevice *dev)
{
const struct wdt_ops *ops = device_get_ops(dev);
if (!ops->reset)
return -ENOSYS;
return ops->reset(dev);
}
int wdt_expire_now(struct udevice *dev, ulong flags)
{
int ret = 0;
const struct wdt_ops *ops;
debug("WDT Resetting: %lu\n", flags);
ops = device_get_ops(dev);
if (ops->expire_now) {
return ops->expire_now(dev, flags);
} else {
if (!ops->start)
return -ENOSYS;
ret = ops->start(dev, 1, flags);
if (ret < 0)
return ret;
hang();
}
return ret;
}
#if defined(CONFIG_WATCHDOG)
/*
* Called by macro WATCHDOG_RESET. This function be called *very* early,
* so we need to make sure, that the watchdog driver is ready before using
* it in this function.
*/
void watchdog_reset(void)
{
static ulong next_reset;
ulong now;
/* Exit if GD is not ready or watchdog is not initialized yet */
if (!gd || !(gd->flags & GD_FLG_WDT_READY))
return;
/* Do not reset the watchdog too often */
now = get_timer(0);
if (now > next_reset) {
next_reset = now + 1000; /* reset every 1000ms */
wdt_reset(gd->watchdog_dev);
}
}
#endif
static int wdt_post_bind(struct udevice *dev)
{
#if defined(CONFIG_NEEDS_MANUAL_RELOC)
struct wdt_ops *ops = (struct wdt_ops *)device_get_ops(dev);
static int reloc_done;
if (!reloc_done) {
if (ops->start)
ops->start += gd->reloc_off;
if (ops->stop)
ops->stop += gd->reloc_off;
if (ops->reset)
ops->reset += gd->reloc_off;
if (ops->expire_now)
ops->expire_now += gd->reloc_off;
reloc_done++;
}
#endif
return 0;
}
UCLASS_DRIVER(wdt) = {
.id = UCLASS_WDT,
.name = "watchdog",
.flags = DM_UC_FLAG_SEQ_ALIAS,
.post_bind = wdt_post_bind,
};
@@ -0,0 +1,127 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Xilinx AXI platforms watchdog timer driver.
*
* Author(s): Michal Simek <michal.simek@xilinx.com>
* Shreenidhi Shedi <yesshedi@gmail.com>
*
* Copyright (c) 2011-2018 Xilinx Inc.
*/
#include <common.h>
#include <dm.h>
#include <wdt.h>
#include <linux/io.h>
#define XWT_CSR0_WRS_MASK 0x00000008 /* Reset status Mask */
#define XWT_CSR0_WDS_MASK 0x00000004 /* Timer state Mask */
#define XWT_CSR0_EWDT1_MASK 0x00000002 /* Enable bit 1 Mask*/
#define XWT_CSRX_EWDT2_MASK 0x00000001 /* Enable bit 2 Mask */
struct watchdog_regs {
u32 twcsr0; /* 0x0 */
u32 twcsr1; /* 0x4 */
u32 tbr; /* 0x8 */
};
struct xlnx_wdt_platdata {
bool enable_once;
struct watchdog_regs *regs;
};
static int xlnx_wdt_reset(struct udevice *dev)
{
u32 reg;
struct xlnx_wdt_platdata *platdata = dev_get_platdata(dev);
debug("%s ", __func__);
/* Read the current contents of TCSR0 */
reg = readl(&platdata->regs->twcsr0);
/* Clear the watchdog WDS bit */
if (reg & (XWT_CSR0_EWDT1_MASK | XWT_CSRX_EWDT2_MASK))
writel(reg | XWT_CSR0_WDS_MASK, &platdata->regs->twcsr0);
return 0;
}
static int xlnx_wdt_stop(struct udevice *dev)
{
u32 reg;
struct xlnx_wdt_platdata *platdata = dev_get_platdata(dev);
if (platdata->enable_once) {
debug("Can't stop Xilinx watchdog.\n");
return -EBUSY;
}
/* Read the current contents of TCSR0 */
reg = readl(&platdata->regs->twcsr0);
writel(reg & ~XWT_CSR0_EWDT1_MASK, &platdata->regs->twcsr0);
writel(~XWT_CSRX_EWDT2_MASK, &platdata->regs->twcsr1);
debug("Watchdog disabled!\n");
return 0;
}
static int xlnx_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
{
struct xlnx_wdt_platdata *platdata = dev_get_platdata(dev);
debug("%s:\n", __func__);
writel((XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK | XWT_CSR0_EWDT1_MASK),
&platdata->regs->twcsr0);
writel(XWT_CSRX_EWDT2_MASK, &platdata->regs->twcsr1);
return 0;
}
static int xlnx_wdt_probe(struct udevice *dev)
{
debug("%s: Probing wdt%u\n", __func__, dev->seq);
return 0;
}
static int xlnx_wdt_ofdata_to_platdata(struct udevice *dev)
{
struct xlnx_wdt_platdata *platdata = dev_get_platdata(dev);
platdata->regs = (struct watchdog_regs *)dev_read_addr(dev);
if (IS_ERR(platdata->regs))
return PTR_ERR(platdata->regs);
platdata->enable_once = dev_read_u32_default(dev,
"xlnx,wdt-enable-once", 0);
debug("%s: wdt-enable-once %d\n", __func__, platdata->enable_once);
return 0;
}
static const struct wdt_ops xlnx_wdt_ops = {
.start = xlnx_wdt_start,
.reset = xlnx_wdt_reset,
.stop = xlnx_wdt_stop,
};
static const struct udevice_id xlnx_wdt_ids[] = {
{ .compatible = "xlnx,xps-timebase-wdt-1.00.a", },
{ .compatible = "xlnx,xps-timebase-wdt-1.01.a", },
{},
};
U_BOOT_DRIVER(xlnx_wdt) = {
.name = "xlnx_wdt",
.id = UCLASS_WDT,
.of_match = xlnx_wdt_ids,
.probe = xlnx_wdt_probe,
.platdata_auto_alloc_size = sizeof(struct xlnx_wdt_platdata),
.ofdata_to_platdata = xlnx_wdt_ofdata_to_platdata,
.ops = &xlnx_wdt_ops,
};