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