xref: /OK3568_Linux_fs/u-boot/drivers/watchdog/rockchip_wdt.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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