blob: c8e6c60cdd2213fa8bf9d8810e8c1be441829b4c [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>
Ashok Reddy Soma4ec630b2021-08-10 00:16:12 -060017#include <dm/device_compat.h>
Ashok Reddy Somaf4aecf42020-03-11 03:06:04 -060018#include <linux/io.h>
19
20/* Refresh Register Masks */
21#define XWT_WWREF_GWRR_MASK BIT(0) /* Refresh and start new period */
22
23/* Generic Control/Status Register Masks */
24#define XWT_WWCSR_GWEN_MASK BIT(0) /* Enable Bit */
25
26/* Register offsets for the Wdt device */
27#define XWT_WWREF_OFFSET 0x1000 /* Refresh Register */
28#define XWT_WWCSR_OFFSET 0x2000 /* Control/Status Register */
29#define XWT_WWOFF_OFFSET 0x2008 /* Offset Register */
30#define XWT_WWCMP0_OFFSET 0x2010 /* Compare Value Register0 */
31#define XWT_WWCMP1_OFFSET 0x2014 /* Compare Value Register1 */
32#define XWT_WWWRST_OFFSET 0x2FD0 /* Warm Reset Register */
33
34struct xlnx_wwdt_priv {
35 bool enable_once;
36 struct regmap *regs;
37 struct clk clk;
38};
39
Simon Glassb75b15b2020-12-03 16:55:23 -070040struct xlnx_wwdt_plat {
Ashok Reddy Somaf4aecf42020-03-11 03:06:04 -060041 bool enable_once;
42};
43
44static int xlnx_wwdt_reset(struct udevice *dev)
45{
46 struct xlnx_wwdt_priv *wdt = dev_get_priv(dev);
47
48 regmap_write(wdt->regs, XWT_WWREF_OFFSET, XWT_WWREF_GWRR_MASK);
49
50 return 0;
51}
52
53static int xlnx_wwdt_stop(struct udevice *dev)
54{
55 u32 csr;
56 struct xlnx_wwdt_priv *wdt = dev_get_priv(dev);
57
58 if (wdt->enable_once) {
59 dev_warn(dev, "Can't stop Xilinx watchdog.\n");
60 return -EBUSY;
61 }
62
63 /* Disable the generic watchdog timer */
64 regmap_read(wdt->regs, XWT_WWCSR_OFFSET, &csr);
65 csr &= ~(XWT_WWCSR_GWEN_MASK);
66 regmap_write(wdt->regs, XWT_WWCSR_OFFSET, csr);
67
68 clk_disable(&wdt->clk);
69
70 dev_dbg(dev, "Watchdog disabled!\n");
71
72 return 0;
73}
74
75static int xlnx_wwdt_start(struct udevice *dev, u64 timeout, ulong flags)
76{
77 int ret;
78 u32 csr;
79 u64 count;
80 unsigned long clock_f;
81 struct xlnx_wwdt_priv *wdt = dev_get_priv(dev);
82
83 clock_f = clk_get_rate(&wdt->clk);
84 if (IS_ERR_VALUE(clock_f)) {
85 dev_err(dev, "failed to get rate\n");
86 return clock_f;
87 }
88
89 dev_dbg(dev, "%s: CLK %ld\n", __func__, clock_f);
90
91 /* Calculate timeout count */
92 count = timeout * clock_f;
93
Ashok Reddy Somaf4aecf42020-03-11 03:06:04 -060094 ret = clk_enable(&wdt->clk);
Michal Simek41710952021-02-09 15:28:15 +010095 if (ret) {
Ashok Reddy Somaf4aecf42020-03-11 03:06:04 -060096 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 Glassb75b15b2020-12-03 16:55:23 -0700128 struct xlnx_wwdt_plat *plat = dev_get_plat(dev);
Ashok Reddy Somaf4aecf42020-03-11 03:06:04 -0600129 struct xlnx_wwdt_priv *wdt = dev_get_priv(dev);
130
Simon Glass75e534b2020-12-16 21:20:07 -0700131 dev_dbg(dev, "%s: Probing wdt%u\n", __func__, dev_seq(dev));
Ashok Reddy Somaf4aecf42020-03-11 03:06:04 -0600132
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 Glass71fa5b42020-12-03 16:55:18 -0700139 wdt->enable_once = plat->enable_once;
Ashok Reddy Somaf4aecf42020-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 Glassaad29ae2020-12-03 16:55:21 -0700148static int xlnx_wwdt_of_to_plat(struct udevice *dev)
Ashok Reddy Somaf4aecf42020-03-11 03:06:04 -0600149{
Simon Glassb75b15b2020-12-03 16:55:23 -0700150 struct xlnx_wwdt_plat *plat = dev_get_plat(dev);
Ashok Reddy Somaf4aecf42020-03-11 03:06:04 -0600151
Simon Glass71fa5b42020-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 Somaf4aecf42020-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 Glass8a2b47f2020-12-03 16:55:17 -0700175 .priv_auto = sizeof(struct xlnx_wwdt_priv),
Simon Glassb75b15b2020-12-03 16:55:23 -0700176 .plat_auto = sizeof(struct xlnx_wwdt_plat),
Simon Glassaad29ae2020-12-03 16:55:21 -0700177 .of_to_plat = xlnx_wwdt_of_to_plat,
Ashok Reddy Somaf4aecf42020-03-11 03:06:04 -0600178 .ops = &xlnx_wwdt_ops,
179};