xref: /rk3399_rockchip-uboot/drivers/watchdog/rockchip_wdt.c (revision 537f93d89461f8bd43ec9c086974fadeb5ed5f04)
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