blob: dd8f93ac3a85073eb75df678ad746d2bf3064179 [file] [log] [blame]
/*
* Copyright 2018 Amlogic, Inc
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <wdt.h>
#include <asm/io.h>
#include <linux/io.h>
#include <clk.h>
#define MESON_WDT_CTRL_REG 0x0
#define MESON_WDT_TCNT_REG 0x8
#define MESON_WDT_RSET_REG 0xc
#define MESON_WDT_CTRL_CLKDIV_EN BIT(25)
#define MESON_WDT_CTRL_CLK_EN BIT(24)
#define MESON_WDT_CTRL_EE_RESET BIT(21)
#define MESON_WDT_CTRL_EN BIT(18)
#define MESON_WDT_CTRL_DIV_MASK (BIT(18) - 1)
#define MESON_WDT_TCNT_SETUP_MASK (BIT(16) - 1)
#define MESON_WDT_TCNT_CNT_SHIFT (16)
#define DEFAULT_TIMEOUT 1
struct meson_wdt_priv {
void __iomem *regs;
};
static int meson_wdt_reset(struct udevice *dev)
{
struct meson_wdt_priv *priv;
assert(dev);
priv = dev_get_priv(dev);
writel(0, priv->regs + MESON_WDT_RSET_REG);
return 0;
}
static int meson_wdt_set_timeout(struct udevice *dev, unsigned int timeout)
{
struct meson_wdt_priv *priv;
unsigned long tcnt = timeout * 1000;
assert(dev);
priv = dev_get_priv(dev);
if (tcnt > MESON_WDT_TCNT_SETUP_MASK)
tcnt = MESON_WDT_TCNT_SETUP_MASK;
meson_wdt_reset(dev);
writel(tcnt, priv->regs + MESON_WDT_TCNT_REG);
return 0;
}
static int meson_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
{
struct meson_wdt_priv *priv;
assert(dev);
priv = dev_get_priv(dev);
meson_wdt_set_timeout(dev, timeout);
writel(readl(priv->regs + MESON_WDT_CTRL_REG) | MESON_WDT_CTRL_EN,
priv->regs + MESON_WDT_CTRL_REG);
return 0;
}
static int meson_wdt_stop(struct udevice *dev)
{
struct meson_wdt_priv *priv;
assert(dev);
priv = dev_get_priv(dev);
writel(readl(priv->regs + MESON_WDT_CTRL_REG) & ~MESON_WDT_CTRL_EN,
priv->regs + MESON_WDT_CTRL_REG);
return 0;
}
static int meson_wdt_expire_now(struct udevice *dev, ulong flags)
{
meson_wdt_start(dev, 1, flags);
return 0;
}
static int meson_wdt_ofdata_to_platdata(struct udevice *dev)
{
struct meson_wdt_priv *priv;
assert(dev);
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 int meson_wdt_probe(struct udevice *dev)
{
struct meson_wdt_priv *priv;
fdt_addr_t addr;
fdt_size_t size;
struct clk w_clk;
unsigned int rate = 0;
int ret;
assert(dev);
priv = dev_get_priv(dev);
addr = devfdt_get_addr_size_index(dev, 0, &size);
if (addr == FDT_ADDR_T_NONE)
return -EINVAL;
priv->regs = (void __iomem *)addr;
ret = clk_get_by_name(dev, "wdt-clk", &w_clk);
if (ret < 0) {
printf("Failed to get wdt-clk.\n");
return ret;
}
rate = clk_get_rate(&w_clk);
if (rate < 0) {
printf("Failed to get wdt-clk rate.\n");
return ret;
}
writel(((rate / 1000) & MESON_WDT_CTRL_DIV_MASK) |
MESON_WDT_CTRL_EE_RESET |
MESON_WDT_CTRL_CLK_EN |
MESON_WDT_CTRL_CLKDIV_EN, priv->regs + MESON_WDT_CTRL_REG);
meson_wdt_set_timeout(dev, DEFAULT_TIMEOUT);
meson_wdt_stop(dev);
return 0;
}
static const struct wdt_ops meson_wdt_ops = {
.start = meson_wdt_start,
.reset = meson_wdt_reset,
.stop = meson_wdt_stop,
.expire_now = meson_wdt_expire_now,
};
static const struct udevice_id meson_wdt_ids[] = {
{ .compatible = "amlogic,meson-gxbb-wdt" },
{}
};
U_BOOT_DRIVER(meson_wdt) = {
.name = "meson_wdt",
.id = UCLASS_WDT,
.of_match = meson_wdt_ids,
.ops = &meson_wdt_ops,
.probe = meson_wdt_probe,
.priv_auto_alloc_size = sizeof(struct meson_wdt_priv),
.ofdata_to_platdata = meson_wdt_ofdata_to_platdata,
};