xref: /OK3568_Linux_fs/u-boot/drivers/watchdog/rockchip_wdt.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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  */
rockchip_wdt_settimeout(u64 timeout,struct rockchip_wdt_priv * priv)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 
rockchip_wdt_enable(struct rockchip_wdt_priv * priv)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 
rockchip_wdt_is_enabled(struct rockchip_wdt_priv * priv)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 
rockchip_wdt_reset(struct udevice * dev)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 
rockchip_wdt_start(struct udevice * dev,u64 timeout,ulong flags)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 	if (priv->rst.dev)
90 		reset_deassert(&priv->rst);
91 
92 	rockchip_wdt_reset(dev);
93 	rockchip_wdt_settimeout(timeout, priv);
94 	rockchip_wdt_enable(priv);
95 	rockchip_wdt_reset(dev);
96 
97 	return 0;
98 }
99 
rockchip_wdt_stop(struct udevice * dev)100 static int rockchip_wdt_stop(struct udevice *dev)
101 {
102 	struct rockchip_wdt_priv *priv = dev_get_priv(dev);
103 
104 	if (priv->rst.dev) {
105 		reset_assert(&priv->rst);
106 		reset_deassert(&priv->rst);
107 	}
108 
109 	printf("Rockchip watchdog stop\n");
110 
111 	return 0;
112 }
113 
114 static const struct wdt_ops rockchip_wdt_ops = {
115 	.start = rockchip_wdt_start,
116 	.reset = rockchip_wdt_reset,
117 	.stop = rockchip_wdt_stop,
118 };
119 
rockchip_wdt_ofdata_to_platdata(struct udevice * dev)120 static int rockchip_wdt_ofdata_to_platdata(struct udevice *dev)
121 {
122 	struct rockchip_wdt_priv *priv = dev_get_priv(dev);
123 
124 	priv->base = dev_read_addr_ptr(dev);
125 	if (!priv->base)
126 		return -ENOENT;
127 
128 	return 0;
129 }
130 
rockchip_wdt_probe(struct udevice * dev)131 static int rockchip_wdt_probe(struct udevice *dev)
132 {
133 	struct rockchip_wdt_priv *priv = dev_get_priv(dev);
134 	int ret;
135 
136 	ret = reset_get_by_name(dev, "reset", &priv->rst);
137 	if (ret) {
138 		pr_err("reset_get_by_name(reset) failed: %d\n", ret);
139 		priv->rst.dev = NULL;
140 	}
141 
142 	ret = clk_get_by_index(dev, 0, &priv->clk);
143 	if (ret < 0)
144 		return ret;
145 
146 	/* Need clk framework support */
147 	priv->rate = clk_get_rate(&priv->clk);
148 	if (priv->rate < 0)
149 		return -EINVAL;
150 
151 	return 0;
152 }
153 
154 static const struct udevice_id rockchip_wdt_ids[] = {
155 	{ .compatible = "snps,dw-wdt" },
156 	{}
157 };
158 
159 U_BOOT_DRIVER(rockchip_wdt) = {
160 	.name = "rockchip_wdt",
161 	.id = UCLASS_WDT,
162 	.of_match = rockchip_wdt_ids,
163 	.probe = rockchip_wdt_probe,
164 	.priv_auto_alloc_size = sizeof(struct rockchip_wdt_priv),
165 	.ofdata_to_platdata = rockchip_wdt_ofdata_to_platdata,
166 	.ops = &rockchip_wdt_ops,
167 };
168