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