1e7c691e7SSimon Xue /*
2e7c691e7SSimon Xue * Copyright (C) 2019 Rockchip Electronics Co., Ltd
3e7c691e7SSimon Xue *
4e7c691e7SSimon Xue * SPDX-License-Identifier: GPL-2.0+
5e7c691e7SSimon Xue */
6e7c691e7SSimon Xue
7e7c691e7SSimon Xue #include <asm/io.h>
8e7c691e7SSimon Xue #include <asm/utils.h>
9e7c691e7SSimon Xue #include <clk.h>
10e7c691e7SSimon Xue #include <dm.h>
11e7c691e7SSimon Xue #include <reset.h>
12e7c691e7SSimon Xue #include <wdt.h>
13e7c691e7SSimon Xue
14e7c691e7SSimon Xue #define WDT_CR 0x00
15e7c691e7SSimon Xue #define WDT_TORR 0x04
16e7c691e7SSimon Xue #define WDT_CRR 0x0C
17e7c691e7SSimon Xue #define WDT_EN_MASK 0x01
18e7c691e7SSimon Xue #define WDT_MODE_MASK 0x02
19e7c691e7SSimon Xue #define WDT_CRR_RESTART_VAL 0x76
20e7c691e7SSimon Xue
21e7c691e7SSimon Xue DECLARE_GLOBAL_DATA_PTR;
22e7c691e7SSimon Xue
23e7c691e7SSimon Xue struct rockchip_wdt_priv {
24e7c691e7SSimon Xue void __iomem *base;
25e7c691e7SSimon Xue struct clk clk;
26e7c691e7SSimon Xue unsigned long rate;
27e7c691e7SSimon Xue struct reset_ctl rst;
28e7c691e7SSimon Xue };
29e7c691e7SSimon Xue
30e7c691e7SSimon Xue /*
31e7c691e7SSimon Xue * Set the watchdog time interval.
32e7c691e7SSimon Xue * Counter is 32 bit.
33e7c691e7SSimon Xue */
rockchip_wdt_settimeout(u64 timeout,struct rockchip_wdt_priv * priv)34e7c691e7SSimon Xue static int rockchip_wdt_settimeout(u64 timeout,
35e7c691e7SSimon Xue struct rockchip_wdt_priv *priv)
36e7c691e7SSimon Xue {
37e7c691e7SSimon Xue signed int i;
38e7c691e7SSimon Xue
39e7c691e7SSimon Xue /* calculate the timeout range value */
40e7c691e7SSimon Xue i = log_2_n_round_up(timeout * priv->rate / 1000) - 16;
41e7c691e7SSimon Xue if (i > 15)
42e7c691e7SSimon Xue i = 15;
43e7c691e7SSimon Xue if (i < 0)
44e7c691e7SSimon Xue i = 0;
45e7c691e7SSimon Xue
46e7c691e7SSimon Xue writel((i | (i << 4)), priv->base + WDT_TORR);
47e7c691e7SSimon Xue
48e7c691e7SSimon Xue return 0;
49e7c691e7SSimon Xue }
50e7c691e7SSimon Xue
rockchip_wdt_enable(struct rockchip_wdt_priv * priv)51e7c691e7SSimon Xue static void rockchip_wdt_enable(struct rockchip_wdt_priv *priv)
52e7c691e7SSimon Xue {
53e7c691e7SSimon Xue u32 val = readl(priv->base + WDT_CR);
54e7c691e7SSimon Xue
55e7c691e7SSimon Xue /* Disable interrupt mode; always perform system reset. */
56e7c691e7SSimon Xue val &= ~WDT_MODE_MASK;
57e7c691e7SSimon Xue /* Enable watchdog. */
58e7c691e7SSimon Xue val |= WDT_EN_MASK;
59e7c691e7SSimon Xue
60e7c691e7SSimon Xue writel(val, priv->base + WDT_CR);
61e7c691e7SSimon Xue }
62e7c691e7SSimon Xue
rockchip_wdt_is_enabled(struct rockchip_wdt_priv * priv)63e7c691e7SSimon Xue static unsigned int rockchip_wdt_is_enabled(struct rockchip_wdt_priv *priv)
64e7c691e7SSimon Xue {
65e7c691e7SSimon Xue unsigned long val;
66e7c691e7SSimon Xue
67e7c691e7SSimon Xue val = readl(priv->base + WDT_CR);
68e7c691e7SSimon Xue
69e7c691e7SSimon Xue return val & WDT_EN_MASK;
70e7c691e7SSimon Xue }
71e7c691e7SSimon Xue
rockchip_wdt_reset(struct udevice * dev)72e7c691e7SSimon Xue static int rockchip_wdt_reset(struct udevice *dev)
73e7c691e7SSimon Xue {
74e7c691e7SSimon Xue struct rockchip_wdt_priv *priv = dev_get_priv(dev);
75e7c691e7SSimon Xue
76e7c691e7SSimon Xue if (rockchip_wdt_is_enabled(priv))
77e7c691e7SSimon Xue /* restart the watchdog counter */
78e7c691e7SSimon Xue writel(WDT_CRR_RESTART_VAL, priv->base + WDT_CRR);
79e7c691e7SSimon Xue
80e7c691e7SSimon Xue return 0;
81e7c691e7SSimon Xue }
82e7c691e7SSimon Xue
rockchip_wdt_start(struct udevice * dev,u64 timeout,ulong flags)83e7c691e7SSimon Xue static int rockchip_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
84e7c691e7SSimon Xue {
85e7c691e7SSimon Xue struct rockchip_wdt_priv *priv = dev_get_priv(dev);
86e7c691e7SSimon Xue
87e7c691e7SSimon Xue printf("Rockchip watchdog timeout: %lld sec\n", timeout / 1000);
88e7c691e7SSimon Xue
89*537f93d8SSimon Xue if (priv->rst.dev)
90e7c691e7SSimon Xue reset_deassert(&priv->rst);
91e7c691e7SSimon Xue
92e7c691e7SSimon Xue rockchip_wdt_reset(dev);
93e7c691e7SSimon Xue rockchip_wdt_settimeout(timeout, priv);
94e7c691e7SSimon Xue rockchip_wdt_enable(priv);
95e7c691e7SSimon Xue rockchip_wdt_reset(dev);
96e7c691e7SSimon Xue
97e7c691e7SSimon Xue return 0;
98e7c691e7SSimon Xue }
99e7c691e7SSimon Xue
rockchip_wdt_stop(struct udevice * dev)100e7c691e7SSimon Xue static int rockchip_wdt_stop(struct udevice *dev)
101e7c691e7SSimon Xue {
102e7c691e7SSimon Xue struct rockchip_wdt_priv *priv = dev_get_priv(dev);
103e7c691e7SSimon Xue
104*537f93d8SSimon Xue if (priv->rst.dev) {
105e7c691e7SSimon Xue reset_assert(&priv->rst);
106e7c691e7SSimon Xue reset_deassert(&priv->rst);
107*537f93d8SSimon Xue }
108e7c691e7SSimon Xue
109e7c691e7SSimon Xue printf("Rockchip watchdog stop\n");
110e7c691e7SSimon Xue
111e7c691e7SSimon Xue return 0;
112e7c691e7SSimon Xue }
113e7c691e7SSimon Xue
114e7c691e7SSimon Xue static const struct wdt_ops rockchip_wdt_ops = {
115e7c691e7SSimon Xue .start = rockchip_wdt_start,
116e7c691e7SSimon Xue .reset = rockchip_wdt_reset,
117e7c691e7SSimon Xue .stop = rockchip_wdt_stop,
118e7c691e7SSimon Xue };
119e7c691e7SSimon Xue
rockchip_wdt_ofdata_to_platdata(struct udevice * dev)120e7c691e7SSimon Xue static int rockchip_wdt_ofdata_to_platdata(struct udevice *dev)
121e7c691e7SSimon Xue {
122e7c691e7SSimon Xue struct rockchip_wdt_priv *priv = dev_get_priv(dev);
123e7c691e7SSimon Xue
124e7c691e7SSimon Xue priv->base = dev_read_addr_ptr(dev);
125e7c691e7SSimon Xue if (!priv->base)
126e7c691e7SSimon Xue return -ENOENT;
127e7c691e7SSimon Xue
128e7c691e7SSimon Xue return 0;
129e7c691e7SSimon Xue }
130e7c691e7SSimon Xue
rockchip_wdt_probe(struct udevice * dev)131e7c691e7SSimon Xue static int rockchip_wdt_probe(struct udevice *dev)
132e7c691e7SSimon Xue {
133e7c691e7SSimon Xue struct rockchip_wdt_priv *priv = dev_get_priv(dev);
134e7c691e7SSimon Xue int ret;
135e7c691e7SSimon Xue
136e7c691e7SSimon Xue ret = reset_get_by_name(dev, "reset", &priv->rst);
137e7c691e7SSimon Xue if (ret) {
138e7c691e7SSimon Xue pr_err("reset_get_by_name(reset) failed: %d\n", ret);
139*537f93d8SSimon Xue priv->rst.dev = NULL;
140e7c691e7SSimon Xue }
141e7c691e7SSimon Xue
142e7c691e7SSimon Xue ret = clk_get_by_index(dev, 0, &priv->clk);
143e7c691e7SSimon Xue if (ret < 0)
144e7c691e7SSimon Xue return ret;
145e7c691e7SSimon Xue
146e7c691e7SSimon Xue /* Need clk framework support */
147e7c691e7SSimon Xue priv->rate = clk_get_rate(&priv->clk);
148e7c691e7SSimon Xue if (priv->rate < 0)
149e7c691e7SSimon Xue return -EINVAL;
150e7c691e7SSimon Xue
151e7c691e7SSimon Xue return 0;
152e7c691e7SSimon Xue }
153e7c691e7SSimon Xue
154e7c691e7SSimon Xue static const struct udevice_id rockchip_wdt_ids[] = {
155e7c691e7SSimon Xue { .compatible = "snps,dw-wdt" },
156e7c691e7SSimon Xue {}
157e7c691e7SSimon Xue };
158e7c691e7SSimon Xue
159e7c691e7SSimon Xue U_BOOT_DRIVER(rockchip_wdt) = {
160e7c691e7SSimon Xue .name = "rockchip_wdt",
161e7c691e7SSimon Xue .id = UCLASS_WDT,
162e7c691e7SSimon Xue .of_match = rockchip_wdt_ids,
163e7c691e7SSimon Xue .probe = rockchip_wdt_probe,
164e7c691e7SSimon Xue .priv_auto_alloc_size = sizeof(struct rockchip_wdt_priv),
165e7c691e7SSimon Xue .ofdata_to_platdata = rockchip_wdt_ofdata_to_platdata,
166e7c691e7SSimon Xue .ops = &rockchip_wdt_ops,
167e7c691e7SSimon Xue };
168