blob: b36488bb5b96f8782d739e67eb2dc14c19fdaf60 [file] [log] [blame]
Angelo Dureghello9b8bc512023-06-24 22:30:18 +02001// SPDX-License-Identifier: GPL-2.0
2/*
3 * mcf_wdt.c - driver for ColdFire on-chip watchdog
4 *
5 * Author: Angelo Dureghello <angelo@kernel-space.org>
6 *
7 */
8
9#include <common.h>
10#include <dm.h>
11#include <hang.h>
12#include <asm/io.h>
13#include <wdt.h>
14#include <linux/bitops.h>
15
16#define DIVIDER_5XXX 4096
17#define DIVIDER_5282 8192
18
19#define WCR_EN BIT(0)
20#define WCR_HALTED BIT(1)
21#define WCR_DOZE BIT(2)
22#define WCR_WAIT BIT(3)
23
24struct watchdog_regs {
25 u16 wcr; /* Control */
26 u16 wmr; /* Service */
27 u16 wcntr; /* Counter */
28 u16 wsr; /* Reset Status */
29};
30
31static void mcf_watchdog_reset(struct watchdog_regs *wdog)
32{
33 if (!IS_ENABLED(CONFIG_WATCHDOG_RESET_DISABLE)) {
34 writew(0x5555, &wdog->wsr);
35 writew(0xaaaa, &wdog->wsr);
36 }
37}
38
39static void mcf_watchdog_init(struct watchdog_regs *wdog, u32 fixed_divider,
40 u64 timeout_msecs)
41{
42 u32 wdog_module, cycles_per_sec;
43
44 cycles_per_sec = CFG_SYS_CLK / fixed_divider;
45
46 wdog_module = cycles_per_sec * ((u32)timeout_msecs / 1000);
47 wdog_module += (cycles_per_sec / 1000) * ((u32)timeout_msecs % 1000);
48
49 /* Limit check, max 16 bits */
50 if (wdog_module > 0xffff)
51 wdog_module = 0xffff;
52
53 /* Set timeout and enable watchdog */
54 writew((u16)wdog_module, &wdog->wmr);
55 writew(WCR_EN, &wdog->wcr);
56
57 mcf_watchdog_reset(wdog);
58}
59
60struct mcf_wdt_priv {
61 void __iomem *base;
62 u32 fixed_divider;
63};
64
65static int mcf_wdt_expire_now(struct udevice *dev, ulong flags)
66{
67 hang();
68
69 return 0;
70}
71
72static int mcf_wdt_reset(struct udevice *dev)
73{
74 struct mcf_wdt_priv *priv = dev_get_priv(dev);
75
76 mcf_watchdog_reset(priv->base);
77
78 return 0;
79}
80
81static int mcf_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
82{
83 struct mcf_wdt_priv *priv = dev_get_priv(dev);
84
85 /* Timeout from fdt (timeout) comes in milliseconds */
86 mcf_watchdog_init(priv->base, priv->fixed_divider, timeout);
87
88 return 0;
89}
90
91static int mcf_wdt_stop(struct udevice *dev)
92{
93 struct mcf_wdt_priv *priv = dev_get_priv(dev);
94 struct watchdog_regs *wdog = (struct watchdog_regs *)priv->base;
95
96 setbits_be16(&wdog->wcr, WCR_HALTED);
97
98 return 0;
99}
100
101static int mcf_wdt_probe(struct udevice *dev)
102{
103 struct mcf_wdt_priv *priv = dev_get_priv(dev);
104
105 priv->base = dev_read_addr_ptr(dev);
106 if (!priv->base)
107 return -ENOENT;
108
109 priv->fixed_divider = (u32)dev_get_driver_data(dev);
110
111 return 0;
112}
113
114static const struct wdt_ops mcf_wdt_ops = {
115 .start = mcf_wdt_start,
116 .stop = mcf_wdt_stop,
117 .reset = mcf_wdt_reset,
118 .expire_now = mcf_wdt_expire_now,
119};
120
121static const struct udevice_id mcf_wdt_ids[] = {
122 { .compatible = "fsl,mcf5208-wdt", .data = DIVIDER_5XXX },
123 { .compatible = "fsl,mcf5282-wdt", .data = DIVIDER_5282 },
124 {}
125};
126
127U_BOOT_DRIVER(mcf_wdt) = {
128 .name = "mcf_wdt",
129 .id = UCLASS_WDT,
130 .of_match = mcf_wdt_ids,
131 .probe = mcf_wdt_probe,
132 .ops = &mcf_wdt_ops,
133 .priv_auto = sizeof(struct mcf_wdt_priv),
134 .flags = DM_FLAG_PRE_RELOC,
135};