1*2ab7704aSMarek Behún /*
2*2ab7704aSMarek Behún * drivers/watchdog/orion_wdt.c
3*2ab7704aSMarek Behún *
4*2ab7704aSMarek Behún * Watchdog driver for Orion/Kirkwood processors
5*2ab7704aSMarek Behún *
6*2ab7704aSMarek Behún * Authors: Tomas Hlavacek <tmshlvck@gmail.com>
7*2ab7704aSMarek Behún * Sylver Bruneau <sylver.bruneau@googlemail.com>
8*2ab7704aSMarek Behún * Marek Behun <marek.behun@nic.cz>
9*2ab7704aSMarek Behún *
10*2ab7704aSMarek Behún * This file is licensed under the terms of the GNU General Public
11*2ab7704aSMarek Behún * License version 2. This program is licensed "as is" without any
12*2ab7704aSMarek Behún * warranty of any kind, whether express or implied.
13*2ab7704aSMarek Behún */
14*2ab7704aSMarek Behún
15*2ab7704aSMarek Behún #include <common.h>
16*2ab7704aSMarek Behún #include <dm.h>
17*2ab7704aSMarek Behún #include <wdt.h>
18*2ab7704aSMarek Behún #include <asm/io.h>
19*2ab7704aSMarek Behún #include <asm/arch/cpu.h>
20*2ab7704aSMarek Behún #include <asm/arch/soc.h>
21*2ab7704aSMarek Behún
22*2ab7704aSMarek Behún DECLARE_GLOBAL_DATA_PTR;
23*2ab7704aSMarek Behún
24*2ab7704aSMarek Behún struct orion_wdt_priv {
25*2ab7704aSMarek Behún void __iomem *reg;
26*2ab7704aSMarek Behún int wdt_counter_offset;
27*2ab7704aSMarek Behún void __iomem *rstout;
28*2ab7704aSMarek Behún void __iomem *rstout_mask;
29*2ab7704aSMarek Behún u32 timeout;
30*2ab7704aSMarek Behún };
31*2ab7704aSMarek Behún
32*2ab7704aSMarek Behún #define RSTOUT_ENABLE_BIT BIT(8)
33*2ab7704aSMarek Behún #define RSTOUT_MASK_BIT BIT(10)
34*2ab7704aSMarek Behún #define WDT_ENABLE_BIT BIT(8)
35*2ab7704aSMarek Behún
36*2ab7704aSMarek Behún #define TIMER_CTRL 0x0000
37*2ab7704aSMarek Behún #define TIMER_A370_STATUS 0x04
38*2ab7704aSMarek Behún
39*2ab7704aSMarek Behún #define WDT_AXP_FIXED_ENABLE_BIT BIT(10)
40*2ab7704aSMarek Behún #define WDT_A370_EXPIRED BIT(31)
41*2ab7704aSMarek Behún
orion_wdt_reset(struct udevice * dev)42*2ab7704aSMarek Behún static int orion_wdt_reset(struct udevice *dev)
43*2ab7704aSMarek Behún {
44*2ab7704aSMarek Behún struct orion_wdt_priv *priv = dev_get_priv(dev);
45*2ab7704aSMarek Behún
46*2ab7704aSMarek Behún /* Reload watchdog duration */
47*2ab7704aSMarek Behún writel(priv->timeout, priv->reg + priv->wdt_counter_offset);
48*2ab7704aSMarek Behún
49*2ab7704aSMarek Behún return 0;
50*2ab7704aSMarek Behún }
51*2ab7704aSMarek Behún
orion_wdt_start(struct udevice * dev,u64 timeout,ulong flags)52*2ab7704aSMarek Behún static int orion_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
53*2ab7704aSMarek Behún {
54*2ab7704aSMarek Behún struct orion_wdt_priv *priv = dev_get_priv(dev);
55*2ab7704aSMarek Behún u32 reg;
56*2ab7704aSMarek Behún
57*2ab7704aSMarek Behún priv->timeout = (u32) timeout;
58*2ab7704aSMarek Behún
59*2ab7704aSMarek Behún /* Enable the fixed watchdog clock input */
60*2ab7704aSMarek Behún reg = readl(priv->reg + TIMER_CTRL);
61*2ab7704aSMarek Behún reg |= WDT_AXP_FIXED_ENABLE_BIT;
62*2ab7704aSMarek Behún writel(reg, priv->reg + TIMER_CTRL);
63*2ab7704aSMarek Behún
64*2ab7704aSMarek Behún /* Set watchdog duration */
65*2ab7704aSMarek Behún writel(priv->timeout, priv->reg + priv->wdt_counter_offset);
66*2ab7704aSMarek Behún
67*2ab7704aSMarek Behún /* Clear the watchdog expiration bit */
68*2ab7704aSMarek Behún reg = readl(priv->reg + TIMER_A370_STATUS);
69*2ab7704aSMarek Behún reg &= ~WDT_A370_EXPIRED;
70*2ab7704aSMarek Behún writel(reg, priv->reg + TIMER_A370_STATUS);
71*2ab7704aSMarek Behún
72*2ab7704aSMarek Behún /* Enable watchdog timer */
73*2ab7704aSMarek Behún reg = readl(priv->reg + TIMER_CTRL);
74*2ab7704aSMarek Behún reg |= WDT_ENABLE_BIT;
75*2ab7704aSMarek Behún writel(reg, priv->reg + TIMER_CTRL);
76*2ab7704aSMarek Behún
77*2ab7704aSMarek Behún /* Enable reset on watchdog */
78*2ab7704aSMarek Behún reg = readl(priv->rstout);
79*2ab7704aSMarek Behún reg |= RSTOUT_ENABLE_BIT;
80*2ab7704aSMarek Behún writel(reg, priv->rstout);
81*2ab7704aSMarek Behún
82*2ab7704aSMarek Behún reg = readl(priv->rstout_mask);
83*2ab7704aSMarek Behún reg &= ~RSTOUT_MASK_BIT;
84*2ab7704aSMarek Behún writel(reg, priv->rstout_mask);
85*2ab7704aSMarek Behún
86*2ab7704aSMarek Behún return 0;
87*2ab7704aSMarek Behún }
88*2ab7704aSMarek Behún
orion_wdt_stop(struct udevice * dev)89*2ab7704aSMarek Behún static int orion_wdt_stop(struct udevice *dev)
90*2ab7704aSMarek Behún {
91*2ab7704aSMarek Behún struct orion_wdt_priv *priv = dev_get_priv(dev);
92*2ab7704aSMarek Behún u32 reg;
93*2ab7704aSMarek Behún
94*2ab7704aSMarek Behún /* Disable reset on watchdog */
95*2ab7704aSMarek Behún reg = readl(priv->rstout_mask);
96*2ab7704aSMarek Behún reg |= RSTOUT_MASK_BIT;
97*2ab7704aSMarek Behún writel(reg, priv->rstout_mask);
98*2ab7704aSMarek Behún
99*2ab7704aSMarek Behún reg = readl(priv->rstout);
100*2ab7704aSMarek Behún reg &= ~RSTOUT_ENABLE_BIT;
101*2ab7704aSMarek Behún writel(reg, priv->rstout);
102*2ab7704aSMarek Behún
103*2ab7704aSMarek Behún /* Disable watchdog timer */
104*2ab7704aSMarek Behún reg = readl(priv->reg + TIMER_CTRL);
105*2ab7704aSMarek Behún reg &= ~WDT_ENABLE_BIT;
106*2ab7704aSMarek Behún writel(reg, priv->reg + TIMER_CTRL);
107*2ab7704aSMarek Behún
108*2ab7704aSMarek Behún return 0;
109*2ab7704aSMarek Behún }
110*2ab7704aSMarek Behún
save_reg_from_ofdata(struct udevice * dev,int index,void __iomem ** reg,int * offset)111*2ab7704aSMarek Behún static inline bool save_reg_from_ofdata(struct udevice *dev, int index,
112*2ab7704aSMarek Behún void __iomem **reg, int *offset)
113*2ab7704aSMarek Behún {
114*2ab7704aSMarek Behún fdt_addr_t addr;
115*2ab7704aSMarek Behún fdt_size_t off;
116*2ab7704aSMarek Behún
117*2ab7704aSMarek Behún addr = fdtdec_get_addr_size_auto_noparent(
118*2ab7704aSMarek Behún gd->fdt_blob, dev_of_offset(dev), "reg", index, &off, true);
119*2ab7704aSMarek Behún
120*2ab7704aSMarek Behún if (addr == FDT_ADDR_T_NONE)
121*2ab7704aSMarek Behún return false;
122*2ab7704aSMarek Behún
123*2ab7704aSMarek Behún *reg = (void __iomem *) addr;
124*2ab7704aSMarek Behún if (offset)
125*2ab7704aSMarek Behún *offset = off;
126*2ab7704aSMarek Behún
127*2ab7704aSMarek Behún return true;
128*2ab7704aSMarek Behún }
129*2ab7704aSMarek Behún
orion_wdt_ofdata_to_platdata(struct udevice * dev)130*2ab7704aSMarek Behún static int orion_wdt_ofdata_to_platdata(struct udevice *dev)
131*2ab7704aSMarek Behún {
132*2ab7704aSMarek Behún struct orion_wdt_priv *priv = dev_get_priv(dev);
133*2ab7704aSMarek Behún
134*2ab7704aSMarek Behún if (!save_reg_from_ofdata(dev, 0, &priv->reg,
135*2ab7704aSMarek Behún &priv->wdt_counter_offset))
136*2ab7704aSMarek Behún goto err;
137*2ab7704aSMarek Behún
138*2ab7704aSMarek Behún if (!save_reg_from_ofdata(dev, 1, &priv->rstout, NULL))
139*2ab7704aSMarek Behún goto err;
140*2ab7704aSMarek Behún
141*2ab7704aSMarek Behún if (!save_reg_from_ofdata(dev, 2, &priv->rstout_mask, NULL))
142*2ab7704aSMarek Behún goto err;
143*2ab7704aSMarek Behún
144*2ab7704aSMarek Behún return 0;
145*2ab7704aSMarek Behún err:
146*2ab7704aSMarek Behún debug("%s: Could not determine Orion wdt IO addresses\n", __func__);
147*2ab7704aSMarek Behún return -ENXIO;
148*2ab7704aSMarek Behún }
149*2ab7704aSMarek Behún
orion_wdt_probe(struct udevice * dev)150*2ab7704aSMarek Behún static int orion_wdt_probe(struct udevice *dev)
151*2ab7704aSMarek Behún {
152*2ab7704aSMarek Behún debug("%s: Probing wdt%u\n", __func__, dev->seq);
153*2ab7704aSMarek Behún orion_wdt_stop(dev);
154*2ab7704aSMarek Behún
155*2ab7704aSMarek Behún return 0;
156*2ab7704aSMarek Behún }
157*2ab7704aSMarek Behún
158*2ab7704aSMarek Behún static const struct wdt_ops orion_wdt_ops = {
159*2ab7704aSMarek Behún .start = orion_wdt_start,
160*2ab7704aSMarek Behún .reset = orion_wdt_reset,
161*2ab7704aSMarek Behún .stop = orion_wdt_stop,
162*2ab7704aSMarek Behún };
163*2ab7704aSMarek Behún
164*2ab7704aSMarek Behún static const struct udevice_id orion_wdt_ids[] = {
165*2ab7704aSMarek Behún { .compatible = "marvell,armada-380-wdt" },
166*2ab7704aSMarek Behún {}
167*2ab7704aSMarek Behún };
168*2ab7704aSMarek Behún
169*2ab7704aSMarek Behún U_BOOT_DRIVER(orion_wdt) = {
170*2ab7704aSMarek Behún .name = "orion_wdt",
171*2ab7704aSMarek Behún .id = UCLASS_WDT,
172*2ab7704aSMarek Behún .of_match = orion_wdt_ids,
173*2ab7704aSMarek Behún .probe = orion_wdt_probe,
174*2ab7704aSMarek Behún .priv_auto_alloc_size = sizeof(struct orion_wdt_priv),
175*2ab7704aSMarek Behún .ofdata_to_platdata = orion_wdt_ofdata_to_platdata,
176*2ab7704aSMarek Behún .ops = &orion_wdt_ops,
177*2ab7704aSMarek Behún };
178