blob: 11b30ae85df0142028dc164672a4e84c0d78d400 [file] [log] [blame]
Ashok Reddy Somaf4aecf42020-03-11 03:06:04 -06001// 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
33struct xlnx_wwdt_priv {
34 bool enable_once;
35 struct regmap *regs;
36 struct clk clk;
37};
38
Simon Glassb75b15b2020-12-03 16:55:23 -070039struct xlnx_wwdt_plat {
Ashok Reddy Somaf4aecf42020-03-11 03:06:04 -060040 bool enable_once;
41};
42
43static 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
52static 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
74static 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
Ashok Reddy Somaf4aecf42020-03-11 03:06:04 -060093 ret = clk_enable(&wdt->clk);
Michal Simek41710952021-02-09 15:28:15 +010094 if (ret) {
Ashok Reddy Somaf4aecf42020-03-11 03:06:04 -060095 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
124static int xlnx_wwdt_probe(struct udevice *dev)
125{
126 int ret;
Simon Glassb75b15b2020-12-03 16:55:23 -0700127 struct xlnx_wwdt_plat *plat = dev_get_plat(dev);
Ashok Reddy Somaf4aecf42020-03-11 03:06:04 -0600128 struct xlnx_wwdt_priv *wdt = dev_get_priv(dev);
129
Simon Glass75e534b2020-12-16 21:20:07 -0700130 dev_dbg(dev, "%s: Probing wdt%u\n", __func__, dev_seq(dev));
Ashok Reddy Somaf4aecf42020-03-11 03:06:04 -0600131
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
Simon Glass71fa5b42020-12-03 16:55:18 -0700138 wdt->enable_once = plat->enable_once;
Ashok Reddy Somaf4aecf42020-03-11 03:06:04 -0600139
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
Simon Glassaad29ae2020-12-03 16:55:21 -0700147static int xlnx_wwdt_of_to_plat(struct udevice *dev)
Ashok Reddy Somaf4aecf42020-03-11 03:06:04 -0600148{
Simon Glassb75b15b2020-12-03 16:55:23 -0700149 struct xlnx_wwdt_plat *plat = dev_get_plat(dev);
Ashok Reddy Somaf4aecf42020-03-11 03:06:04 -0600150
Simon Glass71fa5b42020-12-03 16:55:18 -0700151 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);
Ashok Reddy Somaf4aecf42020-03-11 03:06:04 -0600154
155 return 0;
156}
157
158static const struct wdt_ops xlnx_wwdt_ops = {
159 .start = xlnx_wwdt_start,
160 .reset = xlnx_wwdt_reset,
161 .stop = xlnx_wwdt_stop,
162};
163
164static const struct udevice_id xlnx_wwdt_ids[] = {
165 { .compatible = "xlnx,versal-wwdt-1.0", },
166 {},
167};
168
169U_BOOT_DRIVER(xlnx_wwdt) = {
170 .name = "xlnx_wwdt",
171 .id = UCLASS_WDT,
172 .of_match = xlnx_wwdt_ids,
173 .probe = xlnx_wwdt_probe,
Simon Glass8a2b47f2020-12-03 16:55:17 -0700174 .priv_auto = sizeof(struct xlnx_wwdt_priv),
Simon Glassb75b15b2020-12-03 16:55:23 -0700175 .plat_auto = sizeof(struct xlnx_wwdt_plat),
Simon Glassaad29ae2020-12-03 16:55:21 -0700176 .of_to_plat = xlnx_wwdt_of_to_plat,
Ashok Reddy Somaf4aecf42020-03-11 03:06:04 -0600177 .ops = &xlnx_wwdt_ops,
178};