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