blob: 885821d562ea1ae49eec315a33a6b22144648081 [file] [log] [blame]
Marek Behún0346a1d2017-06-09 19:28:41 +02001/*
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>
Chris Packhamc4f0d032019-02-18 10:30:53 +130017#include <clk.h>
Marek Behún0346a1d2017-06-09 19:28:41 +020018#include <wdt.h>
Chris Packhamc4f0d032019-02-18 10:30:53 +130019#include <linux/kernel.h>
Marek Behún0346a1d2017-06-09 19:28:41 +020020#include <asm/io.h>
21#include <asm/arch/cpu.h>
22#include <asm/arch/soc.h>
23
24DECLARE_GLOBAL_DATA_PTR;
25
26struct orion_wdt_priv {
27 void __iomem *reg;
28 int wdt_counter_offset;
29 void __iomem *rstout;
30 void __iomem *rstout_mask;
31 u32 timeout;
Chris Packhamc4f0d032019-02-18 10:30:53 +130032 unsigned long clk_rate;
33 struct clk clk;
Marek Behún0346a1d2017-06-09 19:28:41 +020034};
35
36#define RSTOUT_ENABLE_BIT BIT(8)
37#define RSTOUT_MASK_BIT BIT(10)
38#define WDT_ENABLE_BIT BIT(8)
39
40#define TIMER_CTRL 0x0000
41#define TIMER_A370_STATUS 0x04
42
43#define WDT_AXP_FIXED_ENABLE_BIT BIT(10)
44#define WDT_A370_EXPIRED BIT(31)
45
46static int orion_wdt_reset(struct udevice *dev)
47{
48 struct orion_wdt_priv *priv = dev_get_priv(dev);
49
50 /* Reload watchdog duration */
Chris Packhamc4f0d032019-02-18 10:30:53 +130051 writel(priv->clk_rate * priv->timeout,
52 priv->reg + priv->wdt_counter_offset);
Marek Behún0346a1d2017-06-09 19:28:41 +020053
54 return 0;
55}
56
Chris Packhamc4f0d032019-02-18 10:30:53 +130057static int orion_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
Marek Behún0346a1d2017-06-09 19:28:41 +020058{
59 struct orion_wdt_priv *priv = dev_get_priv(dev);
60 u32 reg;
61
Chris Packhamc4f0d032019-02-18 10:30:53 +130062 priv->timeout = DIV_ROUND_UP(timeout_ms, 1000);
Marek Behún0346a1d2017-06-09 19:28:41 +020063
64 /* Enable the fixed watchdog clock input */
65 reg = readl(priv->reg + TIMER_CTRL);
66 reg |= WDT_AXP_FIXED_ENABLE_BIT;
67 writel(reg, priv->reg + TIMER_CTRL);
68
69 /* Set watchdog duration */
Chris Packhamc4f0d032019-02-18 10:30:53 +130070 writel(priv->clk_rate * priv->timeout,
71 priv->reg + priv->wdt_counter_offset);
Marek Behún0346a1d2017-06-09 19:28:41 +020072
73 /* Clear the watchdog expiration bit */
74 reg = readl(priv->reg + TIMER_A370_STATUS);
75 reg &= ~WDT_A370_EXPIRED;
76 writel(reg, priv->reg + TIMER_A370_STATUS);
77
78 /* Enable watchdog timer */
79 reg = readl(priv->reg + TIMER_CTRL);
80 reg |= WDT_ENABLE_BIT;
81 writel(reg, priv->reg + TIMER_CTRL);
82
83 /* Enable reset on watchdog */
84 reg = readl(priv->rstout);
85 reg |= RSTOUT_ENABLE_BIT;
86 writel(reg, priv->rstout);
87
88 reg = readl(priv->rstout_mask);
89 reg &= ~RSTOUT_MASK_BIT;
90 writel(reg, priv->rstout_mask);
91
92 return 0;
93}
94
95static int orion_wdt_stop(struct udevice *dev)
96{
97 struct orion_wdt_priv *priv = dev_get_priv(dev);
98 u32 reg;
99
100 /* Disable reset on watchdog */
101 reg = readl(priv->rstout_mask);
102 reg |= RSTOUT_MASK_BIT;
103 writel(reg, priv->rstout_mask);
104
105 reg = readl(priv->rstout);
106 reg &= ~RSTOUT_ENABLE_BIT;
107 writel(reg, priv->rstout);
108
109 /* Disable watchdog timer */
110 reg = readl(priv->reg + TIMER_CTRL);
111 reg &= ~WDT_ENABLE_BIT;
112 writel(reg, priv->reg + TIMER_CTRL);
113
114 return 0;
115}
116
117static inline bool save_reg_from_ofdata(struct udevice *dev, int index,
118 void __iomem **reg, int *offset)
119{
120 fdt_addr_t addr;
121 fdt_size_t off;
122
Chris Packham7d1e45e2019-02-18 10:30:52 +1300123 addr = devfdt_get_addr_size_index(dev, index, &off);
Marek Behún0346a1d2017-06-09 19:28:41 +0200124 if (addr == FDT_ADDR_T_NONE)
125 return false;
126
127 *reg = (void __iomem *) addr;
128 if (offset)
129 *offset = off;
130
131 return true;
132}
133
134static int orion_wdt_ofdata_to_platdata(struct udevice *dev)
135{
136 struct orion_wdt_priv *priv = dev_get_priv(dev);
137
138 if (!save_reg_from_ofdata(dev, 0, &priv->reg,
139 &priv->wdt_counter_offset))
140 goto err;
141
142 if (!save_reg_from_ofdata(dev, 1, &priv->rstout, NULL))
143 goto err;
144
145 if (!save_reg_from_ofdata(dev, 2, &priv->rstout_mask, NULL))
146 goto err;
147
148 return 0;
149err:
150 debug("%s: Could not determine Orion wdt IO addresses\n", __func__);
151 return -ENXIO;
152}
153
154static int orion_wdt_probe(struct udevice *dev)
155{
Chris Packhamc4f0d032019-02-18 10:30:53 +1300156 struct orion_wdt_priv *priv = dev_get_priv(dev);
157 int ret;
158
Marek Behún0346a1d2017-06-09 19:28:41 +0200159 debug("%s: Probing wdt%u\n", __func__, dev->seq);
160 orion_wdt_stop(dev);
161
Chris Packhamc4f0d032019-02-18 10:30:53 +1300162 ret = clk_get_by_name(dev, "fixed", &priv->clk);
163 if (!ret)
164 priv->clk_rate = clk_get_rate(&priv->clk);
165 else
166 priv->clk_rate = 25000000;
167
Marek Behún0346a1d2017-06-09 19:28:41 +0200168 return 0;
169}
170
171static const struct wdt_ops orion_wdt_ops = {
172 .start = orion_wdt_start,
173 .reset = orion_wdt_reset,
174 .stop = orion_wdt_stop,
175};
176
177static const struct udevice_id orion_wdt_ids[] = {
178 { .compatible = "marvell,armada-380-wdt" },
179 {}
180};
181
182U_BOOT_DRIVER(orion_wdt) = {
183 .name = "orion_wdt",
184 .id = UCLASS_WDT,
185 .of_match = orion_wdt_ids,
186 .probe = orion_wdt_probe,
187 .priv_auto_alloc_size = sizeof(struct orion_wdt_priv),
188 .ofdata_to_platdata = orion_wdt_ofdata_to_platdata,
189 .ops = &orion_wdt_ops,
190};