1
2
3
4
5
6
7
8
9
10
11
12
13
14
15#include <common.h>
16#include <dm.h>
17#include <clk.h>
18#include <log.h>
19#include <wdt.h>
20#include <linux/bitops.h>
21#include <linux/kernel.h>
22#include <asm/io.h>
23#include <asm/arch/cpu.h>
24#include <asm/arch/soc.h>
25
26DECLARE_GLOBAL_DATA_PTR;
27
28struct orion_wdt_priv {
29 void __iomem *reg;
30 int wdt_counter_offset;
31 void __iomem *rstout;
32 void __iomem *rstout_mask;
33 u32 timeout;
34 unsigned long clk_rate;
35 struct clk clk;
36};
37
38#define RSTOUT_ENABLE_BIT BIT(8)
39#define RSTOUT_MASK_BIT BIT(10)
40#define WDT_ENABLE_BIT BIT(8)
41
42#define TIMER_CTRL 0x0000
43#define TIMER_A370_STATUS 0x04
44
45#define WDT_AXP_FIXED_ENABLE_BIT BIT(10)
46#define WDT_A370_EXPIRED BIT(31)
47
48static int orion_wdt_reset(struct udevice *dev)
49{
50 struct orion_wdt_priv *priv = dev_get_priv(dev);
51
52
53 writel(priv->clk_rate * priv->timeout,
54 priv->reg + priv->wdt_counter_offset);
55
56 return 0;
57}
58
59static int orion_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
60{
61 struct orion_wdt_priv *priv = dev_get_priv(dev);
62 u32 reg;
63
64 priv->timeout = DIV_ROUND_UP(timeout_ms, 1000);
65
66
67 reg = readl(priv->reg + TIMER_CTRL);
68 reg |= WDT_AXP_FIXED_ENABLE_BIT;
69 writel(reg, priv->reg + TIMER_CTRL);
70
71
72 writel(priv->clk_rate * priv->timeout,
73 priv->reg + priv->wdt_counter_offset);
74
75
76 reg = readl(priv->reg + TIMER_A370_STATUS);
77 reg &= ~WDT_A370_EXPIRED;
78 writel(reg, priv->reg + TIMER_A370_STATUS);
79
80
81 reg = readl(priv->reg + TIMER_CTRL);
82 reg |= WDT_ENABLE_BIT;
83 writel(reg, priv->reg + TIMER_CTRL);
84
85
86 reg = readl(priv->rstout);
87 reg |= RSTOUT_ENABLE_BIT;
88 writel(reg, priv->rstout);
89
90 reg = readl(priv->rstout_mask);
91 reg &= ~RSTOUT_MASK_BIT;
92 writel(reg, priv->rstout_mask);
93
94 return 0;
95}
96
97static int orion_wdt_stop(struct udevice *dev)
98{
99 struct orion_wdt_priv *priv = dev_get_priv(dev);
100 u32 reg;
101
102
103 reg = readl(priv->rstout_mask);
104 reg |= RSTOUT_MASK_BIT;
105 writel(reg, priv->rstout_mask);
106
107 reg = readl(priv->rstout);
108 reg &= ~RSTOUT_ENABLE_BIT;
109 writel(reg, priv->rstout);
110
111
112 reg = readl(priv->reg + TIMER_CTRL);
113 reg &= ~WDT_ENABLE_BIT;
114 writel(reg, priv->reg + TIMER_CTRL);
115
116 return 0;
117}
118
119static inline bool save_reg_from_ofdata(struct udevice *dev, int index,
120 void __iomem **reg, int *offset)
121{
122 fdt_addr_t addr;
123 fdt_size_t off;
124
125 addr = devfdt_get_addr_size_index(dev, index, &off);
126 if (addr == FDT_ADDR_T_NONE)
127 return false;
128
129 *reg = (void __iomem *) addr;
130 if (offset)
131 *offset = off;
132
133 return true;
134}
135
136static int orion_wdt_ofdata_to_platdata(struct udevice *dev)
137{
138 struct orion_wdt_priv *priv = dev_get_priv(dev);
139
140 if (!save_reg_from_ofdata(dev, 0, &priv->reg,
141 &priv->wdt_counter_offset))
142 goto err;
143
144 if (!save_reg_from_ofdata(dev, 1, &priv->rstout, NULL))
145 goto err;
146
147 if (!save_reg_from_ofdata(dev, 2, &priv->rstout_mask, NULL))
148 goto err;
149
150 return 0;
151err:
152 debug("%s: Could not determine Orion wdt IO addresses\n", __func__);
153 return -ENXIO;
154}
155
156static int orion_wdt_probe(struct udevice *dev)
157{
158 struct orion_wdt_priv *priv = dev_get_priv(dev);
159 int ret;
160
161 debug("%s: Probing wdt%u\n", __func__, dev->seq);
162 orion_wdt_stop(dev);
163
164 ret = clk_get_by_name(dev, "fixed", &priv->clk);
165 if (!ret)
166 priv->clk_rate = clk_get_rate(&priv->clk);
167 else
168 priv->clk_rate = 25000000;
169
170 return 0;
171}
172
173static const struct wdt_ops orion_wdt_ops = {
174 .start = orion_wdt_start,
175 .reset = orion_wdt_reset,
176 .stop = orion_wdt_stop,
177};
178
179static const struct udevice_id orion_wdt_ids[] = {
180 { .compatible = "marvell,armada-380-wdt" },
181 {}
182};
183
184U_BOOT_DRIVER(orion_wdt) = {
185 .name = "orion_wdt",
186 .id = UCLASS_WDT,
187 .of_match = orion_wdt_ids,
188 .probe = orion_wdt_probe,
189 .priv_auto_alloc_size = sizeof(struct orion_wdt_priv),
190 .ofdata_to_platdata = orion_wdt_ofdata_to_platdata,
191 .ops = &orion_wdt_ops,
192};
193