1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Xilinx window watchdog timer driver.
4 *
5 * Author(s): Michal Simek <michal.simek@xilinx.com>
6 * Ashok Reddy Soma <ashokred@xilinx.com>
7 *
8 * Copyright (c) 2020, Xilinx Inc.
9 */
10
11 #include <clk.h>
12 #include <common.h>
13 #include <dm.h>
14 #include <regmap.h>
15 #include <wdt.h>
16 #include <linux/compat.h>
17 #include <linux/io.h>
18
19 /* Refresh Register Masks */
20 #define XWT_WWREF_GWRR_MASK BIT(0) /* Refresh and start new period */
21
22 /* Generic Control/Status Register Masks */
23 #define XWT_WWCSR_GWEN_MASK BIT(0) /* Enable Bit */
24
25 /* Register offsets for the Wdt device */
26 #define XWT_WWREF_OFFSET 0x1000 /* Refresh Register */
27 #define XWT_WWCSR_OFFSET 0x2000 /* Control/Status Register */
28 #define XWT_WWOFF_OFFSET 0x2008 /* Offset Register */
29 #define XWT_WWCMP0_OFFSET 0x2010 /* Compare Value Register0 */
30 #define XWT_WWCMP1_OFFSET 0x2014 /* Compare Value Register1 */
31 #define XWT_WWWRST_OFFSET 0x2FD0 /* Warm Reset Register */
32
33 struct xlnx_wwdt_priv {
34 bool enable_once;
35 struct regmap *regs;
36 struct clk clk;
37 };
38
39 struct xlnx_wwdt_plat {
40 bool enable_once;
41 };
42
xlnx_wwdt_reset(struct udevice * dev)43 static int xlnx_wwdt_reset(struct udevice *dev)
44 {
45 struct xlnx_wwdt_priv *wdt = dev_get_priv(dev);
46
47 regmap_write(wdt->regs, XWT_WWREF_OFFSET, XWT_WWREF_GWRR_MASK);
48
49 return 0;
50 }
51
xlnx_wwdt_stop(struct udevice * dev)52 static int xlnx_wwdt_stop(struct udevice *dev)
53 {
54 u32 csr;
55 struct xlnx_wwdt_priv *wdt = dev_get_priv(dev);
56
57 if (wdt->enable_once) {
58 dev_warn(dev, "Can't stop Xilinx watchdog.\n");
59 return -EBUSY;
60 }
61
62 /* Disable the generic watchdog timer */
63 regmap_read(wdt->regs, XWT_WWCSR_OFFSET, &csr);
64 csr &= ~(XWT_WWCSR_GWEN_MASK);
65 regmap_write(wdt->regs, XWT_WWCSR_OFFSET, csr);
66
67 clk_disable(&wdt->clk);
68
69 dev_dbg(dev, "Watchdog disabled!\n");
70
71 return 0;
72 }
73
xlnx_wwdt_start(struct udevice * dev,u64 timeout,ulong flags)74 static int xlnx_wwdt_start(struct udevice *dev, u64 timeout, ulong flags)
75 {
76 int ret;
77 u32 csr;
78 u64 count;
79 unsigned long clock_f;
80 struct xlnx_wwdt_priv *wdt = dev_get_priv(dev);
81
82 clock_f = clk_get_rate(&wdt->clk);
83 if (IS_ERR_VALUE(clock_f)) {
84 dev_err(dev, "failed to get rate\n");
85 return clock_f;
86 }
87
88 dev_dbg(dev, "%s: CLK %ld\n", __func__, clock_f);
89
90 /* Calculate timeout count */
91 count = timeout * clock_f;
92
93 ret = clk_enable(&wdt->clk);
94 if (ret) {
95 dev_err(dev, "failed to enable clock\n");
96 return ret;
97 }
98
99 /*
100 * Timeout count is half as there are two windows
101 * first window overflow is ignored (interrupt),
102 * reset is only generated at second window overflow
103 */
104 count = count >> 1;
105
106 /* Disable the generic watchdog timer */
107 regmap_read(wdt->regs, XWT_WWCSR_OFFSET, &csr);
108 csr &= ~(XWT_WWCSR_GWEN_MASK);
109 regmap_write(wdt->regs, XWT_WWCSR_OFFSET, csr);
110
111 /* Set compare and offset registers for generic watchdog timeout */
112 regmap_write(wdt->regs, XWT_WWCMP0_OFFSET, (u32)count);
113 regmap_write(wdt->regs, XWT_WWCMP1_OFFSET, 0);
114 regmap_write(wdt->regs, XWT_WWOFF_OFFSET, (u32)count);
115
116 /* Enable the generic watchdog timer */
117 regmap_read(wdt->regs, XWT_WWCSR_OFFSET, &csr);
118 csr |= (XWT_WWCSR_GWEN_MASK);
119 regmap_write(wdt->regs, XWT_WWCSR_OFFSET, csr);
120
121 return 0;
122 }
123
xlnx_wwdt_probe(struct udevice * dev)124 static int xlnx_wwdt_probe(struct udevice *dev)
125 {
126 int ret;
127 struct xlnx_wwdt_plat *plat = dev_get_plat(dev);
128 struct xlnx_wwdt_priv *wdt = dev_get_priv(dev);
129
130 dev_dbg(dev, "%s: Probing wdt%u\n", __func__, dev_seq(dev));
131
132 ret = regmap_init_mem(dev_ofnode(dev), &wdt->regs);
133 if (ret) {
134 dev_dbg(dev, "failed to get regbase of wwdt\n");
135 return ret;
136 }
137
138 wdt->enable_once = plat->enable_once;
139
140 ret = clk_get_by_index(dev, 0, &wdt->clk);
141 if (ret < 0)
142 dev_err(dev, "failed to get clock\n");
143
144 return ret;
145 }
146
xlnx_wwdt_of_to_plat(struct udevice * dev)147 static int xlnx_wwdt_of_to_plat(struct udevice *dev)
148 {
149 struct xlnx_wwdt_plat *plat = dev_get_plat(dev);
150
151 plat->enable_once = dev_read_u32_default(dev, "xlnx,wdt-enable-once",
152 0);
153 dev_dbg(dev, "wdt-enable-once %d\n", plat->enable_once);
154
155 return 0;
156 }
157
158 static const struct wdt_ops xlnx_wwdt_ops = {
159 .start = xlnx_wwdt_start,
160 .reset = xlnx_wwdt_reset,
161 .stop = xlnx_wwdt_stop,
162 };
163
164 static const struct udevice_id xlnx_wwdt_ids[] = {
165 { .compatible = "xlnx,versal-wwdt-1.0", },
166 {},
167 };
168
169 U_BOOT_DRIVER(xlnx_wwdt) = {
170 .name = "xlnx_wwdt",
171 .id = UCLASS_WDT,
172 .of_match = xlnx_wwdt_ids,
173 .probe = xlnx_wwdt_probe,
174 .priv_auto = sizeof(struct xlnx_wwdt_priv),
175 .plat_auto = sizeof(struct xlnx_wwdt_plat),
176 .of_to_plat = xlnx_wwdt_of_to_plat,
177 .ops = &xlnx_wwdt_ops,
178 };
179