blob: d33e2ac3dc19005dd045df17df40e262a47b9d94 [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>
Simon Glasscd93d622020-05-10 11:40:13 -060020#include <linux/bitops.h>
Chris Packham8e427ba2019-02-18 10:30:53 +130021#include <linux/kernel.h>
Marek Behún2ab77042017-06-09 19:28:41 +020022#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;
Chris Packham8e427ba2019-02-18 10:30:53 +130034 unsigned long clk_rate;
35 struct clk clk;
Marek Behún2ab77042017-06-09 19:28:41 +020036};
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 /* Reload watchdog duration */
Chris Packham8e427ba2019-02-18 10:30:53 +130053 writel(priv->clk_rate * priv->timeout,
54 priv->reg + priv->wdt_counter_offset);
Marek Behún2ab77042017-06-09 19:28:41 +020055
56 return 0;
57}
58
Chris Packham8e427ba2019-02-18 10:30:53 +130059static int orion_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
Marek Behún2ab77042017-06-09 19:28:41 +020060{
61 struct orion_wdt_priv *priv = dev_get_priv(dev);
62 u32 reg;
63
Chris Packham8e427ba2019-02-18 10:30:53 +130064 priv->timeout = DIV_ROUND_UP(timeout_ms, 1000);
Marek Behún2ab77042017-06-09 19:28:41 +020065
66 /* Enable the fixed watchdog clock input */
67 reg = readl(priv->reg + TIMER_CTRL);
68 reg |= WDT_AXP_FIXED_ENABLE_BIT;
69 writel(reg, priv->reg + TIMER_CTRL);
70
71 /* Set watchdog duration */
Chris Packham8e427ba2019-02-18 10:30:53 +130072 writel(priv->clk_rate * priv->timeout,
73 priv->reg + priv->wdt_counter_offset);
Marek Behún2ab77042017-06-09 19:28:41 +020074
75 /* Clear the watchdog expiration bit */
76 reg = readl(priv->reg + TIMER_A370_STATUS);
77 reg &= ~WDT_A370_EXPIRED;
78 writel(reg, priv->reg + TIMER_A370_STATUS);
79
80 /* Enable watchdog timer */
81 reg = readl(priv->reg + TIMER_CTRL);
82 reg |= WDT_ENABLE_BIT;
83 writel(reg, priv->reg + TIMER_CTRL);
84
85 /* Enable reset on watchdog */
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 /* Disable reset on watchdog */
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 /* Disable watchdog timer */
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
Chris Packham8562e412019-02-18 10:30:52 +1300125 addr = devfdt_get_addr_size_index(dev, index, &off);
Marek Behún2ab77042017-06-09 19:28:41 +0200126 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{
Chris Packham8e427ba2019-02-18 10:30:53 +1300158 struct orion_wdt_priv *priv = dev_get_priv(dev);
159 int ret;
160
Marek Behún2ab77042017-06-09 19:28:41 +0200161 debug("%s: Probing wdt%u\n", __func__, dev->seq);
162 orion_wdt_stop(dev);
163
Chris Packham8e427ba2019-02-18 10:30:53 +1300164 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
Marek Behún2ab77042017-06-09 19:28:41 +0200170 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};