xref: /openbmc/u-boot/drivers/watchdog/orion_wdt.c (revision 203e94f6)
1 /*
2  * drivers/watchdog/orion_wdt.c
3  *
4  * Watchdog driver for Orion/Kirkwood processors
5  *
6  * Authors:	Tomas Hlavacek <tmshlvck@gmail.com>
7  * 		Sylver Bruneau <sylver.bruneau@googlemail.com>
8  * 		Marek Behun <marek.behun@nic.cz>
9  *
10  * This file is licensed under  the terms of the GNU General Public
11  * License version 2. This program is licensed "as is" without any
12  * warranty of any kind, whether express or implied.
13  */
14 
15 #include <common.h>
16 #include <dm.h>
17 #include <wdt.h>
18 #include <asm/io.h>
19 #include <asm/arch/cpu.h>
20 #include <asm/arch/soc.h>
21 
22 DECLARE_GLOBAL_DATA_PTR;
23 
24 struct orion_wdt_priv {
25 	void __iomem *reg;
26 	int wdt_counter_offset;
27 	void __iomem *rstout;
28 	void __iomem *rstout_mask;
29 	u32 timeout;
30 };
31 
32 #define RSTOUT_ENABLE_BIT		BIT(8)
33 #define RSTOUT_MASK_BIT			BIT(10)
34 #define WDT_ENABLE_BIT			BIT(8)
35 
36 #define TIMER_CTRL			0x0000
37 #define TIMER_A370_STATUS		0x04
38 
39 #define WDT_AXP_FIXED_ENABLE_BIT	BIT(10)
40 #define WDT_A370_EXPIRED		BIT(31)
41 
42 static int orion_wdt_reset(struct udevice *dev)
43 {
44 	struct orion_wdt_priv *priv = dev_get_priv(dev);
45 
46 	/* Reload watchdog duration */
47 	writel(priv->timeout, priv->reg + priv->wdt_counter_offset);
48 
49 	return 0;
50 }
51 
52 static int orion_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
53 {
54 	struct orion_wdt_priv *priv = dev_get_priv(dev);
55 	u32 reg;
56 
57 	priv->timeout = (u32) timeout;
58 
59 	/* Enable the fixed watchdog clock input */
60 	reg = readl(priv->reg + TIMER_CTRL);
61 	reg |= WDT_AXP_FIXED_ENABLE_BIT;
62 	writel(reg, priv->reg + TIMER_CTRL);
63 
64 	/* Set watchdog duration */
65 	writel(priv->timeout, priv->reg + priv->wdt_counter_offset);
66 
67 	/* Clear the watchdog expiration bit */
68 	reg = readl(priv->reg + TIMER_A370_STATUS);
69 	reg &= ~WDT_A370_EXPIRED;
70 	writel(reg, priv->reg + TIMER_A370_STATUS);
71 
72 	/* Enable watchdog timer */
73 	reg = readl(priv->reg + TIMER_CTRL);
74 	reg |= WDT_ENABLE_BIT;
75 	writel(reg, priv->reg + TIMER_CTRL);
76 
77 	/* Enable reset on watchdog */
78 	reg = readl(priv->rstout);
79 	reg |= RSTOUT_ENABLE_BIT;
80 	writel(reg, priv->rstout);
81 
82 	reg = readl(priv->rstout_mask);
83 	reg &= ~RSTOUT_MASK_BIT;
84 	writel(reg, priv->rstout_mask);
85 
86 	return 0;
87 }
88 
89 static int orion_wdt_stop(struct udevice *dev)
90 {
91 	struct orion_wdt_priv *priv = dev_get_priv(dev);
92 	u32 reg;
93 
94 	/* Disable reset on watchdog */
95 	reg = readl(priv->rstout_mask);
96 	reg |= RSTOUT_MASK_BIT;
97 	writel(reg, priv->rstout_mask);
98 
99 	reg = readl(priv->rstout);
100 	reg &= ~RSTOUT_ENABLE_BIT;
101 	writel(reg, priv->rstout);
102 
103 	/* Disable watchdog timer */
104 	reg = readl(priv->reg + TIMER_CTRL);
105 	reg &= ~WDT_ENABLE_BIT;
106 	writel(reg, priv->reg + TIMER_CTRL);
107 
108 	return 0;
109 }
110 
111 static inline bool save_reg_from_ofdata(struct udevice *dev, int index,
112 					void __iomem **reg, int *offset)
113 {
114 	fdt_addr_t addr;
115 	fdt_size_t off;
116 
117 	addr = fdtdec_get_addr_size_auto_noparent(
118 		gd->fdt_blob, dev_of_offset(dev), "reg", index, &off, true);
119 
120 	if (addr == FDT_ADDR_T_NONE)
121 		return false;
122 
123 	*reg = (void __iomem *) addr;
124 	if (offset)
125 		*offset = off;
126 
127 	return true;
128 }
129 
130 static int orion_wdt_ofdata_to_platdata(struct udevice *dev)
131 {
132 	struct orion_wdt_priv *priv = dev_get_priv(dev);
133 
134 	if (!save_reg_from_ofdata(dev, 0, &priv->reg,
135 				  &priv->wdt_counter_offset))
136 		goto err;
137 
138 	if (!save_reg_from_ofdata(dev, 1, &priv->rstout, NULL))
139 		goto err;
140 
141 	if (!save_reg_from_ofdata(dev, 2, &priv->rstout_mask, NULL))
142 		goto err;
143 
144 	return 0;
145 err:
146 	debug("%s: Could not determine Orion wdt IO addresses\n", __func__);
147 	return -ENXIO;
148 }
149 
150 static int orion_wdt_probe(struct udevice *dev)
151 {
152 	debug("%s: Probing wdt%u\n", __func__, dev->seq);
153 	orion_wdt_stop(dev);
154 
155 	return 0;
156 }
157 
158 static const struct wdt_ops orion_wdt_ops = {
159 	.start = orion_wdt_start,
160 	.reset = orion_wdt_reset,
161 	.stop = orion_wdt_stop,
162 };
163 
164 static const struct udevice_id orion_wdt_ids[] = {
165 	{ .compatible = "marvell,armada-380-wdt" },
166 	{}
167 };
168 
169 U_BOOT_DRIVER(orion_wdt) = {
170 	.name = "orion_wdt",
171 	.id = UCLASS_WDT,
172 	.of_match = orion_wdt_ids,
173 	.probe = orion_wdt_probe,
174 	.priv_auto_alloc_size = sizeof(struct orion_wdt_priv),
175 	.ofdata_to_platdata = orion_wdt_ofdata_to_platdata,
176 	.ops = &orion_wdt_ops,
177 };
178