1 // SPDX-License-Identifier: GPL-2.0+
3 * Marvell Armada 37xx SoC Watchdog Driver
5 * Marek Behun <marek.behun@nic.cz>
12 #include <asm/arch/cpu.h>
13 #include <asm/arch/soc.h>
15 DECLARE_GLOBAL_DATA_PTR
;
18 void __iomem
*sel_reg
;
25 * We use Counter 1 as watchdog timer, and Counter 0 for re-triggering Counter 1
28 #define CNTR_CTRL(id) ((id) * 0x10)
29 #define CNTR_CTRL_ENABLE 0x0001
30 #define CNTR_CTRL_ACTIVE 0x0002
31 #define CNTR_CTRL_MODE_MASK 0x000c
32 #define CNTR_CTRL_MODE_ONESHOT 0x0000
33 #define CNTR_CTRL_MODE_HWSIG 0x000c
34 #define CNTR_CTRL_TRIG_SRC_MASK 0x00f0
35 #define CNTR_CTRL_TRIG_SRC_PREV_CNTR 0x0050
36 #define CNTR_CTRL_PRESCALE_MASK 0xff00
37 #define CNTR_CTRL_PRESCALE_MIN 2
38 #define CNTR_CTRL_PRESCALE_SHIFT 8
40 #define CNTR_COUNT_LOW(id) (CNTR_CTRL(id) + 0x4)
41 #define CNTR_COUNT_HIGH(id) (CNTR_CTRL(id) + 0x8)
43 static void set_counter_value(struct a37xx_wdt
*priv
, int id
, u64 val
)
45 writel(val
& 0xffffffff, priv
->reg
+ CNTR_COUNT_LOW(id
));
46 writel(val
>> 32, priv
->reg
+ CNTR_COUNT_HIGH(id
));
49 static void counter_enable(struct a37xx_wdt
*priv
, int id
)
51 setbits_le32(priv
->reg
+ CNTR_CTRL(id
), CNTR_CTRL_ENABLE
);
54 static void counter_disable(struct a37xx_wdt
*priv
, int id
)
56 clrbits_le32(priv
->reg
+ CNTR_CTRL(id
), CNTR_CTRL_ENABLE
);
59 static int init_counter(struct a37xx_wdt
*priv
, int id
, u32 mode
, u32 trig_src
)
63 reg
= readl(priv
->reg
+ CNTR_CTRL(id
));
64 if (reg
& CNTR_CTRL_ACTIVE
)
67 reg
&= ~(CNTR_CTRL_MODE_MASK
| CNTR_CTRL_PRESCALE_MASK
|
68 CNTR_CTRL_TRIG_SRC_MASK
);
73 /* set prescaler to the min value */
74 reg
|= CNTR_CTRL_PRESCALE_MIN
<< CNTR_CTRL_PRESCALE_SHIFT
;
76 /* set trigger source */
79 writel(reg
, priv
->reg
+ CNTR_CTRL(id
));
84 static int a37xx_wdt_reset(struct udevice
*dev
)
86 struct a37xx_wdt
*priv
= dev_get_priv(dev
);
91 /* counter 1 is retriggered by forcing end count on counter 0 */
92 counter_disable(priv
, 0);
93 counter_enable(priv
, 0);
98 static int a37xx_wdt_expire_now(struct udevice
*dev
, ulong flags
)
100 struct a37xx_wdt
*priv
= dev_get_priv(dev
);
102 /* first we set timeout to 0 */
103 counter_disable(priv
, 1);
104 set_counter_value(priv
, 1, 0);
105 counter_enable(priv
, 1);
107 /* and then we start counter 1 by forcing end count on counter 0 */
108 counter_disable(priv
, 0);
109 counter_enable(priv
, 0);
114 static int a37xx_wdt_start(struct udevice
*dev
, u64 ms
, ulong flags
)
116 struct a37xx_wdt
*priv
= dev_get_priv(dev
);
119 err
= init_counter(priv
, 0, CNTR_CTRL_MODE_ONESHOT
, 0);
123 err
= init_counter(priv
, 1, CNTR_CTRL_MODE_HWSIG
,
124 CNTR_CTRL_TRIG_SRC_PREV_CNTR
);
128 priv
->timeout
= ms
* priv
->clk_rate
/ 1000 / CNTR_CTRL_PRESCALE_MIN
;
130 set_counter_value(priv
, 0, 0);
131 set_counter_value(priv
, 1, priv
->timeout
);
132 counter_enable(priv
, 1);
134 /* we have to force end count on counter 0 to start counter 1 */
135 counter_enable(priv
, 0);
140 static int a37xx_wdt_stop(struct udevice
*dev
)
142 struct a37xx_wdt
*priv
= dev_get_priv(dev
);
144 counter_disable(priv
, 1);
145 counter_disable(priv
, 0);
146 writel(0, priv
->sel_reg
);
151 static int a37xx_wdt_probe(struct udevice
*dev
)
153 struct a37xx_wdt
*priv
= dev_get_priv(dev
);
156 addr
= dev_read_addr_index(dev
, 0);
157 if (addr
== FDT_ADDR_T_NONE
)
159 priv
->sel_reg
= (void __iomem
*)addr
;
161 addr
= dev_read_addr_index(dev
, 1);
162 if (addr
== FDT_ADDR_T_NONE
)
164 priv
->reg
= (void __iomem
*)addr
;
166 priv
->clk_rate
= (ulong
)get_ref_clk() * 1000000;
169 * We use counter 1 as watchdog timer, therefore we only set bit
170 * TIMER1_IS_WCHDOG_TIMER. Counter 0 is only used to force re-trigger on
173 writel(1 << 1, priv
->sel_reg
);
177 dev_err(dev
, "no io address\n");
181 static const struct wdt_ops a37xx_wdt_ops
= {
182 .start
= a37xx_wdt_start
,
183 .reset
= a37xx_wdt_reset
,
184 .stop
= a37xx_wdt_stop
,
185 .expire_now
= a37xx_wdt_expire_now
,
188 static const struct udevice_id a37xx_wdt_ids
[] = {
189 { .compatible
= "marvell,armada-3700-wdt" },
193 U_BOOT_DRIVER(a37xx_wdt
) = {
194 .name
= "armada_37xx_wdt",
196 .of_match
= a37xx_wdt_ids
,
197 .probe
= a37xx_wdt_probe
,
198 .priv_auto_alloc_size
= sizeof(struct a37xx_wdt
),
199 .ops
= &a37xx_wdt_ops
,