Merge git://git.denx.de/u-boot-socfpga
[platform/kernel/u-boot.git] / drivers / watchdog / orion_wdt.c
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 <clk.h>
18 #include <wdt.h>
19 #include <linux/kernel.h>
20 #include <asm/io.h>
21 #include <asm/arch/cpu.h>
22 #include <asm/arch/soc.h>
23
24 DECLARE_GLOBAL_DATA_PTR;
25
26 struct orion_wdt_priv {
27         void __iomem *reg;
28         int wdt_counter_offset;
29         void __iomem *rstout;
30         void __iomem *rstout_mask;
31         u32 timeout;
32         unsigned long clk_rate;
33         struct clk clk;
34 };
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
46 static int orion_wdt_reset(struct udevice *dev)
47 {
48         struct orion_wdt_priv *priv = dev_get_priv(dev);
49
50         /* Reload watchdog duration */
51         writel(priv->clk_rate * priv->timeout,
52                priv->reg + priv->wdt_counter_offset);
53
54         return 0;
55 }
56
57 static int orion_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
58 {
59         struct orion_wdt_priv *priv = dev_get_priv(dev);
60         u32 reg;
61
62         priv->timeout = DIV_ROUND_UP(timeout_ms, 1000);
63
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 */
70         writel(priv->clk_rate * priv->timeout,
71                priv->reg + priv->wdt_counter_offset);
72
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
95 static 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
117 static 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
123         addr = devfdt_get_addr_size_index(dev, index, &off);
124         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
134 static 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;
149 err:
150         debug("%s: Could not determine Orion wdt IO addresses\n", __func__);
151         return -ENXIO;
152 }
153
154 static int orion_wdt_probe(struct udevice *dev)
155 {
156         struct orion_wdt_priv *priv = dev_get_priv(dev);
157         int ret;
158
159         debug("%s: Probing wdt%u\n", __func__, dev->seq);
160         orion_wdt_stop(dev);
161
162         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
168         return 0;
169 }
170
171 static const struct wdt_ops orion_wdt_ops = {
172         .start = orion_wdt_start,
173         .reset = orion_wdt_reset,
174         .stop = orion_wdt_stop,
175 };
176
177 static const struct udevice_id orion_wdt_ids[] = {
178         { .compatible = "marvell,armada-380-wdt" },
179         {}
180 };
181
182 U_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 };