blob: 3e5063c716118075d5f62b361b59f13240f677b7 [file] [log] [blame]
Ashok Reddy Soma50283582020-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
39struct xlnx_wwdt_platdata {
40 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
93 /* clk_enable will return -ENOSYS when it is not implemented */
94 ret = clk_enable(&wdt->clk);
95 if (ret && ret != -ENOSYS) {
96 dev_err(dev, "failed to enable clock\n");
97 return ret;
98 }
99
100 /*
101 * Timeout count is half as there are two windows
102 * first window overflow is ignored (interrupt),
103 * reset is only generated at second window overflow
104 */
105 count = count >> 1;
106
107 /* Disable the generic watchdog timer */
108 regmap_read(wdt->regs, XWT_WWCSR_OFFSET, &csr);
109 csr &= ~(XWT_WWCSR_GWEN_MASK);
110 regmap_write(wdt->regs, XWT_WWCSR_OFFSET, csr);
111
112 /* Set compare and offset registers for generic watchdog timeout */
113 regmap_write(wdt->regs, XWT_WWCMP0_OFFSET, (u32)count);
114 regmap_write(wdt->regs, XWT_WWCMP1_OFFSET, 0);
115 regmap_write(wdt->regs, XWT_WWOFF_OFFSET, (u32)count);
116
117 /* Enable the generic watchdog timer */
118 regmap_read(wdt->regs, XWT_WWCSR_OFFSET, &csr);
119 csr |= (XWT_WWCSR_GWEN_MASK);
120 regmap_write(wdt->regs, XWT_WWCSR_OFFSET, csr);
121
122 return 0;
123}
124
125static int xlnx_wwdt_probe(struct udevice *dev)
126{
127 int ret;
Simon Glassc69cda22020-12-03 16:55:20 -0700128 struct xlnx_wwdt_platdata *plat = dev_get_plat(dev);
Ashok Reddy Soma50283582020-03-11 03:06:04 -0600129 struct xlnx_wwdt_priv *wdt = dev_get_priv(dev);
130
131 dev_dbg(dev, "%s: Probing wdt%u\n", __func__, dev->seq);
132
133 ret = regmap_init_mem(dev_ofnode(dev), &wdt->regs);
134 if (ret) {
135 dev_dbg(dev, "failed to get regbase of wwdt\n");
136 return ret;
137 }
138
Simon Glasscaa4daa2020-12-03 16:55:18 -0700139 wdt->enable_once = plat->enable_once;
Ashok Reddy Soma50283582020-03-11 03:06:04 -0600140
141 ret = clk_get_by_index(dev, 0, &wdt->clk);
142 if (ret < 0)
143 dev_err(dev, "failed to get clock\n");
144
145 return ret;
146}
147
Simon Glassd1998a92020-12-03 16:55:21 -0700148static int xlnx_wwdt_of_to_plat(struct udevice *dev)
Ashok Reddy Soma50283582020-03-11 03:06:04 -0600149{
Simon Glassc69cda22020-12-03 16:55:20 -0700150 struct xlnx_wwdt_platdata *plat = dev_get_plat(dev);
Ashok Reddy Soma50283582020-03-11 03:06:04 -0600151
Simon Glasscaa4daa2020-12-03 16:55:18 -0700152 plat->enable_once = dev_read_u32_default(dev, "xlnx,wdt-enable-once",
153 0);
154 dev_dbg(dev, "wdt-enable-once %d\n", plat->enable_once);
Ashok Reddy Soma50283582020-03-11 03:06:04 -0600155
156 return 0;
157}
158
159static const struct wdt_ops xlnx_wwdt_ops = {
160 .start = xlnx_wwdt_start,
161 .reset = xlnx_wwdt_reset,
162 .stop = xlnx_wwdt_stop,
163};
164
165static const struct udevice_id xlnx_wwdt_ids[] = {
166 { .compatible = "xlnx,versal-wwdt-1.0", },
167 {},
168};
169
170U_BOOT_DRIVER(xlnx_wwdt) = {
171 .name = "xlnx_wwdt",
172 .id = UCLASS_WDT,
173 .of_match = xlnx_wwdt_ids,
174 .probe = xlnx_wwdt_probe,
Simon Glass41575d82020-12-03 16:55:17 -0700175 .priv_auto = sizeof(struct xlnx_wwdt_priv),
Simon Glasscaa4daa2020-12-03 16:55:18 -0700176 .plat_auto = sizeof(struct xlnx_wwdt_platdata),
Simon Glassd1998a92020-12-03 16:55:21 -0700177 .of_to_plat = xlnx_wwdt_of_to_plat,
Ashok Reddy Soma50283582020-03-11 03:06:04 -0600178 .ops = &xlnx_wwdt_ops,
179};