u-boot/drivers/watchdog/ulp_wdog.c
Tom Rini d678a59d2d Revert "Merge patch series "arm: dts: am62-beagleplay: Fix Beagleplay Ethernet""
When bringing in the series 'arm: dts: am62-beagleplay: Fix Beagleplay
Ethernet"' I failed to notice that b4 noticed it was based on next and
so took that as the base commit and merged that part of next to master.

This reverts commit c8ffd1356d, reversing
changes made to 2ee6f3a5f7.

Reported-by: Jonas Karlman <jonas@kwiboo.se>
Signed-off-by: Tom Rini <trini@konsulko.com>
2024-05-19 08:16:36 -06:00

223 lines
4.9 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016 Freescale Semiconductor, Inc.
*/
#include <common.h>
#include <cpu_func.h>
#include <asm/io.h>
#include <asm/arch/imx-regs.h>
#include <dm.h>
#include <wdt.h>
/*
* MX7ULP WDOG Register Map
*/
struct wdog_regs {
u32 cs;
u32 cnt;
u32 toval;
u32 win;
};
struct ulp_wdt_priv {
struct wdog_regs *wdog;
u32 clk_rate;
};
#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 UNLOCK_WORD 0xD928C520 /* unlock word */
#define REFRESH_WORD 0xB480A602 /* refresh word */
#define WDGCS_WDGE BIT(7)
#define WDGCS_WDGUPDATE BIT(5)
#define WDGCS_RCS BIT(10)
#define WDGCS_ULK BIT(11)
#define WDOG_CS_PRES BIT(12)
#define WDGCS_CMD32EN BIT(13)
#define WDGCS_FLG BIT(14)
#define WDGCS_INT BIT(6)
#define WDG_BUS_CLK (0x0)
#define WDG_LPO_CLK (0x1)
#define WDG_32KHZ_CLK (0x2)
#define WDG_EXT_CLK (0x3)
#define CLK_RATE_1KHZ 1000
#define CLK_RATE_32KHZ 125
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 ulp_watchdog_reset(struct wdog_regs *wdog)
{
if (readl(&wdog->cs) & WDGCS_CMD32EN) {
writel(REFRESH_WORD, &wdog->cnt);
} else {
dmb();
__raw_writel(REFRESH_WORD0, &wdog->cnt);
__raw_writel(REFRESH_WORD1, &wdog->cnt);
dmb();
}
}
void ulp_watchdog_init(struct wdog_regs *wdog, u16 timeout)
{
u32 cmd32 = 0;
if (readl(&wdog->cs) & WDGCS_CMD32EN) {
writel(UNLOCK_WORD, &wdog->cnt);
cmd32 = WDGCS_CMD32EN;
} else {
dmb();
__raw_writel(UNLOCK_WORD0, &wdog->cnt);
__raw_writel(UNLOCK_WORD1, &wdog->cnt);
dmb();
}
/* Wait WDOG Unlock */
while (!(readl(&wdog->cs) & WDGCS_ULK))
;
hw_watchdog_set_timeout(timeout);
writel(0, &wdog->win);
/* setting 1-kHz clock source, enable counter running, and clear interrupt */
if (IS_ENABLED(CONFIG_ARCH_IMX9))
writel((cmd32 | WDGCS_WDGE | WDGCS_WDGUPDATE | (WDG_LPO_CLK << 8) |
WDGCS_FLG | WDOG_CS_PRES | WDGCS_INT), &wdog->cs);
else
writel((cmd32 | WDGCS_WDGE | WDGCS_WDGUPDATE | (WDG_LPO_CLK << 8) |
WDGCS_FLG), &wdog->cs);
/* Wait WDOG reconfiguration */
while (!(readl(&wdog->cs) & WDGCS_RCS))
;
ulp_watchdog_reset(wdog);
}
void hw_watchdog_reset(void)
{
struct wdog_regs *wdog = (struct wdog_regs *)WDOG_BASE_ADDR;
ulp_watchdog_reset(wdog);
}
void hw_watchdog_init(void)
{
struct wdog_regs *wdog = (struct wdog_regs *)WDOG_BASE_ADDR;
ulp_watchdog_init(wdog, CONFIG_WATCHDOG_TIMEOUT_MSECS);
}
#if !CONFIG_IS_ENABLED(SYSRESET)
void reset_cpu(void)
{
struct wdog_regs *wdog = (struct wdog_regs *)WDOG_BASE_ADDR;
u32 cmd32 = 0;
if (readl(&wdog->cs) & WDGCS_CMD32EN) {
writel(UNLOCK_WORD, &wdog->cnt);
cmd32 = WDGCS_CMD32EN;
} else {
dmb();
__raw_writel(UNLOCK_WORD0, &wdog->cnt);
__raw_writel(UNLOCK_WORD1, &wdog->cnt);
dmb();
}
/* Wait WDOG Unlock */
while (!(readl(&wdog->cs) & WDGCS_ULK))
;
hw_watchdog_set_timeout(5); /* 5ms timeout for general; 40ms timeout for imx93 */
writel(0, &wdog->win);
/* enable counter running */
if (IS_ENABLED(CONFIG_ARCH_IMX9))
writel((cmd32 | WDGCS_WDGE | (WDG_LPO_CLK << 8) | WDOG_CS_PRES |
WDGCS_INT), &wdog->cs);
else
writel((cmd32 | WDGCS_WDGE | (WDG_LPO_CLK << 8)), &wdog->cs);
/* Wait WDOG reconfiguration */
while (!(readl(&wdog->cs) & WDGCS_RCS))
;
hw_watchdog_reset();
while (1);
}
#endif
static int ulp_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
{
struct ulp_wdt_priv *priv = dev_get_priv(dev);
u64 timeout = 0;
timeout = (timeout_ms * priv->clk_rate) / 1000;
if (timeout > U16_MAX)
return -EINVAL;
ulp_watchdog_init(priv->wdog, (u16)timeout);
return 0;
}
static int ulp_wdt_reset(struct udevice *dev)
{
struct ulp_wdt_priv *priv = dev_get_priv(dev);
ulp_watchdog_reset(priv->wdog);
return 0;
}
static int ulp_wdt_probe(struct udevice *dev)
{
struct ulp_wdt_priv *priv = dev_get_priv(dev);
priv->wdog = dev_read_addr_ptr(dev);
if (!priv->wdog)
return -EINVAL;
priv->clk_rate = (u32)dev_get_driver_data(dev);
if (!priv->clk_rate)
return -EINVAL;
return 0;
}
static const struct wdt_ops ulp_wdt_ops = {
.start = ulp_wdt_start,
.reset = ulp_wdt_reset,
};
static const struct udevice_id ulp_wdt_ids[] = {
{ .compatible = "fsl,imx7ulp-wdt", .data = CLK_RATE_1KHZ },
{ .compatible = "fsl,imx8ulp-wdt", .data = CLK_RATE_1KHZ },
{ .compatible = "fsl,imx93-wdt", .data = CLK_RATE_32KHZ },
{}
};
U_BOOT_DRIVER(ulp_wdt) = {
.name = "ulp_wdt",
.id = UCLASS_WDT,
.of_match = ulp_wdt_ids,
.priv_auto = sizeof(struct ulp_wdt_priv),
.probe = ulp_wdt_probe,
.ops = &ulp_wdt_ops,
};