265 lines
5.8 KiB
C
265 lines
5.8 KiB
C
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
|
/*
|
|
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <dm.h>
|
|
#include <errno.h>
|
|
#include <wdt.h>
|
|
#include <asm/io.h>
|
|
#include <linux/io.h>
|
|
#include <clk.h>
|
|
#include <asm/arch/bl31_apis.h>
|
|
|
|
#define MESON_WDT_CTRL_REG 0x0
|
|
#define MESON_WDT_CTRL1_REG 0x4
|
|
#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_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 MESON_WDT_RST_SIG_EN BIT(17)
|
|
|
|
#define WDT_DISABLE 1
|
|
#define WDT_ENABLE 2
|
|
#define WDT_PING 3
|
|
#define WDT_INIT 4
|
|
#define WDT_RESETNOW 5
|
|
#define WDT_SETTIMEOUT 6
|
|
#define WDT_OPS 0x82000086
|
|
|
|
#define DEFAULT_TIMEOUT 1 /* second */
|
|
|
|
struct meson_wdt_priv {
|
|
void __iomem *regs;
|
|
};
|
|
|
|
struct meson_wdt_data {
|
|
unsigned char rst_shift;
|
|
struct wdt_ops *ops;
|
|
};
|
|
|
|
/***************** gxbb ***********************/
|
|
static int meson_gxbb_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_gxbb_wdt_set_timeout(struct udevice *dev, u64 timeout_ms)
|
|
{
|
|
struct meson_wdt_priv *priv;
|
|
unsigned long tcnt = timeout_ms;
|
|
|
|
assert(dev);
|
|
priv = dev_get_priv(dev);
|
|
if (tcnt > MESON_WDT_TCNT_SETUP_MASK)
|
|
tcnt = MESON_WDT_TCNT_SETUP_MASK;
|
|
meson_gxbb_wdt_reset(dev);
|
|
writel(tcnt, priv->regs + MESON_WDT_TCNT_REG);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int meson_gxbb_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
|
|
{
|
|
struct meson_wdt_priv *priv;
|
|
|
|
assert(dev);
|
|
priv = dev_get_priv(dev);
|
|
meson_gxbb_wdt_set_timeout(dev, timeout_ms);
|
|
writel(readl(priv->regs + MESON_WDT_CTRL_REG) | MESON_WDT_CTRL_EN,
|
|
priv->regs + MESON_WDT_CTRL_REG);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int meson_gxbb_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_gxbb_wdt_expire_now(struct udevice *dev, ulong flags)
|
|
{
|
|
meson_gxbb_wdt_start(dev, 1, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int meson_gxbb_wdt_probe(struct udevice *dev)
|
|
{
|
|
struct meson_wdt_priv *priv;
|
|
struct meson_wdt_data *data;
|
|
|
|
fdt_addr_t addr;
|
|
fdt_size_t size;
|
|
struct clk w_clk;
|
|
ulong rate = 0;
|
|
unsigned int reset_by_soc = 0;
|
|
int ret;
|
|
|
|
assert(dev);
|
|
priv = dev_get_priv(dev);
|
|
data = (struct meson_wdt_data *)dev_get_driver_data(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 (IS_ERR_VALUE(rate)) {
|
|
printf("Failed to get wdt-clk rate.\n");
|
|
return ret;
|
|
}
|
|
|
|
reset_by_soc = !(readl(priv->regs + MESON_WDT_CTRL1_REG) &
|
|
MESON_WDT_RST_SIG_EN);
|
|
|
|
writel(((rate / 1000) & MESON_WDT_CTRL_DIV_MASK) |
|
|
(reset_by_soc << data->rst_shift) |
|
|
MESON_WDT_CTRL_CLK_EN |
|
|
MESON_WDT_CTRL_CLKDIV_EN, priv->regs + MESON_WDT_CTRL_REG);
|
|
meson_gxbb_wdt_set_timeout(dev, DEFAULT_TIMEOUT * 1000);
|
|
meson_gxbb_wdt_stop(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct wdt_ops meson_gxbb_wdt_ops = {
|
|
.start = meson_gxbb_wdt_start,
|
|
.stop = meson_gxbb_wdt_stop,
|
|
.expire_now = meson_gxbb_wdt_expire_now,
|
|
.reset = meson_gxbb_wdt_reset,
|
|
};
|
|
|
|
static struct meson_wdt_data meson_gxbb_data = {
|
|
.rst_shift = 21,
|
|
.ops = &meson_gxbb_wdt_ops,
|
|
};
|
|
|
|
static struct meson_wdt_data meson_sc2_data = {
|
|
.rst_shift = 22,
|
|
.ops = &meson_gxbb_wdt_ops,
|
|
};
|
|
/**************** a1 **********************/
|
|
void __attribute__((weak)) wdt_send_cmd_to_bl31(uint64_t cmd, uint64_t value)
|
|
{
|
|
|
|
}
|
|
static int meson_a1_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
|
|
{
|
|
wdt_send_cmd_to_bl31(WDT_INIT, timeout_ms);
|
|
wdt_send_cmd_to_bl31(WDT_ENABLE, 0);
|
|
wdt_send_cmd_to_bl31(WDT_PING, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int meson_a1_wdt_stop(struct udevice *dev)
|
|
{
|
|
wdt_send_cmd_to_bl31(WDT_DISABLE, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int meson_a1_wdt_expire_now(struct udevice *dev, ulong flags)
|
|
{
|
|
meson_a1_wdt_start(dev, 1, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int meson_a1_wdt_reset(struct udevice *dev)
|
|
{
|
|
wdt_send_cmd_to_bl31(WDT_PING, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct wdt_ops meson_a1_ops = {
|
|
.start = meson_a1_wdt_start,
|
|
.stop = meson_a1_wdt_stop,
|
|
.expire_now = meson_a1_wdt_expire_now,
|
|
.reset = meson_a1_wdt_reset,
|
|
};
|
|
|
|
static struct meson_wdt_data meson_a1_data = {
|
|
.ops = &meson_a1_ops,
|
|
};
|
|
|
|
/************ common code *******************/
|
|
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_data* priv;
|
|
struct driver* dri;
|
|
|
|
priv =(struct meson_wdt_data *)dev_get_driver_data(dev);
|
|
dri = (struct driver*)dev->driver;
|
|
dri->ops = priv->ops;
|
|
if (device_is_compatible(dev,"amlogic,meson-gxbb-wdt") ||
|
|
device_is_compatible(dev,"amlogic,meson-sc2-wdt"))
|
|
meson_gxbb_wdt_probe(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct udevice_id meson_wdt_ids[] =
|
|
{
|
|
{
|
|
.compatible = "amlogic,meson-gxbb-wdt",
|
|
.data = (ulong)&meson_gxbb_data,
|
|
},
|
|
{
|
|
.compatible = "amlogic,meson-a1-wdt",
|
|
.data = (ulong)&meson_a1_data,
|
|
},
|
|
{
|
|
.compatible = "amlogic,meson-sc2-wdt",
|
|
.data = (ulong)&meson_sc2_data,
|
|
},
|
|
{}
|
|
};
|
|
|
|
U_BOOT_DRIVER(meson_wdt) = {
|
|
.name = "meson_wdt",
|
|
.id = UCLASS_WDT,
|
|
.of_match = meson_wdt_ids,
|
|
.probe = meson_wdt_probe,
|
|
.priv_auto_alloc_size = sizeof(struct meson_wdt_priv),
|
|
.ofdata_to_platdata = meson_wdt_ofdata_to_platdata,
|
|
};
|